diff --git a/src/_apis/gathering/gathering-detail-apis.ts b/src/_apis/gathering/gathering-detail-apis.ts index 3830233a..85b82def 100644 --- a/src/_apis/gathering/gathering-detail-apis.ts +++ b/src/_apis/gathering/gathering-detail-apis.ts @@ -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( @@ -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; + } +} diff --git a/src/_queries/gathering/gathering-detail-queries.ts b/src/_queries/gathering/gathering-detail-queries.ts index 293c2aac..e7e29cc5 100644 --- a/src/_queries/gathering/gathering-detail-queries.ts +++ b/src/_queries/gathering/gathering-detail-queries.ts @@ -1,5 +1,7 @@ -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({ @@ -7,3 +9,21 @@ export function useGetGatheringDetailQuery(crewId: number, gatheringId: number) 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('크루가 생성되었습니다.'); + }, + onError: (error) => { + toast.error(error.message); + }, + }); +} diff --git a/src/app/(crew)/crew/create/page.tsx b/src/app/(crew)/crew/create/page.tsx index 25dcc84d..c1f34671 100644 --- a/src/app/(crew)/crew/create/page.tsx +++ b/src/app/(crew)/crew/create/page.tsx @@ -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'; diff --git a/src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-form/index.tsx b/src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-form/index.tsx index 4028a52d..2e037d86 100644 --- a/src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-form/index.tsx +++ b/src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-form/index.tsx @@ -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'; @@ -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 (
@@ -103,6 +114,7 @@ export default function CreateGatheringForm({ {...field} sample={ImgGatheringSampleUrls} onChange={(newValue) => { + handleFileChange(newValue, field.onChange); field.onChange(newValue); trigger('imageUrl'); }} @@ -162,8 +174,7 @@ export default function CreateGatheringForm({ {...field} fullDate={new Date()} onChange={(date) => { - const formattedDate = date.toLocaleString(); - onChange(formattedDate); + onChange(date); trigger('dateTime'); // 유효성 검사 실행 }} /> diff --git a/src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/container.tsx b/src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/container.tsx index 9ab71265..38919cab 100644 --- a/src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/container.tsx +++ b/src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/container.tsx @@ -1,21 +1,36 @@ '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 = () => { @@ -23,6 +38,13 @@ export default function CreateGatheringModalContainer({ close(); }; + if (isPending) + return ( +
+ +
+ ); + return ( void; - onSubmit: () => void; + onSubmit: (data: CreateGatheringFormTypes) => void; onEdit: () => void; data: CreateGatheringFormTypes; } diff --git a/src/app/(crew)/crew/detail/[id]/_components/create-gathering/index.tsx b/src/app/(crew)/crew/detail/[id]/_components/create-gathering/index.tsx index 53248d9e..6446ed09 100644 --- a/src/app/(crew)/crew/detail/[id]/_components/create-gathering/index.tsx +++ b/src/app/(crew)/crew/detail/[id]/_components/create-gathering/index.tsx @@ -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); @@ -34,7 +34,12 @@ export default function CreateGathering() { - + ); } diff --git a/src/app/(crew)/crew/detail/[id]/edit/page.tsx b/src/app/(crew)/crew/detail/[id]/edit/page.tsx index 78348720..c908e012 100644 --- a/src/app/(crew)/crew/detail/[id]/edit/page.tsx +++ b/src/app/(crew)/crew/detail/[id]/edit/page.tsx @@ -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'; diff --git a/src/app/(crew)/crew/detail/[id]/page.tsx b/src/app/(crew)/crew/detail/[id]/page.tsx index 2753ac93..5437d08b 100644 --- a/src/app/(crew)/crew/detail/[id]/page.tsx +++ b/src/app/(crew)/crew/detail/[id]/page.tsx @@ -23,7 +23,7 @@ export default async function CrewDetailPage({ params }: CrewDetailPageProps) {

크루 약속

- {/* */} +
diff --git a/src/components/common/input/date-time-picker/index.tsx b/src/components/common/input/date-time-picker/index.tsx index 2097a51c..8e96f992 100644 --- a/src/components/common/input/date-time-picker/index.tsx +++ b/src/components/common/input/date-time-picker/index.tsx @@ -22,7 +22,6 @@ export default function DateTimePicker({ fullDate, onChange }: DateTimePickerPro if (isSelected) { setSelected(date); - onChange(date); } };