diff --git a/src/App.tsx b/src/App.tsx index 9d6aef5..89b7409 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,6 +9,7 @@ import FilteredLetterManage from './pages/Admin/FilteredLetter'; import FilteringManage from './pages/Admin/Filtering'; import ReportManage from './pages/Admin/Report'; import AdminRollingPaper from './pages/Admin/RollingPaper'; +import AdminRoute from './layouts/AdminRoute'; import AuthCallbackPage from './pages/Auth'; import Home from './pages/Home'; import Landing from './pages/Landing'; @@ -28,12 +29,10 @@ import RollingPaperPage from './pages/RollingPaper'; import WritePage from './pages/Write'; import ShareApprovalPage from './pages/Share'; import useThemeStore from './stores/themeStore'; -import { useServerSentEvents } from './hooks/useServerSentEvents'; const App = () => { const theme = useThemeStore((state) => state.theme); useViewport(); - useServerSentEvents(); const initializeTheme = () => { if (theme === 'dark') { @@ -81,7 +80,7 @@ const App = () => { - }> + }> }> } /> } /> diff --git a/src/assets/images/background-dark.png b/src/assets/images/background-dark.png deleted file mode 100644 index ed737c5..0000000 Binary files a/src/assets/images/background-dark.png and /dev/null differ diff --git a/src/assets/images/background-dark.webp b/src/assets/images/background-dark.webp new file mode 100644 index 0000000..cc90ceb Binary files /dev/null and b/src/assets/images/background-dark.webp differ diff --git a/src/assets/images/field-4-dark.png b/src/assets/images/field-4-dark.png deleted file mode 100644 index 0f6ab5b..0000000 Binary files a/src/assets/images/field-4-dark.png and /dev/null differ diff --git a/src/assets/images/field-4-dark.webp b/src/assets/images/field-4-dark.webp new file mode 100644 index 0000000..f5b7f3c Binary files /dev/null and b/src/assets/images/field-4-dark.webp differ diff --git a/src/assets/images/field-theme-asset-bird-dark.png b/src/assets/images/field-theme-asset-bird-dark.png deleted file mode 100644 index ffaac1c..0000000 Binary files a/src/assets/images/field-theme-asset-bird-dark.png and /dev/null differ diff --git a/src/assets/images/field-theme-asset-bird-dark.webp b/src/assets/images/field-theme-asset-bird-dark.webp new file mode 100644 index 0000000..960aabe Binary files /dev/null and b/src/assets/images/field-theme-asset-bird-dark.webp differ diff --git a/src/assets/images/go-to-random-letter.png b/src/assets/images/go-to-random-letter.png deleted file mode 100644 index 9e0d54f..0000000 Binary files a/src/assets/images/go-to-random-letter.png and /dev/null differ diff --git a/src/assets/images/go-to-random-letter.webp b/src/assets/images/go-to-random-letter.webp new file mode 100644 index 0000000..4929d39 Binary files /dev/null and b/src/assets/images/go-to-random-letter.webp differ diff --git a/src/assets/images/home-left-mountain-dark.png b/src/assets/images/home-left-mountain-dark.png deleted file mode 100644 index f49e739..0000000 Binary files a/src/assets/images/home-left-mountain-dark.png and /dev/null differ diff --git a/src/assets/images/home-left-mountain-dark.webp b/src/assets/images/home-left-mountain-dark.webp new file mode 100644 index 0000000..e764d28 Binary files /dev/null and b/src/assets/images/home-left-mountain-dark.webp differ diff --git a/src/assets/images/home-left-mountain.png b/src/assets/images/home-left-mountain.png deleted file mode 100644 index 30e99bf..0000000 Binary files a/src/assets/images/home-left-mountain.png and /dev/null differ diff --git a/src/assets/images/home-left-mountain.webp b/src/assets/images/home-left-mountain.webp new file mode 100644 index 0000000..0b37c1e Binary files /dev/null and b/src/assets/images/home-left-mountain.webp differ diff --git a/src/assets/images/home-right-mountain-bottom-dark.png b/src/assets/images/home-right-mountain-bottom-dark.png deleted file mode 100644 index efde328..0000000 Binary files a/src/assets/images/home-right-mountain-bottom-dark.png and /dev/null differ diff --git a/src/assets/images/home-right-mountain-bottom-dark.webp b/src/assets/images/home-right-mountain-bottom-dark.webp new file mode 100644 index 0000000..f13d464 Binary files /dev/null and b/src/assets/images/home-right-mountain-bottom-dark.webp differ diff --git a/src/assets/images/home-right-mountain-bottom.png b/src/assets/images/home-right-mountain-bottom.png deleted file mode 100644 index 05ab386..0000000 Binary files a/src/assets/images/home-right-mountain-bottom.png and /dev/null differ diff --git a/src/assets/images/home-right-mountain-bottom.webp b/src/assets/images/home-right-mountain-bottom.webp new file mode 100644 index 0000000..158deb1 Binary files /dev/null and b/src/assets/images/home-right-mountain-bottom.webp differ diff --git a/src/assets/images/home-right-mountain-top-dark.png b/src/assets/images/home-right-mountain-top-dark.png deleted file mode 100644 index ca10f5f..0000000 Binary files a/src/assets/images/home-right-mountain-top-dark.png and /dev/null differ diff --git a/src/assets/images/home-right-mountain-top-dark.webp b/src/assets/images/home-right-mountain-top-dark.webp new file mode 100644 index 0000000..74ee9f0 Binary files /dev/null and b/src/assets/images/home-right-mountain-top-dark.webp differ diff --git a/src/assets/images/home-right-mountain-top.png b/src/assets/images/home-right-mountain-top.png deleted file mode 100644 index d66d971..0000000 Binary files a/src/assets/images/home-right-mountain-top.png and /dev/null differ diff --git a/src/assets/images/home-right-mountain-top.webp b/src/assets/images/home-right-mountain-top.webp new file mode 100644 index 0000000..3b5262a Binary files /dev/null and b/src/assets/images/home-right-mountain-top.webp differ diff --git a/src/assets/images/landing-dark.png b/src/assets/images/landing-dark.png deleted file mode 100644 index 14ebe02..0000000 Binary files a/src/assets/images/landing-dark.png and /dev/null differ diff --git a/src/assets/images/landing-dark.webp b/src/assets/images/landing-dark.webp new file mode 100644 index 0000000..f22cbe5 Binary files /dev/null and b/src/assets/images/landing-dark.webp differ diff --git a/src/assets/images/landing.png b/src/assets/images/landing.png deleted file mode 100644 index 950916b..0000000 Binary files a/src/assets/images/landing.png and /dev/null differ diff --git a/src/assets/images/landing.webp b/src/assets/images/landing.webp new file mode 100644 index 0000000..dbf563a Binary files /dev/null and b/src/assets/images/landing.webp differ diff --git a/src/components/BackgroundBottom.tsx b/src/components/BackgroundBottom.tsx index 6310b4b..30bced2 100644 --- a/src/components/BackgroundBottom.tsx +++ b/src/components/BackgroundBottom.tsx @@ -1,5 +1,5 @@ import BgItem from '@/assets/images/field-4.png'; -import BgItemDark from '@/assets/images/field-4-dark.png'; +import BgItemDark from '@/assets/images/field-4-dark.webp'; import BackgroundImageWrapper from './BackgroundImageWrapper'; import useThemeStore from '@/stores/themeStore'; diff --git a/src/layouts/AdminRoute.tsx b/src/layouts/AdminRoute.tsx new file mode 100644 index 0000000..b4dfb66 --- /dev/null +++ b/src/layouts/AdminRoute.tsx @@ -0,0 +1,29 @@ +import { useEffect } from 'react'; +import { useNavigate, Outlet } from 'react-router'; + +import useAuthStore from '@/stores/authStore'; + +export default function AdminRoute() { + const isLoggedIn = useAuthStore((state) => state.isLoggedIn); + const isAdmin = useAuthStore((state) => state.isAdmin); + const navigate = useNavigate(); + + useEffect(() => { + if (!isLoggedIn) { + navigate('/login', { replace: true }); + } + if (!isAdmin) { + navigate('/', { replace: true }); + } + }, [isLoggedIn, navigate]); + + if (!isLoggedIn || !isAdmin) { + return null; + } + + return ( + <> + + + ); +} diff --git a/src/layouts/Header.tsx b/src/layouts/Header.tsx index c199a1f..a251770 100644 --- a/src/layouts/Header.tsx +++ b/src/layouts/Header.tsx @@ -19,9 +19,9 @@ const Header = () => {
{theme === 'light' ? ( - - ) : ( + ) : ( + )} diff --git a/src/layouts/PrivateRoute.tsx b/src/layouts/PrivateRoute.tsx index a607485..a433bc4 100644 --- a/src/layouts/PrivateRoute.tsx +++ b/src/layouts/PrivateRoute.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { useNavigate, Outlet } from 'react-router'; import useAuthStore from '@/stores/authStore'; @@ -6,20 +6,18 @@ import { useServerSentEvents } from '@/hooks/useServerSentEvents'; import Toast from '@/components/Toast'; export default function PrivateRoute() { - useServerSentEvents(); const isLoggedIn = useAuthStore((state) => state.isLoggedIn); const navigate = useNavigate(); - const [shouldRender, setShouldRender] = useState(false); + + useServerSentEvents(); useEffect(() => { if (!isLoggedIn) { navigate('/login', { replace: true }); - } else { - setShouldRender(true); } }, [isLoggedIn, navigate]); - if (!shouldRender) { + if (!isLoggedIn) { return null; } diff --git a/src/pages/Auth/index.tsx b/src/pages/Auth/index.tsx index cb562af..6097e53 100644 --- a/src/pages/Auth/index.tsx +++ b/src/pages/Auth/index.tsx @@ -13,7 +13,10 @@ const AuthCallbackPage = () => { const logout = useAuthStore((state) => state.logout); const setAccessToken = useAuthStore((state) => state.setAccessToken); const setZipCode = useAuthStore((state) => state.setZipCode); + const setIsAdmin = useAuthStore((state) => state.setIsAdmin); const navigate = useNavigate(); + let accessToken = ''; + let role = ''; const handleError = (error: unknown) => { console.error('AuthCallback Error:', error); @@ -32,8 +35,7 @@ const AuthCallbackPage = () => { login(); if (userInfo.accessToken) setAccessToken(userInfo.accessToken); - - console.log(redirectURL); + accessToken = userInfo.accessToken; switch (redirectURL) { case 'home': @@ -54,6 +56,7 @@ const AuthCallbackPage = () => { if (!newAccessToken) throw new Error('Missing new access token'); setAccessToken(newAccessToken); + accessToken = newAccessToken; } break; @@ -61,7 +64,15 @@ const AuthCallbackPage = () => { navigate('/notFound'); return; } - navigate(redirectURL === 'onboarding' ? '/onboarding' : '/'); + + role = JSON.parse(atob(accessToken.split('.')[1])).role; + + if (role === 'ADMIN') { + setIsAdmin(); + navigate('/admin'); + } else { + navigate(redirectURL === 'onboarding' ? '/onboarding' : '/'); + } } catch (error) { handleError(error); } diff --git a/src/pages/Home/components/GoToRandomLetter.tsx b/src/pages/Home/components/GoToRandomLetter.tsx index 0ca8e06..8113806 100644 --- a/src/pages/Home/components/GoToRandomLetter.tsx +++ b/src/pages/Home/components/GoToRandomLetter.tsx @@ -1,6 +1,6 @@ import { Link } from 'react-router'; -import goToRandomLetter from '@/assets/images/go-to-random-letter.png'; +import goToRandomLetter from '@/assets/images/go-to-random-letter.webp'; const GoToRandomLetter = () => { return ( @@ -10,7 +10,7 @@ const GoToRandomLetter = () => { 고민편지 보러가기

- go to random letter + go to random letter
diff --git a/src/pages/Home/components/HomeBackgroundLeft.tsx b/src/pages/Home/components/HomeBackgroundLeft.tsx index eb53aa6..844fdc9 100644 --- a/src/pages/Home/components/HomeBackgroundLeft.tsx +++ b/src/pages/Home/components/HomeBackgroundLeft.tsx @@ -1,5 +1,5 @@ -import homeLeftMountain from '@/assets/images/home-left-mountain.png'; -import homeLeftMountainDark from '@/assets/images/home-left-mountain-dark.png'; +import homeLeftMountain from '@/assets/images/home-left-mountain.webp'; +import homeLeftMountainDark from '@/assets/images/home-left-mountain-dark.webp'; import BackgroundImageWrapper from '@/components/BackgroundImageWrapper'; import useThemeStore from '@/stores/themeStore'; diff --git a/src/pages/Home/components/HomeBackgroundRightBottom.tsx b/src/pages/Home/components/HomeBackgroundRightBottom.tsx index 4a15d96..c08f84b 100644 --- a/src/pages/Home/components/HomeBackgroundRightBottom.tsx +++ b/src/pages/Home/components/HomeBackgroundRightBottom.tsx @@ -1,5 +1,5 @@ -import homeRightMountainBottom from '@/assets/images/home-right-mountain-bottom.png'; -import homeRightMountainBottomDark from '@/assets/images/home-right-mountain-bottom-dark.png'; +import homeRightMountainBottom from '@/assets/images/home-right-mountain-bottom.webp'; +import homeRightMountainBottomDark from '@/assets/images/home-right-mountain-bottom-dark.webp'; import BackgroundImageWrapper from '@/components/BackgroundImageWrapper'; import useThemeStore from '@/stores/themeStore'; diff --git a/src/pages/Home/components/HomeBackgroundRightTop.tsx b/src/pages/Home/components/HomeBackgroundRightTop.tsx index e37560b..337d5ad 100644 --- a/src/pages/Home/components/HomeBackgroundRightTop.tsx +++ b/src/pages/Home/components/HomeBackgroundRightTop.tsx @@ -1,5 +1,5 @@ -import homeRightMountainTop from '@/assets/images/home-right-mountain-top.png'; -import homeRightMountainTopDark from '@/assets/images/home-right-mountain-top-dark.png'; +import homeRightMountainTop from '@/assets/images/home-right-mountain-top.webp'; +import homeRightMountainTopDark from '@/assets/images/home-right-mountain-top-dark.webp'; import BackgroundImageWrapper from '@/components/BackgroundImageWrapper'; import useThemeStore from '@/stores/themeStore'; diff --git a/src/pages/Home/components/HomeHeader.tsx b/src/pages/Home/components/HomeHeader.tsx index 567dfa3..e7a503a 100644 --- a/src/pages/Home/components/HomeHeader.tsx +++ b/src/pages/Home/components/HomeHeader.tsx @@ -14,9 +14,9 @@ const HomeHeader = () => {
{theme === 'light' ? ( - - ) : ( + ) : ( + )} diff --git a/src/pages/Home/components/RandomCheer.tsx b/src/pages/Home/components/RandomCheer.tsx index f257844..706930a 100644 --- a/src/pages/Home/components/RandomCheer.tsx +++ b/src/pages/Home/components/RandomCheer.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import randomCheerBird from '@/assets/images/field-theme-asset-bird.png'; -import randomCheerBirdDark from '@/assets/images/field-theme-asset-bird-dark.png'; +import randomCheerBirdDark from '@/assets/images/field-theme-asset-bird-dark.webp'; import { RANDOM_CHEER_LIST } from '../constants'; import useThemeStore from '@/stores/themeStore'; diff --git a/src/pages/Landing/index.tsx b/src/pages/Landing/index.tsx index 2b2fb90..91ceb6a 100644 --- a/src/pages/Landing/index.tsx +++ b/src/pages/Landing/index.tsx @@ -2,8 +2,8 @@ import { useState, useEffect } from 'react'; import { Navigate, useNavigate } from 'react-router'; import { twMerge } from 'tailwind-merge'; -import LandingImg from '@/assets/images/landing.png'; -import LandingImgDark from '@/assets/images/landing-dark.png'; +import LandingImg from '@/assets/images/landing.webp'; +import LandingImgDark from '@/assets/images/landing-dark.webp'; import useAuthStore from '@/stores/authStore'; import useThemeStore from '@/stores/themeStore'; diff --git a/src/pages/LetterBoard/index.tsx b/src/pages/LetterBoard/index.tsx index 4970a1f..c8f747a 100644 --- a/src/pages/LetterBoard/index.tsx +++ b/src/pages/LetterBoard/index.tsx @@ -92,10 +92,12 @@ const LetterBoardPage = () => { })} ) : ( -

게시글이 없습니다.

+

+ 게시글이 없습니다. +

) ) : ( -

+

오류가 발생했습니다. 다시 한 번 시도해주세요

)} diff --git a/src/pages/LetterBoardDetail/components/Header.tsx b/src/pages/LetterBoardDetail/components/Header.tsx index 437ef61..5b915d6 100644 --- a/src/pages/LetterBoardDetail/components/Header.tsx +++ b/src/pages/LetterBoardDetail/components/Header.tsx @@ -25,7 +25,6 @@ const Header = ({ onToggleLike, onOpenReportModal, onDeleteLetter, - isShareLetterPreview = false, }: HeaderProps) => { const navigate = useNavigate(); return ( @@ -34,27 +33,25 @@ const Header = ({ - {!isShareLetterPreview && ( -
-
- -

{likeCount}

-
- {isWriter ? ( - - ) : ( - - )} +
+
+ +

{likeCount}

- )} + {isWriter ? ( + + ) : ( + + )} +
diff --git a/src/pages/LetterBoardDetail/index.tsx b/src/pages/LetterBoardDetail/index.tsx index 9c7ed31..3aefd19 100644 --- a/src/pages/LetterBoardDetail/index.tsx +++ b/src/pages/LetterBoardDetail/index.tsx @@ -16,6 +16,7 @@ import Letter from './components/Letter'; import { useNavigate, useParams } from 'react-router'; import useAuthStore from '@/stores/authStore'; import useToastStore from '@/stores/toastStore'; +import { useQueryClient } from '@tanstack/react-query'; const LetterBoardDetailPage = () => { const [likeCount, setLikeCount] = useState(0); @@ -27,13 +28,14 @@ const LetterBoardDetailPage = () => { const { id } = useParams(); const myZipCode = useAuthStore.getState().zipCode; const setToastActive = useToastStore((state) => state.setToastActive); + const navigate = useNavigate(); + const queryClient = useQueryClient(); const postLike = async (sharePostId: string) => { try { const response = await postSharePostLike(sharePostId); if (!response) throw new Error('error while fetching like count'); - console.log('✅ 편지 좋아요 추가됨:', response); } catch (error) { console.error('❌ 편지 좋아요 추가 중 에러가 발생했습니다', error); } @@ -53,6 +55,7 @@ const LetterBoardDetailPage = () => { if (id) { const response = await deleteSharePost(id); if (!response) throw new Error('deleteSharePost: no response'); + queryClient.invalidateQueries({ queryKey: ['sharePostList'] }); navigate(-1); setToastActive({ toastType: 'Success', @@ -73,6 +76,9 @@ const LetterBoardDetailPage = () => { try { const data = await getSharePostDetail(postId); setPostDetail(data); + if (myZipCode === data.zipCode || !data.zipCode) { + setIsWriter(true); + } } catch (error) { console.error('❌ 공유 게시글 상세 조회에 실패했습니다.', error); } @@ -85,13 +91,6 @@ const LetterBoardDetailPage = () => { console.log('✅ 편지 좋아요 갯수:', response); setLikeCount(response.likeCount); setIsLike(response.liked); - console.log('myZip', myZipCode); - console.log('responseZip', response.zipCode); - console.log('responseZip', response); - - if (myZipCode === response.zipCode || !response.zipCode) { - setIsWriter(true); - } } catch (error) { console.error('❌ 편지 좋아요 갯수를 가져오는 중 에러가 발생했습니다', error); throw new Error('편지 좋아요 갯수 가져오기 실패'); diff --git a/src/pages/LetterBoxDetail/index.tsx b/src/pages/LetterBoxDetail/index.tsx index 8bed0a8..148f728 100644 --- a/src/pages/LetterBoxDetail/index.tsx +++ b/src/pages/LetterBoxDetail/index.tsx @@ -68,7 +68,11 @@ const LetterBoxDetailPage = () => { }, [inView, hasNextPage, isFetchingNextPage, fetchNextPage]); const disconnectMutation = useMutation({ - mutationFn: async () => await postMailboxDisconnect(userInfo.oppositeId), + mutationFn: async () => { + console.log('userInfo', userInfo, userInfo.id); + const response = await postMailboxDisconnect(userInfo.id); + if (!response) throw new Error(`no response`); + }, onSuccess: () => { navigate(-1); setToastActive({ diff --git a/src/pages/Login/components/Background.tsx b/src/pages/Login/components/Background.tsx index 5ec92ca..21742c2 100644 --- a/src/pages/Login/components/Background.tsx +++ b/src/pages/Login/components/Background.tsx @@ -1,7 +1,7 @@ import { Link } from 'react-router'; -import FieldImg from '@/assets/images/home-left-mountain.png'; -import FieldImgDark from '@/assets/images/home-left-mountain-dark.png'; +import FieldImg from '@/assets/images/home-left-mountain.webp'; +import FieldImgDark from '@/assets/images/home-left-mountain-dark.webp'; import BlurImg from '@/assets/images/landing-blur.png'; import EnvelopeImg from '@/assets/images/postoffice-letter.png'; diff --git a/src/stores/authStore.ts b/src/stores/authStore.ts index ff69b02..7557b97 100644 --- a/src/stores/authStore.ts +++ b/src/stores/authStore.ts @@ -6,10 +6,12 @@ interface AuthStore { isLoggedIn: boolean; zipCode: string; accessToken: string; + isAdmin: boolean; login: () => void; logout: () => Promise; setZipCode: (zipCode: string) => void; setAccessToken: (accessToken: string) => void; + setIsAdmin: () => void; } const useAuthStore = create( @@ -18,6 +20,7 @@ const useAuthStore = create( isLoggedIn: false, accessToken: '', zipCode: '', + isAdmin: false, login: () => set({ isLoggedIn: true }), logout: async () => { const theme = useThemeStore.getState().theme; @@ -26,7 +29,7 @@ const useAuthStore = create( if (theme === 'dark') { toggleTheme(); } - set({ isLoggedIn: false, zipCode: '', accessToken: '' }); + set({ isLoggedIn: false, zipCode: '', accessToken: '', isAdmin: false }); // location.reload(); // try { // await postLogout(); @@ -36,6 +39,7 @@ const useAuthStore = create( }, setZipCode: (zipCode) => set({ zipCode: zipCode }), setAccessToken: (accessToken) => set({ accessToken: accessToken }), + setIsAdmin: () => set({ isAdmin: true }), }), { name: 'userInfo', diff --git a/src/styles/preflight.css b/src/styles/preflight.css index dbc3ff5..013981a 100644 --- a/src/styles/preflight.css +++ b/src/styles/preflight.css @@ -84,7 +84,7 @@ left: 0; width: 100%; height: 50%; - background: url('/src/assets/images/background-dark.png') repeat center top; + background: url('/src/assets/images/background-dark.webp') repeat center top; background-size: cover; opacity: 1; z-index: 1;