From cbfeda3ce868c75126ec414771f7bd1d5d6b0337 Mon Sep 17 00:00:00 2001 From: hwanheejung Date: Sat, 3 Aug 2024 01:36:44 +0900 Subject: [PATCH 001/104] #64 feat: Hamburger Menu --- public/icons/hamburger.svg | 8 +++ src/app/(home)/page.tsx | 2 + src/components/HamburgerMenu/Drawer.tsx | 85 +++++++++++++++++++++++++ src/components/HamburgerMenu/index.tsx | 26 ++++++++ 4 files changed, 121 insertions(+) create mode 100644 public/icons/hamburger.svg create mode 100644 src/components/HamburgerMenu/Drawer.tsx create mode 100644 src/components/HamburgerMenu/index.tsx diff --git a/public/icons/hamburger.svg b/public/icons/hamburger.svg new file mode 100644 index 0000000..4e3fa93 --- /dev/null +++ b/public/icons/hamburger.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/app/(home)/page.tsx b/src/app/(home)/page.tsx index 1bb1139..7399da7 100644 --- a/src/app/(home)/page.tsx +++ b/src/app/(home)/page.tsx @@ -1,6 +1,7 @@ import PolaroidsIcon from 'public/icons/home_polaroids.svg' import PolaboLogo from 'public/images/polabo_logo.png' import Image from 'next/image' +import Hamburger from '@/components/HamburgerMenu' import CreateBoardBtn from './components/CreateBoardBtn' import CopyLinkBtn from './components/CopyLinkBtn' import TotalCount from './components/TotalCount' @@ -8,6 +9,7 @@ import TotalCount from './components/TotalCount' const HomePage = () => { return (
+
diff --git a/src/components/HamburgerMenu/Drawer.tsx b/src/components/HamburgerMenu/Drawer.tsx new file mode 100644 index 0000000..e6787cf --- /dev/null +++ b/src/components/HamburgerMenu/Drawer.tsx @@ -0,0 +1,85 @@ +'use client' + +import CloseIcon from 'public/icons/close.svg' +import { ReactNode, createContext, useContext, useMemo } from 'react' +import ReactDOM from 'react-dom' + +interface DrawerContextProps { + isVisible: boolean + onClose: () => void +} + +const DrawerContext = createContext({ + isVisible: false, + onClose: () => {}, +}) + +const ModalOverlay = ({ + children, + closeOnClick, +}: { + children: ReactNode + closeOnClick: boolean +}) => { + const { isVisible, onClose } = useContext(DrawerContext) + + const handleClick = (event: React.MouseEvent) => { + if (closeOnClick && event.target === event.currentTarget) { + onClose() + } + } + + return ( +
+ {children} +
+ ) +} + +interface DrawerProps { + children: ReactNode + isOpen: boolean + onClose: () => void +} + +const Drawer = ({ children, isOpen, onClose }: DrawerProps) => { + const context = useMemo( + () => ({ + isVisible: isOpen, + onClose, + }), + [isOpen, onClose], + ) + + return isOpen + ? ReactDOM.createPortal( + + +
+ {children} +
+
+
, + document.getElementById('modal-root') as HTMLElement, + ) + : null +} + +const DrawerClose = () => { + const { onClose } = useContext(DrawerContext) + return ( + + ) +} + +Drawer.Close = DrawerClose + +export default Drawer diff --git a/src/components/HamburgerMenu/index.tsx b/src/components/HamburgerMenu/index.tsx new file mode 100644 index 0000000..c596af9 --- /dev/null +++ b/src/components/HamburgerMenu/index.tsx @@ -0,0 +1,26 @@ +'use client' + +import HamburgerIcon from 'public/icons/hamburger.svg' +import { useState } from 'react' +import Drawer from './Drawer' + +const Hamburger = ({ + className = '', +}: { + className?: React.ComponentProps<'div'>['className'] +}) => { + const [isOpen, setIsOpen] = useState(false) + return ( +
+ setIsOpen(true)} + /> + setIsOpen(false)}> + + +
+ ) +} + +export default Hamburger From 60da15b990deaf927fd19087201c4650b98e7066 Mon Sep 17 00:00:00 2001 From: hwanheejung Date: Sat, 3 Aug 2024 14:48:36 +0900 Subject: [PATCH 002/104] #64 feat: drawer animation --- src/components/HamburgerMenu/Drawer.tsx | 4 +++- src/components/HamburgerMenu/HamburgerMenu.stories.tsx | 0 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 src/components/HamburgerMenu/HamburgerMenu.stories.tsx diff --git a/src/components/HamburgerMenu/Drawer.tsx b/src/components/HamburgerMenu/Drawer.tsx index e6787cf..3d7a144 100644 --- a/src/components/HamburgerMenu/Drawer.tsx +++ b/src/components/HamburgerMenu/Drawer.tsx @@ -60,7 +60,9 @@ const Drawer = ({ children, isOpen, onClose }: DrawerProps) => { ? ReactDOM.createPortal( -
+
{children}
diff --git a/src/components/HamburgerMenu/HamburgerMenu.stories.tsx b/src/components/HamburgerMenu/HamburgerMenu.stories.tsx new file mode 100644 index 0000000..e69de29 From a1d0395711759c9cb923b62c18237e00b53b2fe1 Mon Sep 17 00:00:00 2001 From: hwanheejung Date: Sun, 4 Aug 2024 15:39:45 +0900 Subject: [PATCH 003/104] #64 feat: drawer open animation --- src/components/HamburgerMenu/Drawer.tsx | 85 +++++++++++++++++++------ src/components/HamburgerMenu/index.tsx | 3 +- tailwind.config.ts | 10 +++ 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/components/HamburgerMenu/Drawer.tsx b/src/components/HamburgerMenu/Drawer.tsx index 3d7a144..e665b66 100644 --- a/src/components/HamburgerMenu/Drawer.tsx +++ b/src/components/HamburgerMenu/Drawer.tsx @@ -1,17 +1,24 @@ 'use client' import CloseIcon from 'public/icons/close.svg' -import { ReactNode, createContext, useContext, useMemo } from 'react' +import { + ReactNode, + createContext, + useContext, + useEffect, + useMemo, + useState, +} from 'react' import ReactDOM from 'react-dom' interface DrawerContextProps { isVisible: boolean - onClose: () => void + onClose: () => Promise } const DrawerContext = createContext({ isVisible: false, - onClose: () => {}, + onClose: async () => {}, }) const ModalOverlay = ({ @@ -31,7 +38,7 @@ const ModalOverlay = ({ return (
{ + const [isVisible, setIsVisible] = useState(false) + const [closePromise, setClosePromise] = useState<{ + resolve: () => void + } | null>(null) + + const closeModal = () => { + setIsVisible(false) + return new Promise((resolve) => { + // onClose() + setClosePromise({ resolve }) + }) + } + + useEffect(() => { + if (isOpen) { + requestAnimationFrame(() => { + setIsVisible(true) + }) + } else { + closeModal() + } + }, [isOpen]) + + const handleTransitionEnd = () => { + if (!isVisible) { + onClose() + if (closePromise) { + closePromise.resolve() + setClosePromise(null) + } + } + } + const context = useMemo( () => ({ - isVisible: isOpen, - onClose, + isVisible, + onClose: closeModal, }), - [isOpen, onClose], + [isVisible, setIsVisible], ) - return isOpen - ? ReactDOM.createPortal( - - -
- {children} -
-
-
, - document.getElementById('modal-root') as HTMLElement, - ) - : null + if (!isOpen && !isVisible) return null + + return ReactDOM.createPortal( +
+ + +
+ {children} +
+
+
+
, + document.getElementById('modal-root') as HTMLElement, + ) } const DrawerClose = () => { diff --git a/src/components/HamburgerMenu/index.tsx b/src/components/HamburgerMenu/index.tsx index c596af9..9b56c38 100644 --- a/src/components/HamburgerMenu/index.tsx +++ b/src/components/HamburgerMenu/index.tsx @@ -10,11 +10,12 @@ const Hamburger = ({ className?: React.ComponentProps<'div'>['className'] }) => { const [isOpen, setIsOpen] = useState(false) + return (
setIsOpen(true)} + className="cursor-pointer" /> setIsOpen(false)}> diff --git a/tailwind.config.ts b/tailwind.config.ts index adcf1e3..87c17cb 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -100,12 +100,22 @@ const config: Config = { transform: 'translateX(0%)', }, }, + 'drawer-slide-in': { + '0%': { transform: 'translateX(-100%)' }, + '100%': { transform: 'translateX(0)' }, + }, + 'drawer-slide-out': { + '0%': { transform: 'translateX(0)' }, + '100%': { transform: 'translateX(-100%)' }, + }, }, animation: { 'slide-left': 'slide-left 30s linear -30s infinite', 'slide-left-delay': 'slide-left-delay 30s linear -15s infinite', 'slide-right': 'slide-right 30s linear -30s infinite', 'slide-right-delay': 'slide-right-delay 30s linear -15s infinite', + 'drawer-slide-in': 'drawer-slide-in 0.3s ease-out forwards', + 'drawer-slide-out': 'drawer-slide-out 0.3s ease-out forwards', }, boxShadow: { header: '0px 1px 2px 0px rgba(0, 0, 0, 0.10)', From ec4205a1340dd5d8b8df7337af41fd478d90efb8 Mon Sep 17 00:00:00 2001 From: hwanheejung Date: Sun, 4 Aug 2024 16:21:29 +0900 Subject: [PATCH 004/104] =?UTF-8?q?#64=20feat:=20drawer=20animation=20?= =?UTF-8?q?=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/HamburgerMenu/Drawer.tsx | 57 +++++++++++-------------- tailwind.config.ts | 10 ----- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/src/components/HamburgerMenu/Drawer.tsx b/src/components/HamburgerMenu/Drawer.tsx index e665b66..022e655 100644 --- a/src/components/HamburgerMenu/Drawer.tsx +++ b/src/components/HamburgerMenu/Drawer.tsx @@ -13,15 +13,15 @@ import ReactDOM from 'react-dom' interface DrawerContextProps { isVisible: boolean - onClose: () => Promise + onClose: () => void } const DrawerContext = createContext({ isVisible: false, - onClose: async () => {}, + onClose: () => {}, }) -const ModalOverlay = ({ +const DrawerOverlay = ({ children, closeOnClick, }: { @@ -38,7 +38,7 @@ const ModalOverlay = ({ return (
{ const [isVisible, setIsVisible] = useState(false) - const [closePromise, setClosePromise] = useState<{ - resolve: () => void - } | null>(null) const closeModal = () => { setIsVisible(false) - return new Promise((resolve) => { - // onClose() - setClosePromise({ resolve }) - }) } useEffect(() => { @@ -81,10 +74,6 @@ const Drawer = ({ children, isOpen, onClose }: DrawerProps) => { const handleTransitionEnd = () => { if (!isVisible) { onClose() - if (closePromise) { - closePromise.resolve() - setClosePromise(null) - } } } @@ -93,26 +82,28 @@ const Drawer = ({ children, isOpen, onClose }: DrawerProps) => { isVisible, onClose: closeModal, }), - [isVisible, setIsVisible], + [isVisible], ) - if (!isOpen && !isVisible) return null - - return ReactDOM.createPortal( -
- - -
- {children} -
-
-
-
, - document.getElementById('modal-root') as HTMLElement, - ) + return isOpen + ? ReactDOM.createPortal( +
+ + +
+ {children} +
+
+
+
, + document.getElementById('modal-root') as HTMLElement, + ) + : null } const DrawerClose = () => { diff --git a/tailwind.config.ts b/tailwind.config.ts index 87c17cb..adcf1e3 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -100,22 +100,12 @@ const config: Config = { transform: 'translateX(0%)', }, }, - 'drawer-slide-in': { - '0%': { transform: 'translateX(-100%)' }, - '100%': { transform: 'translateX(0)' }, - }, - 'drawer-slide-out': { - '0%': { transform: 'translateX(0)' }, - '100%': { transform: 'translateX(-100%)' }, - }, }, animation: { 'slide-left': 'slide-left 30s linear -30s infinite', 'slide-left-delay': 'slide-left-delay 30s linear -15s infinite', 'slide-right': 'slide-right 30s linear -30s infinite', 'slide-right-delay': 'slide-right-delay 30s linear -15s infinite', - 'drawer-slide-in': 'drawer-slide-in 0.3s ease-out forwards', - 'drawer-slide-out': 'drawer-slide-out 0.3s ease-out forwards', }, boxShadow: { header: '0px 1px 2px 0px rgba(0, 0, 0, 0.10)', From c5a95d9b4540f793a2f015b6dd0dc8c589c1389d Mon Sep 17 00:00:00 2001 From: hwanheejung Date: Mon, 5 Aug 2024 00:20:00 +0900 Subject: [PATCH 005/104] #64 feat: Menus --- public/icons/person.svg | 4 + .../HamburgerMenu/HamburgerMenu.stories.tsx | 0 src/components/HamburgerMenu/Menu.tsx | 104 ++++++++++++++++++ src/components/HamburgerMenu/index.tsx | 2 + 4 files changed, 110 insertions(+) create mode 100644 public/icons/person.svg delete mode 100644 src/components/HamburgerMenu/HamburgerMenu.stories.tsx create mode 100644 src/components/HamburgerMenu/Menu.tsx diff --git a/public/icons/person.svg b/public/icons/person.svg new file mode 100644 index 0000000..a45e686 --- /dev/null +++ b/public/icons/person.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/components/HamburgerMenu/HamburgerMenu.stories.tsx b/src/components/HamburgerMenu/HamburgerMenu.stories.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/HamburgerMenu/Menu.tsx b/src/components/HamburgerMenu/Menu.tsx new file mode 100644 index 0000000..2536be9 --- /dev/null +++ b/src/components/HamburgerMenu/Menu.tsx @@ -0,0 +1,104 @@ +import PersonIcon from 'public/icons/person.svg' +import PinIcon from 'public/icons/pinFilled.svg' +import PolaroidIcon from 'public/icons/polaroid.svg' +import { ReactNode } from 'react' + +const Profile = ({ + loggedIn, + onClick, +}: { + loggedIn: boolean + onClick: React.ComponentProps<'div'>['onClick'] +}) => { + return ( +
+ + + {loggedIn ? '정환희' : '로그인해주세요.'} + +
+ ) +} + +const Main = () => { + const handleClick = () => { + console.log('메인 페이지로 이동') + } + return ( +
+ POLABO 메인 +
+ ) +} + +const Divider = () => ( +
+) + +const MyMenu = ({ + icon, + text, + onClick, +}: { + icon: ReactNode + text: string + onClick: React.ComponentProps<'div'>['onClick'] +}) => ( +
+ {icon} + {text} +
+) + +const ServiceMenu = ({ + text, + onClick, +}: { + text: string + onClick: React.ComponentProps<'div'>['onClick'] +}) => ( +
+ {text} +
+) + +const Menu = ({ loggedIn }: { loggedIn: boolean }) => { + return ( +
+ {}} /> +
+ + {loggedIn && ( +
+ } + text="프로필 수정" + onClick={() => {}} + /> + } text="내 보드 목록" onClick={() => {}} /> +
+ )} + +
+ {}} /> + {}} /> + {}} /> + {}} /> +
+
+ ) +} + +export default Menu diff --git a/src/components/HamburgerMenu/index.tsx b/src/components/HamburgerMenu/index.tsx index 9b56c38..e1a47b4 100644 --- a/src/components/HamburgerMenu/index.tsx +++ b/src/components/HamburgerMenu/index.tsx @@ -3,6 +3,7 @@ import HamburgerIcon from 'public/icons/hamburger.svg' import { useState } from 'react' import Drawer from './Drawer' +import Menu from './Menu' const Hamburger = ({ className = '', @@ -19,6 +20,7 @@ const Hamburger = ({ /> setIsOpen(false)}> +
) From bd149591a30f6214e16e511bf65b0aa0ba2d2032 Mon Sep 17 00:00:00 2001 From: hwanheejung Date: Mon, 5 Aug 2024 00:36:47 +0900 Subject: [PATCH 006/104] #64 feat: Added Logout --- src/components/HamburgerMenu/Menu.tsx | 19 ++++++++++++++----- src/components/HamburgerMenu/index.tsx | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/HamburgerMenu/Menu.tsx b/src/components/HamburgerMenu/Menu.tsx index 2536be9..3c94cfe 100644 --- a/src/components/HamburgerMenu/Menu.tsx +++ b/src/components/HamburgerMenu/Menu.tsx @@ -37,9 +37,7 @@ const Main = () => { ) } -const Divider = () => ( -
-) +const Divider = () =>
const MyMenu = ({ icon, @@ -51,7 +49,7 @@ const MyMenu = ({ onClick: React.ComponentProps<'div'>['onClick'] }) => (
{icon} @@ -74,9 +72,18 @@ const ServiceMenu = ({
) +const Logout = () => ( +
{}} + > + 로그아웃 +
+) + const Menu = ({ loggedIn }: { loggedIn: boolean }) => { return ( -
+
{}} />
@@ -97,6 +104,8 @@ const Menu = ({ loggedIn }: { loggedIn: boolean }) => { {}} /> {}} />
+ + {loggedIn && }
) } diff --git a/src/components/HamburgerMenu/index.tsx b/src/components/HamburgerMenu/index.tsx index e1a47b4..8be54c9 100644 --- a/src/components/HamburgerMenu/index.tsx +++ b/src/components/HamburgerMenu/index.tsx @@ -20,7 +20,7 @@ const Hamburger = ({ /> setIsOpen(false)}> - +
) From d894368c3e79114c6feca2a7948cfff6ffc2be20 Mon Sep 17 00:00:00 2001 From: hwanheejung Date: Mon, 5 Aug 2024 02:24:16 +0900 Subject: [PATCH 007/104] #69 feat: login page UI --- public/icons/kakao.svg | 15 +++++++ public/icons/threePolaroids.png | Bin 0 -> 38849 bytes src/app/(home)/components/CreateBoardBtn.tsx | 35 +++++++++++---- src/app/(home)/components/GoToLoginModal.tsx | 26 +++++++++++ .../(onboarding)/login/components/Policy.tsx | 23 ++++++++++ src/app/(onboarding)/login/page.tsx | 41 ++++++++++++++++++ tailwind.config.ts | 1 + 7 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 public/icons/kakao.svg create mode 100644 public/icons/threePolaroids.png create mode 100644 src/app/(home)/components/GoToLoginModal.tsx create mode 100644 src/app/(onboarding)/login/components/Policy.tsx create mode 100644 src/app/(onboarding)/login/page.tsx diff --git a/public/icons/kakao.svg b/public/icons/kakao.svg new file mode 100644 index 0000000..d753209 --- /dev/null +++ b/public/icons/kakao.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/icons/threePolaroids.png b/public/icons/threePolaroids.png new file mode 100644 index 0000000000000000000000000000000000000000..c1f25a9b8fb29c426ab3f17286e0062643cc27d7 GIT binary patch literal 38849 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 literal 0 HcmV?d00001 diff --git a/src/app/(home)/components/CreateBoardBtn.tsx b/src/app/(home)/components/CreateBoardBtn.tsx index 280110c..4d945c2 100644 --- a/src/app/(home)/components/CreateBoardBtn.tsx +++ b/src/app/(home)/components/CreateBoardBtn.tsx @@ -2,20 +2,37 @@ import Button from '@/components/Button' import { useRouter } from 'next/navigation' +import { useState } from 'react' +import GoToLoginModal from './GoToLoginModal' const CreateBoardBtn = () => { const router = useRouter() + const [loginModalOpen, setLoginModalOpen] = useState(false) + + const loggedIn = false // 임시 + + const handleClick = () => { + if (loggedIn) { + router.push('/board/create') + } else { + setLoginModalOpen(true) + } + } return ( - + <> + + setLoginModalOpen(false)} + /> + ) } diff --git a/src/app/(home)/components/GoToLoginModal.tsx b/src/app/(home)/components/GoToLoginModal.tsx new file mode 100644 index 0000000..4484274 --- /dev/null +++ b/src/app/(home)/components/GoToLoginModal.tsx @@ -0,0 +1,26 @@ +import Modal from '@/components/Modal' +import { useRouter } from 'next/navigation' +import SurprisedIcon from 'public/icons/surprised.svg' + +interface ModalProps { + isOpen: boolean + onClose: () => void +} + +const GoToLoginModal = ({ isOpen, onClose }: ModalProps) => { + const router = useRouter() + return ( + + }> + 로그인 후 이용 가능합니다. + 지금 폴라보와 함께 추억을 담아보세요! + router.push('/login')} + /> + + + ) +} + +export default GoToLoginModal diff --git a/src/app/(onboarding)/login/components/Policy.tsx b/src/app/(onboarding)/login/components/Policy.tsx new file mode 100644 index 0000000..3cf60be --- /dev/null +++ b/src/app/(onboarding)/login/components/Policy.tsx @@ -0,0 +1,23 @@ +import Link from 'next/link' + +const LinkTo = ({ text, link }: { text: string; link: string }) => ( + + {text} + +) + +const Policy = () => { + return ( +
+
+ 시작하기 버튼을 누르시면 POLABO의 + 과 +
+
+ 에 동의하게 됩니다. +
+
+ ) +} + +export default Policy diff --git a/src/app/(onboarding)/login/page.tsx b/src/app/(onboarding)/login/page.tsx new file mode 100644 index 0000000..67836c3 --- /dev/null +++ b/src/app/(onboarding)/login/page.tsx @@ -0,0 +1,41 @@ +import Button from '@/components/Button' +import Image from 'next/image' +import PolaroidsIcon from 'public/icons/home_polaroids.svg' +import KakaoIcon from 'public/icons/kakao.svg' +import ThreePolaroids from 'public/icons/threePolaroids.png' +import PolaboLogo from 'public/images/polabo_logo.png' +import Policy from './components/Policy' + +const LoginPage = () => { + return ( +
+
+ + + 함께 꾸미는 폴라로이드 보드 + + logo +
+ polaroids icon +
+
+ 지금 폴라보와 함께 추억을 담아보세요! +
+ + +
+
+ ) +} + +export default LoginPage diff --git a/tailwind.config.ts b/tailwind.config.ts index adcf1e3..909bf12 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -109,6 +109,7 @@ const config: Config = { }, boxShadow: { header: '0px 1px 2px 0px rgba(0, 0, 0, 0.10)', + button: '0px 4px 8px 0px rgba(0, 0, 0, 0.15)', }, }, }, From 3584e5ecc8ab71681ae0dc3a3df50a92f4a1df38 Mon Sep 17 00:00:00 2001 From: junseublim Date: Mon, 5 Aug 2024 23:22:18 +0900 Subject: [PATCH 008/104] =?UTF-8?q?#67=20feature:=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20-?= =?UTF-8?q?=20=EB=82=B4=20=EB=B3=B4=EB=93=9C,=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=88=98=EC=A0=95,=20=ED=83=88=ED=87=B4=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/mypage/boards/page.tsx | 96 ++++++++++++++++++++++ src/app/mypage/leave/page.tsx | 16 ++++ src/app/mypage/profileEdit/page.tsx | 16 ++++ src/components/Header/HeaderBackButton.tsx | 12 +++ src/components/Header/index.tsx | 36 ++++++++ 5 files changed, 176 insertions(+) create mode 100644 src/app/mypage/boards/page.tsx create mode 100644 src/app/mypage/leave/page.tsx create mode 100644 src/app/mypage/profileEdit/page.tsx create mode 100644 src/components/Header/HeaderBackButton.tsx create mode 100644 src/components/Header/index.tsx diff --git a/src/app/mypage/boards/page.tsx b/src/app/mypage/boards/page.tsx new file mode 100644 index 0000000..cdbd221 --- /dev/null +++ b/src/app/mypage/boards/page.tsx @@ -0,0 +1,96 @@ +import Header from '@/components/Header' + +const Page = () => { + return ( +
+
} + /> + +
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ ) +} + +export default Page diff --git a/src/app/mypage/leave/page.tsx b/src/app/mypage/leave/page.tsx new file mode 100644 index 0000000..c00092d --- /dev/null +++ b/src/app/mypage/leave/page.tsx @@ -0,0 +1,16 @@ +import Header from '@/components/Header' + +const Page = () => { + return ( +
+
} /> +
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ ) +} + +export default Page diff --git a/src/app/mypage/profileEdit/page.tsx b/src/app/mypage/profileEdit/page.tsx new file mode 100644 index 0000000..1973579 --- /dev/null +++ b/src/app/mypage/profileEdit/page.tsx @@ -0,0 +1,16 @@ +import Header from '@/components/Header' + +const Page = () => { + return ( +
+
} /> +
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet + atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum + ipsam laborum omnis quae sint tenetur totam ut voluptates. +
+
+ ) +} + +export default Page diff --git a/src/components/Header/HeaderBackButton.tsx b/src/components/Header/HeaderBackButton.tsx new file mode 100644 index 0000000..7495843 --- /dev/null +++ b/src/components/Header/HeaderBackButton.tsx @@ -0,0 +1,12 @@ +'use client' + +import BackIcon from 'public/icons/arrow_back_ios.svg' +import { useRouter } from 'next/navigation' + +const HeaderBackButton = () => { + const router = useRouter() + + return router.back()} /> +} + +export default HeaderBackButton diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx new file mode 100644 index 0000000..f50daf6 --- /dev/null +++ b/src/components/Header/index.tsx @@ -0,0 +1,36 @@ +import { ReactNode } from 'react' +import HeaderBackButton from '@/components/Header/HeaderBackButton' + +interface HeaderProps { + title: string + description?: string + leftButton?: ReactNode + rightButton?: ReactNode +} + +const Header = ({ + title, + description = '', + leftButton =
, + rightButton =
, +}: HeaderProps) => { + return ( + <> +
+ {leftButton} +
+
{title}
+
+ {description} +
+
+ {rightButton} +
+
+ + ) +} + +Header.BackButton = HeaderBackButton + +export default Header From dfb933d35d0cb5add003cec353e1265b0bef839e Mon Sep 17 00:00:00 2001 From: junseublim Date: Mon, 5 Aug 2024 23:25:07 +0900 Subject: [PATCH 009/104] =?UTF-8?q?#67=20style:=20min-h-dvh=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tailwind.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tailwind.config.ts b/tailwind.config.ts index adcf1e3..5983fa8 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -62,6 +62,9 @@ const config: Config = { height: { dvh: 'var(--dynamic-vh)', }, + minHeight: { + dvh: 'var(--dynamic-vh)', + }, fontFamily: { pretendard: ['var(--font-pretendard-variable)'], jooree: ['var(--font-jooree)'], From a76dc03e506bf23f63a100ccef0dc7c5a943dbc0 Mon Sep 17 00:00:00 2001 From: hwanheejung Date: Tue, 6 Aug 2024 13:54:51 +0900 Subject: [PATCH 010/104] #69 feat: NextAuth kakao login setup --- package-lock.json | 136 ++++++++++++++++++ package.json | 1 + .../login/components/KakaoLogin.tsx | 25 ++++ src/app/(onboarding)/login/page.tsx | 13 +- src/app/api/auth/[...nextauth]/route.ts | 34 +++++ 5 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 src/app/(onboarding)/login/components/KakaoLogin.tsx create mode 100644 src/app/api/auth/[...nextauth]/route.ts diff --git a/package-lock.json b/package-lock.json index b23b60f..5f86cc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "browser-image-compression": "^2.0.2", "eslint-import-resolver-typescript": "^3.6.1", "next": "14.2.4", + "next-auth": "^4.24.7", "prettier-plugin-tailwindcss": "^0.6.5", "react": "^18", "react-dom": "^18", @@ -4068,6 +4069,14 @@ "node": ">= 8" } }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -15098,6 +15107,14 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -16428,6 +16445,49 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.7", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.7.tgz", + "integrity": "sha512-iChjE8ov/1K/z98gdKbn2Jw+2vLgJtVV39X+rCP5SGnVQuco7QOr19FRNGMIrD8d3LYhHWV9j9sKLzq1aDWWQQ==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.5.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "next": "^12.2.5 || ^13 || ^14", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + }, + "peerDependenciesMeta": { + "nodemailer": { + "optional": true + } + } + }, + "node_modules/next-auth/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/next-auth/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -16725,6 +16785,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -16866,6 +16931,14 @@ "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", "integrity": "sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==" }, + "node_modules/oidc-token-hash": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", + "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -16899,6 +16972,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openid-client": { + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz", + "integrity": "sha512-5P4qO9nGJzB5PI0LFlhj4Dzg3m4odt0qsJTfyEtZyOlkgpILwEioOhVVJOrS1iVH494S4Ee5OCjjg6Bf5WOj3w==", + "dependencies": { + "jose": "^4.15.5", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/openid-client/node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/openid-client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -17673,6 +17784,31 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/preact": { + "version": "10.23.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.23.1.tgz", + "integrity": "sha512-O5UdRsNh4vdZaTieWe3XOgSpdMAmkIYBCT3VhQDlKrzyCm8lUYsk0fmVEvoQQifoOjFRTaHZO69ylrzTW2BH+A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, + "node_modules/preact-render-to-string/node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index f9b3a4e..7cc57fd 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "browser-image-compression": "^2.0.2", "eslint-import-resolver-typescript": "^3.6.1", "next": "14.2.4", + "next-auth": "^4.24.7", "prettier-plugin-tailwindcss": "^0.6.5", "react": "^18", "react-dom": "^18", diff --git a/src/app/(onboarding)/login/components/KakaoLogin.tsx b/src/app/(onboarding)/login/components/KakaoLogin.tsx new file mode 100644 index 0000000..668388e --- /dev/null +++ b/src/app/(onboarding)/login/components/KakaoLogin.tsx @@ -0,0 +1,25 @@ +'use client' + +import Button from '@/components/Button' +import { signIn } from 'next-auth/react' +import KakaoIcon from 'public/icons/kakao.svg' + +const KakaoLogin = () => { + return ( + + ) +} + +export default KakaoLogin diff --git a/src/app/(onboarding)/login/page.tsx b/src/app/(onboarding)/login/page.tsx index 67836c3..3e894ae 100644 --- a/src/app/(onboarding)/login/page.tsx +++ b/src/app/(onboarding)/login/page.tsx @@ -1,15 +1,14 @@ -import Button from '@/components/Button' import Image from 'next/image' import PolaroidsIcon from 'public/icons/home_polaroids.svg' -import KakaoIcon from 'public/icons/kakao.svg' import ThreePolaroids from 'public/icons/threePolaroids.png' import PolaboLogo from 'public/images/polabo_logo.png' +import KakaoLogin from './components/KakaoLogin' import Policy from './components/Policy' const LoginPage = () => { return (
-
+
함께 꾸미는 폴라로이드 보드 @@ -25,13 +24,7 @@ const LoginPage = () => {
지금 폴라보와 함께 추억을 담아보세요!
- +
diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..f50c0ae --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,34 @@ +import NextAuth from 'next-auth' +import KakaoProvider from 'next-auth/providers/kakao' + +const kakaoCustomProvider = KakaoProvider({ + clientId: process.env.KAKAO_CLIENT_ID ?? '', + clientSecret: process.env.KAKAO_CLIENT_SECRET ?? '', +}) + +const handler = NextAuth({ + providers: [kakaoCustomProvider], + callbacks: { + async signin({ user, account, profile, email, credentials }) { + console.log('signin', user, account, profile, email, credentials) + return true + }, + async jwt({ token, user, account, profile }) { + console.log('=====jwt=====') + console.log('token', token) + console.log('user', user) + console.log('account', account) + console.log('profile', profile) + return token + }, + async session({ session, user, token }) { + console.log('session', session, user, token) + return session + }, + async redirect({ baseUrl }) { + return `${baseUrl}/board/create` + }, + }, +}) + +export { handler as GET, handler as POST } From 686bf45234b3f8205e9b818515f422dc60430cdb Mon Sep 17 00:00:00 2001 From: hwanheejung Date: Tue, 6 Aug 2024 14:17:04 +0900 Subject: [PATCH 011/104] #69 style: added kakao button active color --- src/app/(onboarding)/login/components/KakaoLogin.tsx | 2 +- tailwind.config.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/(onboarding)/login/components/KakaoLogin.tsx b/src/app/(onboarding)/login/components/KakaoLogin.tsx index 668388e..f3f586c 100644 --- a/src/app/(onboarding)/login/components/KakaoLogin.tsx +++ b/src/app/(onboarding)/login/components/KakaoLogin.tsx @@ -8,7 +8,7 @@ const KakaoLogin = () => { return (
polaroids icon diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index f50c0ae..c4ea295 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -1,34 +1,3 @@ -import NextAuth from 'next-auth' -import KakaoProvider from 'next-auth/providers/kakao' +import { handlers } from '@/auth' -const kakaoCustomProvider = KakaoProvider({ - clientId: process.env.KAKAO_CLIENT_ID ?? '', - clientSecret: process.env.KAKAO_CLIENT_SECRET ?? '', -}) - -const handler = NextAuth({ - providers: [kakaoCustomProvider], - callbacks: { - async signin({ user, account, profile, email, credentials }) { - console.log('signin', user, account, profile, email, credentials) - return true - }, - async jwt({ token, user, account, profile }) { - console.log('=====jwt=====') - console.log('token', token) - console.log('user', user) - console.log('account', account) - console.log('profile', profile) - return token - }, - async session({ session, user, token }) { - console.log('session', session, user, token) - return session - }, - async redirect({ baseUrl }) { - return `${baseUrl}/board/create` - }, - }, -}) - -export { handler as GET, handler as POST } +export const { GET, POST } = handlers diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c7f2731..e1e7bc6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,3 +1,4 @@ +import AuthSession from '@/components/AuthSession' import GoogleAnalytics from '@/components/GoogleAnalytics' import '@/styles/globals.css' import type { Metadata } from 'next' @@ -56,9 +57,11 @@ export default function RootLayout({ -
- {children} -
+ +
+ {children} +
+