diff --git a/src/app/meetup/[groupId]/page.tsx b/src/app/meetup/[groupId]/page.tsx index a8601bc4..b2e4d5e2 100644 --- a/src/app/meetup/[groupId]/page.tsx +++ b/src/app/meetup/[groupId]/page.tsx @@ -59,7 +59,7 @@ const MeetupDetailPage = ({ params }: Props) => { isHost, isJoined, isPast, - isAttendDisabled: participantCount >= maxParticipants, + isAttendDisabled: participantCount >= maxParticipants || isPast, }} groupId={groupId} /> diff --git a/src/app/post-meetup/page.tsx b/src/app/post-meetup/page.tsx index 59ac31cc..5e391a57 100644 --- a/src/app/post-meetup/page.tsx +++ b/src/app/post-meetup/page.tsx @@ -2,7 +2,7 @@ import { useRouter } from 'next/navigation'; -import { useForm, useStore } from '@tanstack/react-form'; +import { useForm } from '@tanstack/react-form'; import { MeetupCapField, @@ -15,7 +15,8 @@ import { MeetupTitleField, } from '@/components/pages/post-meetup'; import { useCreateGroup } from '@/hooks/use-group/use-group-create'; -import { CreateGroupPayload, PreUploadGroupImageResponse } from '@/types/service/group'; +import { CreateGroupFormValues, createGroupSchema } from '@/lib/schema/group'; +import { PreUploadGroupImageResponse } from '@/types/service/group'; const PostMeetupPage = () => { const { replace } = useRouter(); @@ -26,15 +27,18 @@ const PostMeetupPage = () => { defaultValues: { title: '', location: '', - locationDetail: '', startTime: '', - endTime: '', tags: [], description: '', maxParticipants: 0, images: [], - } as CreateGroupPayload, + } as CreateGroupFormValues, + validators: { + onSubmit: createGroupSchema, + }, onSubmit: async ({ value }) => { + console.log(value); + const images = [] as PreUploadGroupImageResponse['images']; if (value.images) { @@ -51,10 +55,6 @@ const PostMeetupPage = () => { }, }); - const values = useStore(form.store, (state) => state.values); - - console.log(values); - return (
@@ -74,7 +74,13 @@ const PostMeetupPage = () => { } name='tags' /> - form.handleSubmit()} /> + { + console.log(form.state.fieldMeta.maxParticipants?.isValid); + + form.handleSubmit(); + }} + />
); diff --git a/src/components/pages/meetup/meetup-buttons/index.tsx b/src/components/pages/meetup/meetup-buttons/index.tsx index 7aa073b5..6aa0014c 100644 --- a/src/components/pages/meetup/meetup-buttons/index.tsx +++ b/src/components/pages/meetup/meetup-buttons/index.tsx @@ -49,7 +49,7 @@ export const MeetupButtons = ({ ) : ( + {isInvalid && } ); }; diff --git a/src/components/pages/post-meetup/fields/detail-feild/index.tsx b/src/components/pages/post-meetup/fields/detail-feild/index.tsx index 4e66cc4f..a92f41cc 100644 --- a/src/components/pages/post-meetup/fields/detail-feild/index.tsx +++ b/src/components/pages/post-meetup/fields/detail-feild/index.tsx @@ -2,13 +2,15 @@ import { AnyFieldApi } from '@tanstack/react-form'; -import { Label } from '@/components/ui'; +import { Hint, Label } from '@/components/ui'; interface Props { field: AnyFieldApi; } export const MeetupDetailField = ({ field }: Props) => { + const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; + return (
); }; diff --git a/src/components/pages/post-meetup/fields/location-field/index.tsx b/src/components/pages/post-meetup/fields/location-field/index.tsx index c8505e11..93879c1c 100644 --- a/src/components/pages/post-meetup/fields/location-field/index.tsx +++ b/src/components/pages/post-meetup/fields/location-field/index.tsx @@ -5,7 +5,7 @@ import clsx from 'clsx'; import { Icon } from '@/components/icon'; import { LocationSearchModal } from '@/components/pages/post-meetup/modals/location-search-modal'; -import { Label } from '@/components/ui'; +import { Hint, Label } from '@/components/ui'; import { useModal } from '@/components/ui'; interface Props { @@ -19,6 +19,8 @@ export const MeetupLocationField = ({ field }: Props) => { open(); }; + const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; + return (
); }; diff --git a/src/components/pages/post-meetup/fields/title-field/index.tsx b/src/components/pages/post-meetup/fields/title-field/index.tsx index 8c53ce4e..e490c991 100644 --- a/src/components/pages/post-meetup/fields/title-field/index.tsx +++ b/src/components/pages/post-meetup/fields/title-field/index.tsx @@ -3,13 +3,15 @@ import { AnyFieldApi } from '@tanstack/react-form'; import { Icon } from '@/components/icon'; -import { Input, Label } from '@/components/ui'; +import { Hint, Input, Label } from '@/components/ui'; interface Props { field: AnyFieldApi; } export const MeetupTitleField = ({ field }: Props) => { + const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; + return (
); }; diff --git a/src/lib/schema/group.ts b/src/lib/schema/group.ts new file mode 100644 index 00000000..b395848b --- /dev/null +++ b/src/lib/schema/group.ts @@ -0,0 +1,24 @@ +import { z } from 'zod'; + +export const createGroupSchema = z.object({ + title: z + .string() + .min(2, '제목은 2자 이상 입력해주세요.') + .max(50, '제목은 50자 이내 입력해주세요.'), + location: z.string().nonempty('모임 장소를 입력해주세요.'), + startTime: z.string().nonempty('날짜와 시간을 입력해주세요.'), + tags: z.array(z.string()).optional(), + description: z.string().nonempty('상세 정보를 입력해주세요.'), + maxParticipants: z.number({ error: '' }).min(2, '최대 인원을 입력해주세요.').max(12), + images: z + .array( + z.object({ + sortOrder: z.number(), + imageUrl440x240: z.string(), + imageUrl100x100: z.string(), + }), + ) + .optional(), +}); + +export type CreateGroupFormValues = z.infer;