From 32513ff37e67922e9780bd2b804b9467dcef1c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=98=88=EC=A7=84?= Date: Sun, 21 Jul 2024 19:14:50 +0900 Subject: [PATCH] feat(app/form): integrate form page --- src/app/routes/form._index.tsx | 42 +++++++++++++++++++ src/app/routes/form.tsx | 14 ++++--- src/app/styles/form.module.css | 6 +++ .../form/complete/CompletePage.module.css | 8 ++-- .../complete/UploadLoadingPage.module.css | 1 - src/pages/form/complete/UploadLoadingPage.tsx | 10 ++++- .../form/confirm/FormConfirmPage.module.css | 7 ++-- src/pages/form/confirm/FormConfirmPage.tsx | 6 +-- .../ideal_partner/IdealPartnerPage.module.css | 2 +- .../form/ideal_partner/IdealPartnerPage.tsx | 18 ++++---- .../intro/IdealParterIntroPage.module.css | 1 - .../form/intro/IdealPartnerIntroPage.tsx | 5 ++- .../intro/ProfileFormIntroPage.module.css | 1 - src/pages/form/intro/ProfileFormIntroPage.tsx | 10 ++++- .../form/my_profile/MyProfilePage.module.css | 2 +- src/pages/form/my_profile/MyProfilePage.tsx | 18 ++++---- src/pages/layout/WideDeviceLayout.module.css | 2 +- src/processes/shortcut/Shortcut.tsx | 13 ++++-- src/shared/ui/BottomSheet/BottomSheet.tsx | 2 +- src/shared/ui/SwitchCase.tsx | 18 ++++++++ 20 files changed, 139 insertions(+), 47 deletions(-) create mode 100644 src/app/routes/form._index.tsx create mode 100644 src/app/styles/form.module.css create mode 100644 src/shared/ui/SwitchCase.tsx diff --git a/src/app/routes/form._index.tsx b/src/app/routes/form._index.tsx new file mode 100644 index 0000000..680d02a --- /dev/null +++ b/src/app/routes/form._index.tsx @@ -0,0 +1,42 @@ +import { ProfileFormIntroPage } from 'src/pages/form/intro/ProfileFormIntroPage'; +import { useState } from 'react'; +import { SwitchCase } from 'src/shared/ui/SwitchCase'; +import { MyProfilePage } from 'src/pages/form/my_profile/MyProfilePage'; +import { IdealPartnerIntroPage } from 'src/pages/form/intro/IdealPartnerIntroPage'; +import { IdealPartnerPage } from 'src/pages/form/ideal_partner/IdealPartnerPage'; +import { FormConfirmPage } from 'src/pages/form/confirm/FormConfirmPage'; +import { UploadLoadingPage } from 'src/pages/form/complete/UploadLoadingPage'; +import { CompletePage } from 'src/pages/form/complete/CompletePage'; +import { useProfileFirstName } from 'src/entities/profile/lib/useProfileFirstName'; +import { Shortcut } from 'src/processes/shortcut/Shortcut'; +import styles from 'src/app/styles/form.module.css'; + +const MAX_STEP_COUNT = 7; + +export default function ProfileFormPage() { + const [step, setStep] = useState(0); + + const increase = () => setStep((prev) => (prev + 1 < MAX_STEP_COUNT ? prev + 1 : prev)); + + const name = useProfileFirstName(); + + const showShortcut = [1, 3, 4].includes(step); + + return ( +
+ , + 1: , + 2: , + 3: , + 4: , + 5: , + 6: , + }} + /> + {showShortcut && } +
+ ); +} diff --git a/src/app/routes/form.tsx b/src/app/routes/form.tsx index 6d704b7..9089b41 100644 --- a/src/app/routes/form.tsx +++ b/src/app/routes/form.tsx @@ -1,9 +1,13 @@ -import { ProfileFormIntroPage } from 'src/pages/form/intro/ProfileFormIntroPage'; +import { Outlet } from '@remix-run/react'; +import { MyProfileProvider } from 'src/entities/profile/model/myProfileStore'; +import { IdealPartnerProvider } from 'src/entities/ideal_partner/model/idealPartnerStore'; -export default function ProfileFormPage() { +export default function ProfileFormLayout() { return ( - <> - - + + + + + ); } diff --git a/src/app/styles/form.module.css b/src/app/styles/form.module.css new file mode 100644 index 0000000..14ac1ff --- /dev/null +++ b/src/app/styles/form.module.css @@ -0,0 +1,6 @@ +.Wrapper { + height: 100%; + padding: 0 20px 40px; + overflow: hidden; + position: relative; +} \ No newline at end of file diff --git a/src/pages/form/complete/CompletePage.module.css b/src/pages/form/complete/CompletePage.module.css index 0ee7881..a990eb0 100644 --- a/src/pages/form/complete/CompletePage.module.css +++ b/src/pages/form/complete/CompletePage.module.css @@ -1,5 +1,7 @@ .Wrapper { - padding: 0 24px; + position: relative; + height: 100%; + width: 100%; } .TitleSection { @@ -8,7 +10,7 @@ .Image { width: 80%; - position: fixed; - right: 0; + position: absolute; + right: -20px; bottom: 80px; } \ No newline at end of file diff --git a/src/pages/form/complete/UploadLoadingPage.module.css b/src/pages/form/complete/UploadLoadingPage.module.css index c8f67ac..7f1fd5d 100644 --- a/src/pages/form/complete/UploadLoadingPage.module.css +++ b/src/pages/form/complete/UploadLoadingPage.module.css @@ -1,5 +1,4 @@ .Wrapper { - padding: 0 24px 24px; display: flex; flex-direction: column; justify-content: space-between; diff --git a/src/pages/form/complete/UploadLoadingPage.tsx b/src/pages/form/complete/UploadLoadingPage.tsx index 2abe9c2..eaa13e3 100644 --- a/src/pages/form/complete/UploadLoadingPage.tsx +++ b/src/pages/form/complete/UploadLoadingPage.tsx @@ -1,7 +1,15 @@ import { InfoBox } from 'src/shared/ui/InfoBox/InfoBox'; import styles from './UploadLoadingPage.module.css'; +import { useEffect } from 'react'; + +export const UploadLoadingPage = ({ name, onComplete }: { name: string; onComplete: () => void }) => { + useEffect(() => { + const timer = setTimeout(onComplete, 5_000); + return () => { + clearTimeout(timer); + }; + }, []); -export const UploadLoadingPage = ({ name }: { name: string }) => { return (
diff --git a/src/pages/form/confirm/FormConfirmPage.module.css b/src/pages/form/confirm/FormConfirmPage.module.css index a0d478d..e5fef65 100644 --- a/src/pages/form/confirm/FormConfirmPage.module.css +++ b/src/pages/form/confirm/FormConfirmPage.module.css @@ -6,9 +6,10 @@ } .TitleSection { - padding: 16px 20px 20px; + margin-bottom: 20px; & h2 { + margin-top: 16px; margin-bottom: 8px; } @@ -24,9 +25,9 @@ } .ContentViewport { - padding: 20px; + padding-top: 20px; } .Footer { - padding: 28px 20px 8px; + padding-top: 24px; } \ No newline at end of file diff --git a/src/pages/form/confirm/FormConfirmPage.tsx b/src/pages/form/confirm/FormConfirmPage.tsx index b6f6375..e4a6afd 100644 --- a/src/pages/form/confirm/FormConfirmPage.tsx +++ b/src/pages/form/confirm/FormConfirmPage.tsx @@ -7,12 +7,11 @@ import { useIdealPartnerStore } from 'src/entities/ideal_partner/model/idealPart import { IdealPartnerProfile } from 'src/entities/ideal_partner/ui/IdealPartnerProfile/IdealPartnerProfile'; import styles from './FormConfirmPage.module.css'; import { ScrollView } from 'src/shared/ui/ScrollView/ScrollView'; -import { Shortcut } from 'src/processes/shortcut/Shortcut'; const TAB_TYPE_LIST = ['PROFILE', 'IDEAL_PARTNER'] as const; type TabType = (typeof TAB_TYPE_LIST)[number]; -export const FormConfirmPage = () => { +export const FormConfirmPage = ({ onClickNextStep }: { onClickNextStep: () => void }) => { const profile = useMyProfileStore((state) => state); const idealPartner = useIdealPartnerStore((state) => state); return ( @@ -42,11 +41,10 @@ export const FormConfirmPage = () => {
-
-
); diff --git a/src/pages/form/ideal_partner/IdealPartnerPage.module.css b/src/pages/form/ideal_partner/IdealPartnerPage.module.css index 1c53d3b..0a48575 100644 --- a/src/pages/form/ideal_partner/IdealPartnerPage.module.css +++ b/src/pages/form/ideal_partner/IdealPartnerPage.module.css @@ -41,5 +41,5 @@ .Footer { margin-top: auto; - padding: 15px 0; + padding-top: 24px; } \ No newline at end of file diff --git a/src/pages/form/ideal_partner/IdealPartnerPage.tsx b/src/pages/form/ideal_partner/IdealPartnerPage.tsx index 8430a2d..75d28d9 100644 --- a/src/pages/form/ideal_partner/IdealPartnerPage.tsx +++ b/src/pages/form/ideal_partner/IdealPartnerPage.tsx @@ -7,13 +7,21 @@ import { IdealPartnerStepMeta } from 'src/pages/form/ideal_partner/IdealPartnerS import { useIdealPartnerStore } from 'src/entities/ideal_partner/model/idealPartnerStore'; const Steps = Object.values(IdealPartnerStepMeta); -export const IdealPartnerPage = () => { +export const IdealPartnerPage = ({ onClickNextStep }: { onClickNextStep: () => void }) => { const [currentStepIdx, setCurrentStep] = useState(0); const name = useProfileFirstName(); const currentStep = Steps[currentStepIdx]; const canGoNext = useIdealPartnerStore(currentStep.canGoNext); + const handleClickNext = () => { + if (currentStepIdx < Steps.length - 1) { + setCurrentStep((prev) => prev + 1); + } else { + onClickNextStep(); + } + }; + return (
@@ -28,13 +36,7 @@ export const IdealPartnerPage = () => {
{currentStep.form}
-
diff --git a/src/pages/form/intro/IdealParterIntroPage.module.css b/src/pages/form/intro/IdealParterIntroPage.module.css index ee7c50d..0c6cd7e 100644 --- a/src/pages/form/intro/IdealParterIntroPage.module.css +++ b/src/pages/form/intro/IdealParterIntroPage.module.css @@ -1,7 +1,6 @@ .Wrapper { height: 100%; width: 100%; - padding: 0 20px 8px; display: flex; flex-direction: column; diff --git a/src/pages/form/intro/IdealPartnerIntroPage.tsx b/src/pages/form/intro/IdealPartnerIntroPage.tsx index a074506..0c6770b 100644 --- a/src/pages/form/intro/IdealPartnerIntroPage.tsx +++ b/src/pages/form/intro/IdealPartnerIntroPage.tsx @@ -3,9 +3,10 @@ import styles from './IdealParterIntroPage.module.css'; type IdealPartnerIntroPageProps = { name: string; + onClickNextStep: () => void; }; -export const IdealPartnerIntroPage = ({ name }: IdealPartnerIntroPageProps) => { +export const IdealPartnerIntroPage = ({ name, onClickNextStep }: IdealPartnerIntroPageProps) => { return (
@@ -22,7 +23,7 @@ export const IdealPartnerIntroPage = ({ name }: IdealPartnerIntroPageProps) => {

{'종이를 -
diff --git a/src/pages/form/intro/ProfileFormIntroPage.module.css b/src/pages/form/intro/ProfileFormIntroPage.module.css index 26d7a6f..a7c855c 100644 --- a/src/pages/form/intro/ProfileFormIntroPage.module.css +++ b/src/pages/form/intro/ProfileFormIntroPage.module.css @@ -1,7 +1,6 @@ .Wrapper { height: 100%; width: 100%; - padding: 0 20px 8px; display: flex; flex-direction: column; diff --git a/src/pages/form/intro/ProfileFormIntroPage.tsx b/src/pages/form/intro/ProfileFormIntroPage.tsx index e14446b..2a285dd 100644 --- a/src/pages/form/intro/ProfileFormIntroPage.tsx +++ b/src/pages/form/intro/ProfileFormIntroPage.tsx @@ -5,7 +5,7 @@ import { useBoolean } from 'src/shared/functions/useBoolean'; import styles from './ProfileFormIntroPage.module.css'; import { BottomSheet } from 'src/shared/ui/BottomSheet/BottomSheet'; -export const ProfileFormIntroPage = () => { +export const ProfileFormIntroPage = ({ onClickNextStep }: { onClickNextStep: () => void }) => { const { value: isOpen, setTrue: open, setFalse: close } = useBoolean(false); const { value: checkedPrivacy, toggle: togglePrivacy } = useBoolean(false); @@ -31,7 +31,13 @@ export const ProfileFormIntroPage = () => { + } diff --git a/src/pages/form/my_profile/MyProfilePage.module.css b/src/pages/form/my_profile/MyProfilePage.module.css index aedfc21..3659fba 100644 --- a/src/pages/form/my_profile/MyProfilePage.module.css +++ b/src/pages/form/my_profile/MyProfilePage.module.css @@ -40,5 +40,5 @@ .Footer { margin-top: auto; - padding: 15px 0; + padding-top: 24px; } \ No newline at end of file diff --git a/src/pages/form/my_profile/MyProfilePage.tsx b/src/pages/form/my_profile/MyProfilePage.tsx index 0c32f31..e6d24cf 100644 --- a/src/pages/form/my_profile/MyProfilePage.tsx +++ b/src/pages/form/my_profile/MyProfilePage.tsx @@ -8,13 +8,21 @@ import { MyProfileStepMeta } from 'src/pages/form/my_profile/MyProfileStepMeta'; const Steps = Object.values(MyProfileStepMeta); -export const MyProfilePage = () => { +export const MyProfilePage = ({ onClickNextStep }: { onClickNextStep: () => void }) => { const [currentStepIdx, setCurrentStep] = useState(0); const name = useProfileFirstName(); const currentStep = Steps[currentStepIdx]; const canGoNext = useMyProfileStore(currentStep.canGoNext); + const handleClickNext = () => { + if (currentStepIdx < Steps.length - 1) { + setCurrentStep((prev) => prev + 1); + } else { + onClickNextStep(); + } + }; + return (
@@ -29,13 +37,7 @@ export const MyProfilePage = () => {
{currentStep.form}
-
diff --git a/src/pages/layout/WideDeviceLayout.module.css b/src/pages/layout/WideDeviceLayout.module.css index 1984716..030fdbc 100644 --- a/src/pages/layout/WideDeviceLayout.module.css +++ b/src/pages/layout/WideDeviceLayout.module.css @@ -10,10 +10,10 @@ .Main { height: 100%; width: 100%; + overflow: hidden; margin: auto; box-shadow: 0 0 17px 2px #0000001A; background-color: #fff; - z-index: 1; } .SideSection { diff --git a/src/processes/shortcut/Shortcut.tsx b/src/processes/shortcut/Shortcut.tsx index 0530589..11ed079 100644 --- a/src/processes/shortcut/Shortcut.tsx +++ b/src/processes/shortcut/Shortcut.tsx @@ -1,7 +1,7 @@ import { ArrowLeft, ArrowRight, Close, List } from 'src/shared/ui/icons'; import styles from './Shortcut.module.css'; import { Sheet } from 'react-modal-sheet'; -import { useState } from 'react'; +import { useRef, useState } from 'react'; import { Button } from 'src/shared/ui/Button/Button'; import { MyProfileStepMeta } from 'src/pages/form/my_profile/MyProfileStepMeta'; import { IdealPartnerStepMeta } from 'src/pages/form/ideal_partner/IdealPartnerStepMeta'; @@ -9,7 +9,9 @@ import { useProfileFirstName } from 'src/entities/profile/lib/useProfileFirstNam import { ScrollView } from 'src/shared/ui/ScrollView/ScrollView'; import { Spacing } from 'src/shared/ui/Spacing/Spacing'; -export const Shortcut = () => { +export const Shortcut = ({ right, bottom }: { right: `${number}px`; bottom: `${number}px` }) => { + const floatingButtonPosition = useRef({ right, bottom }); + const [open, setOpen] = useState(false); const [selectedKey, setSelectedKey] = useState< | { @@ -23,7 +25,10 @@ export const Shortcut = () => { | null >(null); - const onClose = () => setOpen(false); + const onClose = () => { + setOpen(false); + setSelectedKey(null); + }; const selectedStep = selectedKey && @@ -40,7 +45,7 @@ export const Shortcut = () => { return ( <> - diff --git a/src/shared/ui/BottomSheet/BottomSheet.tsx b/src/shared/ui/BottomSheet/BottomSheet.tsx index 7090706..ad8cf51 100644 --- a/src/shared/ui/BottomSheet/BottomSheet.tsx +++ b/src/shared/ui/BottomSheet/BottomSheet.tsx @@ -9,7 +9,7 @@ type BottomSheetProps = PropsWithChildren[0]>; export const BottomSheet = ({ children, ...props }: BottomSheetProps) => { return ( - + {children} ); diff --git a/src/shared/ui/SwitchCase.tsx b/src/shared/ui/SwitchCase.tsx new file mode 100644 index 0000000..4487385 --- /dev/null +++ b/src/shared/ui/SwitchCase.tsx @@ -0,0 +1,18 @@ +type Props = { + caseBy: Partial>; + value: Case; + defaultComponent?: JSX.Element | null; +}; + +// https://github.com/toss/slash/blob/main/packages/react/react/src/components/SwitchCase/SwitchCase.tsx +export const SwitchCase = ({ + value, + caseBy, + defaultComponent: defaultComponent = null, +}: Props) => { + if (value == null) { + return defaultComponent; + } + + return caseBy[value] ?? defaultComponent; +};