From 70673b587df02ba87ea1e2edd11bc56113241166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Tue, 23 Jan 2024 17:29:01 +0900 Subject: [PATCH 1/3] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d0659e053be7506a62d42dd80b38a5fc0a730bcc Merge: 3c9fe3e 12758be Author: Jeon Jinho Date: Tue Jan 23 17:28:08 2024 +0900 Merge pull request #144 from team-offonoff/fix/login-flow chore: add pr template commit 12758be2ce7be8d7c2ddfb30960efa4c28cc885b Merge: 415f022 3c9fe3e Author: Jeon Jinho Date: Tue Jan 23 17:26:00 2024 +0900 Merge branch 'dev' into fix/login-flow commit 3c9fe3efd764bee8cf718486ee469ea5a28f7d29 Merge: efac491 b4fd241 Author: Jeon Jinho Date: Tue Jan 23 16:45:28 2024 +0900 Merge pull request #143 from team-offonoff/Jinho1011-patch-1 chore: add pr template commit b4fd241b706d01d594ea31ea0d4f60df44f4e589 Author: Jeon Jinho Date: Tue Jan 23 16:05:13 2024 +0900 Create pull_request_template.md commit 415f0223176c812f0f2f37276eae33d982c7434d Author: 전진호 Date: Tue Jan 23 15:49:28 2024 +0900 feat: add term mutation api commit df7633d1ed91f39dc8a5cee808e07fd21c426e18 Author: 전진호 Date: Tue Jan 23 15:02:58 2024 +0900 feat: implements Terms component commit be6d63bc8397f91fb5afa1b5c9482d075a59a831 Author: 전진호 Date: Tue Jan 23 15:02:47 2024 +0900 feat: toggle 약관동의 bottomsheet on submit commit 3816501560e030d207b8db06b3c8cc6b8c33ff62 Author: 전진호 Date: Tue Jan 23 15:02:22 2024 +0900 feat: add transparent props commit 2bb734064017b16bb2bd54e2f00117eb3872fa20 Author: 전진호 Date: Tue Jan 23 15:02:12 2024 +0900 feat: add checkbox component commit e0610a8a2c1eb002858e015f99e7705e23a70802 Author: 전진호 Date: Tue Jan 23 15:01:59 2024 +0900 fix: add transparent props to bottomsheet commit e99a4bf8ed9b16e4b6cf713b6aa2ec786966f086 Author: 전진호 Date: Tue Jan 23 15:01:46 2024 +0900 feat: add check icon commit b292c8804c0c4c90115662cedac2a3a713d1bf5e Author: 전진호 Date: Mon Jan 22 22:48:06 2024 +0900 fix: genders value commit 36779a0da38e24c27f3a2facc1c4ad205e8194ea Author: 전진호 Date: Mon Jan 22 22:47:56 2024 +0900 fix: file name error commit 75ae65f160ddb4df4a96deae228fdf0055e0d88e Author: 전진호 Date: Mon Jan 22 20:52:04 2024 +0900 fix: remove funnel from signup commit a5ca53e0008ef0d44db8b68b0f3648443eb78f24 Author: 전진호 Date: Mon Jan 22 20:42:22 2024 +0900 feat: setUser when user is not newMember commit e7e237936da06fe7eb3bb103fc1c84fff7e93ebf Author: 전진호 Date: Mon Jan 22 20:42:00 2024 +0900 feat: add useAuthStore's user commit 5e1637b7729920a8a8a608db1806fcfc2c0ee51c Author: 전진호 Date: Mon Jan 22 20:41:27 2024 +0900 fix: make nickname optional commit f2c1b11d898bc6bd37693591184684739d0fbe8b Author: 전진호 Date: Mon Jan 22 20:41:08 2024 +0900 chore: add serve script commit ec39b940fc3643c89db98b4630e859ac6cd3b2b2 Author: 전진호 Date: Mon Jan 22 20:08:30 2024 +0900 feat: add user store commit 739256769a7174947aeba9cf136fc5ccd098915b Author: 전진호 Date: Mon Jan 22 18:17:14 2024 +0900 chore: add zustand commit f95bd5f435bf03a59e7cdc848cf7573f6c40489f Author: 전진호 Date: Mon Jan 22 17:44:43 2024 +0900 fix: navigate to signup when new member signed up --- .github/pull_request_template.md | 11 ++ package.json | 4 +- src/apis/oauth/signup.ts | 24 ++- src/assets/icons/check.svg | 3 + src/assets/icons/index.ts | 2 + .../commons/BottomSheet/BottomSheet.tsx | 8 +- src/components/commons/CheckBox/CheckBox.tsx | 58 +++++++ src/constants/signup.ts | 4 +- src/hooks/useBottomSheet/useBottomSheet.tsx | 4 +- src/interfaces/models/user.ts | 6 + src/routes/Auth/kakao/KakaoLogin.tsx | 19 ++- .../routes/Auth/signup/Signup.styles.tsx | 0 src/routes/Auth/signup/Signup.tsx | 149 ++++++++++++++++-- src/routes/Auth/signup/Terms.tsx | 115 ++++++++++++++ ...36\205\354\204\261\352\263\265.styles.tsx" | 19 --- ...0\354\236\205\354\204\261\352\263\265.tsx" | 17 -- ...5\353\263\264\354\236\205\353\240\245.tsx" | 105 ------------ src/routes/index.tsx | 4 +- src/store/auth.ts | 18 +++ yarn.lock | 12 ++ 20 files changed, 414 insertions(+), 168 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 src/assets/icons/check.svg create mode 100644 src/components/commons/CheckBox/CheckBox.tsx create mode 100644 src/interfaces/models/user.ts rename "src/routes/Auth/signup/\354\240\225\353\263\264\354\236\205\353\240\245.styles.tsx" => src/routes/Auth/signup/Signup.styles.tsx (100%) create mode 100644 src/routes/Auth/signup/Terms.tsx delete mode 100644 "src/routes/Auth/signup/\352\260\200\354\236\205\354\204\261\352\263\265.styles.tsx" delete mode 100644 "src/routes/Auth/signup/\352\260\200\354\236\205\354\204\261\352\263\265.tsx" delete mode 100644 "src/routes/Auth/signup/\354\240\225\353\263\264\354\236\205\353\240\245.tsx" create mode 100644 src/store/auth.ts diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b2ac79f --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ +## What is this PR? 🔍 +- 무슨 목적/이유로 구현했는지 + +### 🛠️ Issue +- Closes #number + +## Changes 📝 +- 작업한 내용 적어주세요 + +## To Reviewers 📢 +- 이 부분 좀 같이 봐주세요 / 혹은 하고 싶은 말 diff --git a/package.json b/package.json index 72ed6bf..6256ef3 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", + "serve": "vite preview", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint:css": "stylelint './src/**/*.tsx'", "preview": "vite preview", @@ -24,7 +25,8 @@ "react-router-dom": "^6.16.0", "styled-components": "^6.0.8", "styled-normalize": "^8.0.7", - "swiper": "^11.0.4" + "swiper": "^11.0.4", + "zustand": "^4.5.0" }, "devDependencies": { "@storybook/addon-essentials": "^7.4.0", diff --git a/src/apis/oauth/signup.ts b/src/apis/oauth/signup.ts index 7b2dd66..9cba33d 100644 --- a/src/apis/oauth/signup.ts +++ b/src/apis/oauth/signup.ts @@ -1,5 +1,7 @@ import { useMutation } from '@tanstack/react-query'; +import { OAuthResponse } from '@interfaces/api/oauth'; + import client from '@apis/fetch'; interface SingnUpRequestDTO { @@ -10,6 +12,13 @@ interface SingnUpRequestDTO { job: string; } +type TermsConsetResponseDTO = Omit; + +interface TermsConsentRequestDTO { + memberId: number; + listen_marketing: boolean; +} + const signup = (req: SingnUpRequestDTO) => { return client.post({ path: '/auth/signup/profile', @@ -17,9 +26,20 @@ const signup = (req: SingnUpRequestDTO) => { }); }; +const terms = (req: TermsConsentRequestDTO) => { + return client.post({ + path: '/auth/signup/terms', + body: req, + }); +}; + const useSignup = () => { return useMutation({ mutationFn: signup }); }; -export { useSignup }; -export type { SingnUpRequestDTO }; +const useTerms = () => { + return useMutation({ mutationFn: terms }); +}; + +export { useSignup, useTerms }; +export type { SingnUpRequestDTO, TermsConsentRequestDTO }; diff --git a/src/assets/icons/check.svg b/src/assets/icons/check.svg new file mode 100644 index 0000000..1b52798 --- /dev/null +++ b/src/assets/icons/check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index 18d87d3..0206198 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -6,6 +6,7 @@ import NewAlarmIcon from './alarm-new.svg?react'; import AppleIcon from './apple.svg?react'; import BLogoIcon from './b-logo.svg?react'; import BigDownChevronIcon from './big-down-chevron.svg?react'; +import CheckIcon from './check.svg?react'; import ClockIcon from './clock.svg?react'; import CloseIcon from './close.svg?react'; import CommentIcon from './comment.svg?react'; @@ -37,6 +38,7 @@ export { AppleIcon, BigDownChevronIcon, BLogoIcon, + CheckIcon, ClockIcon, CloseIcon, CommentIcon, diff --git a/src/components/commons/BottomSheet/BottomSheet.tsx b/src/components/commons/BottomSheet/BottomSheet.tsx index 3b62b71..ac784ed 100644 --- a/src/components/commons/BottomSheet/BottomSheet.tsx +++ b/src/components/commons/BottomSheet/BottomSheet.tsx @@ -16,6 +16,7 @@ interface BottomSheetProps { setIsOpen: React.Dispatch>; snapPoints?: number[]; initialSnap?: number; + transparent?: boolean; children: React.ReactNode; } @@ -24,6 +25,7 @@ const BottomSheet = ({ setIsOpen, snapPoints = [0.9, 0.7, 0], initialSnap = 0.7, + transparent = true, children, }: BottomSheetProps) => { const screenHeight = window.innerHeight; @@ -110,6 +112,7 @@ const BottomSheet = ({ ` position: fixed; top: 0; left: 0; @@ -166,7 +169,8 @@ const Backdrop = styled(motion.div)` width: 100%; height: 100%; - /* background: rgb(0 0 0 / 50%); */ + ${({ transparent }) => + transparent ? 'background: transparent;' : 'background: rgb(0 0 0 / 50%);'} `; const Wrapper = styled(motion.div)` diff --git a/src/components/commons/CheckBox/CheckBox.tsx b/src/components/commons/CheckBox/CheckBox.tsx new file mode 100644 index 0000000..d02689d --- /dev/null +++ b/src/components/commons/CheckBox/CheckBox.tsx @@ -0,0 +1,58 @@ +import styled from 'styled-components'; + +import { CheckIcon } from '@icons/index'; + +import { Row } from '../Flex/Flex'; +import Text from '../Text/Text'; + +interface CheckboxProps { + id: string; + checked: boolean; + onChange: (e: React.ChangeEvent) => void; + children: React.ReactNode; +} + +function Checkbox({ id, checked, onChange, children }: CheckboxProps) { + return ( + + + + {checked && } + + + + ); +} + +export default Checkbox; + +const CheckBoxLabel = styled.label<{ checked: boolean }>` + position: relative; + display: inline-block; + width: 22px; + height: 22px; + cursor: pointer; + background: ${({ checked, theme }) => + checked ? `${theme.colors.purple}` : 'rgb(77 59 124 / 20%)'}; + border-radius: 3px; + + & > svg { + position: absolute; + top: 3px; + left: 3.5px; + } +`; + +const HiddenCheckbox = styled.input` + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: 0; + overflow: hidden; + clip: rect(0 0 0 0); + white-space: nowrap; + border: 0; +`; diff --git a/src/constants/signup.ts b/src/constants/signup.ts index b5c5ffb..d6d959f 100644 --- a/src/constants/signup.ts +++ b/src/constants/signup.ts @@ -8,10 +8,10 @@ export const JOBS = [ export const GENDERS = [ { label: '남성', - value: 'male', + value: 'MALE', }, { label: '여성', - value: 'female', + value: 'FEMALE', }, ]; diff --git a/src/hooks/useBottomSheet/useBottomSheet.tsx b/src/hooks/useBottomSheet/useBottomSheet.tsx index 67d2b22..25ae377 100644 --- a/src/hooks/useBottomSheet/useBottomSheet.tsx +++ b/src/hooks/useBottomSheet/useBottomSheet.tsx @@ -5,10 +5,11 @@ import BottomSheet from '@components/commons/BottomSheet/BottomSheet'; interface UseBottomSheetProps { snapPoints?: number[]; initialSnap?: number; + transparent?: boolean; } const useBottomSheet = (props: UseBottomSheetProps) => { - const { snapPoints = [0.9, 0.7, 0], initialSnap = 0.7 } = props; + const { snapPoints = [0.9, 0.7, 0], initialSnap = 0.7, transparent = true } = props; const [isOpen, setIsOpen] = useState(false); const toggleSheet = useCallback(() => { @@ -21,6 +22,7 @@ const useBottomSheet = (props: UseBottomSheetProps) => { setIsOpen={setIsOpen} snapPoints={snapPoints} initialSnap={initialSnap} + transparent={transparent} > {children} diff --git a/src/interfaces/models/user.ts b/src/interfaces/models/user.ts new file mode 100644 index 0000000..2b01e0f --- /dev/null +++ b/src/interfaces/models/user.ts @@ -0,0 +1,6 @@ +export interface User { + memberId: number; + nickname?: string; + accessToken?: string; + refrehToken?: string; +} diff --git a/src/routes/Auth/kakao/KakaoLogin.tsx b/src/routes/Auth/kakao/KakaoLogin.tsx index 4402c61..188c8a5 100644 --- a/src/routes/Auth/kakao/KakaoLogin.tsx +++ b/src/routes/Auth/kakao/KakaoLogin.tsx @@ -1,13 +1,17 @@ import React, { useEffect } from 'react'; import { useNavigate } from 'react-router'; +import { useAuthStore } from 'src/store/auth'; import { kakaoLogin } from '@apis/oauth/kakao'; import { ResponseError } from '@apis/fetch'; +import Login from '../login/Login'; + import { Container } from './KakaoLogin.styles'; const KakaoLogin = () => { + const setUser = useAuthStore((state) => state.setUser); const kakaoCode = new URL(window.location.href).searchParams.get('code'); const navigate = useNavigate(); @@ -17,7 +21,14 @@ const KakaoLogin = () => { try { const response = await kakaoLogin(kakaoCode); if (response && response.accessToken) { - response.newMember ? navigate('/onboard') : navigate('/'); + if (response.newMember) { + navigate(`/signup`, { + state: { memberId: response.memberId }, + }); + } else { + setUser({ memberId: response.memberId, accessToken: response.accessToken }); + navigate('/'); + } } } catch (err) { if (err instanceof ResponseError) { @@ -38,6 +49,10 @@ const KakaoLogin = () => { handleKakaoLogin(); }, []); - return 카카오로딩화면; + return ( + + + + ); }; export default KakaoLogin; diff --git "a/src/routes/Auth/signup/\354\240\225\353\263\264\354\236\205\353\240\245.styles.tsx" b/src/routes/Auth/signup/Signup.styles.tsx similarity index 100% rename from "src/routes/Auth/signup/\354\240\225\353\263\264\354\236\205\353\240\245.styles.tsx" rename to src/routes/Auth/signup/Signup.styles.tsx diff --git a/src/routes/Auth/signup/Signup.tsx b/src/routes/Auth/signup/Signup.tsx index c3c6b11..9239c75 100644 --- a/src/routes/Auth/signup/Signup.tsx +++ b/src/routes/Auth/signup/Signup.tsx @@ -1,27 +1,144 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; +import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; import { useLocation } from 'react-router-dom'; +import { CONFIG, INPUT_TYPE } from 'src/constants/form'; +import { GENDERS, JOBS } from 'src/constants/signup'; -import useFunnel from '@hooks/useFunnel/useFunnel'; +import { SingnUpRequestDTO, useSignup } from '@apis/oauth/signup'; +import { Col } from '@components/commons/Flex/Flex'; +import InputField from '@components/commons/InputField/InputField'; +import Layout from '@components/commons/Layout/Layout'; +import RadioInput from '@components/commons/RadioInput/RadioInput'; +import SelectInput from '@components/commons/SelectInput/SelectInput'; +import Text from '@components/commons/Text/Text'; +import TextInput from '@components/commons/TextInput/TextInput'; +import useBottomSheet from '@hooks/useBottomSheet/useBottomSheet'; -import 가입성공 from './가입성공'; -import 정보입력 from './정보입력'; +import { colors } from '@styles/theme'; -const Signup = () => { - const steps = ['정보입력', '가입성공']; +import { ResponseError } from '@apis/fetch'; + +import { FormContainer, NextButton } from './Signup.styles'; +import Terms from './Terms'; + +type SignupForm = Omit; - const [Funnel, setStep] = useFunnel(steps); +const MAX_NICKNAME_LENGTH = 8; + +const Signup = () => { const location = useLocation(); - const [registerData, setRegisterData] = useState(); + const methods = useForm>({ mode: 'onChange' }); + const signupMutation = useSignup(); + const { BottomSheet: TermsSheet, toggleSheet } = useBottomSheet({ + snapPoints: [0.5, 0.5, 0], + initialSnap: 0.5, + transparent: false, + }); + + const memberId = location.state.memberId as number; + + const birthdayInput = methods.watch(INPUT_TYPE.BIRTHDAY); + const nicknameProgress = methods.watch(INPUT_TYPE.NICKNAME) + ? `${methods.watch(INPUT_TYPE.NICKNAME)?.length}/${MAX_NICKNAME_LENGTH}` + : ''; + + const handleBirthdayInputKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Backspace' && (birthdayInput?.length === 6 || birthdayInput?.length === 9)) { + methods.setValue(INPUT_TYPE.BIRTHDAY, birthdayInput.slice(0, -2)); + } + if (e.key === 'Backspace' && (birthdayInput?.length === 5 || birthdayInput?.length === 8)) { + methods.setValue(INPUT_TYPE.BIRTHDAY, birthdayInput.slice(0, -1)); + } + }; + + const handleSubmitForm: SubmitHandler = async (data) => { + try { + await signupMutation.mutateAsync({ + ...data, + birth: data.birth.replace(/\//g, '-'), + memberId, + }); + + toggleSheet(); + } catch (error) { + if (error instanceof ResponseError) { + if (error.errorData.errorContent.hint.includes('PERSONAL_REGISTERED')) { + toggleSheet(); + } else { + alert(error.errorData.errorContent.message); + } + } + } + }; + + useEffect(() => { + if (birthdayInput?.length === 4 || birthdayInput?.length === 7) { + methods.setValue(INPUT_TYPE.BIRTHDAY, birthdayInput + '/'); + } + }, [birthdayInput, methods]); + + if (!memberId) { + return
잘못된 접근입니다.
; + } return ( - - - <정보입력 memberId={location.state.memberId} /> - - - <가입성공 /> - - + ( + + 회원정보 입력 + + )} + > + + + + + ( + + {nicknameProgress} + + )} + /> + + + + + + + + + + + + + 다음 + + + + + + + ); }; diff --git a/src/routes/Auth/signup/Terms.tsx b/src/routes/Auth/signup/Terms.tsx new file mode 100644 index 0000000..d7b1acb --- /dev/null +++ b/src/routes/Auth/signup/Terms.tsx @@ -0,0 +1,115 @@ +import React, { ChangeEvent, useState } from 'react'; +import { useAuthStore } from 'src/store/auth'; + +import { useTerms } from '@apis/oauth/signup'; +import Checkbox from '@components/commons/CheckBox/CheckBox'; +import { Col, Row } from '@components/commons/Flex/Flex'; +import Text from '@components/commons/Text/Text'; + +import { colors } from '@styles/theme'; + +import { NextButton } from './Signup.styles'; + +interface TermsProps { + memberId: number; +} + +const Terms = ({ memberId }: TermsProps) => { + const consentToTermMutation = useTerms(); + const setUser = useAuthStore((state) => state.setUser); + const [all, setAll] = useState(false); + const [consentToTerm, setConsentToTerm] = useState(false); + const [consentToCollectAndUseInfo, setConsentToCollectAndUseInfo] = useState(false); + const [consetToMarketing, setConsetToMarketing] = useState(false); + + const disabled = !consentToTerm || !consentToCollectAndUseInfo; + + const handleConsetAll = (e: ChangeEvent) => { + if (e.target.checked) { + setAll(true); + setConsentToTerm(true); + setConsentToCollectAndUseInfo(true); + setConsetToMarketing(true); + } else { + setAll(false); + setConsentToTerm(false); + setConsentToCollectAndUseInfo(false); + setConsetToMarketing(false); + } + }; + + const handleSubmitConsetToTerm = async () => { + if (disabled) return; + + const response = await consentToTermMutation.mutateAsync({ + memberId, + listen_marketing: consetToMarketing, + }); + setUser({ + memberId: response.memberId, + accessToken: response.accessToken, + }); + }; + + return ( + + + + + + 모두 동의하기 + + + +
+ + setConsentToTerm(e.target.checked)} + > + + 서비스 이용 약관(필수) + + + + + setConsentToCollectAndUseInfo(e.target.checked)} + > + + 개인정보 수집 및 이용동의(필수) + + + + + setConsetToMarketing(e.target.checked)} + > + + 마케팅 정보 수신 동의(선택) + + + + + + {/* SAFE AREA */} + + 다음 + + + + ); +}; + +export default Terms; diff --git "a/src/routes/Auth/signup/\352\260\200\354\236\205\354\204\261\352\263\265.styles.tsx" "b/src/routes/Auth/signup/\352\260\200\354\236\205\354\204\261\352\263\265.styles.tsx" deleted file mode 100644 index 844b157..0000000 --- "a/src/routes/Auth/signup/\352\260\200\354\236\205\354\204\261\352\263\265.styles.tsx" +++ /dev/null @@ -1,19 +0,0 @@ -import { styled } from 'styled-components'; - -export const Container = styled.div` - position: relative; - display: flex; - flex-direction: column; - justify-content: center; - height: 100vh; - background-color: ${(props) => props.theme.colors.navy}; -`; - -export const NextButton = styled.button` - width: 100%; - height: 57px; - color: #fff; - cursor: pointer; - background-color: #3c3457; - border-radius: 10px; -`; diff --git "a/src/routes/Auth/signup/\352\260\200\354\236\205\354\204\261\352\263\265.tsx" "b/src/routes/Auth/signup/\352\260\200\354\236\205\354\204\261\352\263\265.tsx" deleted file mode 100644 index 408cedf..0000000 --- "a/src/routes/Auth/signup/\352\260\200\354\236\205\354\204\261\352\263\265.tsx" +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -import { Container, NextButton } from './가입성공.styles'; - -const 가입성공 = () => { - const handleNextButton = () => { - //다음버튼 누르면 - }; - - return ( - - 다음 - - ); -}; - -export default 가입성공; diff --git "a/src/routes/Auth/signup/\354\240\225\353\263\264\354\236\205\353\240\245.tsx" "b/src/routes/Auth/signup/\354\240\225\353\263\264\354\236\205\353\240\245.tsx" deleted file mode 100644 index 42c8a92..0000000 --- "a/src/routes/Auth/signup/\354\240\225\353\263\264\354\236\205\353\240\245.tsx" +++ /dev/null @@ -1,105 +0,0 @@ -import React, { useEffect } from 'react'; -import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import { CONFIG, INPUT_TYPE } from 'src/constants/form'; -import { GENDERS, JOBS } from 'src/constants/signup'; - -import { SingnUpRequestDTO, useSignup } from '@apis/oauth/signup'; -import { Col } from '@components/commons/Flex/Flex'; -import InputField from '@components/commons/InputField/InputField'; -import Layout from '@components/commons/Layout/Layout'; -import RadioInput from '@components/commons/RadioInput/RadioInput'; -import SelectInput from '@components/commons/SelectInput/SelectInput'; -import Text from '@components/commons/Text/Text'; -import TextInput from '@components/commons/TextInput/TextInput'; - -import { colors } from '@styles/theme'; - -import { FormContainer, NextButton } from './정보입력.styles'; - -type SignupForm = Omit; - -const 정보입력 = ({ memberId }: { memberId: number }) => { - const methods = useForm>({ mode: 'onChange' }); - const signupMutation = useSignup(); - - const birthdayInput = methods.watch(INPUT_TYPE.BIRTHDAY); - const nicknameProgress = methods.watch(INPUT_TYPE.NICKNAME) - ? `${methods.watch(INPUT_TYPE.NICKNAME)?.length}/8` - : ''; - - const handleBirthdayInputKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Backspace' && (birthdayInput?.length === 6 || birthdayInput?.length === 9)) { - methods.setValue(INPUT_TYPE.BIRTHDAY, birthdayInput.slice(0, -2)); - } - if (e.key === 'Backspace' && (birthdayInput?.length === 5 || birthdayInput?.length === 8)) { - methods.setValue(INPUT_TYPE.BIRTHDAY, birthdayInput.slice(0, -1)); - } - }; - - const handleSubmitForm: SubmitHandler = (data) => { - signupMutation.mutate({ ...data, memberId }); - }; - - useEffect(() => { - if (birthdayInput?.length === 4 || birthdayInput?.length === 7) { - methods.setValue(INPUT_TYPE.BIRTHDAY, birthdayInput + '/'); - } - }, [birthdayInput, methods]); - - return ( - ( - - 회원정보 입력 - - )} - > - - - - - ( - - {nicknameProgress} - - )} - /> - - - - - - - - - - - - 다음 - - - - ); -}; - -export default 정보입력; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 12441e1..d36c288 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { RouteObject, RouterProvider, createBrowserRouter } from 'react-router-dom'; +import { useAuthStore } from 'src/store/auth'; import GoogleLogin from './Auth/google/GoogleLogin'; import KakaoLogin from './Auth/kakao/KakaoLogin'; @@ -11,7 +12,8 @@ import TopicCreate from './Topic/TopicCreate'; import TopicSideSelection from './Topic/TopicSideSelection'; const Router = () => { - const [isAuthorized, setIsAuthorized] = React.useState(true); + const user = useAuthStore((state) => state.user); + const isAuthorized = user !== null; const authorizedRoutes: RouteObject[] = [ { diff --git a/src/store/auth.ts b/src/store/auth.ts new file mode 100644 index 0000000..13560b4 --- /dev/null +++ b/src/store/auth.ts @@ -0,0 +1,18 @@ +import { create } from 'zustand'; + +import { User } from '@interfaces/models/user'; + +interface AuthState { + user: User | null; +} + +interface AuthAction { + setUser: (user: User) => void; +} + +export const useAuthStore = create((set) => ({ + user: null, + setUser: (user: User) => { + set({ user: user }); + }, +})); diff --git a/yarn.lock b/yarn.lock index aef1611..0bc6545 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9368,6 +9368,11 @@ use-sidecar@^1.1.2: detect-node-es "^1.1.0" tslib "^2.0.0" +use-sync-external-store@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -9832,3 +9837,10 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zustand@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.0.tgz#141354af56f91de378aa6c4b930032ab338f3ef0" + integrity sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A== + dependencies: + use-sync-external-store "1.2.0" From d3661c198434da272a06984b3104dca84331fcb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Tue, 23 Jan 2024 18:53:03 +0900 Subject: [PATCH 2/3] feat: add TextInput variations --- src/assets/styles/theme.ts | 39 ------------------ .../commons/TextInput/TextInput.styles.tsx | 40 +++++++++---------- .../commons/TextInput/TextInput.tsx | 16 ++++---- src/components/commons/TextInput/theme.ts | 39 ++++++++++++++++++ 4 files changed, 66 insertions(+), 68 deletions(-) create mode 100644 src/components/commons/TextInput/theme.ts diff --git a/src/assets/styles/theme.ts b/src/assets/styles/theme.ts index 8dcd91d..a4d25c2 100644 --- a/src/assets/styles/theme.ts +++ b/src/assets/styles/theme.ts @@ -54,42 +54,3 @@ export const theme: DefaultTheme = { colors, zIndex, }; - -export const input = { - t1: { - default: { - border: `none`, - backgroundColor: '#342b52', - }, - error: { - border: `1px solid ${colors.purple2}`, - backgroundColor: '#342b52', - }, - }, - t2: { - default: { - fontSize: '2rem', - fontWeight: 400, - lineHeight: '140%', - padding: '0 0 10px 0', - border: 'none', - borderBottom: `1px solid ${colors.navy2}`, - backgroundColor: 'transparent', - borderRadius: 0, - outline: 'none', - }, - error: { - fontSize: '2rem', - fontWeight: 400, - lineHeight: '140%', - padding: '0 0 10px 0', - border: 'none', - borderBottom: `1px solid ${colors.purple2}`, - borderRadius: 0, - backgroundColor: 'transparent', - outline: 'none', - }, - }, -}; - -export type InputTypes = keyof typeof input; diff --git a/src/components/commons/TextInput/TextInput.styles.tsx b/src/components/commons/TextInput/TextInput.styles.tsx index 5b7450b..0e0c623 100644 --- a/src/components/commons/TextInput/TextInput.styles.tsx +++ b/src/components/commons/TextInput/TextInput.styles.tsx @@ -1,46 +1,46 @@ import { css, styled } from 'styled-components'; -const StyledInput = styled.input<{ hasLeft: boolean }>` +import { TextInputTheme } from './theme'; + +const StyledInput = styled.input<{ inputTheme: TextInputTheme }>` width: 100%; - padding: 14px 16px; font-size: 1.4rem; font-weight: 700; line-height: 1.4; color: ${({ theme }) => theme.colors.white}; - border-radius: 10px; - - ${({ hasLeft }) => - hasLeft && - css` - padding-left: 35px; - `} + appearance: none; + background-color: transparent; + border: none; &:focus { outline: none; } &::placeholder { - color: ${({ theme }) => theme.colors.purple}; + color: ${({ inputTheme }) => inputTheme.placeholderColor}; opacity: 0.6; } `; -const InputContainer = styled.div` +const InputContainer = styled.div<{ hasError: boolean; inputTheme: TextInputTheme }>` position: relative; + display: flex; + align-items: center; + justify-content: space-between; + padding: ${({ inputTheme }) => inputTheme.padding}; + background-color: ${({ inputTheme }) => inputTheme.backgroundColor}; + border-bottom: ${({ inputTheme, theme, hasError }) => + !inputTheme.rounded && `1px solid ${hasError ? theme.colors.purple2 : theme.colors.navy2}`}; + border-radius: ${({ inputTheme }) => inputTheme.rounded && '10px'}; + box-shadow: ${({ theme, inputTheme, hasError }) => + hasError && inputTheme.rounded && `0 0 0 1px ${theme.colors.purple2} inset`}; `; const InputPrefix = styled.div` - position: absolute; - top: 14px; - left: 16px; + margin-right: 10px; `; -const InputSuffix = styled.div` - position: absolute; - right: 16px; - bottom: calc(50% - 10px); - transform: translateY(-50%); -`; +const InputSuffix = styled.div``; const ErrorMessage = styled.div` position: absolute; diff --git a/src/components/commons/TextInput/TextInput.tsx b/src/components/commons/TextInput/TextInput.tsx index 5524da6..56d3201 100644 --- a/src/components/commons/TextInput/TextInput.tsx +++ b/src/components/commons/TextInput/TextInput.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { RegisterOptions, useFormContext } from 'react-hook-form'; -import { ConfigKeys, InputType } from 'src/constants/form'; +import { InputType } from 'src/constants/form'; -import { InputTypes, colors, input } from '@styles/theme'; +import { colors } from '@styles/theme'; import Text from '../Text/Text'; @@ -13,6 +13,7 @@ import { InputSuffix, StyledInput, } from './TextInput.styles'; +import { TextInputTheme, theme1 } from './theme'; interface TextInputProps extends React.InputHTMLAttributes { id: InputType; @@ -21,26 +22,23 @@ interface TextInputProps extends React.InputHTMLAttributes { left?: () => React.ReactNode; right?: () => React.ReactNode; onKeyDown?: (e: React.KeyboardEvent) => void; - theme?: InputTypes; + theme?: TextInputTheme; } const TextInput = (props: TextInputProps) => { - const { id, type = 'text', options, placeholder, left, right, onKeyDown, theme = 't1' } = props; + const { id, type = 'text', options, placeholder, left, right, onKeyDown, theme = theme1 } = props; const { register, formState: { errors }, } = useFormContext(); - const inputTheme = input[theme]; - return (
- + {left && {left()}} Date: Wed, 24 Jan 2024 00:08:52 +0900 Subject: [PATCH 3/3] fix: replace to theme --- src/routes/Topic/ATopicCreate.tsx | 3 ++- src/routes/Topic/BTopicCreate.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/routes/Topic/ATopicCreate.tsx b/src/routes/Topic/ATopicCreate.tsx index d314669..a90c460 100644 --- a/src/routes/Topic/ATopicCreate.tsx +++ b/src/routes/Topic/ATopicCreate.tsx @@ -6,6 +6,7 @@ import DefaultButton from '@components/commons/Button/DefaultButton'; import { Col, Row } from '@components/commons/Flex/Flex'; import Text from '@components/commons/Text/Text'; import TextInput from '@components/commons/TextInput/TextInput'; +import { theme2 } from '@components/commons/TextInput/theme'; import TopicCreateTextInput from '@components/TopicCreate/TopicCreateTextInput'; import { colors } from '@styles/theme'; @@ -41,7 +42,7 @@ const ATopicCreate = () => { id={INPUT_TYPE.TOPICTITLE} options={CONFIG.TOPICTITLE.options} placeholder={'제목을 입력해주세요.'} - theme="t2" + theme={theme2} right={() => ( {titleProgress} diff --git a/src/routes/Topic/BTopicCreate.tsx b/src/routes/Topic/BTopicCreate.tsx index 9c5c6aa..d52fda9 100644 --- a/src/routes/Topic/BTopicCreate.tsx +++ b/src/routes/Topic/BTopicCreate.tsx @@ -5,6 +5,7 @@ import { CONFIG, INPUT_TYPE } from 'src/constants/form'; import { Col } from '@components/commons/Flex/Flex'; import Text from '@components/commons/Text/Text'; import TextInput from '@components/commons/TextInput/TextInput'; +import { theme2 } from '@components/commons/TextInput/theme'; import { colors } from '@styles/theme'; @@ -35,7 +36,7 @@ const BTopicCreate = () => { id={INPUT_TYPE.TOPICTITLE} options={CONFIG.TOPICTITLE.options} placeholder={'제목을 입력해주세요.'} - theme="t2" + theme={theme2} right={() => ( {titleProgress}