From f95bd5f435bf03a59e7cdc848cf7573f6c40489f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 17:44:43 +0900 Subject: [PATCH 01/17] fix: navigate to signup when new member signed up --- src/routes/Auth/kakao/KakaoLogin.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/routes/Auth/kakao/KakaoLogin.tsx b/src/routes/Auth/kakao/KakaoLogin.tsx index 4402c61..ed1c48c 100644 --- a/src/routes/Auth/kakao/KakaoLogin.tsx +++ b/src/routes/Auth/kakao/KakaoLogin.tsx @@ -17,7 +17,11 @@ const KakaoLogin = () => { try { const response = await kakaoLogin(kakaoCode); if (response && response.accessToken) { - response.newMember ? navigate('/onboard') : navigate('/'); + response.newMember + ? navigate(`/signup`, { + state: { memberId: response.memberId }, + }) + : navigate('/'); } } catch (err) { if (err instanceof ResponseError) { From 739256769a7174947aeba9cf136fc5ccd098915b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 18:17:14 +0900 Subject: [PATCH 02/17] chore: add zustand --- package.json | 3 ++- yarn.lock | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 72ed6bf..4247fe2 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,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/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 ec39b940fc3643c89db98b4630e859ac6cd3b2b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 20:08:30 +0900 Subject: [PATCH 03/17] feat: add user store --- src/interfaces/models/user.ts | 6 ++++++ src/store/auth.ts | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/interfaces/models/user.ts create mode 100644 src/store/auth.ts diff --git a/src/interfaces/models/user.ts b/src/interfaces/models/user.ts new file mode 100644 index 0000000..61b3e0f --- /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/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 }); + }, +})); From f2c1b11d898bc6bd37693591184684739d0fbe8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 20:41:08 +0900 Subject: [PATCH 04/17] chore: add serve script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4247fe2..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", From 5e1637b7729920a8a8a608db1806fcfc2c0ee51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 20:41:27 +0900 Subject: [PATCH 05/17] fix: make nickname optional --- src/interfaces/models/user.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/models/user.ts b/src/interfaces/models/user.ts index 61b3e0f..2b01e0f 100644 --- a/src/interfaces/models/user.ts +++ b/src/interfaces/models/user.ts @@ -1,6 +1,6 @@ export interface User { memberId: number; - nickname: string; + nickname?: string; accessToken?: string; refrehToken?: string; } From e7e237936da06fe7eb3bb103fc1c84fff7e93ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 20:42:00 +0900 Subject: [PATCH 06/17] feat: add useAuthStore's user --- src/routes/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 3422b01..358795a 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'; @@ -10,7 +11,8 @@ import Notification from './Notification/Notification'; import TopicCreate from './Topic/TopicCreate'; const Router = () => { - const [isAuthorized, setIsAuthorized] = React.useState(true); + const user = useAuthStore((state) => state.user); + const isAuthorized = user !== null; const authorizedRoutes: RouteObject[] = [ { From a5ca53e0008ef0d44db8b68b0f3648443eb78f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 20:42:22 +0900 Subject: [PATCH 07/17] feat: setUser when user is not newMember --- src/routes/Auth/kakao/KakaoLogin.tsx | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/routes/Auth/kakao/KakaoLogin.tsx b/src/routes/Auth/kakao/KakaoLogin.tsx index ed1c48c..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,11 +21,14 @@ const KakaoLogin = () => { try { const response = await kakaoLogin(kakaoCode); if (response && response.accessToken) { - response.newMember - ? navigate(`/signup`, { - state: { memberId: response.memberId }, - }) - : 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) { @@ -42,6 +49,10 @@ const KakaoLogin = () => { handleKakaoLogin(); }, []); - return 카카오로딩화면; + return ( + + + + ); }; export default KakaoLogin; From 75ae65f160ddb4df4a96deae228fdf0055e0d88e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 20:52:04 +0900 Subject: [PATCH 08/17] fix: remove funnel from signup --- .../routes/Auth/signup/\bSignup.styles.tsx" | 0 src/routes/Auth/signup/Signup.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 ---------------- 5 files changed, 97 insertions(+), 159 deletions(-) rename "src/routes/Auth/signup/\354\240\225\353\263\264\354\236\205\353\240\245.styles.tsx" => "src/routes/Auth/signup/\bSignup.styles.tsx" (100%) 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" 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/\bSignup.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/\bSignup.styles.tsx" diff --git a/src/routes/Auth/signup/Signup.tsx b/src/routes/Auth/signup/Signup.tsx index c3c6b11..67d5c24 100644 --- a/src/routes/Auth/signup/Signup.tsx +++ b/src/routes/Auth/signup/Signup.tsx @@ -1,27 +1,106 @@ -import React, { useEffect, useState } from 'react'; -import { useLocation } from 'react-router-dom'; +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 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 가입성공 from './가입성공'; -import 정보입력 from './정보입력'; +import { colors } from '@styles/theme'; -const Signup = () => { - const steps = ['정보입력', '가입성공']; +import { FormContainer, NextButton } from './Signup.styles'; - const [Funnel, setStep] = useFunnel(steps); - const location = useLocation(); - const [registerData, setRegisterData] = useState(); +type SignupForm = Omit; + +const MAX_NICKNAME_LENGTH = 8; + +const Signup = ({ 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}/${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 = (data) => { + signupMutation.mutate({ ...data, memberId }); + }; + + useEffect(() => { + if (birthdayInput?.length === 4 || birthdayInput?.length === 7) { + methods.setValue(INPUT_TYPE.BIRTHDAY, birthdayInput + '/'); + } + }, [birthdayInput, methods]); return ( - - - <정보입력 memberId={location.state.memberId} /> - - - <가입성공 /> - - + ( + + 회원정보 입력 + + )} + > + + + + + ( + + {nicknameProgress} + + )} + /> + + + + + + + + + + + + 다음 + + + ); }; 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 정보입력; From 36779a0da38e24c27f3a2facc1c4ad205e8194ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 22:47:56 +0900 Subject: [PATCH 09/17] fix: file name error --- .../routes/Auth/signup/Signup.styles.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "src/routes/Auth/signup/\bSignup.styles.tsx" => src/routes/Auth/signup/Signup.styles.tsx (100%) diff --git "a/src/routes/Auth/signup/\bSignup.styles.tsx" b/src/routes/Auth/signup/Signup.styles.tsx similarity index 100% rename from "src/routes/Auth/signup/\bSignup.styles.tsx" rename to src/routes/Auth/signup/Signup.styles.tsx From b292c8804c0c4c90115662cedac2a3a713d1bf5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=EC=A7=84=ED=98=B8?= Date: Mon, 22 Jan 2024 22:48:06 +0900 Subject: [PATCH 10/17] fix: genders value --- src/constants/signup.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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', }, ]; From e99a4bf8ed9b16e4b6cf713b6aa2ec786966f086 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 15:01:46 +0900 Subject: [PATCH 11/17] feat: add check icon --- src/assets/icons/check.svg | 3 +++ src/assets/icons/index.ts | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 src/assets/icons/check.svg 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 af61474..5ac0960 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -5,6 +5,7 @@ import AlarmIcon from './alarm-default.svg?react'; import NewAlarmIcon from './alarm-new.svg?react'; import AppleIcon from './apple.svg?react'; import BLogoIcon from './b-logo.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'; @@ -34,6 +35,7 @@ export { ALogoIcon, AppleIcon, BLogoIcon, + CheckIcon, ClockIcon, CloseIcon, CommentIcon, From e0610a8a2c1eb002858e015f99e7705e23a70802 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 15:01:59 +0900 Subject: [PATCH 12/17] fix: add transparent props to bottomsheet --- src/components/commons/BottomSheet/BottomSheet.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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)` From 2bb734064017b16bb2bd54e2f00117eb3872fa20 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 15:02:12 +0900 Subject: [PATCH 13/17] feat: add checkbox component --- src/components/commons/CheckBox/CheckBox.tsx | 58 ++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/components/commons/CheckBox/CheckBox.tsx 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; +`; From 3816501560e030d207b8db06b3c8cc6b8c33ff62 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 15:02:22 +0900 Subject: [PATCH 14/17] feat: add transparent props --- src/hooks/useBottomSheet/useBottomSheet.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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} From be6d63bc8397f91fb5afa1b5c9482d075a59a831 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 15:02:47 +0900 Subject: [PATCH 15/17] =?UTF-8?q?feat:=20toggle=20=EC=95=BD=EA=B4=80?= =?UTF-8?q?=EB=8F=99=EC=9D=98=20bottomsheet=20on=20submit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/Auth/signup/Signup.tsx | 48 +++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/src/routes/Auth/signup/Signup.tsx b/src/routes/Auth/signup/Signup.tsx index 67d5c24..0f9e0cc 100644 --- a/src/routes/Auth/signup/Signup.tsx +++ b/src/routes/Auth/signup/Signup.tsx @@ -1,5 +1,6 @@ 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'; @@ -11,18 +12,30 @@ 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 { colors } from '@styles/theme'; -import { FormContainer, NextButton } from './Signup.styles'; +import { ResponseError } from '@apis/fetch'; + +import { FormContainer, NextButton } from './Signup.styles'; +import Terms from './Terms'; type SignupForm = Omit; const MAX_NICKNAME_LENGTH = 8; -const Signup = ({ memberId }: { memberId: number }) => { +const Signup = () => { + const location = useLocation(); 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; const birthdayInput = methods.watch(INPUT_TYPE.BIRTHDAY); const nicknameProgress = methods.watch(INPUT_TYPE.NICKNAME) @@ -38,8 +51,24 @@ const Signup = ({ memberId }: { memberId: number }) => { } }; - const handleSubmitForm: SubmitHandler = (data) => { - signupMutation.mutate({ ...data, memberId }); + 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(() => { @@ -48,6 +77,10 @@ const Signup = ({ memberId }: { memberId: number }) => { } }, [birthdayInput, methods]); + if (!memberId) { + return
잘못된 접근입니다.
; + } + return ( { /> - 다음 + + 다음 + + + + ); }; From df7633d1ed91f39dc8a5cee808e07fd21c426e18 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 15:02:58 +0900 Subject: [PATCH 16/17] feat: implements Terms component --- src/routes/Auth/signup/Terms.tsx | 99 ++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/routes/Auth/signup/Terms.tsx diff --git a/src/routes/Auth/signup/Terms.tsx b/src/routes/Auth/signup/Terms.tsx new file mode 100644 index 0000000..9d998f7 --- /dev/null +++ b/src/routes/Auth/signup/Terms.tsx @@ -0,0 +1,99 @@ +import React, { ChangeEvent, useState } from 'react'; + +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'; + +const Terms = () => { + 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 = () => { + if (disabled) return; + console.log('submit'); + }; + + return ( + + + + + + 모두 동의하기 + + + +
+ + setConsentToTerm(e.target.checked)} + > + + 서비스 이용 약관(필수) + + + + + setConsentToCollectAndUseInfo(e.target.checked)} + > + + 개인정보 수집 및 이용동의(필수) + + + + + setConsetToMarketing(e.target.checked)} + > + + 마케팅 정보 수신 동의(선택) + + + + + + {/* SAFE AREA */} + + 다음 + + + + ); +}; + +export default Terms; From 415f0223176c812f0f2f37276eae33d982c7434d 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 15:49:28 +0900 Subject: [PATCH 17/17] feat: add term mutation api --- src/apis/oauth/signup.ts | 24 ++++++++++++++++++++++-- src/routes/Auth/signup/Signup.tsx | 4 ++-- src/routes/Auth/signup/Terms.tsx | 22 +++++++++++++++++++--- 3 files changed, 43 insertions(+), 7 deletions(-) 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/routes/Auth/signup/Signup.tsx b/src/routes/Auth/signup/Signup.tsx index 0f9e0cc..9239c75 100644 --- a/src/routes/Auth/signup/Signup.tsx +++ b/src/routes/Auth/signup/Signup.tsx @@ -35,7 +35,7 @@ const Signup = () => { transparent: false, }); - const memberId = location.state.memberId; + const memberId = location.state.memberId as number; const birthdayInput = methods.watch(INPUT_TYPE.BIRTHDAY); const nicknameProgress = methods.watch(INPUT_TYPE.NICKNAME) @@ -136,7 +136,7 @@ const Signup = () => { - + ); diff --git a/src/routes/Auth/signup/Terms.tsx b/src/routes/Auth/signup/Terms.tsx index 9d998f7..d7b1acb 100644 --- a/src/routes/Auth/signup/Terms.tsx +++ b/src/routes/Auth/signup/Terms.tsx @@ -1,5 +1,7 @@ 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'; @@ -8,7 +10,13 @@ import { colors } from '@styles/theme'; import { NextButton } from './Signup.styles'; -const Terms = () => { +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); @@ -30,9 +38,17 @@ const Terms = () => { } }; - const handleSubmitConsetToTerm = () => { + const handleSubmitConsetToTerm = async () => { if (disabled) return; - console.log('submit'); + + const response = await consentToTermMutation.mutateAsync({ + memberId, + listen_marketing: consetToMarketing, + }); + setUser({ + memberId: response.memberId, + accessToken: response.accessToken, + }); }; return (