diff --git a/apps/web/app/[meetingId]/user/modify/page.tsx b/apps/web/app/[meetingId]/user/modify/page.tsx new file mode 100644 index 00000000..c5c6e1d5 --- /dev/null +++ b/apps/web/app/[meetingId]/user/modify/page.tsx @@ -0,0 +1,3 @@ +import { ModifyUserInfoScreen } from '@/user'; + +export default ModifyUserInfoScreen; diff --git a/apps/web/app/onboarding/page.tsx b/apps/web/app/onboarding/page.tsx new file mode 100644 index 00000000..d6c26e4a --- /dev/null +++ b/apps/web/app/onboarding/page.tsx @@ -0,0 +1,17 @@ +import { OnBoardingScreen, StepType } from '@/onboarding'; + +interface OnBoardingPageProps { + searchParams: { + step: StepType; + }; +} + +const OnBoardingPage = async (props: OnBoardingPageProps) => { + const { + searchParams: { step }, + } = props; + + return ; +}; + +export default OnBoardingPage; diff --git a/packages/web-domains/package.json b/packages/web-domains/package.json index 288302a5..9b2ff22b 100644 --- a/packages/web-domains/package.json +++ b/packages/web-domains/package.json @@ -11,7 +11,8 @@ "./user": "./src/user/index.ts", "./participate-meeting": "./src/participate-meeting/index.ts", "./common": "./src/common/index.ts", - "./relay-question": "./src/relay-question/index.ts" + "./relay-question": "./src/relay-question/index.ts", + "./onboarding": "./src/onboarding/index.ts" }, "scripts": { "lint": "eslint . --max-warnings 0", diff --git a/packages/web-domains/src/relay-question/assets/RelayToolTipPolygon.tsx b/packages/web-domains/src/common/components/ToolTip/RelayToolTipPolygon.tsx similarity index 100% rename from packages/web-domains/src/relay-question/assets/RelayToolTipPolygon.tsx rename to packages/web-domains/src/common/components/ToolTip/RelayToolTipPolygon.tsx diff --git a/packages/web-domains/src/relay-question/features/select-relay-question/components/ToolTip/ToolTip.styles.ts b/packages/web-domains/src/common/components/ToolTip/ToolTip.styles.ts similarity index 100% rename from packages/web-domains/src/relay-question/features/select-relay-question/components/ToolTip/ToolTip.styles.ts rename to packages/web-domains/src/common/components/ToolTip/ToolTip.styles.ts diff --git a/packages/web-domains/src/relay-question/features/select-relay-question/components/ToolTip/ToolTip.tsx b/packages/web-domains/src/common/components/ToolTip/ToolTip.tsx similarity index 88% rename from packages/web-domains/src/relay-question/features/select-relay-question/components/ToolTip/ToolTip.tsx rename to packages/web-domains/src/common/components/ToolTip/ToolTip.tsx index 17e4fad0..68c1360c 100644 --- a/packages/web-domains/src/relay-question/features/select-relay-question/components/ToolTip/ToolTip.tsx +++ b/packages/web-domains/src/common/components/ToolTip/ToolTip.tsx @@ -3,8 +3,7 @@ import { Txt } from '@sambad/sds/components'; import { colors } from '@sambad/sds/theme'; import { PropsWithChildren } from 'react'; -import { RelayToolTipPolygon } from '../../../../assets/RelayToolTipPolygon'; - +import { RelayToolTipPolygon } from './RelayToolTipPolygon'; import { textWrapperCss, wrapperCss } from './ToolTip.styles'; export const ToolTip = ({ children }: PropsWithChildren) => { diff --git a/packages/web-domains/src/home/features/progressing-question/components/QuestionInfo/ActiveQuestion.tsx b/packages/web-domains/src/home/features/progressing-question/components/QuestionInfo/ActiveQuestion.tsx index c395b4e1..6d46cdd4 100644 --- a/packages/web-domains/src/home/features/progressing-question/components/QuestionInfo/ActiveQuestion.tsx +++ b/packages/web-domains/src/home/features/progressing-question/components/QuestionInfo/ActiveQuestion.tsx @@ -2,6 +2,8 @@ import { Txt } from '@sambad/sds/components'; import { borderRadiusVariants, colors, size } from '@sambad/sds/theme'; import Link from 'next/link'; +import { ToolTip } from '@/common/components/ToolTip/ToolTip'; + import { ArrowIcon } from '../../../../../common/asset/arrow'; import { ProgressingQuestionType } from '../../../../common/apis/schema/useGetProgressingQuestionQuery.type'; import { Avatar } from '../../../../common/components/Avatar/Avatar'; @@ -22,7 +24,12 @@ export const ActiveQuestion = ({ question }: ActiveQuestionProps) => { } = question; return ( -
+
+ {!isAnswered && ( + + 답변을 아직 안했어요! + + )}
; +} + +export const useOnBoardingCompleteMutation = ({ options }: Args = {}) => { + return useMutation({ + mutationFn: async () => { + try { + const data = await onBoardingComplete(); + return data; + } catch (error) { + if (isAxiosError(error)) { + console.error(error); + } + } + }, + ...options, + }); +}; + +export async function onBoardingComplete(): Promise { + const data = await Http.PATCH(`/v1/users/onboarding/complete`); + return data; +} diff --git a/packages/web-domains/src/onboarding/common/assets/icons/HandIcon.tsx b/packages/web-domains/src/onboarding/common/assets/icons/HandIcon.tsx new file mode 100644 index 00000000..b1d05c2d --- /dev/null +++ b/packages/web-domains/src/onboarding/common/assets/icons/HandIcon.tsx @@ -0,0 +1,10 @@ +export const HandIcon = () => ( + + + +); diff --git a/packages/web-domains/src/onboarding/common/assets/icons/HumanIcon.tsx b/packages/web-domains/src/onboarding/common/assets/icons/HumanIcon.tsx new file mode 100644 index 00000000..e4d97e30 --- /dev/null +++ b/packages/web-domains/src/onboarding/common/assets/icons/HumanIcon.tsx @@ -0,0 +1,12 @@ +export const HumanIcon = () => ( + + + + +); diff --git a/packages/web-domains/src/onboarding/common/assets/icons/QuestionIcon.tsx b/packages/web-domains/src/onboarding/common/assets/icons/QuestionIcon.tsx new file mode 100644 index 00000000..535cca0c --- /dev/null +++ b/packages/web-domains/src/onboarding/common/assets/icons/QuestionIcon.tsx @@ -0,0 +1,28 @@ +import { IconAssetProps } from '@sds/components/Icon/types'; +import { colors } from '@sds/theme'; + +export const QuestionIcon = (props: IconAssetProps) => { + const { color = colors.primary500 } = props; + + return ( + + + + + + + + + + + + ); +}; diff --git a/packages/web-domains/src/onboarding/common/assets/images/bg-about-me.png b/packages/web-domains/src/onboarding/common/assets/images/bg-about-me.png new file mode 100644 index 00000000..25e88568 Binary files /dev/null and b/packages/web-domains/src/onboarding/common/assets/images/bg-about-me.png differ diff --git a/packages/web-domains/src/onboarding/common/assets/images/bg-friendship.png b/packages/web-domains/src/onboarding/common/assets/images/bg-friendship.png new file mode 100644 index 00000000..e06d2a28 Binary files /dev/null and b/packages/web-domains/src/onboarding/common/assets/images/bg-friendship.png differ diff --git a/packages/web-domains/src/onboarding/common/assets/images/bg-question.png b/packages/web-domains/src/onboarding/common/assets/images/bg-question.png new file mode 100644 index 00000000..c5954916 Binary files /dev/null and b/packages/web-domains/src/onboarding/common/assets/images/bg-question.png differ diff --git a/packages/web-domains/src/onboarding/common/assets/images/bg-result.png b/packages/web-domains/src/onboarding/common/assets/images/bg-result.png new file mode 100644 index 00000000..752338b0 Binary files /dev/null and b/packages/web-domains/src/onboarding/common/assets/images/bg-result.png differ diff --git a/packages/web-domains/src/onboarding/common/assets/images/onboarding-about-me.png b/packages/web-domains/src/onboarding/common/assets/images/onboarding-about-me.png new file mode 100644 index 00000000..c02493bd Binary files /dev/null and b/packages/web-domains/src/onboarding/common/assets/images/onboarding-about-me.png differ diff --git a/packages/web-domains/src/onboarding/common/assets/images/onboarding-friendship.png b/packages/web-domains/src/onboarding/common/assets/images/onboarding-friendship.png new file mode 100644 index 00000000..452bedf1 Binary files /dev/null and b/packages/web-domains/src/onboarding/common/assets/images/onboarding-friendship.png differ diff --git a/packages/web-domains/src/onboarding/common/assets/images/onboarding-question.png b/packages/web-domains/src/onboarding/common/assets/images/onboarding-question.png new file mode 100644 index 00000000..7e1bb6e5 Binary files /dev/null and b/packages/web-domains/src/onboarding/common/assets/images/onboarding-question.png differ diff --git a/packages/web-domains/src/onboarding/common/assets/images/onboarding-result.png b/packages/web-domains/src/onboarding/common/assets/images/onboarding-result.png new file mode 100644 index 00000000..95da5d94 Binary files /dev/null and b/packages/web-domains/src/onboarding/common/assets/images/onboarding-result.png differ diff --git a/packages/web-domains/src/onboarding/common/constants/steps.ts b/packages/web-domains/src/onboarding/common/constants/steps.ts new file mode 100644 index 00000000..eb45f06b --- /dev/null +++ b/packages/web-domains/src/onboarding/common/constants/steps.ts @@ -0,0 +1,8 @@ +export const STEPS = { + QUESTION: 'question', + RESULT: 'result', + FRIENDSHIP: 'friendship', + ABOUT_ME: 'about-me', +} as const; + +export type StepType = (typeof STEPS)[keyof typeof STEPS]; diff --git a/packages/web-domains/src/onboarding/features/components/BackGroundImage/BackGroundImage.tsx b/packages/web-domains/src/onboarding/features/components/BackGroundImage/BackGroundImage.tsx new file mode 100644 index 00000000..a14270d1 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/components/BackGroundImage/BackGroundImage.tsx @@ -0,0 +1,30 @@ +import { css } from '@emotion/react'; +import Image, { StaticImageData } from 'next/image'; + +interface BackGroundProps { + imageUrl?: string | StaticImageData; +} + +export const BackGroundImage = (props: BackGroundProps) => { + const { imageUrl } = props; + + if (!imageUrl) { + return null; + } + + return ( +
+ background-img +
+ ); +}; + +const backGroundContainerCss = css({ + position: 'absolute', + top: '0', + left: '50%', + transform: 'translate(-50%, 0)', + width: '100%', + height: 'calc(100% - 116px)', + zIndex: '-1', +}); diff --git a/packages/web-domains/src/onboarding/features/components/Button/NextButton.tsx b/packages/web-domains/src/onboarding/features/components/Button/NextButton.tsx new file mode 100644 index 00000000..162f3967 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/components/Button/NextButton.tsx @@ -0,0 +1,26 @@ +import { Button } from '@sds/components'; +import { useRouter, useSearchParams } from 'next/navigation'; + +import { StepType, STEPS } from '@/onboarding/common/constants/steps'; + +const stepOrder: StepType[] = [STEPS.QUESTION, STEPS.RESULT, STEPS.FRIENDSHIP, STEPS.ABOUT_ME]; + +export const NextButton = () => { + const router = useRouter(); + const searchParams = useSearchParams(); + + const currentStep = searchParams.get('step') as StepType; + + const currentStepIndex = stepOrder.indexOf(currentStep); + const nextStep = stepOrder[currentStepIndex + 1] || stepOrder[0]; + + const handleGoToNextPage = () => { + router.push(`?step=${nextStep}`); + }; + + return ( + + ); +}; diff --git a/packages/web-domains/src/onboarding/features/components/Button/StartButton.tsx b/packages/web-domains/src/onboarding/features/components/Button/StartButton.tsx new file mode 100644 index 00000000..912e82c0 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/components/Button/StartButton.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { Button } from '@sds/components'; +import { colors } from '@sds/theme'; +import { useRouter } from 'next/navigation'; + +import { useOnBoardingCompleteMutation } from '@/onboarding/common/apis/mutations/useOnBoardingMutation'; + +interface StartButtonProps { + redirectUrl?: string; +} + +export const StartButton = (props: StartButtonProps) => { + const { redirectUrl } = props; + const router = useRouter(); + const { mutateAsync: onBoardingComplete } = useOnBoardingCompleteMutation(); + + const handleOnBoardingComplete = async () => { + if (redirectUrl) { + router.push(redirectUrl); + } + + const data = await onBoardingComplete(); + + // 만약 가입된 모임이 없다면 + if (data?.isNotEnteredAnyMeeting) { + router.push('/user'); + } + // 만약 가입된 모임이 있다면 + else { + router.push('/home'); + } + }; + + return ( + + ); +}; diff --git a/packages/web-domains/src/onboarding/features/components/Header/Header.tsx b/packages/web-domains/src/onboarding/features/components/Header/Header.tsx new file mode 100644 index 00000000..fe22266b --- /dev/null +++ b/packages/web-domains/src/onboarding/features/components/Header/Header.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { Txt } from '@sds/components'; +import { colors, size } from '@sds/theme'; +import { ReactNode } from 'react'; + +interface HeaderProps { + title: string; + subTitle: string; + Icon: ReactNode | (() => JSX.Element); +} +export const Header = (props: HeaderProps) => { + const { title, subTitle, Icon } = props; + return ( +
+ {typeof Icon === 'function' ? Icon() : Icon} + + {title} + {subTitle} + +
+ ); +}; diff --git a/packages/web-domains/src/onboarding/features/components/Layout/OnBoardingLayout.tsx b/packages/web-domains/src/onboarding/features/components/Layout/OnBoardingLayout.tsx new file mode 100644 index 00000000..6c102ee5 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/components/Layout/OnBoardingLayout.tsx @@ -0,0 +1,36 @@ +import { colors } from '@sds/theme'; +import { CSSProperties, PropsWithChildren } from 'react'; + +import { STEPS, StepType } from '@/onboarding/common/constants/steps'; + +interface OnBoardingLayoutProps { + step: StepType; +} + +export const OnBoardingLayout = ({ children, step }: PropsWithChildren) => { + const backgroundColor = layoutStyles[step] || 'none'; + return ( +
+ {children} +
+ ); +}; + +const layoutStyle: CSSProperties = { + position: 'relative', + height: '100dvh', + display: 'flex', + flexDirection: 'column', +}; + +const layoutStyles: Record = { + [STEPS.QUESTION]: colors.primary300, + [STEPS.RESULT]: colors.tertiary300, + [STEPS.FRIENDSHIP]: colors.secondary400, + [STEPS.ABOUT_ME]: colors.quaternary300, +}; diff --git a/packages/web-domains/src/onboarding/features/components/OnBoardingContent/OnBoardingContent.tsx b/packages/web-domains/src/onboarding/features/components/OnBoardingContent/OnBoardingContent.tsx new file mode 100644 index 00000000..ee29469d --- /dev/null +++ b/packages/web-domains/src/onboarding/features/components/OnBoardingContent/OnBoardingContent.tsx @@ -0,0 +1,39 @@ +import Image, { StaticImageData } from 'next/image'; + +import { BackGroundImage } from '../BackGroundImage/BackGroundImage'; + +interface OnBoardingContentProps { + imageUrl?: string | StaticImageData; + bgImageUrl?: string | StaticImageData; +} +export const OnBoardingContent = (props: OnBoardingContentProps) => { + const { imageUrl, bgImageUrl } = props; + + if (!imageUrl) { + return null; + } + + return ( +
+
+ on-boarding-img +
+ +
+ ); +}; diff --git a/packages/web-domains/src/onboarding/features/components/ProgressIndicator/index.tsx b/packages/web-domains/src/onboarding/features/components/ProgressIndicator/index.tsx new file mode 100644 index 00000000..eb7310d9 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/components/ProgressIndicator/index.tsx @@ -0,0 +1,45 @@ +import { colors } from '@sds/theme'; +import { HTMLAttributes, PropsWithChildren, forwardRef } from 'react'; + +import { progressIndicatorCss, stepCss } from './styles'; + +export interface ProgressIndicatorProps extends Omit, 'style'> { + mode?: 'horizontal' | 'vertical'; + totalStep: number; + currentStep: number; +} + +export const ProgressIndicator = forwardRef>( + ({ mode = 'horizontal', totalStep, currentStep, children, ...rest }, ref) => { + const flexDirection = mode === 'horizontal' ? 'row' : 'column'; + const currentStepIndex = Math.min(totalStep, currentStep) - 1; + const basis = 100 / totalStep; + + return ( +
+ {Array.from({ length: totalStep }).map((_, index) => ( + + ))} + {children} +
+ ); + }, +) as React.ForwardRefExoticComponent & { Step: typeof Step }; + +export interface StepProps extends HTMLAttributes { + basis: number; + isCurrent: boolean; +} + +const Step = ({ isCurrent, basis, ...rest }: StepProps) => { + let color = '#FFFFFF4D'; + + if (isCurrent) { + color = colors.white; + } + + return ; +}; + +ProgressIndicator.displayName = 'ProgressIndicator'; +ProgressIndicator.Step = Step; diff --git a/packages/web-domains/src/onboarding/features/components/ProgressIndicator/styles.ts b/packages/web-domains/src/onboarding/features/components/ProgressIndicator/styles.ts new file mode 100644 index 00000000..42b7957f --- /dev/null +++ b/packages/web-domains/src/onboarding/features/components/ProgressIndicator/styles.ts @@ -0,0 +1,16 @@ +import { css } from '@emotion/react'; + +import { ProgressIndicatorCssArgs, StepCssArgs } from './type'; + +export const progressIndicatorCss = ({ flexDirection }: ProgressIndicatorCssArgs) => + css({ width: '100%', display: 'flex', flexWrap: 'nowrap', flexDirection }); + +export const stepCss = ({ basis, color }: StepCssArgs) => + css({ + boxSizing: 'border-box', + flex: `${basis}% 1 1`, + height: '4px', + backgroundColor: color, + borderRadius: '8px', + margin: '2px', + }); diff --git a/packages/web-domains/src/onboarding/features/components/ProgressIndicator/type.ts b/packages/web-domains/src/onboarding/features/components/ProgressIndicator/type.ts new file mode 100644 index 00000000..3c610f8b --- /dev/null +++ b/packages/web-domains/src/onboarding/features/components/ProgressIndicator/type.ts @@ -0,0 +1,8 @@ +export type ProgressIndicatorCssArgs = { + flexDirection: 'row' | 'column'; +}; + +export type StepCssArgs = { + color: string; + basis: number; +}; diff --git a/packages/web-domains/src/onboarding/features/containers/FloatingButtonContainer/FloatingButtonContainer.tsx b/packages/web-domains/src/onboarding/features/containers/FloatingButtonContainer/FloatingButtonContainer.tsx new file mode 100644 index 00000000..08365aa5 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/containers/FloatingButtonContainer/FloatingButtonContainer.tsx @@ -0,0 +1,39 @@ +'use client'; + +import { css } from '@emotion/react'; + +import { STEPS, StepType } from '@/onboarding/common/constants/steps'; + +import { NextButton } from '../../components/Button/NextButton'; +import { StartButton } from '../../components/Button/StartButton'; + +interface FloatingButtonContainerProps { + step: StepType; + redirectUrl?: string; +} + +export const FloatingButtonContainer = (props: FloatingButtonContainerProps) => { + const { step, redirectUrl } = props; + + return ( +
+ {step === STEPS.ABOUT_ME ? : } +
+ ); +}; + +const backGroundStyles: Record = { + [STEPS.QUESTION]: 'linear-gradient(180deg, rgba(255, 187, 162, 0.00) 0%, #FFBBA2 100%)', + [STEPS.RESULT]: 'linear-gradient(180deg, rgba(194, 164, 252, 0.00)0%, #C2A4FC 100%)', + [STEPS.FRIENDSHIP]: 'linear-gradient(180deg, rgba(201, 243, 60, 0.00)0%, #C9F33C 100%)', + [STEPS.ABOUT_ME]: 'linear-gradient(180deg, rgba(254, 237, 155, 0.00)0%, #FEED9B 100%)', +}; + +const buttonWrapperCss = css({ + position: 'fixed', + zIndex: '10', + bottom: '0', + width: '100%', + maxWidth: '600px', + padding: '20px 106px 40px', +}); diff --git a/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingAboutMeContainer.tsx b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingAboutMeContainer.tsx new file mode 100644 index 00000000..6c4438ea --- /dev/null +++ b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingAboutMeContainer.tsx @@ -0,0 +1,19 @@ +'use client'; + +import { Fragment } from 'react/jsx-runtime'; + +import { HumanIcon } from '@/onboarding/common/assets/icons/HumanIcon'; +import BgImage from '@/onboarding/common/assets/images/bg-about-me.png'; +import OnBoardingImage from '@/onboarding/common/assets/images/onboarding-about-me.png'; + +import { Header } from '../../components/Header/Header'; +import { OnBoardingContent } from '../../components/OnBoardingContent/OnBoardingContent'; + +export const OnBoardingAboutMeContainer = () => { + return ( + +
} title="릴레이 질문으로" subTitle="나만의 자기소개서 완성!" /> + + + ); +}; diff --git a/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingContainer.tsx b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingContainer.tsx new file mode 100644 index 00000000..43f88e55 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingContainer.tsx @@ -0,0 +1,29 @@ +import { notFound } from 'next/navigation'; + +import { STEPS, StepType } from '@/onboarding/common/constants/steps'; + +import { OnBoardingAboutMeContainer } from './OnBoardingAboutMeContainer'; +import { OnBoardingFriendShipContainer } from './OnBoardingFriendShipContainer'; +import { OnBoardingQuestionContainer } from './OnBoardingQuestionContainer'; +import { OnBoardingResultContainer } from './onBoardingResultContainer'; + +interface OnBoardingContainerProps { + step: StepType; +} + +export const OnBoardingContainer = (props: OnBoardingContainerProps) => { + const { step } = props; + + switch (step) { + case STEPS.QUESTION: + return ; + case STEPS.RESULT: + return ; + case STEPS.FRIENDSHIP: + return ; + case STEPS.ABOUT_ME: + return ; + default: + return notFound(); + } +}; diff --git a/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingFriendShipContainer.tsx b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingFriendShipContainer.tsx new file mode 100644 index 00000000..eb815398 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingFriendShipContainer.tsx @@ -0,0 +1,19 @@ +'use client'; + +import { Fragment } from 'react/jsx-runtime'; + +import { HandIcon } from '@/onboarding/common/assets/icons/HandIcon'; +import BgImage from '@/onboarding/common/assets/images/bg-friendship.png'; +import OnBoardingImage from '@/onboarding/common/assets/images/onboarding-friendship.png'; + +import { Header } from '../../components/Header/Header'; +import { OnBoardingContent } from '../../components/OnBoardingContent/OnBoardingContent'; + +export const OnBoardingFriendShipContainer = () => { + return ( + +
} title="릴레이 질문으로" subTitle="나만의 자기소개서 완성!" /> + + + ); +}; diff --git a/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingQuestionContainer.tsx b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingQuestionContainer.tsx new file mode 100644 index 00000000..030786c4 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/OnBoardingQuestionContainer.tsx @@ -0,0 +1,18 @@ +'use client'; +import { Fragment } from 'react/jsx-runtime'; + +import { QuestionIcon } from '@/onboarding/common/assets/icons/QuestionIcon'; +import BgImage from '@/onboarding/common/assets/images/bg-question.png'; +import OnBoardingImage from '@/onboarding/common/assets/images/onboarding-question.png'; + +import { Header } from '../../components/Header/Header'; +import { OnBoardingContent } from '../../components/OnBoardingContent/OnBoardingContent'; + +export const OnBoardingQuestionContainer = () => { + return ( + +
} title="모임원들에게" subTitle="궁금한 것을 물어보면" /> + + + ); +}; diff --git a/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/onBoardingResultContainer.tsx b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/onBoardingResultContainer.tsx new file mode 100644 index 00000000..22d9420e --- /dev/null +++ b/packages/web-domains/src/onboarding/features/containers/OnBoardingContainer/onBoardingResultContainer.tsx @@ -0,0 +1,23 @@ +'use client'; +import { colors } from '@sds/theme'; +import { Fragment } from 'react/jsx-runtime'; + +import { QuestionIcon } from '@/onboarding/common/assets/icons/QuestionIcon'; +import BgImage from '@/onboarding/common/assets/images/bg-result.png'; +import OnBoardingImage from '@/onboarding/common/assets/images/onboarding-result.png'; + +import { Header } from '../../components/Header/Header'; +import { OnBoardingContent } from '../../components/OnBoardingContent/OnBoardingContent'; + +export const OnBoardingResultContainer = () => { + return ( + +
} + title="우리 모임원들이" + subTitle="어떤 답변을 했는지 볼 수 있고" + /> + + + ); +}; diff --git a/packages/web-domains/src/onboarding/features/containers/ProgressIndicatorContainer/ProgressIndicatorContainer.tsx b/packages/web-domains/src/onboarding/features/containers/ProgressIndicatorContainer/ProgressIndicatorContainer.tsx new file mode 100644 index 00000000..0e03a627 --- /dev/null +++ b/packages/web-domains/src/onboarding/features/containers/ProgressIndicatorContainer/ProgressIndicatorContainer.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { size } from '@sds/theme'; + +import { STEPS, StepType } from '@/onboarding/common/constants/steps'; + +import { ProgressIndicator } from '../../components/ProgressIndicator'; + +interface ProgressIndicatorContainerProps { + step: StepType; +} + +const stepToCurrentStepMap: Record = { + [STEPS.QUESTION]: 1, + [STEPS.RESULT]: 2, + [STEPS.FRIENDSHIP]: 3, + [STEPS.ABOUT_ME]: 4, +}; + +export const ProgressIndicatorContainer = (props: ProgressIndicatorContainerProps) => { + const { step } = props; + + const currentStep = stepToCurrentStepMap[step] || 1; + + return ( +
+ +
+ ); +}; diff --git a/packages/web-domains/src/onboarding/image.d.ts b/packages/web-domains/src/onboarding/image.d.ts new file mode 100644 index 00000000..a666151b --- /dev/null +++ b/packages/web-domains/src/onboarding/image.d.ts @@ -0,0 +1,4 @@ +declare module '*.png'; +declare module '*.svg'; +declare module '*.jpeg'; +declare module '*.jpg'; diff --git a/packages/web-domains/src/onboarding/index.ts b/packages/web-domains/src/onboarding/index.ts new file mode 100644 index 00000000..1a8efe72 --- /dev/null +++ b/packages/web-domains/src/onboarding/index.ts @@ -0,0 +1,2 @@ +export { OnBoardingScreen } from './screens/OnBoardingScreen'; +export type { StepType } from './common/constants/steps'; diff --git a/packages/web-domains/src/onboarding/screens/OnBoardingScreen.tsx b/packages/web-domains/src/onboarding/screens/OnBoardingScreen.tsx new file mode 100644 index 00000000..3427017a --- /dev/null +++ b/packages/web-domains/src/onboarding/screens/OnBoardingScreen.tsx @@ -0,0 +1,34 @@ +import { cookies } from 'next/headers'; +import { redirect } from 'next/navigation'; +import { Suspense } from 'react'; + +import { STEPS, StepType } from '../common/constants/steps'; +import { OnBoardingLayout } from '../features/components/Layout/OnBoardingLayout'; +import { FloatingButtonContainer } from '../features/containers/FloatingButtonContainer/FloatingButtonContainer'; +import { OnBoardingContainer } from '../features/containers/OnBoardingContainer/OnBoardingContainer'; +import { ProgressIndicatorContainer } from '../features/containers/ProgressIndicatorContainer/ProgressIndicatorContainer'; + +interface OnBoardingScreenProps { + step: StepType; +} + +const STEPS_VALUES = Object.values(STEPS); + +export const OnBoardingScreen = ({ step }: OnBoardingScreenProps) => { + if (!STEPS_VALUES.includes(step)) { + redirect(`/onboarding/?step=${STEPS.QUESTION}`); + } + + const cookieStore = cookies(); + const redirectUrl = cookieStore.get('client_redirect_url')?.value ?? ''; + + return ( + + + + + + + + ); +}; diff --git a/packages/web-domains/src/relay-question/features/select-relay-question/containers/RandomPickContainer/RandomPickContainer.tsx b/packages/web-domains/src/relay-question/features/select-relay-question/containers/RandomPickContainer/RandomPickContainer.tsx index f4b6b1cb..bf1f00a2 100644 --- a/packages/web-domains/src/relay-question/features/select-relay-question/containers/RandomPickContainer/RandomPickContainer.tsx +++ b/packages/web-domains/src/relay-question/features/select-relay-question/containers/RandomPickContainer/RandomPickContainer.tsx @@ -6,12 +6,12 @@ import { size } from '@sambad/sds/theme'; import { useParams, useRouter, useSearchParams } from 'next/navigation'; import { useState } from 'react'; +import { ToolTip } from '../../../../../common/components/ToolTip/ToolTip'; import { RelayRandomButtonDocumentIcon } from '../../../../assets/RelayRandomButtonIcon'; import { Modal } from '../../../../common/Modal'; import { FIRST_STEP } from '../../../../constants'; import { QuestionDetail } from '../../components/QuestionDetail/QuestionDetail'; import { QuestionerDetail } from '../../components/QuestionerDetail/QuestionerDetail'; -import { ToolTip } from '../../components/ToolTip/ToolTip'; import { useQueryStringContext } from '../../contexts/QueryStringContext'; import { usePostRelayQuestionInfo } from '../../hooks/mutations/usePostRelayQuestionInfo'; import { useMemberMeQuery } from '../../hooks/queries/useMemberMeQuery'; diff --git a/packages/web-domains/src/user/common/api/mutations/useModifyUserInfo.ts b/packages/web-domains/src/user/common/api/mutations/useModifyUserInfo.ts new file mode 100644 index 00000000..02a5ebea --- /dev/null +++ b/packages/web-domains/src/user/common/api/mutations/useModifyUserInfo.ts @@ -0,0 +1,46 @@ +import { UseMutationOptions, useMutation } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; + +import { Http } from '@/common/apis/base.api'; +import { MbtiType } from '@/user/common/constants/mbti'; + +import { MeetingMemberResponse } from '../schema/MeetingMemberResponse'; + +export type Params = { + meetingId: number; + name: string; + gender: 'FEMALE' | 'MALE'; + birth: string; + job: string; + location: string; + hobbyIds: number[]; + mbti?: MbtiType; + introduction?: string; + // Todo: 삭제 + role: string; +}; + +interface ErrorModifyUserInfoResponse { + code: string; + message: string; +} + +interface Args { + options?: UseMutationOptions, Params>; +} + +export const useModifyUserInfo = ({ options }: Args) => { + return useMutation({ + mutationFn: async (params: Params) => { + return await modifyUserInfo(params); + }, + ...options, + }); +}; + +async function modifyUserInfo(params: Params): Promise { + const { meetingId, ...restParams } = params; + console.log(restParams); + const data = await Http.PATCH(`/v1/meetings/${meetingId}/members/me`, restParams); + return data; +} diff --git a/packages/web-domains/src/user/common/api/schema/MeetingMemberResponse.ts b/packages/web-domains/src/user/common/api/schema/MeetingMemberResponse.ts new file mode 100644 index 00000000..908f7fbd --- /dev/null +++ b/packages/web-domains/src/user/common/api/schema/MeetingMemberResponse.ts @@ -0,0 +1,4 @@ +export interface MeetingMemberResponse { + meetingId: number; + meetingMemberId: number; +} diff --git a/packages/web-domains/src/user/features/get-user-info/containers/GetUserBasicInfoContainer.tsx b/packages/web-domains/src/user/features/get-user-info/containers/GetUserBasicInfoContainer.tsx index 2e150efd..1d026ee0 100644 --- a/packages/web-domains/src/user/features/get-user-info/containers/GetUserBasicInfoContainer.tsx +++ b/packages/web-domains/src/user/features/get-user-info/containers/GetUserBasicInfoContainer.tsx @@ -1,16 +1,9 @@ import { FormTitle, BasicInfoForm } from '../components/Form'; -import { useGetRoleName } from '../hooks/useGetRoleName'; export const GetUserBasicInfoContainer = () => { - const role = useGetRoleName(); return ( <> - + ); diff --git a/packages/web-domains/src/user/features/get-user-info/containers/GetUserExtraInfoContainer.tsx b/packages/web-domains/src/user/features/get-user-info/containers/GetUserExtraInfoContainer.tsx index f654e73d..0c7a7d1b 100644 --- a/packages/web-domains/src/user/features/get-user-info/containers/GetUserExtraInfoContainer.tsx +++ b/packages/web-domains/src/user/features/get-user-info/containers/GetUserExtraInfoContainer.tsx @@ -1,17 +1,9 @@ import { FormTitle, ExtraInfoForm } from '../components/Form'; -import { useGetRoleName } from '../hooks/useGetRoleName'; export const GetUserExtraInfoContainer = () => { - const role = useGetRoleName(); - return ( <> - + ); diff --git a/packages/web-domains/src/user/features/modify-user-info/components/ModifyIntroForm.tsx b/packages/web-domains/src/user/features/modify-user-info/components/ModifyIntroForm.tsx new file mode 100644 index 00000000..212fdc5d --- /dev/null +++ b/packages/web-domains/src/user/features/modify-user-info/components/ModifyIntroForm.tsx @@ -0,0 +1,41 @@ +import { Txt, Button } from '@sds/components'; +import { colors } from '@sds/theme'; +import { useForm } from 'react-hook-form'; + +import { buttonWrapperCss } from '../../get-user-info/components/Form/styles'; +import { TextArea } from '../../get-user-info/components/TextInput/TextArea'; +import { useModifyUserInfoService } from '../services/useModifyUserInfoService'; + +export interface IntroFormType { + introduction: string; +} + +const MAX_LENGTH = 3000; + +export const ModifyIntroForm = () => { + const { + register, + formState: { isValid }, + handleSubmit, + } = useForm({ defaultValues: { introduction: '' } }); + + const { handleModifyUserInfo } = useModifyUserInfoService(); + + return ( +
+