From 31a6d8408d65123defd77dc1ee6e07efb7e72923 Mon Sep 17 00:00:00 2001 From: harry kim <73218463+hanyugeon@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:54:57 +0900 Subject: [PATCH] =?UTF-8?q?[#553]=20[=EB=AA=A8=EC=9E=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1]=20=EB=AA=A8=EC=9E=84=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=ED=8D=BC=EB=84=90=20(#558)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: RadioButton 컴포넌트 수정 -value와 label props 분리 * feat: 모임 상세 퍼널 스텝 작성 * feat: 모임 상세 퍼널 스텝 스토리 작성 * refactor: 라디오 옵션 constants로 분리 * refactor: isMaxMemberCountCustom 변수명 수정 - isCustomMemberCount로 수정 * chore: 오탈자 수정 * refactor: 코드리뷰 반영 --- src/constants/groupRadioValues.ts | 9 + .../create/funnel/SetUpDetailStep.stories.tsx | 38 ++- src/v1/base/RadioButton.tsx | 12 +- .../create/funnel/SetUpDetailStep.tsx | 298 +++++++++++++++--- 4 files changed, 301 insertions(+), 56 deletions(-) diff --git a/src/constants/groupRadioValues.ts b/src/constants/groupRadioValues.ts index b49e44caa..264d931c3 100644 --- a/src/constants/groupRadioValues.ts +++ b/src/constants/groupRadioValues.ts @@ -40,3 +40,12 @@ export const HAS_JOIN_PASSWORD_VALUE = [ ]; export const HAS_JOIN_PASSWORD_DEFAULT_VALUE = 'false'; + +export const MAX_MEMBER_COUNT_OPTIONS = [ + { label: '제한없음', value: 9999 }, + { label: '50명', value: 50 }, + { label: '100명', value: 100 }, + { label: '200명', value: 200 }, + { label: '500명', value: 500 }, + { label: '직접 입력', value: 'custom' }, +] as const; diff --git a/src/stories/bookGroup/create/funnel/SetUpDetailStep.stories.tsx b/src/stories/bookGroup/create/funnel/SetUpDetailStep.stories.tsx index 2d18eb57f..007ecf142 100644 --- a/src/stories/bookGroup/create/funnel/SetUpDetailStep.stories.tsx +++ b/src/stories/bookGroup/create/funnel/SetUpDetailStep.stories.tsx @@ -1,16 +1,50 @@ +import { FormProvider, useForm } from 'react-hook-form'; +import type { APICreateGroup } from '@/types/group'; +import { getTodayDate } from '@/utils/date'; + import { SetUpDetailStep } from '@/v1/bookGroup/create/funnel'; import { Meta, StoryObj } from '@storybook/react'; +import { appLayoutMeta } from '@/stories/meta'; const meta: Meta = { title: 'bookGroup/funnel/SetUpDetailStep', component: SetUpDetailStep, - tags: ['autodocs'], + ...appLayoutMeta, }; export default meta; type Story = StoryObj; +interface FunnelFormValues extends APICreateGroup { + customMemberCount: string | number; +} + +const SetUpDetailForm = () => { + const methods = useForm({ + mode: 'all', + defaultValues: { + bookId: 23, + title: '', + introduce: '', + maxMemberCount: 9999, + startDate: getTodayDate(), + endDate: '', + isPublic: false, + hasJoinPasswd: false, + joinQuestion: '', + joinPasswd: '', + }, + }); + + return ( + +
+ + +
+ ); +}; export const Default: Story = { - args: {}, + render: () => , }; diff --git a/src/v1/base/RadioButton.tsx b/src/v1/base/RadioButton.tsx index 2072b61d6..219ab84ee 100644 --- a/src/v1/base/RadioButton.tsx +++ b/src/v1/base/RadioButton.tsx @@ -1,14 +1,14 @@ import { ComponentPropsWithoutRef, forwardRef, Ref } from 'react'; +type RadioButtonProps = { + label?: string; +} & Omit, 'id' | 'type' | 'className'>; + const BASE_RADIO_BUTTON_CLASSES = 'px-[1.2rem] py-[0.5rem] bg-main-600/[0.18] text-main-900 text-sm font-normal rounded-[2rem] cursor-pointer w-full h-full peer-checked:bg-main-900 peer-checked:text-white'; const RadioButton = ( - { - name, - value, - ...props - }: Omit, 'id' | 'type' | 'className'>, + { name, value, label, ...props }: RadioButtonProps, ref: Ref ) => { return ( @@ -22,7 +22,7 @@ const RadioButton = ( ref={ref} {...props} /> - {value} + {label ?? value} ); }; diff --git a/src/v1/bookGroup/create/funnel/SetUpDetailStep.tsx b/src/v1/bookGroup/create/funnel/SetUpDetailStep.tsx index 9d3b8780a..483fda81e 100644 --- a/src/v1/bookGroup/create/funnel/SetUpDetailStep.tsx +++ b/src/v1/bookGroup/create/funnel/SetUpDetailStep.tsx @@ -1,3 +1,11 @@ +import { useFormContext } from 'react-hook-form'; + +import type { APICreateGroup } from '@/types/group'; + +import { getTodayDate } from '@/utils/date'; +import { MAX_MEMBER_COUNT_OPTIONS } from '@/constants'; + +import BottomActionButton from '@/v1/base/BottomActionButton'; import DatePicker from '@/v1/base/DatePicker'; import ErrorMessage from '@/v1/base/ErrorMessage'; import Input from '@/v1/base/Input'; @@ -7,65 +15,259 @@ import Switch from '@/v1/base/Switch'; import TextArea from '@/v1/base/TextArea'; import BookInfoCard from '../../BookInfoCard'; -const SetUpDetailStep = () => { +interface MoveFunnelStepProps { + onPrevStep?: () => void; + onNextStep?: () => void; + onSubmit?: () => void; +} + +interface SetUpDetailStepValues + extends Pick< + APICreateGroup, + 'bookId' | 'title' | 'introduce' | 'startDate' | 'endDate' | 'isPublic' + > { + maxMemberCount: string; + customMemberCount: string; +} + +const SetUpDetailStep = ({ + // FIXME: goToSelectBookStep, + onNextStep, +}: MoveFunnelStepProps) => { + const { handleSubmit, getValues } = useFormContext(); + return ( -
-
- -
- - {true && {'마크업용 에러 표시'}} -
-
+
+ + -
- + +
+ +
-
-

활동 내용

- <> - +
+ ); +}; + +const MaxMemberCountField = ({ name }: SetUpDetailFieldProps) => { + const { + register, + formState: { errors }, + } = useFormContext(); + + const maxMemberCountErrors = errors[name]; + + return ( + <> +

최대 인원

+
+ {MAX_MEMBER_COUNT_OPTIONS.map(option => ( + - -
+ ))} +
+ {maxMemberCountErrors?.message} + + ); +}; -
-

최대 인원

-
- - - - - - +const CustomMemberCountField = ({ name }: SetUpDetailFieldProps) => { + const { + register, + watch, + formState: { errors }, + } = useFormContext(); + + const customMemberCountErrors = errors[name]; + const isCustomInputCount = watch('maxMemberCount') === 'custom'; + + return ( + <> + {isCustomInputCount && ( +
+ + {customMemberCountErrors?.message}
- {false && } -
+ )} + + ); +}; + +const PickStartDateField = ({ name }: SetUpDetailFieldProps) => { + const { + register, + formState: { errors }, + } = useFormContext(); -
+ const startDateErrors = errors[name]; + + return ( +
+

모임 시작일

- -
+ + + {startDateErrors?.message} +
+ ); +}; -
-

모임 종료일

- -
+const PickEndDateField = ({ name }: SetUpDetailFieldProps) => { + const { + register, + watch, + formState: { errors }, + } = useFormContext(); -
-
-

댓글 공개 여부

-

- 모임에 가입하지 않은 사람도 댓글을 볼 수 있어요. -

-
- -
- + const endDateErrors = errors[name]; + + return ( +
+
+

모임 종료일

+ +
+ {endDateErrors?.message} +
); }; -export default SetUpDetailStep; +const SwitchIsPublicField = ({ name }: SetUpDetailFieldProps) => { + const { register } = useFormContext(); + + return ( +
+
+

댓글 공개 여부

+

+ 모임에 가입하지 않은 사람도 댓글을 볼 수 있어요. +

+
+ +
+ ); +};