From a9975f8186b6171c2a3a7d7f5cda60faf47b355e Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Wed, 22 May 2024 02:05:17 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20useFunnel=EB=A1=9C=20step=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/Auth/ProgressBar/index.tsx | 8 +- src/page/ShopRegistration/index.tsx | 8 +- .../view/Mobile/Main/index.tsx | 6 +- .../view/Mobile/ShopCategory/index.tsx | 6 +- .../view/Mobile/ShopConfirmation/index.tsx | 15 ++- .../view/Mobile/ShopEntry/index.tsx | 6 +- .../view/Mobile/Sub/index.tsx | 6 +- .../ShopRegistration/view/Mobile/index.tsx | 126 ++++++++++++------ src/page/ShopRegistration/view/PC/index.tsx | 2 +- src/utils/constant/progress.ts | 4 + src/utils/hooks/useFunnel.ts | 30 +++++ 11 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 src/utils/hooks/useFunnel.ts diff --git a/src/component/common/Auth/ProgressBar/index.tsx b/src/component/common/Auth/ProgressBar/index.tsx index f1d07ab4..c4a6b6ef 100644 --- a/src/component/common/Auth/ProgressBar/index.tsx +++ b/src/component/common/Auth/ProgressBar/index.tsx @@ -3,17 +3,17 @@ import styles from './ProgressBar.module.scss'; interface ProgressBarProps { step: number; total: number; - progressTitle: { step: number; title: string }[]; + progressTitle: string; } export default function ProgressBar({ step, total, progressTitle }: ProgressBarProps) { return (
- {`${progressTitle[step].step}. ${progressTitle[step].title}`} - {`${progressTitle[step].step} / ${total}`} + {`${step}. ${progressTitle}`} + {`${step} / ${total}`}
- +
); } diff --git a/src/page/ShopRegistration/index.tsx b/src/page/ShopRegistration/index.tsx index 40e55ecb..2b95d535 100644 --- a/src/page/ShopRegistration/index.tsx +++ b/src/page/ShopRegistration/index.tsx @@ -1,13 +1,7 @@ -import useMediaQuery from 'utils/hooks/useMediaQuery'; import ShopRegistrationMobile from './view/Mobile'; -import ShopRegistrationPC from './view/PC'; export default function ShopRegistration() { - const { isMobile } = useMediaQuery(); - return ( -
- {isMobile ? : } -
+ ); } diff --git a/src/page/ShopRegistration/view/Mobile/Main/index.tsx b/src/page/ShopRegistration/view/Mobile/Main/index.tsx index 73b5d608..438de38f 100644 --- a/src/page/ShopRegistration/view/Mobile/Main/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/Main/index.tsx @@ -1,6 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { ReactComponent as EmptyImgIcon } from 'assets/svg/shopRegistration/mobile-empty-img.svg'; -import useStepStore from 'store/useStepStore'; import useShopRegistrationStore from 'store/shopRegistration'; import { useEffect, useState } from 'react'; import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; @@ -9,9 +8,8 @@ import cn from 'utils/ts/className'; import useImagesUpload from 'utils/hooks/useImagesUpload'; import styles from './Main.module.scss'; -export default function Main() { +export default function Main({ onNext }:{ onNext: () => void }) { const [isError, setIsError] = useState(false); - const { increaseStep } = useStepStore(); const { imageFile, imgRef, saveImgFile, uploadError, } = useImagesUpload(); @@ -24,7 +22,7 @@ export default function Main() { setIsError(true); } else { setIsError(false); - increaseStep(); + onNext(); } }; diff --git a/src/page/ShopRegistration/view/Mobile/ShopCategory/index.tsx b/src/page/ShopRegistration/view/Mobile/ShopCategory/index.tsx index ddb93bee..71618c79 100644 --- a/src/page/ShopRegistration/view/Mobile/ShopCategory/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/ShopCategory/index.tsx @@ -1,4 +1,3 @@ -import useStepStore from 'store/useStepStore'; import useMyShop from 'query/shop'; import cn from 'utils/ts/className'; import { Category as CategoryProps } from 'model/category/shopCategory'; @@ -8,10 +7,9 @@ import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; import styles from './ShopCategory.module.scss'; -export default function ShopCategory() { +export default function ShopCategory({ onNext }:{ onNext: () => void }) { const [isError, setIsError] = useState(false); const { categoryList } = useMyShop(); - const { increaseStep } = useStepStore(); const { category, setCategory, setCategoryId, } = useShopRegistrationStore(); @@ -26,7 +24,7 @@ export default function ShopCategory() { setIsError(true); } else { setIsError(false); - increaseStep(); + onNext(); } }; diff --git a/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx b/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx index ba4d2f84..6218ebfc 100644 --- a/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx @@ -15,12 +15,20 @@ import { isKoinError } from '@bcsdlab/koin'; import showToast from 'utils/ts/showToast'; import styles from './ShopConfirmation.module.scss'; -export const usePostData = (setStep: (step: number) => void) => { +interface UsePostDataProps { + onNext?:() => void +} + +export const usePostData = ({ onNext } : UsePostDataProps) => { const queryClient = useQueryClient(); + const { setStep } = useStepStore(); const mutation = useMutation({ mutationFn: (form: OwnerShop) => postShop(form), onSuccess: () => { setStep(5); + if (onNext) { + onNext(); + } queryClient.refetchQueries(); }, onError: (e) => { @@ -32,8 +40,7 @@ export const usePostData = (setStep: (step: number) => void) => { return mutation; }; -export default function ShopConfirmation() { - const { setStep } = useStepStore(); +export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { const { category, categoryId, @@ -54,7 +61,7 @@ export default function ShopConfirmation() { resolver: zodResolver(OwnerShop), }); - const mutation = usePostData(setStep); + const mutation = usePostData({ onNext }); const onSubmit: SubmitHandler = (data) => { mutation.mutate(data); diff --git a/src/page/ShopRegistration/view/Mobile/ShopEntry/index.tsx b/src/page/ShopRegistration/view/Mobile/ShopEntry/index.tsx index 2dab6b92..7818ac21 100644 --- a/src/page/ShopRegistration/view/Mobile/ShopEntry/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/ShopEntry/index.tsx @@ -1,9 +1,7 @@ import { ReactComponent as Memo } from 'assets/svg/shopRegistration/memo.svg'; -import useStepStore from 'store/useStepStore'; import styles from './ShopEntry.module.scss'; -export default function ShopEntry() { - const { increaseStep } = useStepStore(); +export default function ShopEntry({ onNext }:{ onNext: () => void }) { return (
@@ -15,7 +13,7 @@ export default function ShopEntry() { 학생들에게 최신 가게 정보를 알려주세요. - +
); diff --git a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx index 3f855e71..fe3feb60 100644 --- a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx @@ -1,7 +1,6 @@ /* eslint-disable jsx-a11y/label-has-associated-control */ import OperateTimeMobile from 'page/ShopRegistration/component/Modal/OperateTimeMobile'; import useBooleanState from 'utils/hooks/useBooleanState'; -import useStepStore from 'store/useStepStore'; import useShopRegistrationStore from 'store/shopRegistration'; import useOperateTimeState from 'page/ShopRegistration/hooks/useOperateTimeState'; import CheckSameTime from 'page/ShopRegistration/hooks/CheckSameTime'; @@ -13,8 +12,7 @@ import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; import cn from 'utils/ts/className'; import styles from './Sub.module.scss'; -export default function Sub() { - const { increaseStep } = useStepStore(); +export default function Sub({ onNext }:{ onNext: () => void }) { const { value: showOperateTime, setTrue: openOperateTime, @@ -57,7 +55,7 @@ export default function Sub() { setIsError(true); } else { setIsError(false); - increaseStep(); + onNext(); } }; diff --git a/src/page/ShopRegistration/view/Mobile/index.tsx b/src/page/ShopRegistration/view/Mobile/index.tsx index aed91226..68b96615 100644 --- a/src/page/ShopRegistration/view/Mobile/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/index.tsx @@ -1,57 +1,107 @@ -/* eslint-disable jsx-a11y/label-has-associated-control */ import PreviousStep from 'component/common/Auth/PreviousStep'; import ProgressBar from 'component/common/Auth/ProgressBar'; import Complete from 'component/common/Auth/Complete'; import SubTitle from 'component/common/Auth/SubTitle'; -import useStepStore from 'store/useStepStore'; import PROGRESS_TITLE from 'utils/constant/progress'; import ShopEntry from 'page/ShopRegistration/view/Mobile/ShopEntry'; import ShopCategory from 'page/ShopRegistration/view/Mobile/ShopCategory'; import Main from 'page/ShopRegistration/view/Mobile/Main'; import Sub from 'page/ShopRegistration/view/Mobile/Sub'; import ShopConfirmation from 'page/ShopRegistration/view/Mobile/ShopConfirmation'; +import { useFunnel } from 'utils/hooks/useFunnel'; import styles from './ShopRegistrationMobile.module.scss'; export default function ShopRegistrationMobile() { - const { TOTAL_STEP, step, decreaseStep } = useStepStore(); - // 임시로 step 0 일때 뒤로가기 버튼 삭제 + const { + Funnel, Step, setStep, currentStep, + } = useFunnel('가게 등록'); + + const currentIndex = PROGRESS_TITLE.findIndex((step) => step.title === currentStep); + + const decreaseStep = () => { + if (currentIndex > 0) { + setStep(PROGRESS_TITLE[currentIndex - 1].title); + } + }; + return (
- {step !== 0 && } + {currentStep !== '가게 등록' && }
- {step === 0 && } - {step === 1 && ( - <> - - - - - )} - {step === 2 && ( - <> - - -
- - )} - {step === 3 && ( - <> - - - - - )} - {step === 4 && ( - <> - -
- - - - )} - {step === 5 && ( - - )} + + + setStep('가게 카테고리 설정')} /> + + + <> + + + setStep('메인 정보 입력')} /> + + + + <> + + +
setStep('세부 정보 입력')} /> + + + + <> + + + setStep('가게 정보 확인')} /> + + + + <> + +
+ + setStep('가게 등록 완료')} /> + + + + + +
); diff --git a/src/page/ShopRegistration/view/PC/index.tsx b/src/page/ShopRegistration/view/PC/index.tsx index cee89e98..7c0dfd34 100644 --- a/src/page/ShopRegistration/view/PC/index.tsx +++ b/src/page/ShopRegistration/view/PC/index.tsx @@ -100,7 +100,7 @@ export default function ShopRegistrationPC() { isAllClosed, } = CheckSameTime(); - const mutation = usePostData(setStep); + const mutation = usePostData({}); const { register, handleSubmit, setValue, formState: { errors }, diff --git a/src/utils/constant/progress.ts b/src/utils/constant/progress.ts index 78a43bc4..29bc9dd9 100644 --- a/src/utils/constant/progress.ts +++ b/src/utils/constant/progress.ts @@ -1,4 +1,8 @@ const PROGRESS_TITLE = [ + { + step: 0, + title: '가게 등록', + }, { step: 1, title: '가게 카테고리 설정', diff --git a/src/utils/hooks/useFunnel.ts b/src/utils/hooks/useFunnel.ts new file mode 100644 index 00000000..f6dc7efa --- /dev/null +++ b/src/utils/hooks/useFunnel.ts @@ -0,0 +1,30 @@ +import { ReactElement, ReactNode, useState } from 'react'; + +export interface StepProps { + name: string; + children: ReactNode; +} + +export interface FunnelProps { + children: ReactElement[]; +} + +export const useFunnel = (defaultStep: string) => { + const [step, setStep] = useState(defaultStep); + + const Step = (props: StepProps): ReactElement | any => props.children; + + function Funnel({ children }: FunnelProps): ReactElement | null { + const targetStep = children.find((child) => child.props.name === step); + + if (!targetStep) { + return null; + } + + return targetStep; + } + + return { + Funnel, Step, setStep, currentStep: step, + } as const; +}; From 436fe940e27d9fa65aaf517eed3f32dd8b6d1b39 Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Thu, 30 May 2024 01:48:46 +0900 Subject: [PATCH 02/14] =?UTF-8?q?fix:=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=ED=8F=B4=EB=8D=94=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/{common => }/Auth/Complete/Complete.module.scss | 0 src/component/{common => }/Auth/Complete/index.tsx | 0 .../{common => }/Auth/PreviousStep/PreviousStep.module.scss | 0 src/component/{common => }/Auth/PreviousStep/index.tsx | 0 .../{common => }/Auth/ProgressBar/ProgressBar.module.scss | 0 src/component/{common => }/Auth/ProgressBar/index.tsx | 0 src/component/{common => }/Auth/SubTitle/SubTitle.module.scss | 0 src/component/{common => }/Auth/SubTitle/index.tsx | 0 .../common}/ErrorMessage/ErrorMessage.module.scss | 0 .../component => component/common}/ErrorMessage/index.tsx | 0 src/page/AddMenu/components/AddMenuImgModal/index.tsx | 2 +- src/page/AddMenu/components/MenuImage/index.tsx | 2 +- src/page/Auth/Signup/component/UserEmail/index.tsx | 2 +- src/page/Auth/Signup/component/UserId/index.tsx | 2 +- src/page/Auth/Signup/component/UserPassword/index.tsx | 2 +- src/page/Auth/Signup/index.tsx | 2 +- src/page/Auth/Signup/view/OwnerDataPage/index.tsx | 2 +- src/page/ShopRegistration/component/ConfirmPopup/index.tsx | 2 +- src/page/ShopRegistration/view/PC/index.tsx | 4 ++-- 19 files changed, 10 insertions(+), 10 deletions(-) rename src/component/{common => }/Auth/Complete/Complete.module.scss (100%) rename src/component/{common => }/Auth/Complete/index.tsx (100%) rename src/component/{common => }/Auth/PreviousStep/PreviousStep.module.scss (100%) rename src/component/{common => }/Auth/PreviousStep/index.tsx (100%) rename src/component/{common => }/Auth/ProgressBar/ProgressBar.module.scss (100%) rename src/component/{common => }/Auth/ProgressBar/index.tsx (100%) rename src/component/{common => }/Auth/SubTitle/SubTitle.module.scss (100%) rename src/component/{common => }/Auth/SubTitle/index.tsx (100%) rename src/{page/Auth/Signup/component => component/common}/ErrorMessage/ErrorMessage.module.scss (100%) rename src/{page/Auth/Signup/component => component/common}/ErrorMessage/index.tsx (100%) diff --git a/src/component/common/Auth/Complete/Complete.module.scss b/src/component/Auth/Complete/Complete.module.scss similarity index 100% rename from src/component/common/Auth/Complete/Complete.module.scss rename to src/component/Auth/Complete/Complete.module.scss diff --git a/src/component/common/Auth/Complete/index.tsx b/src/component/Auth/Complete/index.tsx similarity index 100% rename from src/component/common/Auth/Complete/index.tsx rename to src/component/Auth/Complete/index.tsx diff --git a/src/component/common/Auth/PreviousStep/PreviousStep.module.scss b/src/component/Auth/PreviousStep/PreviousStep.module.scss similarity index 100% rename from src/component/common/Auth/PreviousStep/PreviousStep.module.scss rename to src/component/Auth/PreviousStep/PreviousStep.module.scss diff --git a/src/component/common/Auth/PreviousStep/index.tsx b/src/component/Auth/PreviousStep/index.tsx similarity index 100% rename from src/component/common/Auth/PreviousStep/index.tsx rename to src/component/Auth/PreviousStep/index.tsx diff --git a/src/component/common/Auth/ProgressBar/ProgressBar.module.scss b/src/component/Auth/ProgressBar/ProgressBar.module.scss similarity index 100% rename from src/component/common/Auth/ProgressBar/ProgressBar.module.scss rename to src/component/Auth/ProgressBar/ProgressBar.module.scss diff --git a/src/component/common/Auth/ProgressBar/index.tsx b/src/component/Auth/ProgressBar/index.tsx similarity index 100% rename from src/component/common/Auth/ProgressBar/index.tsx rename to src/component/Auth/ProgressBar/index.tsx diff --git a/src/component/common/Auth/SubTitle/SubTitle.module.scss b/src/component/Auth/SubTitle/SubTitle.module.scss similarity index 100% rename from src/component/common/Auth/SubTitle/SubTitle.module.scss rename to src/component/Auth/SubTitle/SubTitle.module.scss diff --git a/src/component/common/Auth/SubTitle/index.tsx b/src/component/Auth/SubTitle/index.tsx similarity index 100% rename from src/component/common/Auth/SubTitle/index.tsx rename to src/component/Auth/SubTitle/index.tsx diff --git a/src/page/Auth/Signup/component/ErrorMessage/ErrorMessage.module.scss b/src/component/common/ErrorMessage/ErrorMessage.module.scss similarity index 100% rename from src/page/Auth/Signup/component/ErrorMessage/ErrorMessage.module.scss rename to src/component/common/ErrorMessage/ErrorMessage.module.scss diff --git a/src/page/Auth/Signup/component/ErrorMessage/index.tsx b/src/component/common/ErrorMessage/index.tsx similarity index 100% rename from src/page/Auth/Signup/component/ErrorMessage/index.tsx rename to src/component/common/ErrorMessage/index.tsx diff --git a/src/page/AddMenu/components/AddMenuImgModal/index.tsx b/src/page/AddMenu/components/AddMenuImgModal/index.tsx index 463313b1..d6cb6b99 100644 --- a/src/page/AddMenu/components/AddMenuImgModal/index.tsx +++ b/src/page/AddMenu/components/AddMenuImgModal/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { createPortal } from 'react-dom'; import { ReactComponent as CancelIcon } from 'assets/svg/addmenu/mobile-cancle-icon.svg'; import useAddMenuStore from 'store/addMenu'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; import { UploadError } from 'utils/hooks/useImagesUpload'; import styles from './AddMenuImgModal.module.scss'; diff --git a/src/page/AddMenu/components/MenuImage/index.tsx b/src/page/AddMenu/components/MenuImage/index.tsx index 238e597e..76466f1e 100644 --- a/src/page/AddMenu/components/MenuImage/index.tsx +++ b/src/page/AddMenu/components/MenuImage/index.tsx @@ -5,7 +5,7 @@ import useMediaQuery from 'utils/hooks/useMediaQuery'; import useBooleanState from 'utils/hooks/useBooleanState'; import AddMenuImgModal from 'page/AddMenu/components/AddMenuImgModal'; import useAddMenuStore from 'store/addMenu'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; import useImagesUpload from 'utils/hooks/useImagesUpload'; import styles from './MenuImage.module.scss'; diff --git a/src/page/Auth/Signup/component/UserEmail/index.tsx b/src/page/Auth/Signup/component/UserEmail/index.tsx index 138614e1..d162be14 100644 --- a/src/page/Auth/Signup/component/UserEmail/index.tsx +++ b/src/page/Auth/Signup/component/UserEmail/index.tsx @@ -3,7 +3,7 @@ import CustomButton from 'page/Auth/Signup/component/CustomButton'; import useValidateEmail from 'page/Auth/Signup/hooks/useValidateEmail'; import useAuthCheck from 'page/Auth/Signup/hooks/useAuthCheck'; import useVerification from 'page/Auth/Signup/hooks/useVerification'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import useRegisterInfo from 'store/registerStore'; import useTimer from 'page/Auth/Signup/hooks/useTimer'; import { useEffect } from 'react'; diff --git a/src/page/Auth/Signup/component/UserId/index.tsx b/src/page/Auth/Signup/component/UserId/index.tsx index 718a2352..b35dd568 100644 --- a/src/page/Auth/Signup/component/UserId/index.tsx +++ b/src/page/Auth/Signup/component/UserId/index.tsx @@ -2,7 +2,7 @@ import useMediaQuery from 'utils/hooks/useMediaQuery'; import CustomButton from 'page/Auth/Signup/component/CustomButton'; import useValidateEmail from 'page/Auth/Signup/hooks/useValidateEmail'; import useCheckEmailDuplicate from 'page/Auth/Signup/hooks/useCheckEmailDuplicate'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import styles from './UserId.module.scss'; export default function UserId() { diff --git a/src/page/Auth/Signup/component/UserPassword/index.tsx b/src/page/Auth/Signup/component/UserPassword/index.tsx index 61f34256..1405445e 100644 --- a/src/page/Auth/Signup/component/UserPassword/index.tsx +++ b/src/page/Auth/Signup/component/UserPassword/index.tsx @@ -6,7 +6,7 @@ import usePasswordConfirm from 'page/Auth/Signup/hooks/usePasswordConfirm'; import { User } from 'page/Auth/Signup/types/User'; import { SubmitHandler } from 'react-hook-form'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import useRegisterInfo from 'store/registerStore'; import styles from './UserPassword.module.scss'; diff --git a/src/page/Auth/Signup/index.tsx b/src/page/Auth/Signup/index.tsx index 699dd463..e4e97004 100644 --- a/src/page/Auth/Signup/index.tsx +++ b/src/page/Auth/Signup/index.tsx @@ -3,7 +3,7 @@ import { ReactComponent as Logo } from 'assets/svg/auth/koin-logo.svg'; import { ReactComponent as Back } from 'assets/svg/common/back-arrow.svg'; import { Link } from 'react-router-dom'; import ProgressBar from 'component/common/ProgressBar'; -import PreviousStep from 'component/common/Auth/PreviousStep'; +import PreviousStep from 'component/Auth/PreviousStep'; import { useRef } from 'react'; import OwnerData from './view/OwnerDataPage'; import TermsOfService from './view/TermsOfServicePage'; diff --git a/src/page/Auth/Signup/view/OwnerDataPage/index.tsx b/src/page/Auth/Signup/view/OwnerDataPage/index.tsx index 1984d8be..02ce1395 100644 --- a/src/page/Auth/Signup/view/OwnerDataPage/index.tsx +++ b/src/page/Auth/Signup/view/OwnerDataPage/index.tsx @@ -6,7 +6,7 @@ import SearchShop from 'page/ShopRegistration/component/Modal/SearchShop'; import { ReactComponent as FileImage } from 'assets/svg/auth/default-file.svg'; import CustomModal from 'component/common/CustomModal'; import useCheckOwnerData from 'page/Auth/Signup/hooks/useOwnerData'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import useFileController from 'page/Auth/Signup/hooks/useFileController'; import useCheckNext from 'page/Auth/Signup/hooks/useCheckNext'; import { useEffect } from 'react'; diff --git a/src/page/ShopRegistration/component/ConfirmPopup/index.tsx b/src/page/ShopRegistration/component/ConfirmPopup/index.tsx index 63a59e44..0ce875cf 100644 --- a/src/page/ShopRegistration/component/ConfirmPopup/index.tsx +++ b/src/page/ShopRegistration/component/ConfirmPopup/index.tsx @@ -1,4 +1,4 @@ -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import { FieldErrors } from 'react-hook-form'; import { useState } from 'react'; import styles from './ConfirmPopup.module.scss'; diff --git a/src/page/ShopRegistration/view/PC/index.tsx b/src/page/ShopRegistration/view/PC/index.tsx index 7c0dfd34..3235cec3 100644 --- a/src/page/ShopRegistration/view/PC/index.tsx +++ b/src/page/ShopRegistration/view/PC/index.tsx @@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'; import useStepStore from 'store/useStepStore'; import Copyright from 'component/common/Copyright'; import CustomButton from 'page/Auth/Signup/component/CustomButton'; -import Complete from 'component/common/Auth/Complete'; +import Complete from 'component/Auth/Complete'; import Category from 'page/ShopRegistration/component/Modal/Category'; import SearchShop from 'page/ShopRegistration/component/Modal/SearchShop'; import OperateTimePC from 'page/ShopRegistration/component/Modal/OperateTimePC'; @@ -24,7 +24,7 @@ import useImagesUpload from 'utils/hooks/useImagesUpload'; import CheckSameTime from 'page/ShopRegistration/hooks/CheckSameTime'; import useOperateTimeState from 'page/ShopRegistration/hooks/useOperateTimeState'; import useShopRegistrationStore from 'store/shopRegistration'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; import { usePostData } from 'page/ShopRegistration/view/Mobile/ShopConfirmation/index'; import { ReactComponent as FileImage } from 'assets/svg/auth/default-file.svg'; From d941ad57ea0a9937272e1cce193dca4485260576 Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Thu, 30 May 2024 16:17:28 +0900 Subject: [PATCH 03/14] =?UTF-8?q?refactor:=20=EA=B0=80=EA=B2=8C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/OperateTimeMobile/index.tsx | 13 ++- .../view/Mobile/Main/Main.module.scss | 30 +++++ .../view/Mobile/Main/index.tsx | 82 +++++++++----- .../view/Mobile/ShopCategory/index.tsx | 20 ++-- .../view/Mobile/ShopConfirmation/index.tsx | 75 +++---------- .../view/Mobile/Sub/index.tsx | 104 +++++++++++------- .../ShopRegistration/view/Mobile/index.tsx | 29 ++++- src/store/modalStore.ts | 20 +++- src/store/shopRegistration.ts | 2 +- src/utils/hooks/useImagesUpload.ts | 1 - 10 files changed, 218 insertions(+), 158 deletions(-) diff --git a/src/page/ShopRegistration/component/Modal/OperateTimeMobile/index.tsx b/src/page/ShopRegistration/component/Modal/OperateTimeMobile/index.tsx index 164fddb0..0ef285dd 100644 --- a/src/page/ShopRegistration/component/Modal/OperateTimeMobile/index.tsx +++ b/src/page/ShopRegistration/component/Modal/OperateTimeMobile/index.tsx @@ -1,11 +1,12 @@ -import PreviousStep from 'component/common/Auth/PreviousStep'; -import SubTitle from 'component/common/Auth/SubTitle'; -import useStepStore from 'store/useStepStore'; +import PreviousStep from 'component/Auth/PreviousStep'; +import SubTitle from 'component/Auth/SubTitle'; import TimePicker from 'page/ShopRegistration/component/TimePicker'; import { WEEK } from 'utils/constant/week'; import { createPortal } from 'react-dom'; import useModalStore, { OperatingTime } from 'store/modalStore'; import cn from 'utils/ts/className'; +import { useFunnel } from 'utils/hooks/useFunnel'; +import PROGRESS_TITLE from 'utils/constant/progress'; import styles from './OperateTimeMobile.module.scss'; interface OperateTimeMobileProps { @@ -14,7 +15,9 @@ interface OperateTimeMobileProps { } export default function OperateTimeMobile({ isOpen, closeModal }: OperateTimeMobileProps) { - const step = useStepStore((state) => state.step); + const { currentStep } = useFunnel('세부 정보 입력'); + const currentIndex = PROGRESS_TITLE.findIndex((step) => step.title === currentStep); + const { shopClosedState } = useModalStore(); const handleShopClosedChange = (day: typeof WEEK[number]) => { @@ -43,7 +46,7 @@ export default function OperateTimeMobile({ isOpen, closeModal }: OperateTimeMob
- +
diff --git a/src/page/ShopRegistration/view/Mobile/Main/Main.module.scss b/src/page/ShopRegistration/view/Mobile/Main/Main.module.scss index ce0cd881..67fd63f9 100644 --- a/src/page/ShopRegistration/view/Mobile/Main/Main.module.scss +++ b/src/page/ShopRegistration/view/Mobile/Main/Main.module.scss @@ -48,10 +48,40 @@ } &__main-menu { + display: flex; + overflow-y: hidden; + flex-direction: row; + white-space: nowrap; max-width: 295px; max-height: 200px; } + &__main-menu-image { + object-fit: contain; + max-width: 295px; + height: 200px; + } + + &__delete-img-button { + position: relative; + right: 15px; + display: flex; + justify-content: center; + align-items: center; + background-color: #f7941e; + border-radius: 50%; + height: 24px; + + svg { + width: 24px; + height: 10px; + } + + &:hover { + cursor: pointer; + } + } + &__text { margin-top: 8px; font-size: 14px; diff --git a/src/page/ShopRegistration/view/Mobile/Main/index.tsx b/src/page/ShopRegistration/view/Mobile/Main/index.tsx index 438de38f..a2f669c2 100644 --- a/src/page/ShopRegistration/view/Mobile/Main/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/Main/index.tsx @@ -1,41 +1,48 @@ -/* eslint-disable react-hooks/exhaustive-deps */ import { ReactComponent as EmptyImgIcon } from 'assets/svg/shopRegistration/mobile-empty-img.svg'; -import useShopRegistrationStore from 'store/shopRegistration'; -import { useEffect, useState } from 'react'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import { ReactComponent as MobileDeleteImgIcon } from 'assets/svg/addmenu/mobile-delete-new-image.svg'; +import { useEffect } from 'react'; import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import cn from 'utils/ts/className'; import useImagesUpload from 'utils/hooks/useImagesUpload'; +import { useFormContext, useWatch } from 'react-hook-form'; import styles from './Main.module.scss'; export default function Main({ onNext }:{ onNext: () => void }) { - const [isError, setIsError] = useState(false); const { - imageFile, imgRef, saveImgFile, uploadError, - } = useImagesUpload(); + register, control, setValue, trigger, formState: { errors }, + } = useFormContext(); + + const name = useWatch({ control, name: 'name' }); + const address = useWatch({ control, name: 'address' }); + const imageUrls = useWatch({ control, name: 'image_urls' }); + const { - name, setName, address, setAddress, imageUrls, setImageUrls, - } = useShopRegistrationStore(); + imageFile, imgRef, saveImgFile, uploadError, setImageFile, + } = useImagesUpload(); - const handleNextClick = () => { - if (name === '' || address === '' || imageUrls.length === 0 || uploadError !== '') { - setIsError(true); - } else { - setIsError(false); - onNext(); - } + const handleDeleteImage = (image: string) => { + setImageFile(imageFile.filter((img) => img !== image)); }; useEffect(() => { - if (imageFile.length > 0 || uploadError !== '') setImageUrls(imageFile); - }, [imageFile]); + setValue('image_urls', imageFile); + }, [imageFile, setValue, uploadError]); + + const handleNextClick = async () => { + const isValid = await trigger(['image_urls', 'name', 'address']); + if (!isValid) { + return; + } + onNext(); + }; return (
- {uploadError === '' && imageUrls.length === 0 && isError && } + {errors.image_urls && } {uploadError !== '' && }
- {name === '' && isError && } + {errors.name && }
- {address === '' && isError && } + {errors.address && }
diff --git a/src/page/ShopRegistration/view/Mobile/ShopCategory/index.tsx b/src/page/ShopRegistration/view/Mobile/ShopCategory/index.tsx index 71618c79..252bdfe3 100644 --- a/src/page/ShopRegistration/view/Mobile/ShopCategory/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/ShopCategory/index.tsx @@ -1,26 +1,24 @@ import useMyShop from 'query/shop'; import cn from 'utils/ts/className'; import { Category as CategoryProps } from 'model/category/shopCategory'; -import useShopRegistrationStore from 'store/shopRegistration'; import { useState } from 'react'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; +import { useFormContext, useWatch } from 'react-hook-form'; import styles from './ShopCategory.module.scss'; export default function ShopCategory({ onNext }:{ onNext: () => void }) { const [isError, setIsError] = useState(false); const { categoryList } = useMyShop(); - const { - category, setCategory, setCategoryId, - } = useShopRegistrationStore(); + const { control, setValue } = useFormContext(); + const categoryId = useWatch({ control, name: 'category_ids' }); const handleCategoryClick = (categoryInfo: CategoryProps) => { - setCategory(categoryInfo.name); - setCategoryId(categoryInfo.id); + setValue('category_ids', [categoryInfo.id, 0]); }; const handleNextClick = () => { - if (category.length === 0) { + if (!categoryId) { setIsError(true); } else { setIsError(false); @@ -36,7 +34,7 @@ export default function ShopCategory({ onNext }:{ onNext: () => void }) { ))}
- {category.length === 0 && isError && } + {isError && }
diff --git a/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx b/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx index 6218ebfc..6309b3ab 100644 --- a/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx @@ -1,18 +1,16 @@ /* eslint-disable react-hooks/exhaustive-deps */ import useStepStore from 'store/useStepStore'; -import useShopRegistrationStore from 'store/shopRegistration'; import useOperateTimeState from 'page/ShopRegistration/hooks/useOperateTimeState'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import { SubmitHandler, useFormContext } from 'react-hook-form'; import { OwnerShop } from 'model/shopInfo/ownerShop'; -import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { postShop } from 'api/shop'; -import { useEffect } from 'react'; -import { DAY_OF_WEEK, WEEK } from 'utils/constant/week'; +import { WEEK } from 'utils/constant/week'; import useModalStore from 'store/modalStore'; import CheckSameTime from 'page/ShopRegistration/hooks/CheckSameTime'; import { isKoinError } from '@bcsdlab/koin'; import showToast from 'utils/ts/showToast'; +import useMyShop from 'query/shop'; import styles from './ShopConfirmation.module.scss'; interface UsePostDataProps { @@ -41,82 +39,43 @@ export const usePostData = ({ onNext } : UsePostDataProps) => { }; export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { - const { - category, - categoryId, - imageUrls, - name, - address, - phone, - deliveryPrice, - description, - delivery, - payBank, - payCard, - } = useShopRegistrationStore(); - + const { categoryList } = useMyShop(); const operateTimeState = useOperateTimeState(); - const { handleSubmit, setValue } = useForm({ - resolver: zodResolver(OwnerShop), - }); + const { handleSubmit, getValues } = useFormContext(); + const values = getValues(); + const categoryId = categoryList?.shop_categories[values.category_ids[0] - 1].name; const mutation = usePostData({ onNext }); - const onSubmit: SubmitHandler = (data) => { mutation.mutate(data); }; - const { openTimeState, closeTimeState, shopClosedState } = useModalStore(); + const { shopClosedState } = useModalStore(); const { isAllSameTime, hasClosedDay, isSpecificDayClosedAndAllSameTime } = CheckSameTime(); - const openTimeArray = Object.values(openTimeState); - const closeTimeArray = Object.values(closeTimeState); - const shopClosedArray = Object.values(shopClosedState); - - useEffect(() => { - const openValue = DAY_OF_WEEK.map((day, index) => ({ - close_time: closeTimeArray[index], - closed: shopClosedArray[index], - day_of_week: day, - open_time: openTimeArray[index], - })); - setValue('image_urls', imageUrls); - setValue('category_ids', [categoryId]); - setValue('name', name); - setValue('address', address); - setValue('phone', phone); - setValue('delivery_price', Number(deliveryPrice)); - setValue('description', description); - setValue('delivery', delivery); - setValue('pay_bank', payBank); - setValue('pay_card', payCard); - setValue('open', openValue); - }, [openTimeArray, closeTimeArray, shopClosedArray, categoryId, name, - address, phone, deliveryPrice, description, delivery, payBank, payCard, imageUrls]); - return (
카테고리 - {category} + {categoryId ?? values.category_ids}
가게명 - {name} + {values.name}
주소정보 - {address} + {values.address}
전화번호 - {phone} + {values.phone}
배달금액 - {deliveryPrice === 0 ? '무료' : `${deliveryPrice}원`} + {Number(values.delivery_price) === 0 ? '무료' : `${Number(values.delivery_price)}원`}
운영시간 @@ -154,19 +113,19 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) {
기타정보 - {description} + {values.description}
diff --git a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx index fe3feb60..a0dfc5f1 100644 --- a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx @@ -1,15 +1,14 @@ -/* eslint-disable jsx-a11y/label-has-associated-control */ import OperateTimeMobile from 'page/ShopRegistration/component/Modal/OperateTimeMobile'; import useBooleanState from 'utils/hooks/useBooleanState'; -import useShopRegistrationStore from 'store/shopRegistration'; import useOperateTimeState from 'page/ShopRegistration/hooks/useOperateTimeState'; import CheckSameTime from 'page/ShopRegistration/hooks/CheckSameTime'; -import { WEEK } from 'utils/constant/week'; +import { DAY_OF_WEEK, WEEK } from 'utils/constant/week'; import useModalStore from 'store/modalStore'; -import { useState } from 'react'; -import ErrorMessage from 'page/Auth/Signup/component/ErrorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; import cn from 'utils/ts/className'; +import { useFormContext, useWatch } from 'react-hook-form'; +import { useEffect } from 'react'; import styles from './Sub.module.scss'; export default function Sub({ onNext }:{ onNext: () => void }) { @@ -18,18 +17,6 @@ export default function Sub({ onNext }:{ onNext: () => void }) { setTrue: openOperateTime, setFalse: closeOperateTime, } = useBooleanState(false); - const { - setPhone, setDeliveryPrice, setDescription, setDelivery, setPayBank, setPayCard, - } = useShopRegistrationStore(); - - const { - phone, - deliveryPrice, - description, - delivery, - payBank, - payCard, - } = useShopRegistrationStore(); const operateTimeState = useOperateTimeState(); const { @@ -38,25 +25,53 @@ export default function Sub({ onNext }:{ onNext: () => void }) { isSpecificDayClosedAndAllSameTime, isAllClosed, } = CheckSameTime(); - const { shopClosedState } = useModalStore(); - const [isError, setIsError] = useState(false); - const formatPhoneNumber = (inputNumber: string) => { + const { openTimeState, closeTimeState, shopClosedState } = useModalStore(); + const openTimeArray = Object.values(openTimeState); + const closeTimeArray = Object.values(closeTimeState); + const shopClosedArray = Object.values(shopClosedState); + + const { + register, control, trigger, setValue, formState: { errors }, + } = useFormContext(); + + const phone = useWatch({ control, name: 'phone' }); + const deliveryPrice = useWatch({ control, name: 'delivery_price' }); + const description = useWatch({ control, name: 'description' }); + const delivery = useWatch({ control, name: 'delivery' }); + const payBank = useWatch({ control, name: 'paya_bank' }); + const payCard = useWatch({ control, name: 'pay_card' }); + + useEffect(() => { + const openValue = DAY_OF_WEEK.map((day, index) => ({ + close_time: closeTimeArray[index], + closed: shopClosedArray[index], + day_of_week: day, + open_time: openTimeArray[index], + })); + setValue('open', openValue); + }, [closeTimeArray, openTimeArray, setValue, shopClosedArray]); + + const formatPhoneNumber = (inputNumber:string) => { const phoneNumber = inputNumber.replace(/\D/g, ''); - const formattedPhoneNumber = phoneNumber.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'); - if (formattedPhoneNumber.length > 13) return formattedPhoneNumber.slice(0, 13); - return formattedPhoneNumber; + const phoneNumberLength = phoneNumber.length; + + if (phoneNumberLength < 4) return phoneNumber; + if (phoneNumberLength < 7) return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`; + return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 7)}-${phoneNumber.slice(7, 11)}`; + }; + + const handlePhoneChange = (event:React.ChangeEvent) => { + const formattedValue = formatPhoneNumber(event.target.value); + setValue('phone', formattedValue); }; - const phoneNumberPattern = /^\d{3}-\d{4}-\d{4}$/; - const isValidPhoneNumber = phoneNumberPattern.test(phone); - const handleNextClick = () => { - if (phone === '' || Number.isNaN(deliveryPrice) || !isValidPhoneNumber) { - setIsError(true); - } else { - setIsError(false); - onNext(); + const handleNextClick = async () => { + const isValid = await trigger(['phone']); + if (!isValid) { + return; } + onNext(); }; if (showOperateTime) { @@ -71,21 +86,27 @@ export default function Sub({ onNext }:{ onNext: () => void }) { htmlFor="phone" className={cn({ [styles.form__label]: true, - [styles['form__label--error']]: (phone === '' || !isValidPhoneNumber) && isError, + [styles['form__label--error']]: errors.phone !== undefined, })} > 전화번호 setPhone(formatPhoneNumber(e.target.value))} value={phone} className={styles.form__input} + {...register('phone', { + required: true, + pattern: { + value: /^\d{3}-\d{3,4}-\d{4}$/, + message: ERRORMESSAGE.invalidPhone, + }, + onChange: handlePhoneChange, + })} />
- {phone === '' && isError && } - {(!isValidPhoneNumber && phone !== '' && isError) && } + {errors.phone && }
@@ -150,8 +172,8 @@ export default function Sub({ onNext }:{ onNext: () => void }) { type="text" id="extra-info" className={styles.form__input} - onChange={(e) => setDescription(e.target.value)} value={description} + {...register('description')} />
@@ -159,9 +181,9 @@ export default function Sub({ onNext }:{ onNext: () => void }) { setDelivery(e.target.checked)} className={styles['form__checkbox-input']} checked={delivery} + {...register('delivery')} /> 배달 가능 @@ -169,9 +191,9 @@ export default function Sub({ onNext }:{ onNext: () => void }) { setPayCard(e.target.checked)} className={styles['form__checkbox-input']} checked={payCard} + {...register('pay_card')} /> 카드 가능 @@ -179,9 +201,9 @@ export default function Sub({ onNext }:{ onNext: () => void }) { setPayBank(e.target.checked)} className={styles['form__checkbox-input']} checked={payBank} + {...register('pay_bank')} /> 계좌이체 가능 diff --git a/src/page/ShopRegistration/view/Mobile/index.tsx b/src/page/ShopRegistration/view/Mobile/index.tsx index 68b96615..8da8e2a6 100644 --- a/src/page/ShopRegistration/view/Mobile/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/index.tsx @@ -1,7 +1,7 @@ -import PreviousStep from 'component/common/Auth/PreviousStep'; -import ProgressBar from 'component/common/Auth/ProgressBar'; -import Complete from 'component/common/Auth/Complete'; -import SubTitle from 'component/common/Auth/SubTitle'; +import PreviousStep from 'component/Auth/PreviousStep'; +import ProgressBar from 'component/Auth/ProgressBar'; +import Complete from 'component/Auth/Complete'; +import SubTitle from 'component/Auth/SubTitle'; import PROGRESS_TITLE from 'utils/constant/progress'; import ShopEntry from 'page/ShopRegistration/view/Mobile/ShopEntry'; import ShopCategory from 'page/ShopRegistration/view/Mobile/ShopCategory'; @@ -9,6 +9,7 @@ import Main from 'page/ShopRegistration/view/Mobile/Main'; import Sub from 'page/ShopRegistration/view/Mobile/Sub'; import ShopConfirmation from 'page/ShopRegistration/view/Mobile/ShopConfirmation'; import { useFunnel } from 'utils/hooks/useFunnel'; +import { FormProvider, useForm } from 'react-hook-form'; import styles from './ShopRegistrationMobile.module.scss'; export default function ShopRegistrationMobile() { @@ -24,8 +25,24 @@ export default function ShopRegistrationMobile() { } }; + const methods = useForm({ + defaultValues: { + category_ids: [], + delivery_price: 0, + description: '', + image_urls: [], + owner: '', + name: '', + phone: '', + address: '', + delivery: false, + pay_bank: false, + pay_card: false, + }, + }); + return ( -
+ {currentStep !== '가게 등록' && }
@@ -103,6 +120,6 @@ export default function ShopRegistrationMobile() {
-
+ ); } diff --git a/src/store/modalStore.ts b/src/store/modalStore.ts index b2bcc5f5..54520f83 100644 --- a/src/store/modalStore.ts +++ b/src/store/modalStore.ts @@ -12,8 +12,9 @@ interface ModalStore { setOpenTimeState: (state: OperatingTime) => void; setCloseTimeState: (state: OperatingTime) => void; setShopClosedState: (state: { [key: string]: boolean }) => void; - setSearchShopState: (state: string) => void; // 수정 요망 - setSelectedShopId:(state:string) => void; // 수정 요망 + setSearchShopState: (state: string) => void; + setSelectedShopId:(state:string) => void; + resetOperatingTime: ()=> void; } const initialOperatingTime: OperatingTime = { @@ -40,13 +41,20 @@ const useModalStore = create((set) => ({ openTimeState: initialOperatingTime, closeTimeState: initialOperatingTime, shopClosedState: initialShopClosed, - searchShopState: '', // 수정 요망 - selectedShopId: '', // 수정 요망 + searchShopState: '', + selectedShopId: '', setOpenTimeState: (state) => set(() => ({ openTimeState: state })), setCloseTimeState: (state) => set(() => ({ closeTimeState: state })), setShopClosedState: (state) => set({ shopClosedState: state }), - setSearchShopState: (state) => set({ searchShopState: state }), // 수정 요망 - setSelectedShopId: (state) => set({ selectedShopId: state }), // 수정 요망 + setSearchShopState: (state) => set({ searchShopState: state }), + setSelectedShopId: (state) => set({ selectedShopId: state }), + resetOperatingTime: () => { + set(() => ({ + openTimeState: initialOperatingTime, + closeTimeState: initialOperatingTime, + shopClosedState: initialShopClosed, + })); + }, })); export default useModalStore; diff --git a/src/store/shopRegistration.ts b/src/store/shopRegistration.ts index 0b478eaf..ebb9dda4 100644 --- a/src/store/shopRegistration.ts +++ b/src/store/shopRegistration.ts @@ -39,7 +39,7 @@ const useShopRegistrationStore = create((set) => ({ deliveryPrice: 0, description: '', imageUrl: '', - imageUrls: ['aa'], + imageUrls: [], owner: '', name: '', phone: '', diff --git a/src/utils/hooks/useImagesUpload.ts b/src/utils/hooks/useImagesUpload.ts index 9eed66ac..bcd076c2 100644 --- a/src/utils/hooks/useImagesUpload.ts +++ b/src/utils/hooks/useImagesUpload.ts @@ -15,7 +15,6 @@ export default function useImagesUpload() { const saveImgFile = async () => { const files = imgRef.current?.files; - console.log(files?.length) // imageFile.length + files.length을 통해 저장된 이미지 + 새로 추가할 이미지의 개수를 파악함 if (files && (files.length > 3 || imageFile.length >= 3 || imageFile.length + files.length > 3)) { showToast('error', '파일은 3개까지 등록할 수 있습니다.') From c0933a47ea9bbba47944de7fb55bfcc23a3ebc76 Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Thu, 30 May 2024 17:38:47 +0900 Subject: [PATCH 04/14] =?UTF-8?q?fix:=20=EB=B2=88=ED=98=B8=20=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=9A=B4=EC=98=81?= =?UTF-8?q?=20=EC=8B=9C=EA=B0=84=20=EC=B4=88=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/ShopRegistration/view/Mobile/Main/index.tsx | 6 ++++-- .../view/Mobile/ShopConfirmation/index.tsx | 7 ++++--- src/page/ShopRegistration/view/Mobile/Sub/index.tsx | 11 ++++------- src/page/ShopRegistration/view/Mobile/index.tsx | 1 - 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/page/ShopRegistration/view/Mobile/Main/index.tsx b/src/page/ShopRegistration/view/Mobile/Main/index.tsx index a2f669c2..12821f05 100644 --- a/src/page/ShopRegistration/view/Mobile/Main/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/Main/index.tsx @@ -26,8 +26,10 @@ export default function Main({ onNext }:{ onNext: () => void }) { }; useEffect(() => { - setValue('image_urls', imageFile); - }, [imageFile, setValue, uploadError]); + if (imageFile.length > 0) { + setValue('image_urls', imageFile); + } + }, [imageFile, setValue]); const handleNextClick = async () => { const isValid = await trigger(['image_urls', 'name', 'address']); diff --git a/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx b/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx index 6309b3ab..5a84db97 100644 --- a/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx @@ -20,6 +20,7 @@ interface UsePostDataProps { export const usePostData = ({ onNext } : UsePostDataProps) => { const queryClient = useQueryClient(); const { setStep } = useStepStore(); + const { resetOperatingTime } = useModalStore(); const mutation = useMutation({ mutationFn: (form: OwnerShop) => postShop(form), onSuccess: () => { @@ -28,6 +29,7 @@ export const usePostData = ({ onNext } : UsePostDataProps) => { onNext(); } queryClient.refetchQueries(); + resetOperatingTime(); }, onError: (e) => { if (isKoinError(e)) { @@ -45,7 +47,6 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { const { handleSubmit, getValues } = useFormContext(); const values = getValues(); const categoryId = categoryList?.shop_categories[values.category_ids[0] - 1].name; - const mutation = usePostData({ onNext }); const onSubmit: SubmitHandler = (data) => { mutation.mutate(data); @@ -59,7 +60,7 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) {
카테고리 - {categoryId ?? values.category_ids} + {categoryId}
가게명 @@ -75,7 +76,7 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) {
배달금액 - {Number(values.delivery_price) === 0 ? '무료' : `${Number(values.delivery_price)}원`} + {values.delivery_price === 0 ? '무료' : `${values.delivery_price}원`}
운영시간 diff --git a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx index a0dfc5f1..85d10c98 100644 --- a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx @@ -54,11 +54,8 @@ export default function Sub({ onNext }:{ onNext: () => void }) { const formatPhoneNumber = (inputNumber:string) => { const phoneNumber = inputNumber.replace(/\D/g, ''); - const phoneNumberLength = phoneNumber.length; - - if (phoneNumberLength < 4) return phoneNumber; - if (phoneNumberLength < 7) return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`; - return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 7)}-${phoneNumber.slice(7, 11)}`; + const formattedPhoneNumber = phoneNumber.replace(/^(\d{3})(\d{4})(\d{4})$/, '$1-$2-$3'); + return formattedPhoneNumber; }; const handlePhoneChange = (event:React.ChangeEvent) => { @@ -67,7 +64,7 @@ export default function Sub({ onNext }:{ onNext: () => void }) { }; const handleNextClick = async () => { - const isValid = await trigger(['phone']); + const isValid = await trigger(['phone', 'delivery_price']); if (!isValid) { return; } @@ -116,7 +113,7 @@ export default function Sub({ onNext }:{ onNext: () => void }) { (e.target as HTMLElement).blur()} // 마우스 스크롤로 숫자 변경 방지 diff --git a/src/page/ShopRegistration/view/Mobile/index.tsx b/src/page/ShopRegistration/view/Mobile/index.tsx index 8da8e2a6..d5af46f6 100644 --- a/src/page/ShopRegistration/view/Mobile/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/index.tsx @@ -31,7 +31,6 @@ export default function ShopRegistrationMobile() { delivery_price: 0, description: '', image_urls: [], - owner: '', name: '', phone: '', address: '', From 49d1d3cc7097e16bd64ae9ccae9c4376886327b5 Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Mon, 3 Jun 2024 13:21:44 +0900 Subject: [PATCH 05/14] =?UTF-8?q?refactor:=20=EA=B0=80=EA=B2=8C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=98=EA=B8=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EditShopInfoModal.module.scss | 14 +- .../components/EditShopInfoModal/index.tsx | 221 ++++++++---------- .../ShopRegistration/constant/errorMessage.ts | 1 + src/page/ShopRegistration/index.tsx | 8 +- 4 files changed, 118 insertions(+), 126 deletions(-) diff --git a/src/page/MyShopPage/components/EditShopInfoModal/EditShopInfoModal.module.scss b/src/page/MyShopPage/components/EditShopInfoModal/EditShopInfoModal.module.scss index 20bd0081..51cf6f9c 100644 --- a/src/page/MyShopPage/components/EditShopInfoModal/EditShopInfoModal.module.scss +++ b/src/page/MyShopPage/components/EditShopInfoModal/EditShopInfoModal.module.scss @@ -138,6 +138,10 @@ margin-bottom: 24px; } + &__error-message { + margin-bottom: 5px; + } + &__header { font-size: 18px; color: #17518e; @@ -316,7 +320,15 @@ &__label { display: flex; - margin-bottom: 16px; + margin-bottom: 24px; + + &--error { + margin-bottom: 0px; + } + } + + &__error-message { + margin-bottom: 5px; } &__header { diff --git a/src/page/MyShopPage/components/EditShopInfoModal/index.tsx b/src/page/MyShopPage/components/EditShopInfoModal/index.tsx index add1cb16..22f007e1 100644 --- a/src/page/MyShopPage/components/EditShopInfoModal/index.tsx +++ b/src/page/MyShopPage/components/EditShopInfoModal/index.tsx @@ -1,13 +1,9 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import { - Dispatch, SetStateAction, useEffect, -} from 'react'; +import { Dispatch, SetStateAction, useEffect } from 'react'; import { ReactComponent as DeleteImgIcon } from 'assets/svg/addmenu/mobile-delete-new-image.svg'; import { MyShopInfoRes } from 'model/shopInfo/myShopInfo'; import { ReactComponent as ImgPlusIcon } from 'assets/svg/myshop/imgplus.svg'; -import { DAY_OF_WEEK, WEEK } from 'utils/constant/week'; -import useShopRegistrationStore from 'store/shopRegistration'; -import { SubmitHandler, useForm } from 'react-hook-form'; +import { WEEK } from 'utils/constant/week'; +import { SubmitHandler, useForm, useWatch } from 'react-hook-form'; import { OwnerShop } from 'model/shopInfo/ownerShop'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation } from '@tanstack/react-query'; @@ -21,10 +17,12 @@ import CheckSameTime from 'page/ShopRegistration/hooks/CheckSameTime'; import useModalStore from 'store/modalStore'; import useMediaQuery from 'utils/hooks/useMediaQuery'; import OperateTimeMobile from 'page/ShopRegistration/component/Modal/OperateTimeMobile'; -import { TOTAL_CATEGORY } from 'utils/constant/category'; import useImagesUpload from 'utils/hooks/useImagesUpload'; import { isKoinError, sendClientError } from '@bcsdlab/koin'; import showToast from 'utils/ts/showToast'; +import cn from 'utils/ts/className'; +import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; +import ErrorMessage from 'component/common/ErrorMessage'; import styles from './EditShopInfoModal.module.scss'; interface EditShopInfoModalProps { @@ -44,30 +42,14 @@ export default function EditShopInfoModal({ setFalse: closeOperateTimeModal, value: isOperateTimeModalOpen, } = useBooleanState(false); - const { - imageFile, imgRef, saveImgFile, uploadError, setImageFile, - } = useImagesUpload(); const { - setName, setAddress, setPhone, setDeliveryPrice, setDescription, - setImageUrls, setDelivery, setPayBank, setPayCard, setCategoryId, - } = useShopRegistrationStore(); - const { - name, address, phone, deliveryPrice, description, imageUrls, - delivery, payBank, payCard, categoryId, removeImageUrl, - } = useShopRegistrationStore(); + imageFile, imgRef, saveImgFile, setImageFile, + } = useImagesUpload(); const { categoryList } = useShopCategory(); - const { - openTimeState, - closeTimeState, - shopClosedState, - } = useModalStore(); - - const openTimeArray = Object.values(openTimeState); - const closeTimeArray = Object.values(closeTimeState); - const shopClosedArray = Object.values(shopClosedState); + const { shopClosedState } = useModalStore(); const { isAllSameTime, @@ -76,21 +58,54 @@ export default function EditShopInfoModal({ isAllClosed, } = CheckSameTime(); - const handleCategoryIdChange = (e: React.ChangeEvent) => { - setCategoryId(Number(e.target.value)); - }; - const { - handleSubmit, setValue, + register, control, handleSubmit, setValue, formState: { errors }, } = useForm({ resolver: zodResolver(OwnerShop), + defaultValues: { + ...shopInfo, + category_ids: shopInfo.shop_categories.map((category) => category.id), + }, }); + const imageUrls = useWatch({ control, name: 'image_urls' }); + const name = useWatch({ control, name: 'name' }); + const categoryId = useWatch({ control, name: 'category_ids' }); + const phone = useWatch({ control, name: 'phone' }); + const address = useWatch({ control, name: 'address' }); + const deliveryPrice = useWatch({ control, name: 'delivery_price' }); + const description = useWatch({ control, name: 'description' }); + const delivery = useWatch({ control, name: 'delivery' }); + const payCard = useWatch({ control, name: 'pay_card' }); + const payBank = useWatch({ control, name: 'pay_bank' }); + + const handleCategoryIdChange = (e: React.ChangeEvent) => { + setValue('category_ids', [Number(e.target.value), 0]); + }; + + const handleDeleteImage = (image: string) => { + setImageFile(imageFile.filter((img) => img !== image)); + }; + + const formatPhoneNumber = (inputNumber:string) => { + const phoneNumber = inputNumber.replace(/\D/g, ''); + const formattedPhoneNumber = phoneNumber.replace(/^(\d{3})(\d{4})(\d{4})$/, '$1-$2-$3'); + return formattedPhoneNumber; + }; + + const handlePhoneChange = (event:React.ChangeEvent) => { + const formattedValue = formatPhoneNumber(event.target.value); + setValue('phone', formattedValue); + }; + useEffect(() => { - if (imageFile && !uploadError) { // 초기에 이 값이 true기 때문에 imageUrls가 빈 배열로 초기화되고 있었음 - setImageUrls(imageFile); + if (imageFile.length > 0) { + setValue('image_urls', imageFile); + } else if (imageFile.length !== imageUrls.length) { + setImageFile(imageUrls); } - }, [imageFile, setImageUrls]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [imageFile]); const mutation = useMutation({ mutationFn: (form: OwnerShop) => putShop(shopInfo.id, form), @@ -106,70 +121,12 @@ export default function EditShopInfoModal({ sendClientError(e); }, }); - useEffect(() => { - setImageUrls(shopInfo.image_urls); - setImageFile(shopInfo.image_urls); - setName(shopInfo.name); - setAddress(shopInfo.address); - setPhone(shopInfo.phone); - setDeliveryPrice(shopInfo.delivery_price); - setDescription(shopInfo.description); - setDelivery(shopInfo.delivery); - setPayBank(shopInfo.pay_bank); - setPayCard(shopInfo.pay_card); - setCategoryId(shopInfo.shop_categories[1] - ? shopInfo.shop_categories[1].id - : TOTAL_CATEGORY); - shopInfo.open.forEach((day, index) => { - useModalStore.setState((prev) => ({ - ...prev, - openTimeState: { - ...prev.openTimeState, - [WEEK[index]]: day.open_time, - }, - closeTimeState: { - ...prev.closeTimeState, - [WEEK[index]]: day.close_time, - }, - shopClosedState: { - ...prev.shopClosedState, - [WEEK[index]]: day.closed, - }, - })); - }); - }, []); + const operateTimeState = useOperateTimeState(); const holiday = WEEK.filter((day) => shopClosedState[day]).length > 0 ? `매주 ${WEEK.filter((day) => shopClosedState[day]).join('요일, ')}요일` : '휴무일 없음'; - useEffect(() => { - setValue('image_urls', imageUrls); - const openValue = DAY_OF_WEEK.map((day, index) => ({ - close_time: closeTimeArray[index], - closed: shopClosedArray[index], - day_of_week: day, - open_time: openTimeArray[index], - })); - // shop_categories[0]은 전체보기이므로 따로 처리 - if (shopInfo.shop_categories.length === 1) { - setValue('category_ids', [shopInfo.shop_categories[0].id]); - } else { - const categoryIds = shopInfo.shop_categories.map((category) => category.id); - setValue('category_ids', categoryIds); - } - setValue('open', openValue); - setValue('delivery_price', Number(deliveryPrice)); - setValue('description', description); - setValue('delivery', delivery); - setValue('pay_bank', payBank); - setValue('pay_card', payCard); - setValue('name', name); - setValue('phone', phone); - setValue('address', address); - }, [imageUrls, openTimeState, closeTimeState, shopClosedState, deliveryPrice, - description, delivery, payBank, payCard, name, phone, address, categoryId]); - const onSubmit: SubmitHandler = (data) => { mutation.mutate(data); }; @@ -193,10 +150,7 @@ export default function EditShopInfoModal({ {`Selected diff --git a/src/page/ShopRegistration/component/Modal/Category/index.tsx b/src/page/ShopRegistration/component/Modal/Category/index.tsx index 7b09a54d..d7bb374d 100644 --- a/src/page/ShopRegistration/component/Modal/Category/index.tsx +++ b/src/page/ShopRegistration/component/Modal/Category/index.tsx @@ -1,18 +1,16 @@ import useMyShop from 'query/shop'; import cn from 'utils/ts/className'; import { Category as CategoryProps } from 'model/category/shopCategory'; -import useShopRegistrationStore from 'store/shopRegistration'; +import { useFormContext, useWatch } from 'react-hook-form'; import styles from './Category.module.scss'; export default function Category() { const { categoryList } = useMyShop(); - const { - category, setCategory, setCategoryId, - } = useShopRegistrationStore(); + const { control, setValue } = useFormContext(); + const categoryId = useWatch({ control, name: 'category_ids' }); const handleCategoryClick = (categoryInfo: CategoryProps) => { - setCategory(categoryInfo.name); - setCategoryId(categoryInfo.id); + setValue('category_ids', [categoryInfo.id, 0]); }; return ( @@ -21,10 +19,10 @@ export default function Category() { ))}
- + {values.id && }
); } diff --git a/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx b/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx index 5a84db97..af5bb047 100644 --- a/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/ShopConfirmation/index.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import useStepStore from 'store/useStepStore'; import useOperateTimeState from 'page/ShopRegistration/hooks/useOperateTimeState'; import { SubmitHandler, useFormContext } from 'react-hook-form'; import { OwnerShop } from 'model/shopInfo/ownerShop'; @@ -19,12 +17,10 @@ interface UsePostDataProps { export const usePostData = ({ onNext } : UsePostDataProps) => { const queryClient = useQueryClient(); - const { setStep } = useStepStore(); const { resetOperatingTime } = useModalStore(); const mutation = useMutation({ mutationFn: (form: OwnerShop) => postShop(form), onSuccess: () => { - setStep(5); if (onNext) { onNext(); } @@ -82,33 +78,26 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { 운영시간 - { - isAllSameTime && !hasClosedDay ? ( -
- {operateTimeState.time} -
- ) - : null - } - { - isSpecificDayClosedAndAllSameTime ? ( -
-
{operateTimeState.time}
-
{operateTimeState.holiday}
-
- ) : null - } - { - !isAllSameTime && !isSpecificDayClosedAndAllSameTime ? ( - <> - {WEEK.map((day) => ( -
- {shopClosedState[day] ? `${operateTimeState[day]}` : `${day} : ${operateTimeState[day]}`} -
- ))} - - ) : null - } + {isAllSameTime && !hasClosedDay ? ( +
+ {operateTimeState.time} +
+ ) : null} + {isSpecificDayClosedAndAllSameTime ? ( +
+
{operateTimeState.time}
+
{operateTimeState.holiday}
+
+ ) : null} + {!isAllSameTime && !isSpecificDayClosedAndAllSameTime ? ( + <> + {WEEK.map((day) => ( +
+ {shopClosedState[day] ? `${operateTimeState[day]}` : `${day} : ${operateTimeState[day]}`} +
+ ))} + + ) : null}
diff --git a/src/page/ShopRegistration/view/Mobile/index.tsx b/src/page/ShopRegistration/view/Mobile/index.tsx index d5af46f6..00be4503 100644 --- a/src/page/ShopRegistration/view/Mobile/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/index.tsx @@ -12,6 +12,30 @@ import { useFunnel } from 'utils/hooks/useFunnel'; import { FormProvider, useForm } from 'react-hook-form'; import styles from './ShopRegistrationMobile.module.scss'; +const OPEN_DEFAULT_VALUES = [ + { + day_of_week: 'MONDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'TUESDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'WEDNESDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'THURSDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'FRIDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'SATURDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'SUNDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, +]; + export default function ShopRegistrationMobile() { const { Funnel, Step, setStep, currentStep, @@ -37,6 +61,7 @@ export default function ShopRegistrationMobile() { delivery: false, pay_bank: false, pay_card: false, + open: OPEN_DEFAULT_VALUES, }, }); diff --git a/src/page/ShopRegistration/view/PC/ShopConfirmation/ShopConfirmation.module.scss b/src/page/ShopRegistration/view/PC/ShopConfirmation/ShopConfirmation.module.scss new file mode 100644 index 00000000..9a4972c4 --- /dev/null +++ b/src/page/ShopRegistration/view/PC/ShopConfirmation/ShopConfirmation.module.scss @@ -0,0 +1,177 @@ +.wrapper { + position: relative; +} + +.container { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + padding: 80px 0 94px; + + &__koin-logo { + width: 368px; + position: relative; + margin-bottom: 56px; + background-color: #ffffff; + } +} + +.form { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + + &__title { + display: block; + font-size: 18px; + margin-bottom: 8px; + } + + &__image-upload { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 210px; + height: 93px; + border: 1px solid #d2dae2; + padding: 53px 80px 54px; + cursor: pointer; + + &--active { + display: flex; + flex-direction: column; + padding: 10px 20px; + width: 340px; + height: 180px; + } + } + + &__upload-file { + display: none; + } + + &__main-menu { + max-width: 370px; + max-height: 200px; + } + + &__main-item { + max-width: 370px; + display: flex; + } + + &__main-text { + width: 90%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + word-break: break-all; + color: #858585; + } + + &__cutlery-cross { + width: 64px; + height: 64px; + margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; + position: relative; + } + + &__text { + text-align: center; + font-size: 14px; + display: block; + color: #858585; + margin-top: 8px; + } + + &__section { + display: flex; + justify-content: space-between; + gap: 16px; + margin-top: 8px; + } + + &__input { + width: 240px; + border: 1px solid #d2dae2; + height: 22px; + padding: 13px 16px; + + &:focus { + border-bottom: 1px solid black; + } + } + + &__input-large { + width: 336px; + border: 1px solid #d2dae2; + height: 22px; + padding: 13px 16px; + + &:focus { + border-bottom: 1px solid black; + } + } + + &__operate-time { + display: flex; + align-items: center; + width: 272px; + height: auto; + font-size: 16px; + color: #858585; + } + + &__checkbox { + display: flex; + flex-direction: row; + justify-content: flex-start; + gap: 24px; + width: 368px; + } + + &__checkbox-label { + display: flex; + align-items: center; + font-weight: 500; + font-size: 14px; + color: #858585; + box-sizing: content-box; + cursor: pointer; + + input[type="checkbox"]:checked + span { + color: #f7941e; + } + } + + &__checkbox-input { + appearance: none; + width: 14px; + height: 14px; + border-radius: 100%; + box-sizing: border-box; + border: 1px solid #858585; + margin-right: 8px; + background-size: cover; + cursor: pointer; + + &:checked { + border: 2px solid #f7941e; + padding: 1px; + background-clip: content-box; + background-color: #f7941e; + } + } + + &__next-button { + width: 368px; + margin-top: 56px; + } +} diff --git a/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx b/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx new file mode 100644 index 00000000..9bc974e8 --- /dev/null +++ b/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx @@ -0,0 +1,378 @@ +import { ReactComponent as Logo } from 'assets/svg/auth/koin-logo.svg'; +import { ReactComponent as Cutlery } from 'assets/svg/shopRegistration/cutlery.svg'; +import Copyright from 'component/common/Copyright'; +import CustomButton from 'page/Auth/Signup/component/CustomButton'; +import Category from 'page/ShopRegistration/component/Modal/Category'; +import SearchShop from 'page/ShopRegistration/component/Modal/SearchShop'; +import OperateTimePC from 'page/ShopRegistration/component/Modal/OperateTimePC'; +import ConfirmPopup from 'page/ShopRegistration/component/ConfirmPopup'; +import CustomModal from 'component/common/CustomModal'; +import cn from 'utils/ts/className'; +import useModalStore from 'store/modalStore'; +import { WEEK, DAY_OF_WEEK } from 'utils/constant/week'; +import { SubmitHandler, useFormContext, useWatch } from 'react-hook-form'; +import { OwnerShop } from 'model/shopInfo/ownerShop'; +import useImagesUpload from 'utils/hooks/useImagesUpload'; +import CheckSameTime from 'page/ShopRegistration/hooks/CheckSameTime'; +import useOperateTimeState from 'page/ShopRegistration/hooks/useOperateTimeState'; +import ErrorMessage from 'component/common/ErrorMessage'; +import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; +import { usePostData } from 'page/ShopRegistration/view/Mobile/ShopConfirmation/index'; +import { ReactComponent as FileImage } from 'assets/svg/auth/default-file.svg'; +import useMyShop from 'query/shop'; +import { useEffect } from 'react'; +import useBooleanState from 'utils/hooks/useBooleanState'; +import styles from './ShopConfirmation.module.scss'; + +export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { + const { + value: showCategory, + setTrue: openCategory, + setFalse: closeCategory, + } = useBooleanState(false); + const { + value: showOperateTime, + setTrue: openOperateTime, + setFalse: closeOperateTime, + } = useBooleanState(false); + const { + value: showSearchShop, + setTrue: openSearchShop, + setFalse: closeSearchShop, + } = useBooleanState(false); + const { + value: showConfirmPopup, + setTrue: openConfirmPopup, + setFalse: closeConfirmPopup, + } = useBooleanState(false); + + const { + openTimeState, + closeTimeState, + shopClosedState, + } = useModalStore(); + + const { categoryList } = useMyShop(); + + const { + register, control, trigger, setValue, handleSubmit, formState: { errors }, + } = useFormContext(); + const { + imageFile, imgRef, saveImgFile, uploadError, setImageFile, + } = useImagesUpload(); + + const operateTimeState = useOperateTimeState(); + + const imageUrls = useWatch({ control, name: 'image_urls' }); + const name = useWatch({ control, name: 'name' }); + const categoryId = useWatch({ control, name: 'category_ids' }); + const phone = useWatch({ control, name: 'phone' }); + const address = useWatch({ control, name: 'address' }); + const deliveryPrice = useWatch({ control, name: 'delivery_price' }); + const description = useWatch({ control, name: 'description' }); + const delivery = useWatch({ control, name: 'delivery' }); + const payCard = useWatch({ control, name: 'pay_card' }); + const payBank = useWatch({ control, name: 'pay_bank' }); + const selectedId = categoryList?.shop_categories[categoryId[0] - 1]?.name; + + const { + isAllSameTime, + hasClosedDay, + isSpecificDayClosedAndAllSameTime, + isAllClosed, + } = CheckSameTime(); + + const formatPhoneNumber = (inputNumber: string) => { + const phoneNumber = inputNumber.replace(/\D/g, ''); + const formattedPhoneNumber = phoneNumber.replace(/^(\d{3})(\d{4})(\d{4})$/, '$1-$2-$3'); + return formattedPhoneNumber; + }; + + const handlePhoneChange = (e: React.ChangeEvent) => { + const formattedValue = formatPhoneNumber(e.target.value); + setValue('phone', formattedValue); + }; + + const handleNextClick = async () => { + const isValid = await trigger(['image_urls', 'name', 'address', 'category_ids', 'phone']); + if (!isValid) { + return; + } + openConfirmPopup(); + }; + + const openTimeArray = Object.values(openTimeState); + const closeTimeArray = Object.values(closeTimeState); + const shopClosedArray = Object.values(shopClosedState); + + const onClickRemoveImageUrl = (e: React.MouseEvent, imageUrl: string) => { + e.preventDefault(); + setImageFile(imageFile.filter((img) => img !== imageUrl)); + }; + + useEffect(() => { + const openValue = DAY_OF_WEEK.map((day, index) => ({ + close_time: closeTimeArray[index], + closed: shopClosedArray[index], + day_of_week: day, + open_time: openTimeArray[index], + })); + setValue('open', openValue); + }, [closeTimeArray, imageFile, openTimeArray, setValue, shopClosedArray]); + + useEffect(() => { + if (imageFile.length > 0) { + setValue('image_urls', imageFile); + } + }, [imageFile, setValue]); + + const mutation = usePostData({ onNext }); + + const onSubmit: SubmitHandler = (data) => { + mutation.mutate(data); + }; + + return ( +
+
+ + +
+ 대표 이미지 + + {uploadError === '' && errors.image_urls + && } + {uploadError !== '' && } +
+
+ 카테고리 +
+ + + + +
+ {errors.category_ids && } +
+ + + +
+ 가게명 +
+ + +
+ {errors.name && } +
+ + + +
+ 주소정보 +
+ +
+ {errors.address && } +
+
+ 전화번호 +
+ +
+ {errors.phone && } +
+
+ 배달금액 +
+ (e.target as HTMLElement).blur()} + /> +
+
+
+ 운영시간 +
+
+
+ {isAllSameTime && !hasClosedDay ? ( +
+ {operateTimeState.time} +
+ ) : null} + {isSpecificDayClosedAndAllSameTime ? ( +
+
{operateTimeState.time}
+
{operateTimeState.holiday}
+
+ ) : null} + {!isAllSameTime && !isSpecificDayClosedAndAllSameTime && !isAllClosed ? ( + <> + {WEEK.map((day) => ( +
+ {shopClosedState[day] ? `${operateTimeState[day]}` : `${day} : ${operateTimeState[day]}`} +
+ ))} + + ) : null} + {isAllClosed ? ( + 매일 휴무 + ) : null} +
+
+ +
+
+ + + +
+ 기타사항 +
+ +
+
+
+ + + +
+
+ +
+ + +
+ +
+ ); +} diff --git a/src/page/ShopRegistration/view/PC/ShopEntry/ShopEntry.module.scss b/src/page/ShopRegistration/view/PC/ShopEntry/ShopEntry.module.scss new file mode 100644 index 00000000..a2f11362 --- /dev/null +++ b/src/page/ShopRegistration/view/PC/ShopEntry/ShopEntry.module.scss @@ -0,0 +1,56 @@ +.wrapper { + position: relative; +} + +.block { + height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + &__writing-icon { + margin-bottom: 72px; + } + + &__title { + display: block; + text-align: center; + font-size: 36px; + font-weight: 700; + color: #175c8e; + margin-bottom: 24px; + height: 53px; + } + + &__text { + display: flex; + flex-direction: column; + gap: 5px; + text-align: center; + font-size: 16px; + font-weight: 400; + margin-bottom: 80px; + color: #858585; + + span { + line-height: 180%; + } + } + + &__next-button { + display: flex; + justify-content: center; + align-items: center; + width: 368px; + height: 48px; + font-weight: 500; + color: #ffffff; + background-color: #175c8e; + text-decoration: none; + + &:hover { + cursor: pointer; + } + } +} diff --git a/src/page/ShopRegistration/view/PC/ShopEntry/index.tsx b/src/page/ShopRegistration/view/PC/ShopEntry/index.tsx new file mode 100644 index 00000000..fe3a9dd3 --- /dev/null +++ b/src/page/ShopRegistration/view/PC/ShopEntry/index.tsx @@ -0,0 +1,29 @@ +import { ReactComponent as Memo } from 'assets/svg/shopRegistration/memo.svg'; +import Copyright from 'component/common/Copyright'; +import styles from './ShopEntry.module.scss'; + +export default function ShopEntry({ onNext }:{ onNext: () => void }) { + return ( +
+
+ + 가게 정보 기입 +
+ + 가게의 다양한 정보를 입력 및 수정하여 +
+ 학생들에게 최신 가게 정보를 알려주세요 +
+
+ +
+ +
+ ); +} diff --git a/src/page/ShopRegistration/view/PC/ShopRegistrationPC.module.scss b/src/page/ShopRegistration/view/PC/ShopRegistrationPC.module.scss index a5f3393c..e993d6f2 100644 --- a/src/page/ShopRegistration/view/PC/ShopRegistrationPC.module.scss +++ b/src/page/ShopRegistration/view/PC/ShopRegistrationPC.module.scss @@ -106,7 +106,6 @@ input::-webkit-inner-spin-button { padding: 10px 20px; width: 340px; height: 180px; - justify-content: flex-start; } } @@ -122,7 +121,6 @@ input::-webkit-inner-spin-button { &__main-item { max-width: 370px; display: flex; - z-index: 99; } &__main-text { diff --git a/src/page/ShopRegistration/view/PC/index.tsx b/src/page/ShopRegistration/view/PC/index.tsx index 3235cec3..243c92c7 100644 --- a/src/page/ShopRegistration/view/PC/index.tsx +++ b/src/page/ShopRegistration/view/PC/index.tsx @@ -1,458 +1,71 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import { ReactComponent as Memo } from 'assets/svg/shopRegistration/memo.svg'; -import { ReactComponent as Logo } from 'assets/svg/auth/koin-logo.svg'; -import { ReactComponent as Cutlery } from 'assets/svg/shopRegistration/cutlery.svg'; -import { useEffect, useState } from 'react'; -import useStepStore from 'store/useStepStore'; import Copyright from 'component/common/Copyright'; -import CustomButton from 'page/Auth/Signup/component/CustomButton'; import Complete from 'component/Auth/Complete'; -import Category from 'page/ShopRegistration/component/Modal/Category'; -import SearchShop from 'page/ShopRegistration/component/Modal/SearchShop'; -import OperateTimePC from 'page/ShopRegistration/component/Modal/OperateTimePC'; -import ConfirmPopup from 'page/ShopRegistration/component/ConfirmPopup'; -import useMediaQuery from 'utils/hooks/useMediaQuery'; -import useBooleanState from 'utils/hooks/useBooleanState'; -import CustomModal from 'component/common/CustomModal'; -import cn from 'utils/ts/className'; -import useModalStore from 'store/modalStore'; -import { WEEK, DAY_OF_WEEK } from 'utils/constant/week'; -import { SubmitHandler, useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; +import { FormProvider, useForm } from 'react-hook-form'; import { OwnerShop } from 'model/shopInfo/ownerShop'; -import useImagesUpload from 'utils/hooks/useImagesUpload'; -import CheckSameTime from 'page/ShopRegistration/hooks/CheckSameTime'; -import useOperateTimeState from 'page/ShopRegistration/hooks/useOperateTimeState'; -import useShopRegistrationStore from 'store/shopRegistration'; -import ErrorMessage from 'component/common/ErrorMessage'; -import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; -import { usePostData } from 'page/ShopRegistration/view/Mobile/ShopConfirmation/index'; -import { ReactComponent as FileImage } from 'assets/svg/auth/default-file.svg'; +import { useFunnel } from 'utils/hooks/useFunnel'; import styles from './ShopRegistrationPC.module.scss'; +import ShopEntry from './ShopEntry'; +import ShopConfirmation from './ShopConfirmation'; -export default function ShopRegistrationPC() { - const { isMobile } = useMediaQuery(); - const { step, setStep } = useStepStore(); - const { - value: showCategory, - setTrue: openCategory, - setFalse: closeCategory, - changeValue: toggleCategory, - } = useBooleanState(false); - const { - value: showOperateTime, - setTrue: openOperateTime, - setFalse: closeOperateTime, - } = useBooleanState(false); - const { - value: showSearchShop, - setTrue: openSearchShop, - setFalse: closeSearchShop, - } = useBooleanState(false); - const { - value: showConfirmPopup, - setTrue: openConfirmPopup, - setFalse: closeConfirmPopup, - } = useBooleanState(false); - const { - imageFile, imgRef, saveImgFile, uploadError, setImageFile, - } = useImagesUpload(); - const [isError, setIsError] = useState(false); - - const { - openTimeState, - closeTimeState, - shopClosedState, - } = useModalStore(); - - const { - setImageUrls, - setName, - setDelivery, - setPayCard, - setPayBank, - setAddress, - setPhone, - setDeliveryPrice, - setDescription, - removeImageUrl, - } = useShopRegistrationStore(); - - const { - imageUrls, - categoryId, - category, - name, - delivery, - payCard, - payBank, - address, - phone, - deliveryPrice, - description, - } = useShopRegistrationStore(); - const operateTimeState = useOperateTimeState(); - - const { - isAllSameTime, - hasClosedDay, - isSpecificDayClosedAndAllSameTime, - isAllClosed, - } = CheckSameTime(); +const OPEN_DEFAULT_VALUES = [ + { + day_of_week: 'MONDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'TUESDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'WEDNESDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'THURSDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'FRIDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'SATURDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, + { + day_of_week: 'SUNDAY', closed: false, open_time: '00:00', close_time: '00:00', + }, +]; - const mutation = usePostData({}); - - const { - register, handleSubmit, setValue, formState: { errors }, - } = useForm({ - resolver: zodResolver(OwnerShop), +export default function ShopRegistrationPC() { + const methods = useForm({ + defaultValues: { + category_ids: [], + delivery_price: 0, + description: '', + image_urls: [], + name: '', + phone: '', + address: '', + delivery: false, + pay_bank: false, + pay_card: false, + open: OPEN_DEFAULT_VALUES, + }, }); - const formatPhoneNumber = (inputNumber: string) => { - const phoneNumber = inputNumber.replace(/\D/g, ''); - const formattedPhoneNumber = phoneNumber.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'); - if (formattedPhoneNumber.length > 13) return formattedPhoneNumber.slice(0, 13); - return formattedPhoneNumber; - }; - const phoneNumberPattern = /^\d{3}-\d{4}-\d{4}$/; - const isValidPhoneNumber = phoneNumberPattern.test(phone); - const handleNextClick = () => { - if (imageUrls.length === 0 || name === '' || category.length === 0 - || address === '' || phone === '' || !isValidPhoneNumber) { - setIsError(true); - } else { - setIsError(false); - openConfirmPopup(); - } - }; - const openTimeArray = Object.values(openTimeState); - const closeTimeArray = Object.values(closeTimeState); - const shopClosedArray = Object.values(shopClosedState); - - const onClickRemoveImageUrl = (e: React.MouseEvent, imageUrl: string) => { - e.preventDefault(); - setImageFile(imageFile.filter((img) => img !== imageUrl)); - removeImageUrl(imageUrl); - }; + const { Funnel, Step, setStep } = useFunnel('가게 등록'); - useEffect(() => { - if (imageFile.length > 0 || uploadError !== '') setImageUrls(imageFile); - const openValue = DAY_OF_WEEK.map((day, index) => ({ - close_time: closeTimeArray[index], - closed: shopClosedArray[index], - day_of_week: day, - open_time: openTimeArray[index], - })); - setValue('open', openValue); - setValue('category_ids', [categoryId]); - setValue('delivery_price', Number(deliveryPrice)); - setValue('name', name); - setValue('image_urls', imageUrls); - }, [openTimeState, closeTimeState, shopClosedState, imageUrls, - imageFile, categoryId, deliveryPrice, uploadError, name]); - const onSubmit: SubmitHandler = (data) => { - mutation.mutate(data); - }; - - // step 1일 때 그리고 모바일에서 PC로 변경 될 때 카테고리 모달을 자동으로 켜줌 - useEffect(() => { - if (!isMobile && step === 1) { - toggleCategory(); - } - }, [isMobile]); return ( - <> - {step === 0 && ( -
-
- - 가게 정보 기입 -
- - 가게의 다양한 정보를 입력 및 수정하여 -
- 학생들에게 최신 가게 정보를 알려주세요 -
-
- -
- -
- )} - {step >= 1 && step <= 4 && ( -
-
- -
-
- 대표 이미지 - - {uploadError === '' && imageUrls.length === 0 && isError - && } - {uploadError !== '' && } -
-
- 카테고리 -
- - -
- {category.length === 0 - && isError - && } -
- - - -
- 가게명 -
- { - setName(e.target.value); - }} - /> - -
- {name === '' && isError && } -
- - - -
- 주소정보 -
- { - setAddress(e.target.value); - }} - /> -
- {address === '' && isError && } -
-
- 전화번호 -
- { - setPhone(formatPhoneNumber(e.target.value)); - }} - /> -
- {phone === '' && isError && } - {phone !== '' && !isValidPhoneNumber && isError && } -
-
- 배달금액 -
- { - setDeliveryPrice(Number(e.target.value)); - }} - /> -
-
-
- 운영시간 -
-
-
- { - isAllSameTime && !hasClosedDay ? ( -
- {operateTimeState.time} -
- ) - : null - } - { - isSpecificDayClosedAndAllSameTime ? ( -
-
{operateTimeState.time}
-
{operateTimeState.holiday}
-
- ) : null - } - { - !isAllSameTime && !isSpecificDayClosedAndAllSameTime && !isAllClosed ? ( - <> - {WEEK.map((day) => ( -
- {shopClosedState[day] ? `${operateTimeState[day]}` : `${day} : ${operateTimeState[day]}`} -
- ))} - - ) : null - } - { - isAllClosed ? ( - 매일 휴무 - ) : null - } -
-
- -
-
- - - -
- 기타사항 -
- { - setDescription(e.target.value); - }} - /> -
-
-
- - - -
-
- -
- - + + + + setStep('가게 정보 입력')} /> + + + setStep('가게 등록 완료')} /> + + +
+ +
- -
- )} - {step === 5 && ( -
- - -
- )} - + + + ); } From 7f179bdec53ba95cddd877d16d19639f910c8879 Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Tue, 11 Jun 2024 14:56:54 +0900 Subject: [PATCH 09/14] =?UTF-8?q?fix:=20=EC=97=90=EB=9F=AC=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/Mobile/Sub/index.tsx | 2 ++ .../view/PC/ShopConfirmation/index.tsx | 30 +++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx index 85d10c98..8fe784ea 100644 --- a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx @@ -89,6 +89,7 @@ export default function Sub({ onNext }:{ onNext: () => void }) { 전화번호 void }) { 배달금액 void }) { const { categoryList } = useMyShop(); const { - register, control, trigger, setValue, handleSubmit, formState: { errors }, + register, control, setValue, handleSubmit, formState: { errors }, } = useFormContext(); const { imageFile, imgRef, saveImgFile, uploadError, setImageFile, } = useImagesUpload(); + const [isError, setIsError] = useState(false); + const operateTimeState = useOperateTimeState(); const imageUrls = useWatch({ control, name: 'image_urls' }); @@ -93,12 +95,14 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { setValue('phone', formattedValue); }; - const handleNextClick = async () => { - const isValid = await trigger(['image_urls', 'name', 'address', 'category_ids', 'phone']); - if (!isValid) { - return; + const handleNextClick = () => { + if (imageUrls.length === 0 || name === '' || categoryId.length === 0 + || address === '' || phone === '') { + setIsError(true); + } else { + setIsError(false); + openConfirmPopup(); } - openConfirmPopup(); }; const openTimeArray = Object.values(openTimeState); @@ -175,7 +179,7 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { )} - {uploadError === '' && errors.image_urls + {uploadError === '' && isError && imageUrls.length === 0 && } {uploadError !== '' && }
@@ -196,7 +200,7 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) {
- {errors.category_ids && } + {isError && categoryId.length === 0 && }
void }) { />
- {errors.name && } + {isError && name === '' && }
void }) { {...register('address', { required: true })} />
- {errors.address && } + {isError && address === '' && }
전화번호
void }) { })} />
- {errors.phone && } + {isError && phone === '' && }
배달금액
Date: Tue, 11 Jun 2024 15:07:01 +0900 Subject: [PATCH 10/14] =?UTF-8?q?fix:=20=EB=8B=A8=EC=9C=84=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/EditShopInfoModal/EditShopInfoModal.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/page/MyShopPage/components/EditShopInfoModal/EditShopInfoModal.module.scss b/src/page/MyShopPage/components/EditShopInfoModal/EditShopInfoModal.module.scss index 51cf6f9c..7b8a138a 100644 --- a/src/page/MyShopPage/components/EditShopInfoModal/EditShopInfoModal.module.scss +++ b/src/page/MyShopPage/components/EditShopInfoModal/EditShopInfoModal.module.scss @@ -323,7 +323,7 @@ margin-bottom: 24px; &--error { - margin-bottom: 0px; + margin-bottom: 0; } } From 818d6e0e37f914dc468c9df9824bf6dae7551abe Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Tue, 11 Jun 2024 21:00:11 +0900 Subject: [PATCH 11/14] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20css=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/PC/ShopRegistrationPC.module.scss | 233 ------------------ 1 file changed, 233 deletions(-) diff --git a/src/page/ShopRegistration/view/PC/ShopRegistrationPC.module.scss b/src/page/ShopRegistration/view/PC/ShopRegistrationPC.module.scss index e993d6f2..d5b080d2 100644 --- a/src/page/ShopRegistration/view/PC/ShopRegistrationPC.module.scss +++ b/src/page/ShopRegistration/view/PC/ShopRegistrationPC.module.scss @@ -1,236 +1,3 @@ -input::-webkit-outer-spin-button, -input::-webkit-inner-spin-button { - appearance: none; - margin: 0; -} - .wrapper { position: relative; } - -.block { - height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - - &__writing-icon { - margin-bottom: 72px; - } - - &__title { - display: block; - text-align: center; - font-size: 36px; - font-weight: 700; - color: #175c8e; - margin-bottom: 24px; - height: 53px; - } - - &__text { - display: flex; - flex-direction: column; - gap: 5px; - text-align: center; - font-size: 16px; - font-weight: 400; - margin-bottom: 80px; - color: #858585; - - span { - line-height: 180%; - } - } - - &__next-button { - display: flex; - justify-content: center; - align-items: center; - width: 368px; - height: 48px; - font-weight: 500; - color: #ffffff; - background-color: #175c8e; - text-decoration: none; - - &:hover { - cursor: pointer; - } - } -} - -.container { - position: relative; - display: flex; - flex-direction: column; - align-items: center; - padding: 80px 0 94px; - - &__koin-logo { - width: 368px; - position: relative; - margin-bottom: 56px; - background-color: #ffffff; - } -} - -.form { - position: relative; - display: flex; - flex-direction: column; - align-items: center; - gap: 16px; - - &__title { - display: block; - font-size: 18px; - margin-bottom: 8px; - } - - &__image-upload { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 210px; - height: 93px; - border: 1px solid #d2dae2; - padding: 53px 80px 54px; - cursor: pointer; - - &--active { - display: flex; - flex-direction: column; - padding: 10px 20px; - width: 340px; - height: 180px; - } - } - - &__upload-file { - display: none; - } - - &__main-menu { - max-width: 370px; - max-height: 200px; - } - - &__main-item { - max-width: 370px; - display: flex; - } - - &__main-text { - width: 90%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - word-break: break-all; - color: #858585; - } - - &__cutlery-cross { - width: 64px; - height: 64px; - margin: 0 auto; - display: flex; - justify-content: center; - align-items: center; - position: relative; - } - - &__text { - text-align: center; - font-size: 14px; - display: block; - color: #858585; - margin-top: 8px; - } - - &__section { - display: flex; - justify-content: space-between; - gap: 16px; - margin-top: 8px; - } - - &__input { - width: 240px; - border: 1px solid #d2dae2; - height: 22px; - padding: 13px 16px; - - &:focus { - border-bottom: 1px solid black; - } - } - - &__input-large { - width: 336px; - border: 1px solid #d2dae2; - height: 22px; - padding: 13px 16px; - - &:focus { - border-bottom: 1px solid black; - } - } - - &__operate-time { - display: flex; - align-items: center; - width: 272px; - height: auto; - font-size: 16px; - color: #858585; - } - - &__checkbox { - display: flex; - flex-direction: row; - justify-content: flex-start; - gap: 24px; - width: 368px; - } - - &__checkbox-label { - display: flex; - align-items: center; - font-weight: 500; - font-size: 14px; - color: #858585; - box-sizing: content-box; - cursor: pointer; - - input[type="checkbox"]:checked + span { - color: #f7941e; - } - } - - &__checkbox-input { - appearance: none; - width: 14px; - height: 14px; - border-radius: 100%; - box-sizing: border-box; - border: 1px solid #858585; - margin-right: 8px; - background-size: cover; - cursor: pointer; - - &:checked { - border: 2px solid #f7941e; - padding: 1px; - background-clip: content-box; - background-color: #f7941e; - } - } - - &__next-button { - width: 368px; - margin-top: 56px; - } -} From c9d0d3a80ec16dcc06088d03250b8de372940d45 Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Wed, 12 Jun 2024 16:50:34 +0900 Subject: [PATCH 12/14] =?UTF-8?q?fix:=20=EC=9A=B4=EC=98=81=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20useStoreTimeSetUp=20=ED=9B=85=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooks/useStoreTimeSetUp.ts | 26 ++++++++++++++ .../view/Mobile/Sub/index.tsx | 24 ++++--------- .../view/PC/ShopConfirmation/index.tsx | 36 ++++++------------- 3 files changed, 44 insertions(+), 42 deletions(-) create mode 100644 src/page/ShopRegistration/hooks/useStoreTimeSetUp.ts diff --git a/src/page/ShopRegistration/hooks/useStoreTimeSetUp.ts b/src/page/ShopRegistration/hooks/useStoreTimeSetUp.ts new file mode 100644 index 00000000..e28ed3b4 --- /dev/null +++ b/src/page/ShopRegistration/hooks/useStoreTimeSetUp.ts @@ -0,0 +1,26 @@ +import { OwnerShop } from 'model/shopInfo/ownerShop'; +import { useEffect } from 'react'; +import { UseFormSetValue } from 'react-hook-form'; +import useModalStore from 'store/modalStore'; +import { DAY_OF_WEEK } from 'utils/constant/week'; + +interface StoreTimeSetUpProps { + setValue : UseFormSetValue; +} + +export default function useStoreTimeSetUp({ setValue }: StoreTimeSetUpProps) { + const { openTimeState, closeTimeState, shopClosedState } = useModalStore(); + const openTimeArray = Object.values(openTimeState); + const closeTimeArray = Object.values(closeTimeState); + const shopClosedArray = Object.values(shopClosedState); + + useEffect(() => { + const openValue = DAY_OF_WEEK.map((day, index) => ({ + close_time: closeTimeArray[index], + closed: shopClosedArray[index], + day_of_week: day, + open_time: openTimeArray[index], + })); + setValue('open', openValue); + }, [closeTimeArray, openTimeArray, setValue, shopClosedArray]); +} diff --git a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx index 8fe784ea..d5690671 100644 --- a/src/page/ShopRegistration/view/Mobile/Sub/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/Sub/index.tsx @@ -2,13 +2,14 @@ import OperateTimeMobile from 'page/ShopRegistration/component/Modal/OperateTime import useBooleanState from 'utils/hooks/useBooleanState'; import useOperateTimeState from 'page/ShopRegistration/hooks/useOperateTimeState'; import CheckSameTime from 'page/ShopRegistration/hooks/CheckSameTime'; -import { DAY_OF_WEEK, WEEK } from 'utils/constant/week'; +import { WEEK } from 'utils/constant/week'; import useModalStore from 'store/modalStore'; import ErrorMessage from 'component/common/ErrorMessage'; import { ERRORMESSAGE } from 'page/ShopRegistration/constant/errorMessage'; import cn from 'utils/ts/className'; import { useFormContext, useWatch } from 'react-hook-form'; -import { useEffect } from 'react'; +import useStoreTimeSetUp from 'page/ShopRegistration/hooks/useStoreTimeSetUp'; +import { OwnerShop } from 'model/shopInfo/ownerShop'; import styles from './Sub.module.scss'; export default function Sub({ onNext }:{ onNext: () => void }) { @@ -26,31 +27,20 @@ export default function Sub({ onNext }:{ onNext: () => void }) { isAllClosed, } = CheckSameTime(); - const { openTimeState, closeTimeState, shopClosedState } = useModalStore(); - const openTimeArray = Object.values(openTimeState); - const closeTimeArray = Object.values(closeTimeState); - const shopClosedArray = Object.values(shopClosedState); + const { shopClosedState } = useModalStore(); const { register, control, trigger, setValue, formState: { errors }, - } = useFormContext(); + } = useFormContext(); const phone = useWatch({ control, name: 'phone' }); const deliveryPrice = useWatch({ control, name: 'delivery_price' }); const description = useWatch({ control, name: 'description' }); const delivery = useWatch({ control, name: 'delivery' }); - const payBank = useWatch({ control, name: 'paya_bank' }); + const payBank = useWatch({ control, name: 'pay_bank' }); const payCard = useWatch({ control, name: 'pay_card' }); - useEffect(() => { - const openValue = DAY_OF_WEEK.map((day, index) => ({ - close_time: closeTimeArray[index], - closed: shopClosedArray[index], - day_of_week: day, - open_time: openTimeArray[index], - })); - setValue('open', openValue); - }, [closeTimeArray, openTimeArray, setValue, shopClosedArray]); + useStoreTimeSetUp({ setValue }); const formatPhoneNumber = (inputNumber:string) => { const phoneNumber = inputNumber.replace(/\D/g, ''); diff --git a/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx b/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx index e620703e..ed81da79 100644 --- a/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx +++ b/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx @@ -9,7 +9,7 @@ import ConfirmPopup from 'page/ShopRegistration/component/ConfirmPopup'; import CustomModal from 'component/common/CustomModal'; import cn from 'utils/ts/className'; import useModalStore from 'store/modalStore'; -import { WEEK, DAY_OF_WEEK } from 'utils/constant/week'; +import { WEEK } from 'utils/constant/week'; import { SubmitHandler, useFormContext, useWatch } from 'react-hook-form'; import { OwnerShop } from 'model/shopInfo/ownerShop'; import useImagesUpload from 'utils/hooks/useImagesUpload'; @@ -22,6 +22,7 @@ import { ReactComponent as FileImage } from 'assets/svg/auth/default-file.svg'; import useMyShop from 'query/shop'; import { useEffect, useState } from 'react'; import useBooleanState from 'utils/hooks/useBooleanState'; +import useStoreTimeSetUp from 'page/ShopRegistration/hooks/useStoreTimeSetUp'; import styles from './ShopConfirmation.module.scss'; export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { @@ -46,17 +47,21 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { setFalse: closeConfirmPopup, } = useBooleanState(false); + const { shopClosedState } = useModalStore(); + const { - openTimeState, - closeTimeState, - shopClosedState, - } = useModalStore(); + isAllSameTime, + hasClosedDay, + isSpecificDayClosedAndAllSameTime, + isAllClosed, + } = CheckSameTime(); const { categoryList } = useMyShop(); const { register, control, setValue, handleSubmit, formState: { errors }, } = useFormContext(); + const { imageFile, imgRef, saveImgFile, uploadError, setImageFile, } = useImagesUpload(); @@ -77,12 +82,7 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { const payBank = useWatch({ control, name: 'pay_bank' }); const selectedId = categoryList?.shop_categories[categoryId[0] - 1]?.name; - const { - isAllSameTime, - hasClosedDay, - isSpecificDayClosedAndAllSameTime, - isAllClosed, - } = CheckSameTime(); + useStoreTimeSetUp({ setValue }); const formatPhoneNumber = (inputNumber: string) => { const phoneNumber = inputNumber.replace(/\D/g, ''); @@ -105,25 +105,11 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { } }; - const openTimeArray = Object.values(openTimeState); - const closeTimeArray = Object.values(closeTimeState); - const shopClosedArray = Object.values(shopClosedState); - const onClickRemoveImageUrl = (e: React.MouseEvent, imageUrl: string) => { e.preventDefault(); setImageFile(imageFile.filter((img) => img !== imageUrl)); }; - useEffect(() => { - const openValue = DAY_OF_WEEK.map((day, index) => ({ - close_time: closeTimeArray[index], - closed: shopClosedArray[index], - day_of_week: day, - open_time: openTimeArray[index], - })); - setValue('open', openValue); - }, [closeTimeArray, imageFile, openTimeArray, setValue, shopClosedArray]); - useEffect(() => { if (imageFile.length > 0) { setValue('image_urls', imageFile); From e60d0e232779ba687cf507fe2509a4cb73c79c78 Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Wed, 12 Jun 2024 17:00:53 +0900 Subject: [PATCH 13/14] =?UTF-8?q?fix:=20=EB=B3=80=EC=88=98=EB=AA=85,=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=AA=85=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/Mobile/Main/index.tsx | 10 +++++----- .../view/Mobile/ShopConfirmation/index.tsx | 12 +++++------ .../view/PC/ShopConfirmation/index.tsx | 20 +++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/page/ShopRegistration/view/Mobile/Main/index.tsx b/src/page/ShopRegistration/view/Mobile/Main/index.tsx index 12821f05..5b454d85 100644 --- a/src/page/ShopRegistration/view/Mobile/Main/index.tsx +++ b/src/page/ShopRegistration/view/Mobile/Main/index.tsx @@ -21,8 +21,8 @@ export default function Main({ onNext }:{ onNext: () => void }) { imageFile, imgRef, saveImgFile, uploadError, setImageFile, } = useImagesUpload(); - const handleDeleteImage = (image: string) => { - setImageFile(imageFile.filter((img) => img !== image)); + const handleDeleteImage = (url: string) => { + setImageFile(imageFile.filter((img) => img !== url)); }; useEffect(() => { @@ -60,12 +60,12 @@ export default function Main({ onNext }:{ onNext: () => void }) { {imageUrls.length !== 0 ? (
- {imageUrls.map((image:string) => ( + {imageUrls.map((url: string) => ( <> - 대표 이미지 + 대표 이미지
diff --git a/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx b/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx index ed81da79..2efe31d6 100644 --- a/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx +++ b/src/page/ShopRegistration/view/PC/ShopConfirmation/index.tsx @@ -105,7 +105,7 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) { } }; - const onClickRemoveImageUrl = (e: React.MouseEvent, imageUrl: string) => { + const handleDeleteImage = (e: React.MouseEvent, imageUrl: string) => { e.preventDefault(); setImageFile(imageFile.filter((img) => img !== imageUrl)); }; @@ -151,7 +151,7 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) {
onClickRemoveImageUrl(e, imageUrl)} + onClick={(e) => handleDeleteImage(e, imageUrl)} aria-hidden > @@ -268,18 +268,18 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) {
- {isAllSameTime && !hasClosedDay ? ( + {isAllSameTime && !hasClosedDay && (
{operateTimeState.time}
- ) : null} - {isSpecificDayClosedAndAllSameTime ? ( + )} + {isSpecificDayClosedAndAllSameTime && (
{operateTimeState.time}
{operateTimeState.holiday}
- ) : null} - {!isAllSameTime && !isSpecificDayClosedAndAllSameTime && !isAllClosed ? ( + )} + {!isAllSameTime && !isSpecificDayClosedAndAllSameTime && !isAllClosed && ( <> {WEEK.map((day) => (
@@ -287,10 +287,10 @@ export default function ShopConfirmation({ onNext }:{ onNext: () => void }) {
))} - ) : null} - {isAllClosed ? ( + )} + {isAllClosed && ( 매일 휴무 - ) : null} + )}
From 8e77f8595a28a5e75815a422f314bb4d166e1401 Mon Sep 17 00:00:00 2001 From: Daeeui Kim Date: Wed, 12 Jun 2024 18:34:08 +0900 Subject: [PATCH 14/14] =?UTF-8?q?feat:=20jsDoc=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/hooks/useFunnel.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/utils/hooks/useFunnel.ts b/src/utils/hooks/useFunnel.ts index 399da5b3..cf3675c8 100644 --- a/src/utils/hooks/useFunnel.ts +++ b/src/utils/hooks/useFunnel.ts @@ -9,6 +9,11 @@ export interface FunnelProps { children: ReactElement[]; } +/** +* 단계별 입력 폼을 진행할 시 사용 권장 +* @param { string } defaultStep 시작할 초기 단계 +* @returns { Funnel, Step, setStep, currentStep } 단계별 입력 폼을 관리하는데 사용하는 유틸리티 +*/ export const useFunnel = (defaultStep: string) => { const [step, setStep] = useState(defaultStep);