diff --git a/.github/workflows/autoSync-develop.yml b/.github/workflows/autoSync-develop.yml new file mode 100644 index 0000000..1df8e65 --- /dev/null +++ b/.github/workflows/autoSync-develop.yml @@ -0,0 +1,32 @@ +name: (Develop) Synchronize to forked repo +on: + push: + branches: + - develop + +jobs: + sync: + name: Sync forked repo + runs-on: ubuntu-latest + + steps: + - name: Checkout develop + uses: actions/checkout@v4 + with: + token: ${{ secrets.DEV_AUTO_SYNC_TOKEN }} + fetch-depth: 0 + ref: develop + + - name: Add remote-url + run: | + git remote add forked-repo https://hyeonjin6530:${{ secrets.DEV_AUTO_SYNC_TOKEN }}@github.com/hyeonjin6530/billilge-frontend + git config user.name hyeonjin6530 + git config user.email ${{ secrets.DEV_EMAIL }} + + - name: Push changes to forked-repo + run: | + git push -f forked-repo develop + + - name: Clean up + run: | + git remote remove forked-repo diff --git a/.github/workflows/autoSync.yml b/.github/workflows/autoSync-main.yml similarity index 81% rename from .github/workflows/autoSync.yml rename to .github/workflows/autoSync-main.yml index 9e0f29b..f5f1a89 100644 --- a/.github/workflows/autoSync.yml +++ b/.github/workflows/autoSync-main.yml @@ -1,8 +1,8 @@ -name: Synchronize to forked repo +name: (Main) Synchronize to forked repo on: push: branches: - - develop + - main jobs: sync: @@ -10,12 +10,12 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout develop + - name: Checkout main uses: actions/checkout@v4 with: token: ${{ secrets.AUTO_SYNC_TOKEN }} fetch-depth: 0 - ref: develop + ref: main - name: Add remote-url run: | @@ -25,7 +25,7 @@ jobs: - name: Push changes to forked-repo run: | - git push -f forked-repo develop + git push -f forked-repo main - name: Clean up run: | diff --git a/package.json b/package.json index ffb5cc2..799db06 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "dotenv": "^16.4.5", "firebase": "^11.3.1", "js-base64": "^3.7.7", + "js-cookie": "^3.0.5", "lodash": "^4.17.21", "lucide-react": "^0.475.0", "next": "^15.0.3", @@ -62,6 +63,7 @@ "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@types/jest": "^29.5.12", + "@types/js-cookie": "^3.0.6", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/public/assets/icons/icon-fee-check.svg b/public/assets/icons/icon-fee-check.svg new file mode 100644 index 0000000..e1a68dc --- /dev/null +++ b/public/assets/icons/icon-fee-check.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/icons/side-menu/admin-homepage.svg b/public/assets/icons/side-menu/admin-homepage.svg new file mode 100644 index 0000000..beccd3c --- /dev/null +++ b/public/assets/icons/side-menu/admin-homepage.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/callback/page.tsx b/src/app/callback/page.tsx index 046effd..f816f0f 100644 --- a/src/app/callback/page.tsx +++ b/src/app/callback/page.tsx @@ -3,6 +3,7 @@ import { useEffect, Suspense } from 'react'; import { useSearchParams, useRouter } from 'next/navigation'; import { handleLoginSuccess } from '@/utils/loginHandler'; +import Cookies from 'js-cookie'; function CallbackContent() { const searchParams = useSearchParams(); @@ -13,7 +14,7 @@ function CallbackContent() { const email = searchParams.get('email'); const accessToken = searchParams.get('accessToken'); - localStorage.setItem('email', email || ''); + Cookies.set('email', email || ''); if (!status) return; diff --git a/src/app/mobile/admin/dashboard/_components/DashboardItem.tsx b/src/app/mobile/admin/dashboard/_components/DashboardItem.tsx index ef74f0f..a80f549 100644 --- a/src/app/mobile/admin/dashboard/_components/DashboardItem.tsx +++ b/src/app/mobile/admin/dashboard/_components/DashboardItem.tsx @@ -9,7 +9,7 @@ export default function DashboardItem({ renterName, studentId, status, - applicatedAt, + rentAt, rentedCount, handleApproveBtnClick, handleCancelBtnClick, @@ -29,7 +29,7 @@ export default function DashboardItem({ handleCancelBtnClick(); }; - const applicatedTime = convertTime(applicatedAt); + const applicatedTime = convertTime(rentAt); return (
@@ -59,9 +59,10 @@ export default function DashboardItem({
-
신청 시간
+
대여 시간
- {applicatedTime.formattedDate} {applicatedTime.formattedTime} + {applicatedTime.formattedDate}{' '} + {applicatedTime.formattedTime.split(':').slice(0, 2).join(':')}
diff --git a/src/app/mobile/admin/dashboard/page.tsx b/src/app/mobile/admin/dashboard/page.tsx index 69fc4cc..42b229d 100644 --- a/src/app/mobile/admin/dashboard/page.tsx +++ b/src/app/mobile/admin/dashboard/page.tsx @@ -1,7 +1,6 @@ 'use client'; import { useState, useEffect } from 'react'; -import MobileLayout from '@/components/mobile/layout'; import Header from '@/components/mobile/Header'; import Dropdown from '@/components/mobile/Dropdown'; import useDropdown from '@/hooks/useDropdown'; @@ -104,7 +103,7 @@ export default function Dashboard() { }; return ( - +
@@ -139,7 +138,7 @@ export default function Dashboard() { renterName={item.renterName} studentId={item.studentId} status={item.status} - applicatedAt={item.applicatedAt} + rentAt={item.rentAt} rentedCount={item.rentedCount} handleApproveBtnClick={() => { if (item.rentalHistoryId !== undefined) { @@ -171,6 +170,6 @@ export default function Dashboard() { hideDropdown={hideDropdown} positionClasses="top-[80px] right-5" /> - +
); } diff --git a/src/app/mobile/admin/notification/page.tsx b/src/app/mobile/admin/notification/page.tsx index d46a9f4..3c8f229 100644 --- a/src/app/mobile/admin/notification/page.tsx +++ b/src/app/mobile/admin/notification/page.tsx @@ -1,14 +1,13 @@ 'use client'; import { useEffect, useState } from 'react'; -import MobileLayout from '@/components/mobile/layout'; import NotificationItem from '@/components/mobile/NotificationItem'; import Header from '@/components/mobile/Header'; import { elapsedTime } from '@/utils/elapsedTime'; import { NotificationProps } from '@/types/notificationType'; import { adminNotificationGet, - readNotificationPost, + readNotificationPatch, } from '@/services/notification'; type AdminNotificationType = NotificationProps; @@ -28,11 +27,11 @@ export default function Notification() { }, []); const handleReadNotification = async (notificationId: number) => { - await readNotificationPost(notificationId); + await readNotificationPatch(notificationId); }; return ( - +
{notificationDetail?.length === 0 ? (
@@ -53,6 +52,6 @@ export default function Notification() { /> )) )} - +
); } diff --git a/src/app/mobile/history/page.tsx b/src/app/mobile/history/page.tsx index b884577..1d9ce7c 100644 --- a/src/app/mobile/history/page.tsx +++ b/src/app/mobile/history/page.tsx @@ -1,7 +1,6 @@ 'use client'; import { useEffect, useState, useMemo } from 'react'; -import MobileLayout from '@/components/mobile/layout'; import Header from '@/components/mobile/Header'; import ReturnItem from '@/app/mobile/history/_components/ReturnItem'; import RentalItem from '@/app/mobile/history/_components/RentalItem'; @@ -194,7 +193,7 @@ export default function UserRentalList() { ); return ( - +
{/* 반납이 필요한 물품 */} @@ -287,6 +286,6 @@ export default function UserRentalList() {
© wink
-
+ ); } diff --git a/src/app/mobile/layout.tsx b/src/app/mobile/layout.tsx new file mode 100644 index 0000000..98216de --- /dev/null +++ b/src/app/mobile/layout.tsx @@ -0,0 +1,44 @@ +'use client'; + +import useAuthRedirect from '@/hooks/useAuthRedirect'; +import React, { useEffect } from 'react'; +import { usePathname } from 'next/navigation'; + +export default function MobileLayout({ + children, +}: { + children: React.ReactNode; +}) { + const pathname = usePathname(); + + // 제외할 경로 목록 + const excludedRoutes = ['/mobile/sign-in', '/mobile/sign-up']; + + const isExcluded = excludedRoutes.includes(pathname); + + useAuthRedirect(); + + useEffect(() => { + if (!isExcluded) { + const originalBgColor = document.body.style.backgroundColor; + document.body.style.backgroundColor = '#F3F4F6'; + + return () => { + document.body.style.backgroundColor = originalBgColor; + }; + } + + return undefined; + }, [isExcluded]); + + if (isExcluded) { + // 레이아웃 없이 children만 렌더링 + return children; + } + + return ( +
+
{children}
+
+ ); +} diff --git a/src/app/mobile/main/_components/MainHeader/index.tsx b/src/app/mobile/main/_components/MainHeader/index.tsx index b46936b..9626b8c 100644 --- a/src/app/mobile/main/_components/MainHeader/index.tsx +++ b/src/app/mobile/main/_components/MainHeader/index.tsx @@ -5,7 +5,9 @@ import Sidebar from '@/components/mobile/SidebarMenu/index'; import { useEffect, useState } from 'react'; import IconAlarm from 'public/assets/icons/icon-alarm.svg'; import IconHamburger from 'public/assets/icons/icon-hamburger.svg'; +import IconFeeCheck from 'public/assets/icons/icon-fee-check.svg'; import { getNotificationCount } from '@/apis/notification'; +import Cookies from 'js-cookie'; export default function MainHeader() { const router = useRouter(); @@ -15,6 +17,7 @@ export default function MainHeader() { name: string; id: string; role: string; + isFeePaid: boolean; } | null>(null); useEffect(() => { @@ -31,7 +34,7 @@ export default function MainHeader() { }, []); useEffect(() => { - const storedUser = localStorage.getItem('user'); + const storedUser = Cookies.get('user'); if (storedUser) { setUser(JSON.parse(storedUser)); } @@ -40,8 +43,8 @@ export default function MainHeader() { return ( <>
-
- {user?.name}님 +
+ {user?.name}님{user?.isFeePaid ? : null}
+
+ + {notificationDetail.map((item) => ( + + item.notificationId && + handleReadNotification(item.notificationId) + } + /> + ))} +
)} - + ); } diff --git a/src/app/mobile/privacy-policy/page.tsx b/src/app/mobile/privacy-policy/page.tsx index 4b80c03..60772dc 100644 --- a/src/app/mobile/privacy-policy/page.tsx +++ b/src/app/mobile/privacy-policy/page.tsx @@ -1,12 +1,11 @@ 'use client'; -import MobileLayout from '@/components/mobile/layout'; import Header from '@/components/mobile/Header'; import privacyPolicy from './privacyPolicyData'; export default function UserRentalList() { return ( - +
{/* 반납이 필요한 물품 */} @@ -42,6 +41,6 @@ export default function UserRentalList() {
© wink
- +
); } diff --git a/src/app/mobile/sign-in/page.tsx b/src/app/mobile/sign-in/page.tsx index 29028cb..b494e8e 100644 --- a/src/app/mobile/sign-in/page.tsx +++ b/src/app/mobile/sign-in/page.tsx @@ -6,6 +6,7 @@ import { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import { getUA } from 'react-device-detect'; import Alert from '@/components/mobile/Alert'; +import Cookies from 'js-cookie'; export default function SignIn() { const router = useRouter(); @@ -102,7 +103,7 @@ export default function SignIn() { }; useEffect(() => { - if (localStorage.getItem('token') && localStorage.getItem('user')) { + if (Cookies.get('token') && Cookies.get('user')) { router.replace('/mobile/main'); } }, []); diff --git a/src/app/mobile/sign-up/page.tsx b/src/app/mobile/sign-up/page.tsx index b362fa4..8fc16c8 100644 --- a/src/app/mobile/sign-up/page.tsx +++ b/src/app/mobile/sign-up/page.tsx @@ -4,6 +4,7 @@ import axios from 'axios'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; import postSignUp from '@/services/sign-up'; +import Cookies from 'js-cookie'; export default function SignUp() { const router = useRouter(); @@ -38,7 +39,7 @@ export default function SignUp() { const handleSignUp = async () => { if (!validateForm()) return; - const email = localStorage.getItem('email'); + const email = Cookies.get('email'); if (!email) { alert('이메일 정보가 없습니다. 다시 로그인해 주세요.'); return; @@ -53,8 +54,8 @@ export default function SignUp() { const userInfo = { name: studentName, id: studentId, role: 'USER' }; - localStorage.setItem('token', data.accessToken); - localStorage.setItem('user', JSON.stringify(userInfo)); + Cookies.set('token', data.accessToken); + Cookies.set('user', JSON.stringify(userInfo)); router.push('/mobile/main'); } catch (e) { diff --git a/src/components/desktop/NavBar/NavBar.tsx b/src/components/desktop/NavBar/NavBar.tsx index f3ef492..dadc6fa 100644 --- a/src/components/desktop/NavBar/NavBar.tsx +++ b/src/components/desktop/NavBar/NavBar.tsx @@ -3,6 +3,7 @@ import { useRouter } from 'next/navigation'; import { useState } from 'react'; import toast from 'react-hot-toast'; +import { clearAllCookies } from '@/utils/clearAllCookies'; interface NavBarItem { name: string; @@ -40,7 +41,7 @@ export default function NavBar() { router.push(`${item.link}`); if (item.name === '로그아웃') { - localStorage.clear(); + clearAllCookies(); toast.success('로그아웃에 성공했습니다!'); } diff --git a/src/components/mobile/BottomSheet/index.tsx b/src/components/mobile/BottomSheet/index.tsx index ea08f67..b9038f0 100644 --- a/src/components/mobile/BottomSheet/index.tsx +++ b/src/components/mobile/BottomSheet/index.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useState, useEffect, useRef, useCallback } from 'react'; +import React, { useState, useEffect } from 'react'; import IconClose from 'public/assets/icons/bottom-sheet/icon-close.svg'; import IconHomeIndicator from 'public/assets/icons/bottom-sheet/icon-home-indicator.svg'; import Image from 'next/image'; @@ -16,11 +16,6 @@ interface BottomSheetProps { item: Item | null; } -function useReRenderer() { - const [, setState] = useState({}); - return useCallback(() => setState({}), []); -} - export default function BottomSheet({ isOpen, onCloseAction, @@ -46,8 +41,7 @@ export default function BottomSheet({ }); // 버튼 중복 클릭 방지 - const isLoadingRef = useRef(false); - const reRender = useReRenderer(); + const [isLoading, setIsLoading] = useState(false); const maxQuantity = item?.count || 0; @@ -197,13 +191,9 @@ export default function BottomSheet({ ) { return; } + if (isLoading) return; - if (isLoadingRef.current) { - return; // 이미 로딩 중이면 무시 - } - - isLoadingRef.current = true; - reRender(); // 렌더링을 강제로 트리거 + setIsLoading(true); try { await requestItems({ @@ -261,8 +251,7 @@ export default function BottomSheet({ }, 300); } } finally { - isLoadingRef.current = false; - reRender(); // 렌더링을 다시 트리거 + setIsLoading(false); } }; @@ -380,7 +369,8 @@ export default function BottomSheet({ !errors.time && quantity !== '' && hour !== '' && - minute !== '' + minute !== '' && + !isLoading ? 'bg-return-blue text-white-primary' : 'cursor-not-allowed bg-gray-tertiary text-gray-secondary' }`} @@ -391,7 +381,7 @@ export default function BottomSheet({ quantity === '' || hour === '' || minute === '' - ) || isLoadingRef.current // 로딩 중일 때도 비활성화 + ) || isLoading } > 대여하기 diff --git a/src/components/mobile/Header/index.tsx b/src/components/mobile/Header/index.tsx index 8379a23..39932a0 100644 --- a/src/components/mobile/Header/index.tsx +++ b/src/components/mobile/Header/index.tsx @@ -5,6 +5,7 @@ import Sidebar from '@/components/mobile/SidebarMenu/index'; import { useEffect, useState } from 'react'; import IconArrow from 'public/assets/icons/icon-arrow.svg'; import IconHamburger from 'public/assets/icons/icon-hamburger.svg'; +import Cookies from 'js-cookie'; interface HeaderProps { title: string; @@ -21,7 +22,7 @@ export default function Header({ title, menu = false }: HeaderProps) { } | null>(null); useEffect(() => { - const storedUser = localStorage.getItem('user'); + const storedUser = Cookies.get('user'); if (storedUser) { setUser(JSON.parse(storedUser)); } diff --git a/src/components/mobile/SidebarMenu/index.tsx b/src/components/mobile/SidebarMenu/index.tsx index 6a9c60c..43004ac 100644 --- a/src/components/mobile/SidebarMenu/index.tsx +++ b/src/components/mobile/SidebarMenu/index.tsx @@ -8,7 +8,9 @@ import IconAdminDashboard from 'public/assets/icons/side-menu/admin-dashboard.sv import IconAdminAlarm from 'public/assets/icons/side-menu/admin-alarm.svg'; import IconLogout from 'public/assets/icons/side-menu/logout.svg'; import IconPrivacyPolicy from 'public/assets/icons/side-menu/privacy-policy.svg'; +import IconAdminHomepage from 'public/assets/icons/side-menu/admin-homepage.svg'; import { useRouter } from 'next/navigation'; +import { clearAllCookies } from '@/utils/clearAllCookies'; interface SidebarProps { isOpen: boolean; @@ -33,6 +35,11 @@ const adminItems = [ label: '관리자 알림', href: '/mobile/admin/notification', }, + { + icon: IconAdminHomepage, + label: '관리자 홈페이지', + href: 'https://www.billilge.site/desktop/login', + }, ]; export default function Sidebar({ @@ -43,7 +50,7 @@ export default function Sidebar({ const router = useRouter(); const handleLogout = () => { - localStorage.clear(); // 로컬 스토리지 전체 삭제 + clearAllCookies(); router.replace('/mobile/sign-in'); // 로그인 페이지로 이동 }; @@ -96,17 +103,23 @@ export default function Sidebar({ {role === 'ADMIN' && ( <>
    - {adminItems.map(({ icon: Icon, label, href }) => ( -
  • - - - {label} - -
  • - ))} + {adminItems.map(({ icon: Icon, label, href }) => { + const isExternal = label === '관리자 홈페이지'; + + return ( +
  • + + + {label} + +
  • + ); + })}
{/* 두 번째 구분선 */} diff --git a/src/components/mobile/layout/index.tsx b/src/components/mobile/layout/index.tsx deleted file mode 100644 index 2fe616b..0000000 --- a/src/components/mobile/layout/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -'use client'; - -import useAuthRedirect from '@/hooks/useAuthRedirect'; -import React, { useEffect } from 'react'; - -interface MobileLayoutProps { - children: React.ReactNode; -} - -export default function MobileLayout({ children }: MobileLayoutProps) { - useAuthRedirect(); - - useEffect(() => { - // 기존 body 배경색 저장 - const originalBgColor = document.body.style.backgroundColor; - - // MobileLayout이 마운트될 때 배경색 적용(모바일에서만 적용하기 위함) - document.body.style.backgroundColor = '#F3F4F6'; - - return () => { - // MobileLayout이 언마운트될 때 원래 배경색 복구 - document.body.style.backgroundColor = originalBgColor; - }; - }, []); - - return ( -
-
{children}
-
- ); -} diff --git a/src/hooks/useAuthRedirect.ts b/src/hooks/useAuthRedirect.ts index b7e9bce..ae72099 100644 --- a/src/hooks/useAuthRedirect.ts +++ b/src/hooks/useAuthRedirect.ts @@ -2,6 +2,7 @@ import { useEffect } from 'react'; import { useRouter, usePathname } from 'next/navigation'; +import Cookies from 'js-cookie'; const useAuthRedirect = () => { const router = useRouter(); @@ -15,8 +16,8 @@ const useAuthRedirect = () => { currentPage === '/mobile/sign-in' || currentPage === '/desktop/login'; - const userString = localStorage.getItem('user'); - const isLogin = !!(userString && localStorage.getItem('token')); + const userString = Cookies.get('user'); + const isLogin = !!(userString && Cookies.get('token')); const user = userString ? JSON.parse(userString) : undefined; if (!isLogin) { diff --git a/src/lib/firebase.ts b/src/lib/firebase.ts index 702fca7..3f8c374 100644 --- a/src/lib/firebase.ts +++ b/src/lib/firebase.ts @@ -3,6 +3,7 @@ import { initializeApp } from 'firebase/app'; import { getMessaging, getToken } from '@firebase/messaging'; import postFCMToken from '@/services/fcm'; import { isSupported } from 'firebase/messaging'; +import Cookies from 'js-cookie'; const firebaseConfig = { apiKey: `${process.env.NEXT_PUBLIC_FIREBASE_API_KEY}`, @@ -35,7 +36,7 @@ export const handleFCMToken = async () => { if (!currentToken) { alert('알림을 허용해 주세요.'); } else { - localStorage.setItem('fcmToken', currentToken); + Cookies.set('fcmToken', currentToken); await postFCMToken(currentToken); } }) diff --git a/src/services/notification.ts b/src/services/notification.ts index 5770f96..2778aeb 100644 --- a/src/services/notification.ts +++ b/src/services/notification.ts @@ -18,7 +18,7 @@ const adminNotificationGet = async () => { } }; -const readNotificationPost = async (notificationId: number) => { +const readNotificationPatch = async (notificationId: number) => { try { const response = await PrivateAxiosInstance.patch( `notifications/${notificationId}`, @@ -29,4 +29,18 @@ const readNotificationPost = async (notificationId: number) => { } }; -export { adminNotificationGet, userNotificationGet, readNotificationPost }; +const readNotificationAllPatch = async () => { + try { + const response = await PrivateAxiosInstance.patch('notifications/all'); + return response.data; + } catch (error) { + return []; + } +}; + +export { + adminNotificationGet, + readNotificationAllPatch, + readNotificationPatch, + userNotificationGet, +}; diff --git a/src/services/privateAxiosInstance.ts b/src/services/privateAxiosInstance.ts index 3849380..750b697 100644 --- a/src/services/privateAxiosInstance.ts +++ b/src/services/privateAxiosInstance.ts @@ -1,13 +1,15 @@ import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios'; +import Cookies from 'js-cookie'; +import { clearAllCookies } from '@/utils/clearAllCookies'; const getAccessToken = (): string | null => { - const token = localStorage.getItem('token'); + const token = Cookies.get('token'); if (token) { try { return token || ''; } catch (error) { - console.error('Failed to parse user from localStorage', error); + console.error('Failed to parse user from Cookies', error); return ''; } } @@ -53,7 +55,7 @@ PrivateAxiosInstance.interceptors.response.use( (response) => response, (error: AxiosError) => { if (error.response?.status === 401) { - localStorage.clear(); + clearAllCookies(); redirectToLogin(); } return Promise.reject(error); diff --git a/src/types/dashboardType.ts b/src/types/dashboardType.ts index 8598a26..5afa7f5 100644 --- a/src/types/dashboardType.ts +++ b/src/types/dashboardType.ts @@ -5,7 +5,7 @@ export interface DashboardProps { renterName: string; studentId: string; status: string; - applicatedAt: string; + rentAt: string; rentedCount: number; handleApproveBtnClick: () => void; handleCancelBtnClick: () => void; diff --git a/src/utils/clearAllCookies.ts b/src/utils/clearAllCookies.ts new file mode 100644 index 0000000..7e42cb5 --- /dev/null +++ b/src/utils/clearAllCookies.ts @@ -0,0 +1,8 @@ +import Cookies from 'js-cookie'; + +export const clearAllCookies = () => { + const allCookies = Cookies.get(); + Object.keys(allCookies).forEach((cookieName) => { + Cookies.remove(cookieName); + }); +}; diff --git a/src/utils/loginHandler.ts b/src/utils/loginHandler.ts index 99226ae..de5db66 100644 --- a/src/utils/loginHandler.ts +++ b/src/utils/loginHandler.ts @@ -1,15 +1,26 @@ import { decode } from 'js-base64'; +import Cookies from 'js-cookie'; export const handleLoginSuccess = (accessToken: string | null) => { if (accessToken) { const payload = accessToken.split('.')[1] || ''; const decodedPayload = decode(payload); const payloadObject = JSON.parse(decodedPayload); - const { role: tokenRole, name: tokenName, sub: tokenId } = payloadObject; + const { + role: tokenRole, + name: tokenName, + sub: tokenId, + isFeePaid: tokenFeePaid, + } = payloadObject; - const userInfo = { name: tokenName, id: tokenId, role: tokenRole }; + const userInfo = { + name: tokenName, + id: tokenId, + role: tokenRole, + isFeePaid: tokenFeePaid, + }; - localStorage.setItem('token', accessToken); - localStorage.setItem('user', JSON.stringify(userInfo)); + Cookies.set('token', accessToken); + Cookies.set('user', JSON.stringify(userInfo)); } }; diff --git a/yarn.lock b/yarn.lock index b92bb26..f7b1d6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2764,6 +2764,11 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@types/js-cookie@^3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.6.tgz#a04ca19e877687bd449f5ad37d33b104b71fdf95" + integrity sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ== + "@types/jsdom@^20.0.0": version "20.0.1" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" @@ -6295,6 +6300,11 @@ js-base64@^3.7.7: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79" integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"