Skip to content

Commit

Permalink
Merge pull request #45 from DevKor-github/signup
Browse files Browse the repository at this point in the history
Signup
  • Loading branch information
halionaz authored Aug 20, 2024
2 parents f3ba1ac + f10d2a8 commit 6e764b1
Show file tree
Hide file tree
Showing 28 changed files with 952 additions and 4 deletions.
12 changes: 12 additions & 0 deletions app/signup/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const TOTAL_PROGRESS = 4;

export type SchoolType = 'korea' | 'yonsei' | null;

export interface SignupFormType {
school: SchoolType;
nickname: string;
phoneNumber: string;
agreement: boolean;
}

export type SignupElements = keyof SignupFormType;
128 changes: 127 additions & 1 deletion app/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,133 @@
'use client';

import styled from 'styled-components';
import { SwiperRef } from 'swiper/react';
import { useRouter } from 'next/navigation';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useSignupError, useSignupForm } from '@/app/signup/store';

import { TOTAL_PROGRESS } from '@/app/signup/constants';
import SignupTopBar from '@/components/Signup/SignupTopBar';
import SignupProgress from '@/components/Signup/SignupProgress';
import SignupFunnel from '@/components/Signup/SignupFunnel';

export default function SignUp() {
const router = useRouter();

const formState = useSignupForm();
const errorState = useSignupError();
const [clickable, setClickable] = useState(false);

const totalProgress = TOTAL_PROGRESS;
const [progress, setProgress] = useState(0);
const swiperRef = useRef<SwiperRef>(null);
useEffect(() => {
// 스와이퍼와 progress 동기화
swiperRef.current?.swiper.slideTo(progress);
}, [progress]);

useEffect(() => {
// TODO: Auth Check
if (false) {
router.push('/');
}
}, [router]);

const handlePrevButton = useCallback(() => {
if (progress === 0) {
router.back();
} else {
setProgress((prev) => prev - 1);
}
}, [router, progress]);

// 다음 버튼 활성화 로직
useEffect(() => {
switch (progress) {
case 0:
setClickable(formState.school !== null);
break;
case 1:
setClickable(formState.nickname !== '');
break;
case 2:
setClickable(
formState.phoneNumber.length === 11 &&
/^01([0|1|6|7|8|9]?)?([0-9]{3,4})?([0-9]{4})$/.test(formState.phoneNumber),
);
break;
case 3:
setClickable(formState.agreement);
break;
}
}, [progress, formState]);

const handleNextButton = () => {
if (clickable) {
switch (progress) {
case 0:
setProgress((prev) => prev + 1);
break;
case 1:
// TODO: Nickname Validation Check
if (formState.nickname !== 'error' && formState.nickname !== '에러') {
setProgress((prev) => prev + 1);
} else {
errorState.setError('nickname');
}
break;
case 2:
setProgress((prev) => prev + 1);
break;
case 3:
setProgress((prev) => prev + 1);
// TODO: Sign-up Form Submit
console.log(formState);
break;
case 4:
// 토키 시작하기
router.push('/');
break;
}
}
};

return (
<div>
<h1>Sign Up</h1>
<SignupTopBar handlePrevButton={handlePrevButton} />
<SignupProgress curProgress={progress} totalProgress={totalProgress} />
<SignupFunnel ref={swiperRef} />
<SignupFooter $isDone={clickable} onClick={handleNextButton}>
{progress === totalProgress ? '토키 시작하기' : '다음'}
</SignupFooter>
</div>
);
}

const SignupFooter = styled.button<{ $isDone: boolean }>`
position: fixed;
bottom: 0;
width: 100%;
height: 64px;
padding: 20px 0;
background: var(
--Background-5,
linear-gradient(0deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.05) 100%),
#121212
);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: 700;
letter-spacing: -0.8px;
color: ${({ $isDone }) =>
$isDone
? 'var(--white-high-emphasis-87, rgba(255, 255, 255, 0.87))'
: 'var(--white-15, rgba(255, 255, 255, 0.15))'};
transition: all 0.2s;
`;
30 changes: 30 additions & 0 deletions app/signup/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { create } from 'zustand';
import { SignupElements, SchoolType, SignupFormType } from '@/app/signup/constants';

interface SignupFormStore extends SignupFormType {
setSchool: (select: SchoolType) => void;
setNickname: (input: string) => void;
setPhoneNumber: (input: string) => void;
setAgreement: (input: boolean) => void;
}
export const useSignupForm = create<SignupFormStore>((set) => ({
school: null,
nickname: '',
phoneNumber: '',
agreement: false,
setSchool: (select: SchoolType) => set({ school: select }),
setNickname: (input: string) => set({ nickname: input }),
setPhoneNumber: (input: string) => set({ phoneNumber: input }),
setAgreement: (input: boolean) => set({ agreement: input }),
}));

interface ErrorStore {
error: SignupElements | null;
setError: (message: SignupElements) => void;
clearError: () => void;
}
export const useSignupError = create<ErrorStore>((set) => ({
error: null,
setError: (code: SignupElements) => set({ error: code }),
clearError: () => set({ error: null }),
}));
70 changes: 70 additions & 0 deletions components/Signup/InputBox/InputBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { SignupElements } from '@/app/signup/constants';
import { useSignupError } from '@/app/signup/store';
import { useState } from 'react';
import styled from 'styled-components';

type StatusType = 'default' | 'focus' | 'filled';

interface InputBoxProps {
type: SignupElements;
value: string;
setValue: (value: string) => void;
placeholder?: string;
maxLength?: number;
}
export function InputBox({ type, value, setValue, placeholder, maxLength }: InputBoxProps) {
const [status, setStatus] = useState<StatusType>('default');
const errorState = useSignupError();

return (
<Input
$status={status}
$error={errorState.error === type}
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder={placeholder}
onFocus={() => {
setStatus('focus');
errorState.clearError();
}}
onBlur={() => {
if (value.length === 0) {
setStatus('default');
} else {
setStatus('filled');
}
}}
maxLength={maxLength}
/>
);
}

const Input = styled.input<{ $status: StatusType; $error?: boolean }>`
padding: 16px;
border: none;
outline: none;
background: none;
border-radius: 8px;
border: 1px solid;
border-color: ${({ $status, $error }) => {
if ($error) return `var(--Light-Red, #F95B6E)`;
switch ($status) {
case 'focus':
case 'filled':
return `var(--white-high-emphasis-87, rgba(255, 255, 255, 0.87))`;
case 'default':
return `var(--white-15, rgba(255, 255, 255, 0.15))`;
}
}};
color: var(--white-high-emphasis-87, rgba(255, 255, 255, 0.87));
font-size: 15px;
letter-spacing: -0.6px;
&::placeholder {
color: var(--white-disabled-38, rgba(255, 255, 255, 0.38));
}
transition: all 0.2s;
`;
1 change: 1 addition & 0 deletions components/Signup/InputBox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { InputBox as default } from './InputBox';
78 changes: 78 additions & 0 deletions components/Signup/SetNickname/SetNickname.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import styled from 'styled-components';
import { useSignupError, useSignupForm } from '@/app/signup/store';

import InputBox from '@/components/Signup/InputBox';

export function SetNickname() {
const nickname = useSignupForm((state) => state.nickname);
const setNickname = useSignupForm((state) => state.setNickname);
const error = useSignupError((state) => state.error);

return (
<Wrapper>
<Guide>
<p>나중에 수정 가능해요!</p>
<h2>
<strong>닉네임</strong>을 입력해주세요.
</h2>
</Guide>
<FormWrapper>
<InputBox placeholder="닉네임" value={nickname} setValue={setNickname} type="nickname" maxLength={10} />
<InputStatus>
<NicknameCount>{nickname.length}/10</NicknameCount>
{error === 'nickname' && <ErrorMessage>이미 존재하는 닉네임 입니다.</ErrorMessage>}
</InputStatus>
</FormWrapper>
</Wrapper>
);
}

const Wrapper = styled.div`
display: flex;
flex-direction: column;
gap: 34px;
`;
const Guide = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
color: var(--_60, rgba(255, 255, 255, 0.6));
& p {
font-size: 12px;
font-weight: 300;
letter-spacing: -0.48px;
}
& h2 {
font-size: 22px;
letter-spacing: -0.88px;
& strong {
color: var(--white_0, #fff);
}
}
`;

const FormWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
`;

const InputStatus = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
`;

const NicknameCount = styled.span`
color: var(--white-medium-emphasis-60, rgba(255, 255, 255, 0.6));
font-size: 14px;
`;

const ErrorMessage = styled.span`
color: #f95b6e;
font-size: 12px;
font-weight: 300;
letter-spacing: -0.48px;
`;
1 change: 1 addition & 0 deletions components/Signup/SetNickname/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SetNickname as default } from './SetNickname';
Loading

0 comments on commit 6e764b1

Please sign in to comment.