Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/_apis/gathering/gathering-detail-apis.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fetchApi } from '@/src/utils/api';
import { GatheringDetailType } from '@/src/types/gathering-data';
import { CreateGatheringRequestTypes, GatheringDetailType } from '@/src/types/gathering-data';

// NOTE: 약속 디테일 불러오기
export async function GetGatheringDetail(
Expand Down Expand Up @@ -52,3 +52,18 @@ export async function LeaveGathering(crewId: number, gatheringId: number): Promi
},
});
}

export async function createGathering(id: number, data: CreateGatheringRequestTypes) {
try {
await fetchApi(`/api/crews/${id}/gatherings`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include', // Include authentication credentials
body: JSON.stringify(data),
});
} catch (error) {
throw error;
}
}
Comment on lines +56 to +69
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

API 구현에 대한 몇 가지 개선사항이 있습니다.

다음 사항들을 개선하면 좋을 것 같습니다:

  1. 에러 처리가 너무 일반적입니다. 구체적인 에러 케이스를 처리해주세요.
  2. 함수의 반환 타입이 명시되어 있지 않습니다.
  3. id 매개변수의 이름이 모호합니다. crewId로 변경하면 좋겠습니다.
  4. 입력값 유효성 검사가 없습니다.

다음과 같이 수정하는 것을 제안드립니다:

-export async function createGathering(id: number, data: CreateGatheringRequestTypes) {
+export async function createGathering(
+  crewId: number,
+  data: CreateGatheringRequestTypes
+): Promise<void> {
+  if (!crewId || crewId <= 0) {
+    throw new Error('유효하지 않은 크루 ID입니다.');
+  }
+
   try {
-    await fetchApi(`/api/crews/${id}/gatherings`, {
+    await fetchApi(`/api/crews/${crewId}/gatherings`, {
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
       },
       credentials: 'include',
       body: JSON.stringify(data),
     });
   } catch (error) {
-    throw error;
+    if (error instanceof Error) {
+      throw new Error(`모임 생성 중 오류 발생: ${error.message}`);
+    }
+    throw new Error('알 수 없는 오류가 발생했습니다.');
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function createGathering(id: number, data: CreateGatheringRequestTypes) {
try {
await fetchApi(`/api/crews/${id}/gatherings`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include', // Include authentication credentials
body: JSON.stringify(data),
});
} catch (error) {
throw error;
}
}
export async function createGathering(
crewId: number,
data: CreateGatheringRequestTypes
): Promise<void> {
if (!crewId || crewId <= 0) {
throw new Error('유효하지 않은 크루 ID입니다.');
}
try {
await fetchApi(`/api/crews/${crewId}/gatherings`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify(data),
});
} catch (error) {
if (error instanceof Error) {
throw new Error(`모임 생성 중 오류 발생: ${error.message}`);
}
throw new Error('알 수 없는 오류가 발생했습니다.');
}
}

24 changes: 22 additions & 2 deletions src/_queries/gathering/gathering-detail-queries.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
import { useQuery } from '@tanstack/react-query';
import { GetGatheringDetail } from '@/src/_apis/gathering/gathering-detail-apis';
import { toast } from 'react-toastify';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { GetGatheringDetail, createGathering } from '@/src/_apis/gathering/gathering-detail-apis';
import { CreateGatheringRequestTypes } from '@/src/types/gathering-data';

export function useGetGatheringDetailQuery(crewId: number, gatheringId: number) {
return useQuery({
queryKey: ['gatheringDetail', crewId, gatheringId],
queryFn: () => GetGatheringDetail(crewId, gatheringId),
});
}

export function useCreateGatheringQuery(crewId: number) {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (data: CreateGatheringRequestTypes) => createGathering(crewId, data),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['gatheringList', crewId],
refetchType: 'all',
});
toast.success('크루가 생성되었습니다.');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

성공 메시지의 용어를 수정해야 합니다.

성공 메시지에서 "크루가 생성되었습니다"라고 되어 있는데, PR의 목적에 따르면 이는 "모임"을 생성하는 기능입니다.

다음과 같이 수정하는 것을 제안드립니다:

-      toast.success('크루가 생성되었습니다.');
+      toast.success('모임이 생성되었습니다.');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
toast.success('크루가 생성되었습니다.');
toast.success('모임이 생성되었습니다.');

},
onError: (error) => {
toast.error(error.message);
},
});
}
1 change: 0 additions & 1 deletion src/app/(crew)/crew/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import Image from 'next/image';
import { Loader } from '@mantine/core';
import { getImageUrl } from '@/src/_apis/image/get-image-url';
import { useCreateCrewQuery } from '@/src/_queries/crew/crew-detail-queries';
import CreateCrewForm from '@/src/app/(crew)/crew/create/_components/create-crew-form';
import { CreateCrewFormTypes, CreateCrewRequestTypes } from '@/src/types/create-crew';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { Controller, useForm, useWatch } from 'react-hook-form';
import { NumberInput } from '@mantine/core';
import { getImageUrl } from '@/src/_apis/image/get-image-url';
import Button from '@/src/components/common/input/button';
import DateTimePicker from '@/src/components/common/input/date-time-picker';
import FileInputWrap from '@/src/components/common/input/file-input-wrap';
Expand Down Expand Up @@ -38,6 +39,16 @@ export default function CreateGatheringForm({
const location = useWatch({ control, name: 'location' });
const introduce = useWatch({ control, name: 'introduce' });

const handleFileChange = async (
file: File | string | null,
onChange: (value: string | File) => void,
) => {
if (file instanceof File) {
const imgResponse = await getImageUrl(file, 'CREW');
onChange(imgResponse?.imageUrl || '');
}
};

return (
<form onSubmit={isEdit ? handleSubmit(onEdit) : handleSubmit(onSubmit)}>
<div className="flex flex-col gap-6">
Expand Down Expand Up @@ -103,6 +114,7 @@ export default function CreateGatheringForm({
{...field}
sample={ImgGatheringSampleUrls}
onChange={(newValue) => {
handleFileChange(newValue, field.onChange);
field.onChange(newValue);
trigger('imageUrl');
Comment on lines +117 to 119
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

onChange 핸들러의 중복 호출 문제를 수정해주세요.

현재 코드에서는 handleFileChangefield.onChange가 연속적으로 호출되어 잠재적인 상태 불일치 문제가 발생할 수 있습니다.

다음과 같이 수정하는 것을 추천드립니다:

-handleFileChange(newValue, field.onChange);
-field.onChange(newValue);
-trigger('imageUrl');
+handleFileChange(newValue, (value) => {
+  field.onChange(value);
+  trigger('imageUrl');
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
handleFileChange(newValue, field.onChange);
field.onChange(newValue);
trigger('imageUrl');
handleFileChange(newValue, (value) => {
field.onChange(value);
trigger('imageUrl');
});

}}
Expand Down Expand Up @@ -162,8 +174,7 @@ export default function CreateGatheringForm({
{...field}
fullDate={new Date()}
onChange={(date) => {
const formattedDate = date.toLocaleString();
onChange(formattedDate);
onChange(date);
trigger('dateTime'); // 유효성 검사 실행
}}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,50 @@
'use client';

import { CreateGatheringFormTypes } from '@/src/types/gathering-data';
import { Loader } from '@mantine/core';
import { useCreateGatheringQuery } from '@/src/_queries/gathering/gathering-detail-queries';
import { CreateGatheringFormTypes, CreateGatheringRequestTypes } from '@/src/types/gathering-data';
import CreateGatheringModalPresenter from './presenter';

export interface CreateGatheringModalContainerProps {
crewId: number;
opened: boolean;
close: () => void;
data: CreateGatheringFormTypes;
}

export default function CreateGatheringModalContainer({
crewId,
opened,
close,
data,
}: CreateGatheringModalContainerProps) {
const handleSubmit = () => {
// TODO : 약속 만들기 API 연결
const { isPending, mutate } = useCreateGatheringQuery(crewId);

const handleSubmit = async (createdData: CreateGatheringFormTypes) => {
const newData: CreateGatheringRequestTypes = {
title: createdData.title,
imageUrl: (createdData.imageUrl as string) ?? '',
dateTime: createdData.dateTime,
location: createdData.location,
totalCount: createdData.totalCount,
introduce: createdData.introduce,
};

mutate(newData);
close();
};
const handleEdit = () => {
// TODO : 약속 수정하기 API 연결
close();
};

if (isPending)
return (
<div className="fixed inset-0 z-10 flex items-center justify-center">
<Loader size="sm" />
</div>
);

return (
<CreateGatheringModalPresenter
data={data}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CreateGatheringFormTypes } from '@/src/types/gathering-data';
export interface GatheringDetailModalProps {
opened: boolean;
onClose: () => void;
onSubmit: () => void;
onSubmit: (data: CreateGatheringFormTypes) => void;
onEdit: () => void;
data: CreateGatheringFormTypes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import CreateGatheringModalContainer from '@/src/app/(crew)/crew/detail/[id]/_co
import Button from '@/src/components/common/input/button';
import { CreateGatheringFormTypes } from '@/src/types/gathering-data';

export default function CreateGathering() {
export default function CreateGathering({ crewId }: { crewId: number }) {
const { isAuth } = useAuthStore();
const router = useRouter();
const [opened, { open, close }] = useDisclosure(false);
Expand All @@ -34,7 +34,12 @@ export default function CreateGathering() {
<Button type="button" className="btn-filled px-4" onClick={handleButtonClick}>
약속 만들기
</Button>
<CreateGatheringModalContainer opened={opened} close={close} data={initialValue} />
<CreateGatheringModalContainer
crewId={crewId}
opened={opened}
close={close}
data={initialValue}
/>
</>
);
}
1 change: 0 additions & 1 deletion src/app/(crew)/crew/detail/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import Image from 'next/image';
import { useParams } from 'next/navigation';
import { Loader } from '@mantine/core';
import { getImageUrl } from '@/src/_apis/image/get-image-url';
import { useEditCrewQuery, useGetCrewDetailQuery } from '@/src/_queries/crew/crew-detail-queries';
import CreateCrewForm from '@/src/app/(crew)/crew/create/_components/create-crew-form';
import { CreateCrewFormTypes, EditCrewRequestTypes } from '@/src/types/create-crew';
Expand Down
2 changes: 1 addition & 1 deletion src/app/(crew)/crew/detail/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default async function CrewDetailPage({ params }: CrewDetailPageProps) {
<article className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-semibold">크루 약속</h2>
{/* <CreateGathering /> */}
<CreateGathering crewId={Number(params.id)} />
</div>
<div className="flex w-full">
<GatheringListSection id={id} />
Expand Down
1 change: 0 additions & 1 deletion src/components/common/input/date-time-picker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export default function DateTimePicker({ fullDate, onChange }: DateTimePickerPro

if (isSelected) {
setSelected(date);
onChange(date);
}
};

Expand Down
Loading