Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions src/pages/sign-up/components/agreement-step.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Button from '@components/button/button/button';
import Icon from '@components/icon/icon';
import CheckboxRow from '@pages/sign-up/components/checkbox-row';
import { useState } from 'react';

interface AgreementStepProps {
next: () => void;
}

const AgreementStep = ({ next }: AgreementStepProps) => {
const [terms, setTerms] = useState(false);
const [privacy, setPrivacy] = useState(false);

const isAllChecked = terms && privacy;
Comment on lines +11 to +14
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각각 이용약관 동의, 개인정보 수집 동의 값을 저장하는 상태와, 전체 항목이 체크되었는지 검사를 위한 isAllChecked입니다. 둘다 true여야지 활성화 됩니다


const handleCheckAll = () => {
const next = !isAllChecked;
setTerms(next);
setPrivacy(next);
};

const handleCheckTerms = () => {
const next = !terms;
setTerms(next);
};

const handleCheckPrivacy = () => {
const next = !privacy;
setPrivacy(next);
};

return (
<div className="h-full flex-col justify-between pt-[4.8rem] pb-[2.4rem]">
<div className="flex-col gap-[0.8rem] px-[1.6rem]">
<h1 className="title_24_sb">서비스 이용약관</h1>
<p className="body_16_m text-gray-500">
서비스 가입을 위해 <br /> 아래 항목에 동의해주세요.
</p>
</div>

<div className="flex-col gap-[2.4rem]">
<div className="flex-col gap-[0.8rem]">
<CheckboxRow
label="약관 전체 동의"
onClick={handleCheckAll}
checked={isAllChecked}
divider
/>
<CheckboxRow
label="이용약관 동의 (필수)"
onClick={handleCheckTerms}
checked={terms}
svg={<Icon name="arrow-right-18" size={1.8} />}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클릭시 이동할 주소는 아직 전달 못받은거져??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네! 여기도 페이지로 퍼블리싱 작업 들어갈 것 같아요 ㅠㅠ

/>

<CheckboxRow
label="개인정보 수집 및 이용동의 (필수)"
onClick={handleCheckPrivacy}
checked={privacy}
svg={<Icon name="arrow-right-18" size={1.8} />}
/>
</div>
<div className="px-[1.6rem]">
<Button label="다음으로" disabled={!isAllChecked} onClick={next} />
</div>
</div>
</div>
);
};

export default AgreementStep;
35 changes: 35 additions & 0 deletions src/pages/sign-up/components/checkbox-row.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Icon from '@components/icon/icon';
import { cn } from '@libs/cn';
import type { ReactNode } from 'react';

interface CheckboxProps {
label: ReactNode;
checked: boolean;
onClick: () => void;
svg?: ReactNode;
divider?: boolean;
className?: string;
}

const CheckboxRow = ({ label, checked, onClick, svg, divider, className }: CheckboxProps) => {
return (
<button
type="button"
className={cn(
'flex w-full items-center justify-between gap-[0.8rem] p-[0.8rem] px-[1.6rem] text-left',
divider && 'border-gray-200 border-b',
className,
)}
>
<div className="flex items-center gap-[0.8rem]">
<button type="button" className="cursor-pointer" onClick={onClick}>
<Icon name="check-filled" className={checked ? 'text-main-800' : 'text-gray-300'} />
</button>
<span className="body_16_m">{label}</span>
</div>
{svg}
</button>
);
};

export default CheckboxRow;
39 changes: 35 additions & 4 deletions src/pages/sign-up/components/signup-step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ import queryClient from '@libs/query-client';
import {
BIRTHYEAR_RULE_MESSAGE,
BIRTHYEAR_SUCCESS_MESSAGE,
INFORMATION_RULE_MESSAGE,
NICKNAME_RULE_MESSAGE,
NICKNAME_SUCCESS_MESSAGE,
NICKNAME_TITLE,
} from '@pages/sign-up/constants/NOTICE';
import { BIRTH_PLACEHOLDER, NICKNAME_PLACEHOLDER } from '@pages/sign-up/constants/validation';
import {
BIRTH_PLACEHOLDER,
INFORMATION_MAX_LENGTH,
INFORMATION_PLACEHOLDER,
NICKNAME_PLACEHOLDER,
} from '@pages/sign-up/constants/validation';
import { type NicknameFormValues, NicknameSchema } from '@pages/sign-up/schema/validation-schema';
import { ROUTES } from '@routes/routes-config';
import { useMutation } from '@tanstack/react-query';
Expand All @@ -28,21 +34,25 @@ const SignupStep = () => {
} = useForm<NicknameFormValues>({
mode: 'onChange',
resolver: zodResolver(NicknameSchema),
defaultValues: { nickname: '', gender: undefined, birthYear: '' },
defaultValues: { nickname: '', gender: undefined, birthYear: '', information: '' },
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

한 줄 소개 항목이 추가가 되어서 form 에도 등록해두었습니다. 서버 api는 아직 업데이트가 안돼서 api 수정은 안되어있어용

});

const navigate = useNavigate();

const nicknameValue = watch('nickname');
const birthYearValue = watch('birthYear');
const genderValue = watch('gender');
const informationValue = watch('information');

const isNicknameValid = !errors.nickname && nicknameValue.length > 0;
const isBirthYearValid = !errors.birthYear && birthYearValue.length > 0;
const isInformationValid = !errors.information && informationValue.length > 0;

const nicknameMutation = useMutation(userMutations.NICKNAME());
const userInfoMutation = useMutation(userMutations.USER_INFO());

const informationLength = informationValue.length ?? 0;

const onSubmit = (data: NicknameFormValues) => {
nicknameMutation.mutate(
{ nickname: data.nickname },
Expand Down Expand Up @@ -79,14 +89,20 @@ const SignupStep = () => {
...birthYearInputProps
} = register('birthYear');

const {
onBlur: onInformationBlur,
ref: informationRef,
...informationInputProps
} = register('information');

const handleGenderClick = (gender: '여성' | '남성') => {
setValue('gender', gender, { shouldValidate: true, shouldDirty: true });
};

return (
<form
onSubmit={handleSubmit(onSubmit)}
className="h-full w-full flex-col justify-between gap-[4rem]"
className="h-full w-full flex-col justify-between gap-[4rem] px-[1.6rem] pt-[4rem] pb-[1.6rem]"
>
<div className="w-full flex-col gap-[4rem]">
<h1 className="title_24_sb whitespace-pre-line">{NICKNAME_TITLE}</h1>
Expand All @@ -102,9 +118,24 @@ const SignupStep = () => {
ref={nicknameRef}
{...nicknameInputProps}
/>
<Input
placeholder={INFORMATION_PLACEHOLDER}
className="h-[10.4rem]"
label="한 줄 소개"
defaultMessage={INFORMATION_RULE_MESSAGE}
multiline
maxLength={INFORMATION_MAX_LENGTH}
isError={!!errors.information}
isValid={isInformationValid}
onBlur={onInformationBlur}
ref={informationRef}
length={informationLength}
hasLength
{...informationInputProps}
/>
<Input
placeholder={BIRTH_PLACEHOLDER}
label="생년"
label="출생 연도"
defaultMessage={isBirthYearValid ? BIRTHYEAR_SUCCESS_MESSAGE : BIRTHYEAR_RULE_MESSAGE}
validationMessage={errors.birthYear?.message}
isError={!!errors.birthYear}
Expand Down
2 changes: 2 additions & 0 deletions src/pages/sign-up/constants/NOTICE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export const NICKNAME_SUCCESS_MESSAGE = '사용 가능한 닉네임이에요.';
export const BIRTHYEAR_RULE_MESSAGE = '4자리 숫자만 입력 가능';

export const BIRTHYEAR_SUCCESS_MESSAGE = '올바른 입력 값이에요.';

export const INFORMATION_RULE_MESSAGE = '비방, 욕설 등 불쾌감을 줄 수 있는 내용 제한';
10 changes: 9 additions & 1 deletion src/pages/sign-up/constants/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export const NICKNAME_REGEX = {

export const NICKNAME_PLACEHOLDER = '2-6자 이내의 닉네임을 입력하세요.';

export const BIRTH_PLACEHOLDER = '생년을 YYYY 형태로 입력하세요.';
export const BIRTH_PLACEHOLDER = 'YYYY 형태로 입력하세요.';

export const INFORMATION_PLACEHOLDER =
'엘지팬입니다! 같이 응원가 떼창해요~\n잠실에서 김말국 먹으면서 직관하고 싶어요 ㅎㅎ';

export const BIRTH_ERROR_MESSAGES = {
LENGTH: '숫자로 4자리 입력만 가능해요.',
Expand All @@ -32,3 +35,8 @@ export const GENDER_ERROR_MESSAGES = {
};

export const GENDER_OPTIONS = ['남성', '여성'] as const;

export const SIGNUP_STEPS = ['AGREEMENT', 'INFORMATION'];

export const INFORMATION_MIN_LENGTH = 1;
export const INFORMATION_MAX_LENGTH = 50;
3 changes: 3 additions & 0 deletions src/pages/sign-up/schema/validation-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
BIRTH_ERROR_MESSAGES,
GENDER_ERROR_MESSAGES,
GENDER_OPTIONS,
INFORMATION_MAX_LENGTH,
INFORMATION_MIN_LENGTH,
NICKNAME_ERROR_MESSAGES,
NICKNAME_MAX_LENGTH,
NICKNAME_MIN_LENGTH,
Expand Down Expand Up @@ -37,6 +39,7 @@ export const NicknameSchema = z.object({
gender: z.enum(GENDER_OPTIONS, {
required_error: GENDER_ERROR_MESSAGES.REQUIRED,
}),
information: z.string().trim().min(INFORMATION_MIN_LENGTH).max(INFORMATION_MAX_LENGTH),
});

export type NicknameFormValues = z.infer<typeof NicknameSchema>;
19 changes: 17 additions & 2 deletions src/pages/sign-up/sign-up.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
import { useFunnel } from '@hooks/use-funnel';
import AgreementStep from '@pages/sign-up/components/agreement-step';
import SignupStep from '@pages/sign-up/components/signup-step';
import { SIGNUP_STEPS } from '@pages/sign-up/constants/validation';
import { ROUTES } from '@routes/routes-config';

const SignUp = () => {
const { Funnel, Step, goNext } = useFunnel(SIGNUP_STEPS, ROUTES.HOME);

return (
<div className="signup-layout h-full bg-gray-white">
<SignupStep />
<div className="h-full flex-col bg-gray-white">
<div className="flex-1">
<Funnel>
<Step name="AGREEMENT">
<AgreementStep next={goNext} />
</Step>
<Step name="INFORMATION">
<SignupStep />
</Step>
</Funnel>
</div>
</div>
);
};
Expand Down
3 changes: 3 additions & 0 deletions src/shared/assets/svgs/arrow-right-18.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/shared/assets/svgs/check-filled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading