From 70d5e7c8c439bc4b21aaf4b8bafd347598ed0083 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Sun, 1 Jun 2025 22:41:59 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=9C=A0=EC=A0=80=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EC=99=80=20=EA=B3=B5=ED=86=B5=EC=9C=BC=EB=A1=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/searchBar/SearchBar.styled.ts | 40 ++++++++++ src/components/admin/searchBar/SearchBar.tsx | 75 +++++++++++++++++++ .../admin/sidebar/AdminSidebar.styled.ts | 1 + .../common/admin/title/AdminTitle.styled.ts | 4 +- .../noticeDetail/NoticeDetailBundle.styled.ts | 4 +- .../noticeDetail/NoticeDetailBundle.tsx | 15 +++- .../noticeDetail/bottom/button/ListButton.tsx | 11 ++- src/constants/routes.ts | 1 + src/hooks/user/useMyInfo.ts | 1 + .../admin/adminInquiries/AdminInquiries.tsx | 2 +- .../admin/adminNotice/AdminNotice.styled.ts | 10 ++- src/pages/admin/adminNotice/AdminNotice.tsx | 8 +- .../adminNoticeList/AdminNoticeList.styled.ts | 9 +++ .../adminNoticeList/AdminNoticeList.tsx | 66 ++++++++++++++++ .../AdminNoticeDetail.styled.ts | 0 .../adminNoticeDetail/AdminNoticeDetail.tsx | 5 ++ .../user/customerService/notice/Notice.tsx | 44 ++++------- .../notice/noticeItem/NoticeItem.styled.ts | 10 +++ .../notice/noticeItem/NoticeItem.tsx | 46 ++++++++++++ .../noticeDetail/NoticeDetail.tsx | 2 +- src/routes/AdminRoutes.tsx | 18 ++++- src/routes/AppRoutes.tsx | 1 + src/routes/MergeRoutes.tsx | 7 +- src/routes/ProtectAdminRoute.tsx | 31 +++++++- 24 files changed, 356 insertions(+), 55 deletions(-) create mode 100644 src/components/admin/searchBar/SearchBar.styled.ts create mode 100644 src/components/admin/searchBar/SearchBar.tsx create mode 100644 src/pages/admin/adminNotice/adminNoticeList/AdminNoticeList.styled.ts create mode 100644 src/pages/admin/adminNotice/adminNoticeList/AdminNoticeList.tsx create mode 100644 src/pages/admin/adminNoticeDetail/AdminNoticeDetail.styled.ts create mode 100644 src/pages/admin/adminNoticeDetail/AdminNoticeDetail.tsx create mode 100644 src/pages/user/customerService/notice/noticeItem/NoticeItem.styled.ts create mode 100644 src/pages/user/customerService/notice/noticeItem/NoticeItem.tsx diff --git a/src/components/admin/searchBar/SearchBar.styled.ts b/src/components/admin/searchBar/SearchBar.styled.ts new file mode 100644 index 00000000..527ec672 --- /dev/null +++ b/src/components/admin/searchBar/SearchBar.styled.ts @@ -0,0 +1,40 @@ +import styled from 'styled-components'; + +export const AdminSearchBarContainer = styled.form` + width: 100%; + display: flex; + justify-content: center; + margin-bottom: 2rem; +`; + +export const AdminSearchBarWrapper = styled.div` + display: flex; + width: 70%; + justify-content: space-between; + padding: 0.5rem 0.5rem 0.5rem 1rem; + border: 1px solid ${({ theme }) => theme.color.deepGrey}; + border-radius: ${({ theme }) => theme.borderRadius.large} 0 0 + ${({ theme }) => theme.borderRadius.large}; +`; + +export const AdminSearchBarInput = styled.input` + width: 100%; + font-size: 1.3rem; +`; + +export const AdminSearchBarBackIcon = styled.button` + svg { + width: 1.5rem; + } +`; + +export const AdminSearchBarButton = styled.button` + width: 10%; + border: 1px solid ${({ theme }) => theme.color.navy}; + background: ${({ theme }) => theme.color.navy}; + border-radius: 0 ${({ theme }) => theme.borderRadius.large} + ${({ theme }) => theme.borderRadius.large} 0; + font-size: 1.3rem; + color: ${({ theme }) => theme.color.white}; + padding: 0.5rem 1rem 0.5rem 0.5rem; +`; diff --git a/src/components/admin/searchBar/SearchBar.tsx b/src/components/admin/searchBar/SearchBar.tsx new file mode 100644 index 00000000..8fdf0804 --- /dev/null +++ b/src/components/admin/searchBar/SearchBar.tsx @@ -0,0 +1,75 @@ +import { XMarkIcon } from '@heroicons/react/24/outline'; +import { MODAL_MESSAGE_CUSTOMER_SERVICE } from '../../../constants/user/customerService'; +import * as S from './SearchBar.styled'; +import { useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { useModal } from '../../../hooks/useModal'; +import Modal from '../../common/modal/Modal'; + +interface SearchBarProps { + onGetKeyword: (value: string) => void; + value: string; +} + +export default function SearchBar({ onGetKeyword, value }: SearchBarProps) { + const [keyword, setKeyword] = useState(''); + const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); + const [searchParams, setSearchParams] = useSearchParams(); + + const handleKeyword = (inputValue: string) => { + const newSearchParams = new URLSearchParams(searchParams); + if (inputValue === '') { + newSearchParams.delete('keyword'); + } else { + newSearchParams.set('keyword', inputValue); + } + setSearchParams(newSearchParams); + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (keyword.trim() === '') { + return handleModalOpen(MODAL_MESSAGE_CUSTOMER_SERVICE.noKeyword); + } else { + onGetKeyword(keyword); + handleKeyword(keyword); + return; + } + }; + + const handleChangeKeyword = (e: React.ChangeEvent) => { + const value = e.target.value; + setKeyword(value); + }; + + const handleClickSearchDefault = () => { + setKeyword(''); + onGetKeyword(''); + handleKeyword(''); + }; + + return ( + + + + {value && ( + + + + )} + + 검색 + + {message} + + + ); +} diff --git a/src/components/common/admin/sidebar/AdminSidebar.styled.ts b/src/components/common/admin/sidebar/AdminSidebar.styled.ts index bdbea3d5..726be486 100644 --- a/src/components/common/admin/sidebar/AdminSidebar.styled.ts +++ b/src/components/common/admin/sidebar/AdminSidebar.styled.ts @@ -1,6 +1,7 @@ import styled from 'styled-components'; export const LayoutContainer = styled.div` + max-width: 1440px; height: 100vh; display: flex; `; diff --git a/src/components/common/admin/title/AdminTitle.styled.ts b/src/components/common/admin/title/AdminTitle.styled.ts index 6a12e940..fcaa1c05 100644 --- a/src/components/common/admin/title/AdminTitle.styled.ts +++ b/src/components/common/admin/title/AdminTitle.styled.ts @@ -1,6 +1,8 @@ import styled from 'styled-components'; -export const TitleContainer = styled.header``; +export const TitleContainer = styled.header` + margin-bottom: 1rem; +`; export const TitleWrapper = styled.div` margin-bottom: 2rem; diff --git a/src/components/user/customerService/noticeDetail/NoticeDetailBundle.styled.ts b/src/components/user/customerService/noticeDetail/NoticeDetailBundle.styled.ts index c9b9e6aa..c02c8f6c 100644 --- a/src/components/user/customerService/noticeDetail/NoticeDetailBundle.styled.ts +++ b/src/components/user/customerService/noticeDetail/NoticeDetailBundle.styled.ts @@ -3,8 +3,8 @@ import { SpinnerWrapperStyled } from '../../mypage/Spinner.styled'; export const SpinnerWrapper = styled(SpinnerWrapperStyled)``; -export const Container = styled.section` - width: 75%; +export const Container = styled.section<{ $width: string }>` + width: ${({ $width }) => $width}; margin: 0 auto; margin-bottom: 2rem; `; diff --git a/src/components/user/customerService/noticeDetail/NoticeDetailBundle.tsx b/src/components/user/customerService/noticeDetail/NoticeDetailBundle.tsx index f9f6dd0a..f8f8abc6 100644 --- a/src/components/user/customerService/noticeDetail/NoticeDetailBundle.tsx +++ b/src/components/user/customerService/noticeDetail/NoticeDetailBundle.tsx @@ -7,11 +7,18 @@ import NoticeDetailHeader from './header/NoticeDetailHeader'; import Spinner from '../../mypage/Spinner'; import ListButton from './bottom/button/ListButton'; -export default function NoticeDetailBundle() { +interface NoticeDetailBundleProps { + $width: string; +} + +export default function NoticeDetailBundle({ + $width, +}: NoticeDetailBundleProps) { const location = useLocation(); const { noticeId } = useParams(); const id = noticeId || String(location.state.id); const keyword = location.state?.keyword ?? ''; + const includesAdmin = location.pathname.includes('admin') ?? false; const { noticeDetail: noticeDetailData, isLoading } = useGetNoticeDetail(id); @@ -25,7 +32,7 @@ export default function NoticeDetailBundle() { if (!noticeDetailData) { return ( - + - + + {!includesAdmin && } 목록 diff --git a/src/constants/routes.ts b/src/constants/routes.ts index fe64fdfd..7f086ee0 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -41,4 +41,5 @@ export const ADMIN_ROUTE = { reports: 'reports', inquiries: 'inquiries', manage: 'manage', + detail: 'detail', }; diff --git a/src/hooks/user/useMyInfo.ts b/src/hooks/user/useMyInfo.ts index c0ebecbc..1096a30e 100644 --- a/src/hooks/user/useMyInfo.ts +++ b/src/hooks/user/useMyInfo.ts @@ -26,6 +26,7 @@ export const useMyProfileInfo = () => { queryFn: () => getMyInfo(), staleTime: Infinity, gcTime: Infinity, + refetchInterval: 1000 * 60 * 60, enabled: isLoggedIn, }); diff --git a/src/pages/admin/adminInquiries/AdminInquiries.tsx b/src/pages/admin/adminInquiries/AdminInquiries.tsx index 2d180680..09a40280 100644 --- a/src/pages/admin/adminInquiries/AdminInquiries.tsx +++ b/src/pages/admin/adminInquiries/AdminInquiries.tsx @@ -1,4 +1,4 @@ -import * as S from './AdminInquires.styled'; +import * as S from './AdminInquiries.styled'; export default function AdminInquires() { return ; diff --git a/src/pages/admin/adminNotice/AdminNotice.styled.ts b/src/pages/admin/adminNotice/AdminNotice.styled.ts index c3389834..1bbefc66 100644 --- a/src/pages/admin/adminNotice/AdminNotice.styled.ts +++ b/src/pages/admin/adminNotice/AdminNotice.styled.ts @@ -1,3 +1,11 @@ import styled from 'styled-components'; +import { SpinnerWrapperStyled } from '../../../components/user/mypage/Spinner.styled'; -export const Container = styled.div``; +export const SpinnerWrapper = styled(SpinnerWrapperStyled)``; + +export const AdminNoticeContainer = styled.div``; + +export const NoticeItemWrapper = styled.section` + display: flex; + justify-content: center; +`; diff --git a/src/pages/admin/adminNotice/AdminNotice.tsx b/src/pages/admin/adminNotice/AdminNotice.tsx index 9a760a49..a3639e8c 100644 --- a/src/pages/admin/adminNotice/AdminNotice.tsx +++ b/src/pages/admin/adminNotice/AdminNotice.tsx @@ -1,12 +1,12 @@ +import { Outlet } from 'react-router-dom'; import AdminTitle from '../../../components/common/admin/title/AdminTitle'; import * as S from './AdminNotice.styled'; export default function AdminNotice() { - console.log('공지사항 렌더'); - return ( - + - + + ); } diff --git a/src/pages/admin/adminNotice/adminNoticeList/AdminNoticeList.styled.ts b/src/pages/admin/adminNotice/adminNoticeList/AdminNoticeList.styled.ts new file mode 100644 index 00000000..c0db96bb --- /dev/null +++ b/src/pages/admin/adminNotice/adminNoticeList/AdminNoticeList.styled.ts @@ -0,0 +1,9 @@ +import styled from 'styled-components'; +import { SpinnerWrapperStyled } from '../../../../components/user/mypage/Spinner.styled'; + +export const SpinnerWrapper = styled(SpinnerWrapperStyled)``; + +export const NoticeItemWrapper = styled.section` + display: flex; + justify-content: center; +`; diff --git a/src/pages/admin/adminNotice/adminNoticeList/AdminNoticeList.tsx b/src/pages/admin/adminNotice/adminNoticeList/AdminNoticeList.tsx new file mode 100644 index 00000000..62378d87 --- /dev/null +++ b/src/pages/admin/adminNotice/adminNoticeList/AdminNoticeList.tsx @@ -0,0 +1,66 @@ +import { useEffect, useState } from 'react'; +import SearchBar from '../../../../components/admin/searchBar/SearchBar'; +import NoticeItem from '../../../user/customerService/notice/noticeItem/NoticeItem'; +import * as S from './AdminNoticeList.styled'; +import type { NoticeSearch } from '../../../../models/customerService'; +import { useGetNotice } from '../../../../hooks/user/useGetNotice'; +import { useSearchParams } from 'react-router-dom'; +import { Spinner } from '../../../../components/common/loadingSpinner/LoadingSpinner.styled'; +import Pagination from '../../../../components/common/pagination/Pagination'; + +export default function AdminNoticeList() { + const [noticeSearch, setNoticeSearch] = useState({ + keyword: '', + page: 1, + }); + const [value, setValue] = useState(''); + const { noticeData, isLoading } = useGetNotice(noticeSearch); + const [searchParams] = useSearchParams(); + + useEffect(() => { + const searchKeyword = searchParams.get('keyword'); + + if (searchKeyword) { + setNoticeSearch((prev) => ({ ...prev, keyword: searchKeyword })); + setValue((prev) => (searchKeyword ? searchKeyword : prev)); + } + }, [searchParams]); + + const handleGetKeyword = (keyword: string) => { + setNoticeSearch((prev) => ({ ...prev, keyword })); + setValue(keyword); + }; + const handleChangePagination = (page: number) => { + setNoticeSearch((prev) => ({ ...prev, page })); + }; + + if (isLoading) { + return ( + + + + ); + } + + if (!noticeData) return; + + const lastPage = Number(noticeData.totalPages); + + return ( + <> + + + + + + + ); +} diff --git a/src/pages/admin/adminNoticeDetail/AdminNoticeDetail.styled.ts b/src/pages/admin/adminNoticeDetail/AdminNoticeDetail.styled.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/pages/admin/adminNoticeDetail/AdminNoticeDetail.tsx b/src/pages/admin/adminNoticeDetail/AdminNoticeDetail.tsx new file mode 100644 index 00000000..f61dc2b2 --- /dev/null +++ b/src/pages/admin/adminNoticeDetail/AdminNoticeDetail.tsx @@ -0,0 +1,5 @@ +import NoticeDetailBundle from '../../../components/user/customerService/noticeDetail/NoticeDetailBundle'; + +export default function AdminNoticeDetail() { + return ; +} diff --git a/src/pages/user/customerService/notice/Notice.tsx b/src/pages/user/customerService/notice/Notice.tsx index d52184aa..e78bf6cc 100644 --- a/src/pages/user/customerService/notice/Notice.tsx +++ b/src/pages/user/customerService/notice/Notice.tsx @@ -4,12 +4,9 @@ import type { NoticeSearch } from '../../../../models/customerService'; import { useGetNotice } from '../../../../hooks/user/useGetNotice'; import { Spinner } from '../../../../components/common/loadingSpinner/LoadingSpinner.styled'; import CustomerServiceHeader from '../../../../components/user/customerService/CustomerServiceHeader'; -import ContentBorder from '../../../../components/common/contentBorder/ContentBorder'; -import { ROUTES } from '../../../../constants/routes'; -import NoticeList from '../../../../components/user/customerService/notice/NoticeList'; -import NoResult from '../../../../components/common/noResult/NoResult'; import Pagination from '../../../../components/common/pagination/Pagination'; -import { useLocation } from 'react-router-dom'; +import { useSearchParams } from 'react-router-dom'; +import NoticeItem from './noticeItem/NoticeItem'; export default function Notice() { const [noticeSearch, setNoticeSearch] = useState({ @@ -18,17 +15,16 @@ export default function Notice() { }); const [value, setValue] = useState(''); const { noticeData, isLoading } = useGetNotice(noticeSearch); - const location = useLocation(); - const hasKeyword = location.search - ? decodeURI(location.search.split('=')[1]) - : ''; + const [searchParams] = useSearchParams(); useEffect(() => { - if (hasKeyword) { - setNoticeSearch((prev) => ({ ...prev, keyword: hasKeyword })); - setValue(hasKeyword); + const searchKeyword = searchParams.get('keyword'); + + if (searchKeyword) { + setNoticeSearch((prev) => ({ ...prev, keyword: searchKeyword })); + setValue((prev) => (searchKeyword ? searchKeyword : prev)); } - }, [hasKeyword]); + }, [searchParams]); const handleGetKeyword = (keyword: string) => { setNoticeSearch((prev) => ({ ...prev, keyword })); @@ -58,23 +54,11 @@ export default function Notice() { onGetKeyword={handleGetKeyword} /> - - {noticeData.notices.length > 0 && } - {noticeData.notices.length > 0 ? ( - noticeData.notices.map((list) => ( - - - - - )) - ) : ( - - )} - + ` + width: ${({ $width }) => $width}; + display: flex; + flex-direction: column; +`; + +export const NoticeDetailLink = styled(Link)``; diff --git a/src/pages/user/customerService/notice/noticeItem/NoticeItem.tsx b/src/pages/user/customerService/notice/noticeItem/NoticeItem.tsx new file mode 100644 index 00000000..1806031a --- /dev/null +++ b/src/pages/user/customerService/notice/noticeItem/NoticeItem.tsx @@ -0,0 +1,46 @@ +import * as S from './NoticeItem.styled'; +import ContentBorder from '../../../../../components/common/contentBorder/ContentBorder'; +import { ADMIN_ROUTE, ROUTES } from '../../../../../constants/routes'; +import NoticeList from '../../../../../components/user/customerService/notice/NoticeList'; +import NoResult from '../../../../../components/common/noResult/NoResult'; +import type { NoticeList as TNoticeList } from '../../../../../models/customerService'; +import { useLocation } from 'react-router-dom'; + +interface NoticeItemProps { + noticeData: TNoticeList[]; + value: string; + $width: string; +} + +export default function NoticeItem({ + noticeData, + value, + $width = '75%', +}: NoticeItemProps) { + const location = useLocation(); + const includesAdmin = location.pathname.includes('admin'); + + return ( + + {noticeData.length > 0 && } + {noticeData.length > 0 ? ( + noticeData.map((list) => ( + + + + + )) + ) : ( + + )} + + ); +} diff --git a/src/pages/user/customerService/noticeDetail/NoticeDetail.tsx b/src/pages/user/customerService/noticeDetail/NoticeDetail.tsx index 8cea59fe..7acc5121 100644 --- a/src/pages/user/customerService/noticeDetail/NoticeDetail.tsx +++ b/src/pages/user/customerService/noticeDetail/NoticeDetail.tsx @@ -1,5 +1,5 @@ import NoticeDetailBundle from '../../../../components/user/customerService/noticeDetail/NoticeDetailBundle'; export default function NoticeDetail() { - return ; + return ; } diff --git a/src/routes/AdminRoutes.tsx b/src/routes/AdminRoutes.tsx index 29431060..45eec80f 100644 --- a/src/routes/AdminRoutes.tsx +++ b/src/routes/AdminRoutes.tsx @@ -8,6 +8,12 @@ const Sidebar = lazy( ); const Main = lazy(() => import('../pages/admin/adminMain/AdminMain')); const Notice = lazy(() => import('../pages/admin/adminNotice/AdminNotice')); +const NoticeList = lazy( + () => import('../pages/admin/adminNotice/adminNoticeList/AdminNoticeList') +); +const NoticeDetail = lazy( + () => import('../pages/admin/adminNoticeDetail/AdminNoticeDetail') +); const Banner = lazy(() => import('../pages/admin/adminBanner/AdminBanner')); const Tags = lazy(() => import('../pages/admin/adminTags/AdminTags')); const AllUser = lazy(() => import('../pages/admin/adminAllUser/AdminAllUser')); @@ -27,10 +33,20 @@ export const AdminRoutes = () => { ), children: [ - { index: true, element:
}, + { + index: true, + element:
, + }, { path: ADMIN_ROUTE.notice, element: , + children: [ + { index: true, element: }, + { + path: `${ADMIN_ROUTE.detail}/:noticeId`, + element: , + }, + ], }, { path: ADMIN_ROUTE.banner, diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index 0a26c8d3..cf3438a3 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -12,6 +12,7 @@ import ProtectRoute from '../components/common/ProtectRoute'; import NotFoundPage from '../pages/notFoundPage/NotFoundPage'; import QueryErrorBoundary from '../components/common/error/QueryErrorBoundary'; import { ADMIN_ROUTE, ROUTES } from '../constants/routes'; +import ProtectLoggedIn from './ProtectLoggedIn'; const Login = lazy(() => import('../pages/login/Login')); const LoginSuccess = lazy(() => import('../pages/login/LoginSuccess')); diff --git a/src/routes/MergeRoutes.tsx b/src/routes/MergeRoutes.tsx index dd084465..751136df 100644 --- a/src/routes/MergeRoutes.tsx +++ b/src/routes/MergeRoutes.tsx @@ -3,7 +3,6 @@ import AdminRoutes from './AdminRoutes'; import AppRoutes from './AppRoutes'; import NotFoundPage from '../pages/notFoundPage/NotFoundPage'; import { ToastProvider } from '../components/common/Toast/ToastProvider'; -import ProtectAdminRoute from './ProtectAdminRoute'; export default function MergeRoutes() { const router = createBrowserRouter([ @@ -16,11 +15,7 @@ export default function MergeRoutes() { children: [...AppRoutes()], }, { - element: ( - - - - ), + element: , children: [...AdminRoutes()], }, { path: '*', element: }, diff --git a/src/routes/ProtectAdminRoute.tsx b/src/routes/ProtectAdminRoute.tsx index cfe01688..6c4085e0 100644 --- a/src/routes/ProtectAdminRoute.tsx +++ b/src/routes/ProtectAdminRoute.tsx @@ -23,11 +23,31 @@ export default function ProtectAdminRoute({ const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); useEffect(() => { + let timer: NodeJS.Timeout; + + const handleStorageChange = () => { + const authStorage = localStorage.getItem('auth-storage'); + if (!authStorage) { + handleModalOpen(MODAL_MESSAGE.needAuth); + timer = setTimeout(() => { + logout(); + navigate(ROUTES.main); + }, 1000); + } + }; + + if (!isLoggedIn) { + handleModalOpen(MODAL_MESSAGE.needAuth); + timer = setTimeout(() => { + navigate(ROUTES.main); + }, 1000); + return; + } if (isLoggedIn && !isAdmin) { handleModalOpen(MODAL_MESSAGE.needAuth); - setTimeout(() => { + timer = setTimeout(() => { navigate(ROUTES.main); - }, 200); + }, 1000); return; } if (isLoggedIn && isAdmin && !redirectAdminBool) { @@ -35,6 +55,13 @@ export default function ProtectAdminRoute({ replace(); return; } + + window.addEventListener('storage', handleStorageChange); + + return () => { + window.removeEventListener('storage', handleStorageChange); + if (timer) clearTimeout(timer); + }; }, [ redirectAdminBool, isLoggedIn, From 847b43cb357bdb85cca22b3b5c1c7c8af85e5120 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Mon, 2 Jun 2025 16:21:53 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EC=9E=91=EC=84=B1=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EC=B6=94=EA=B0=80=20=EC=A4=91=20searchbar=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B3=B5=EC=9C=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/customerService/Notice.api.ts | 37 +++++++ src/api/alarm.api.ts | 22 ++-- .../admin/searchBar/SearchBar.styled.ts | 27 ++++- src/components/admin/searchBar/SearchBar.tsx | 37 ++++--- src/constants/admin/adminModal.ts | 7 ++ src/constants/routes.ts | 1 + src/hooks/admin/useAdminNotice.ts | 95 ++++++++++++++++ src/models/customerService.ts | 6 + .../AdminNoticeWrite.styled.ts | 36 ++++++ .../adminNoticeWrite/AdminNoticeWrite.tsx | 103 ++++++++++++++++++ src/routes/AdminRoutes.tsx | 7 ++ src/routes/AppRoutes.tsx | 17 +-- src/routes/MergeRoutes.tsx | 11 +- 13 files changed, 361 insertions(+), 45 deletions(-) create mode 100644 src/api/admin/customerService/Notice.api.ts create mode 100644 src/constants/admin/adminModal.ts create mode 100644 src/hooks/admin/useAdminNotice.ts create mode 100644 src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.styled.ts create mode 100644 src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx diff --git a/src/api/admin/customerService/Notice.api.ts b/src/api/admin/customerService/Notice.api.ts new file mode 100644 index 00000000..42887605 --- /dev/null +++ b/src/api/admin/customerService/Notice.api.ts @@ -0,0 +1,37 @@ +import type { ApiCommonBasicType } from '../../../models/apiCommon'; +import { httpClient } from '../../http.api'; + +export const postNotice = async (formData: FormData) => { + try { + await httpClient.post(`/notice`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + } catch (e) { + console.error(e); + throw e; + } +}; + +export const putNotice = async (id: number, formData: FormData) => { + try { + await httpClient.put(`/notice/${id}`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + } catch (e) { + console.error(e); + throw e; + } +}; + +export const deleteNotice = async (id: number) => { + try { + await httpClient.delete(`/notice/${id}`); + } catch (e) { + console.error(e); + throw e; + } +}; diff --git a/src/api/alarm.api.ts b/src/api/alarm.api.ts index 3ec22180..5917e4bb 100644 --- a/src/api/alarm.api.ts +++ b/src/api/alarm.api.ts @@ -1,4 +1,5 @@ import type { ApiAlarmList } from '../models/alarm'; +import useAuthStore from '../store/authStore'; import { httpClient } from './http.api'; export const getAlarmList = async () => { @@ -36,14 +37,19 @@ export const patchAlarm = async (id: number) => { }; export const testLiveAlarm = async () => { - try { - const response = await httpClient.get( - `/user/send-alarm?alarmFilter=0` - ); + const { accessToken } = useAuthStore.getState(); + if (accessToken) { + try { + const response = await httpClient.get( + '/user/send-alarm?alarmFilter=0' + ); - return response; - } catch (e) { - console.error(e); - throw e; + return response; + } catch (e) { + console.error(e); + throw e; + } + } else { + return; } }; diff --git a/src/components/admin/searchBar/SearchBar.styled.ts b/src/components/admin/searchBar/SearchBar.styled.ts index 527ec672..08b4031e 100644 --- a/src/components/admin/searchBar/SearchBar.styled.ts +++ b/src/components/admin/searchBar/SearchBar.styled.ts @@ -1,15 +1,21 @@ +import { Link } from 'react-router-dom'; import styled from 'styled-components'; export const AdminSearchBarContainer = styled.form` width: 100%; display: flex; - justify-content: center; + justify-content: space-evenly; margin-bottom: 2rem; `; export const AdminSearchBarWrapper = styled.div` display: flex; - width: 70%; + width: 60%; +`; + +export const AdminSearchBarInputWrapper = styled.div` + display: flex; + width: 100%; justify-content: space-between; padding: 0.5rem 0.5rem 0.5rem 1rem; border: 1px solid ${({ theme }) => theme.color.deepGrey}; @@ -29,7 +35,7 @@ export const AdminSearchBarBackIcon = styled.button` `; export const AdminSearchBarButton = styled.button` - width: 10%; + width: 15%; border: 1px solid ${({ theme }) => theme.color.navy}; background: ${({ theme }) => theme.color.navy}; border-radius: 0 ${({ theme }) => theme.borderRadius.large} @@ -38,3 +44,18 @@ export const AdminSearchBarButton = styled.button` color: ${({ theme }) => theme.color.white}; padding: 0.5rem 1rem 0.5rem 0.5rem; `; + +export const WriteLink = styled(Link)` + border: 1px solid ${({ theme }) => theme.color.navy}; + background: ${({ theme }) => theme.color.navy}; + border-radius: ${({ theme }) => theme.borderRadius.large}; + font-size: 1rem; + color: ${({ theme }) => theme.color.white}; + padding: 0.5rem 1rem; + transition: all 300ms ease-in-out; + + &:hover { + background: ${({ theme }) => theme.color.white}; + color: ${({ theme }) => theme.color.navy}; + } +`; diff --git a/src/components/admin/searchBar/SearchBar.tsx b/src/components/admin/searchBar/SearchBar.tsx index 8fdf0804..cb9b3119 100644 --- a/src/components/admin/searchBar/SearchBar.tsx +++ b/src/components/admin/searchBar/SearchBar.tsx @@ -2,9 +2,10 @@ import { XMarkIcon } from '@heroicons/react/24/outline'; import { MODAL_MESSAGE_CUSTOMER_SERVICE } from '../../../constants/user/customerService'; import * as S from './SearchBar.styled'; import { useState } from 'react'; -import { useSearchParams } from 'react-router-dom'; +import { useLocation, useSearchParams } from 'react-router-dom'; import { useModal } from '../../../hooks/useModal'; import Modal from '../../common/modal/Modal'; +import { ADMIN_ROUTE } from '../../../constants/routes'; interface SearchBarProps { onGetKeyword: (value: string) => void; @@ -15,6 +16,7 @@ export default function SearchBar({ onGetKeyword, value }: SearchBarProps) { const [keyword, setKeyword] = useState(''); const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); const [searchParams, setSearchParams] = useSearchParams(); + const location = useLocation(); const handleKeyword = (inputValue: string) => { const newSearchParams = new URLSearchParams(searchParams); @@ -52,21 +54,26 @@ export default function SearchBar({ onGetKeyword, value }: SearchBarProps) { return ( - - {value && ( - - - - )} + + + {value && ( + + + + )} + + 검색 - 검색 + + 작성하기 + {message} diff --git a/src/constants/admin/adminModal.ts b/src/constants/admin/adminModal.ts new file mode 100644 index 00000000..a4f8082f --- /dev/null +++ b/src/constants/admin/adminModal.ts @@ -0,0 +1,7 @@ +export const ADMIN_MODAL_MESSAGE = { + writeSuccess: '성공적으로 작성되었습니다.', + writeFail: '작성이 실패하였습니다.', + writeDeleteSuccess: '삭제되었습니다.', + writeDeleteFail: '삭제가 실패하였습니다.', + writeError: '알수없는 에러가 발생했습니다.', +}; diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 7f086ee0..5dabdd8d 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -42,4 +42,5 @@ export const ADMIN_ROUTE = { inquiries: 'inquiries', manage: 'manage', detail: 'detail', + write: 'write', }; diff --git a/src/hooks/admin/useAdminNotice.ts b/src/hooks/admin/useAdminNotice.ts new file mode 100644 index 00000000..9ab68553 --- /dev/null +++ b/src/hooks/admin/useAdminNotice.ts @@ -0,0 +1,95 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { + deleteNotice, + postNotice, + putNotice, +} from '../../api/admin/customerService/Notice.api'; +import { AxiosError } from 'axios'; +import { CustomerService } from '../queries/user/keys'; +import { useNavigate } from 'react-router-dom'; +import { ADMIN_MODAL_MESSAGE } from '../../constants/admin/adminModal'; + +type State = 'success' | 'fail'; + +export const useAdminNotice = ( + handleModalOpen: (message: string) => void, + pathname: string +) => { + const queryClient = useQueryClient(); + const navigate = useNavigate(); + + const handleButtonState = (state: State, isDeleteApi: boolean = false) => { + switch (state) { + case 'success': + if (!isDeleteApi) { + handleModalOpen(ADMIN_MODAL_MESSAGE.writeSuccess); + } else { + handleModalOpen(ADMIN_MODAL_MESSAGE.writeDeleteSuccess); + } + setTimeout(() => { + if (pathname) { + return navigate(pathname); + } else { + return navigate(-1); + } + }, 1000); + break; + case 'fail': + if (!isDeleteApi) { + handleModalOpen(ADMIN_MODAL_MESSAGE.writeFail); + } else { + handleModalOpen(ADMIN_MODAL_MESSAGE.writeDeleteFail); + } + break; + default: + handleModalOpen(ADMIN_MODAL_MESSAGE.writeError); + break; + } + }; + + const postNoticeMutate = useMutation({ + mutationFn: (formData) => postNotice(formData), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [CustomerService.notice], + }); + handleButtonState('success'); + }, + onError: () => { + handleButtonState('fail'); + }, + }); + + const putNoticeMutate = useMutation< + void, + AxiosError, + { id: number; formData: FormData } + >({ + mutationFn: ({ id, formData }: { id: number; formData: FormData }) => + putNotice(id, formData), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [CustomerService.notice], + }); + handleButtonState('success'); + }, + onError: () => { + handleButtonState('fail'); + }, + }); + + const deleteNoticeMutate = useMutation({ + mutationFn: (id) => deleteNotice(id), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [CustomerService.notice], + }); + handleButtonState('success', true); + }, + onError: () => { + handleButtonState('fail', true); + }, + }); + + return { postNoticeMutate, putNoticeMutate, deleteNoticeMutate }; +}; diff --git a/src/models/customerService.ts b/src/models/customerService.ts index 7d058a0a..6f5cf87e 100644 --- a/src/models/customerService.ts +++ b/src/models/customerService.ts @@ -46,3 +46,9 @@ export interface SearchKeyword { export interface NoticeSearch extends SearchKeyword { page: number; } + +// admin +export interface WriteBody { + title: string; + content: string; +} diff --git a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.styled.ts b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.styled.ts new file mode 100644 index 00000000..3cc98540 --- /dev/null +++ b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.styled.ts @@ -0,0 +1,36 @@ +import styled from 'styled-components'; +import { + Container, + InputInquiryTitle, + InquiryForm, + InquiryWrapper, + Nav, + ContentWrapper, + Content, + SendButtonWrapper, + SendButton, +} from '../../../../components/user/customerService/inquiry/Inquiry.styled'; + +export const AdminNoticeContainer = styled(Container)``; + +export const AdminNoticeForm = styled(InquiryForm)``; + +export const AdminNoticeWrapper = styled(InquiryWrapper)` + width: 90%; +`; + +export const AdminNoticeNav = styled(Nav)``; + +export const AdminNoticeInputTitle = styled(InputInquiryTitle)` + width: 100%; +`; + +export const AdminNoticeContentWrapper = styled(ContentWrapper)``; + +export const AdminNoticeContent = styled(Content)` + height: 60vh; +`; + +export const AdminNoticeSendButtonWrapper = styled(SendButtonWrapper)``; + +export const AdminNoticeSendButton = styled(SendButton)``; diff --git a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx new file mode 100644 index 00000000..37e9ed75 --- /dev/null +++ b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx @@ -0,0 +1,103 @@ +import { INQUIRY_MESSAGE } from '../../../../constants/user/customerService'; +import * as S from './AdminNoticeWrite.styled'; +import React, { useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { useModal } from '../../../../hooks/useModal'; +import Modal from '../../../../components/common/modal/Modal'; +import type { WriteBody } from '../../../../models/customerService'; +import { useAdminNotice } from '../../../../hooks/admin/useAdminNotice'; + +export default function AdminNoticeWrite() { + const location = useLocation(); + const { + isOpen: isModalOpen, + message, + handleModalOpen, + handleModalClose, + } = useModal(); + const { postNoticeMutate } = useAdminNotice( + handleModalOpen, + location.state.from || '' + ); + const [form, setForm] = useState({ + title: '', + content: '', + }); + + const handleSubmitInquiry = (e: React.FormEvent) => { + e.preventDefault(); + + const formData = new FormData(e.currentTarget as HTMLFormElement); + + const formDataObj: WriteBody = { + title: formData.get('title') as string, + content: formData.get('content') as string, + }; + + const data = new Blob([JSON.stringify(formDataObj)], { + type: 'application/json', + }); + + formData.append('inquiryDto', data); + + const isValid = { + title: form.title.trim() !== '', + content: form.content.trim() !== '', + }; + + if (!isValid.title) { + return handleModalOpen(INQUIRY_MESSAGE.writeTitle); + } + if (!isValid.content) { + return handleModalOpen(INQUIRY_MESSAGE.writeContent); + } + + postNoticeMutate.mutate(formData); + setForm({ + title: '', + content: '', + }); + }; + + return ( + + + + + + setForm((prev) => ({ ...prev, title: e.target.value })) + } + /> + + + + setForm((prev) => ({ ...prev, content: e.target.value })) + } + > + + + + 제출 + + + + + + {message} + + + ); +} diff --git a/src/routes/AdminRoutes.tsx b/src/routes/AdminRoutes.tsx index 45eec80f..1e8ae8c5 100644 --- a/src/routes/AdminRoutes.tsx +++ b/src/routes/AdminRoutes.tsx @@ -11,6 +11,9 @@ const Notice = lazy(() => import('../pages/admin/adminNotice/AdminNotice')); const NoticeList = lazy( () => import('../pages/admin/adminNotice/adminNoticeList/AdminNoticeList') ); +const NoticeWrite = lazy( + () => import('../pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite') +); const NoticeDetail = lazy( () => import('../pages/admin/adminNoticeDetail/AdminNoticeDetail') ); @@ -42,6 +45,10 @@ export const AdminRoutes = () => { element: , children: [ { index: true, element: }, + { + path: ADMIN_ROUTE.write, + element: , + }, { path: `${ADMIN_ROUTE.detail}/:noticeId`, element: , diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index a8d3c54c..df884360 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -384,22 +384,7 @@ export const AppRoutes = () => { }; }); - const router = createBrowserRouter([ - { - element: ( - - - - - - - ), - - children: [...newRouteList, { path: '*', element: }], - }, - ]); - - return ; + return newRouteList; }; export default AppRoutes; diff --git a/src/routes/MergeRoutes.tsx b/src/routes/MergeRoutes.tsx index 751136df..48f0f910 100644 --- a/src/routes/MergeRoutes.tsx +++ b/src/routes/MergeRoutes.tsx @@ -3,14 +3,19 @@ import AdminRoutes from './AdminRoutes'; import AppRoutes from './AppRoutes'; import NotFoundPage from '../pages/notFoundPage/NotFoundPage'; import { ToastProvider } from '../components/common/Toast/ToastProvider'; +import { NotificationProvider } from '../components/user/notificationLive/NotificationProvider'; +import NotificationInitializer from '../components/user/notificationLive/NotificationInitializer'; export default function MergeRoutes() { const router = createBrowserRouter([ { element: ( - - - + + + + + + ), children: [...AppRoutes()], }, From a66faca352d82e0c8eb35b60198beb691157e5c6 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Mon, 2 Jun 2025 16:39:29 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EA=B3=B5=EC=A7=80=EC=82=AC=ED=95=AD=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/customerService/Notice.api.ts | 17 +++++------------ src/hooks/admin/useAdminNotice.ts | 7 ++++--- .../adminNoticeWrite/AdminNoticeWrite.tsx | 8 +------- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/api/admin/customerService/Notice.api.ts b/src/api/admin/customerService/Notice.api.ts index 42887605..a8b77b0e 100644 --- a/src/api/admin/customerService/Notice.api.ts +++ b/src/api/admin/customerService/Notice.api.ts @@ -1,26 +1,19 @@ import type { ApiCommonBasicType } from '../../../models/apiCommon'; +import type { WriteBody } from '../../../models/customerService'; import { httpClient } from '../../http.api'; -export const postNotice = async (formData: FormData) => { +export const postNotice = async (formData: WriteBody) => { try { - await httpClient.post(`/notice`, formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); + await httpClient.post(`/notice`, formData); } catch (e) { console.error(e); throw e; } }; -export const putNotice = async (id: number, formData: FormData) => { +export const putNotice = async (id: number, formData: WriteBody) => { try { - await httpClient.put(`/notice/${id}`, formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); + await httpClient.put(`/notice/${id}`, formData); } catch (e) { console.error(e); throw e; diff --git a/src/hooks/admin/useAdminNotice.ts b/src/hooks/admin/useAdminNotice.ts index 9ab68553..c1b8b5b7 100644 --- a/src/hooks/admin/useAdminNotice.ts +++ b/src/hooks/admin/useAdminNotice.ts @@ -8,6 +8,7 @@ import { AxiosError } from 'axios'; import { CustomerService } from '../queries/user/keys'; import { useNavigate } from 'react-router-dom'; import { ADMIN_MODAL_MESSAGE } from '../../constants/admin/adminModal'; +import { WriteBody } from '../../models/customerService'; type State = 'success' | 'fail'; @@ -47,7 +48,7 @@ export const useAdminNotice = ( } }; - const postNoticeMutate = useMutation({ + const postNoticeMutate = useMutation({ mutationFn: (formData) => postNotice(formData), onSuccess: () => { queryClient.invalidateQueries({ @@ -63,9 +64,9 @@ export const useAdminNotice = ( const putNoticeMutate = useMutation< void, AxiosError, - { id: number; formData: FormData } + { id: number; formData: WriteBody } >({ - mutationFn: ({ id, formData }: { id: number; formData: FormData }) => + mutationFn: ({ id, formData }: { id: number; formData: WriteBody }) => putNotice(id, formData), onSuccess: () => { queryClient.invalidateQueries({ diff --git a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx index 37e9ed75..94495b76 100644 --- a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx +++ b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx @@ -34,12 +34,6 @@ export default function AdminNoticeWrite() { content: formData.get('content') as string, }; - const data = new Blob([JSON.stringify(formDataObj)], { - type: 'application/json', - }); - - formData.append('inquiryDto', data); - const isValid = { title: form.title.trim() !== '', content: form.content.trim() !== '', @@ -52,7 +46,7 @@ export default function AdminNoticeWrite() { return handleModalOpen(INQUIRY_MESSAGE.writeContent); } - postNoticeMutate.mutate(formData); + postNoticeMutate.mutate(formDataObj); setForm({ title: '', content: '', From 29c405d79af0464b0cf7b188c3753d19835932bd Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 3 Jun 2025 09:07:49 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EC=88=98=EC=A0=95,=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/customerService/Notice.api.ts | 4 +- src/api/customerService.api.ts | 2 +- .../admin/searchBar/SearchBar.styled.ts | 1 + src/components/common/dropDown/DropDown.tsx | 4 +- .../content/NoticeDetailContent.styled.ts | 47 ++++++++++++ .../content/NoticeDetailContent.tsx | 73 ++++++++++++++++--- src/constants/routes.ts | 1 + src/hooks/admin/useAdminNotice.ts | 10 +-- src/hooks/user/useGetNoticeDetail.ts | 1 + src/hooks/user/useMyInfo.ts | 5 +- .../adminNoticeWrite/AdminNoticeWrite.tsx | 23 +++++- src/routes/AdminRoutes.tsx | 4 + 12 files changed, 147 insertions(+), 28 deletions(-) diff --git a/src/api/admin/customerService/Notice.api.ts b/src/api/admin/customerService/Notice.api.ts index a8b77b0e..2455cc93 100644 --- a/src/api/admin/customerService/Notice.api.ts +++ b/src/api/admin/customerService/Notice.api.ts @@ -11,7 +11,7 @@ export const postNotice = async (formData: WriteBody) => { } }; -export const putNotice = async (id: number, formData: WriteBody) => { +export const putNotice = async (id: string, formData: WriteBody) => { try { await httpClient.put(`/notice/${id}`, formData); } catch (e) { @@ -20,7 +20,7 @@ export const putNotice = async (id: number, formData: WriteBody) => { } }; -export const deleteNotice = async (id: number) => { +export const deleteNotice = async (id: string) => { try { await httpClient.delete(`/notice/${id}`); } catch (e) { diff --git a/src/api/customerService.api.ts b/src/api/customerService.api.ts index 19483fb9..d8264340 100644 --- a/src/api/customerService.api.ts +++ b/src/api/customerService.api.ts @@ -36,6 +36,6 @@ export const getNoticeDetail = async (id: string) => { return response.data.data; } catch (e) { console.error(e); - throw e; + // throw e; } }; diff --git a/src/components/admin/searchBar/SearchBar.styled.ts b/src/components/admin/searchBar/SearchBar.styled.ts index 08b4031e..9c432908 100644 --- a/src/components/admin/searchBar/SearchBar.styled.ts +++ b/src/components/admin/searchBar/SearchBar.styled.ts @@ -36,6 +36,7 @@ export const AdminSearchBarBackIcon = styled.button` export const AdminSearchBarButton = styled.button` width: 15%; + min-width: 5rem; border: 1px solid ${({ theme }) => theme.color.navy}; background: ${({ theme }) => theme.color.navy}; border-radius: 0 ${({ theme }) => theme.borderRadius.large} diff --git a/src/components/common/dropDown/DropDown.tsx b/src/components/common/dropDown/DropDown.tsx index 32c40a6e..2293abc4 100644 --- a/src/components/common/dropDown/DropDown.tsx +++ b/src/components/common/dropDown/DropDown.tsx @@ -7,14 +7,14 @@ interface DropDownProps { children: React.ReactNode; toggleButton: React.ReactNode; isOpen?: boolean; - comment: boolean; + comment?: boolean; } const DropDown = ({ children, toggleButton, isOpen = false, - comment, + comment = false, ...props }: DropDownProps) => { const [open, setOpen] = useState(isOpen); diff --git a/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.styled.ts b/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.styled.ts index 25ac54d6..70e12b74 100644 --- a/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.styled.ts +++ b/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.styled.ts @@ -1,3 +1,4 @@ +import { Link } from 'react-router-dom'; import styled from 'styled-components'; export const Container = styled.div` @@ -33,6 +34,52 @@ export const InfoWrapper = styled.div` gap: 1rem; `; +export const AdminAuthWrapper = styled.div` + display: flex; + justify-content: space-between; +`; + +export const AdminAuthButton = styled.button` + height: fit-content; + display: flex; + justify-content: center; + align-items: center; + + svg { + width: 1rem; + height: 1rem; + } +`; + +export const AdminDropdownWrapper = styled.div` + position: relative; +`; + +export const AdminLinkWrapper = styled.nav` + position: absolute; + top: -1rem; + left: -5.5rem; + width: 5.5rem; + display: flex; + flex-direction: column; + background: ${({ theme }) => theme.color.white}; + border-radius: ${({ theme }) => theme.borderRadius.primary}; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + overflow: hidden; +`; + +export const AdminLink = styled(Link)` + width: 100%; + padding: 0.5rem; + display: flex; + justify-content: center; + + &:hover { + background: ${({ theme }) => theme.color.lightgrey}; + color: ${({ theme }) => theme.color.deepGrey}; + } +`; + export const NoticeContentDate = styled.span` font-size: 0.8rem; `; diff --git a/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx b/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx index d85e94a2..7b42dc3f 100644 --- a/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx +++ b/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx @@ -1,8 +1,15 @@ -import { EyeIcon } from '@heroicons/react/24/outline'; +import { EllipsisVerticalIcon, EyeIcon } from '@heroicons/react/24/outline'; import { formatDate } from '../../../../../util/format'; import * as S from './NoticeDetailContent.styled'; import logo from '../../../../../assets/mainlogo.svg'; import ContentBorder from '../../../../common/contentBorder/ContentBorder'; +import useAuthStore from '../../../../../store/authStore'; +import DropDown from '../../../../common/dropDown/DropDown'; +import { ADMIN_ROUTE } from '../../../../../constants/routes'; +import { useLocation, useParams } from 'react-router-dom'; +import { useAdminNotice } from '../../../../../hooks/admin/useAdminNotice'; +import Modal from '../../../../common/modal/Modal'; +import { useModal } from '../../../../../hooks/useModal'; interface NoticeDetailContentProps { id: number; @@ -19,6 +26,20 @@ export default function NoticeDetailContent({ createdAt, viewCount, }: NoticeDetailContentProps) { + const isAdmin = useAuthStore((state) => state.userData?.admin) ?? false; + const { noticeId } = useParams() || ''; + const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); + const location = useLocation(); + const { deleteNoticeMutate } = useAdminNotice( + handleModalOpen, + `${ADMIN_ROUTE.admin}/${ADMIN_ROUTE.notice}` + ); + + const handleClickDeleteNotice = (e: React.MouseEvent) => { + e.preventDefault(); + deleteNoticeMutate.mutate(noticeId as string); + }; + return ( @@ -27,22 +48,50 @@ export default function NoticeDetailContent({ DevPals - {Boolean(id) && ( - - {formatDate(createdAt)} - - - - - {viewCount} - - - )} + + {Boolean(id) && ( + + {formatDate(createdAt)} + + + + + {viewCount} + + + )} + {isAdmin && ( + + + + + } + > + + + 수정하기 + + + 삭제하기 + + + + + )} + {content} + + {message} + ); } diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 5dabdd8d..f65de3b6 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -43,4 +43,5 @@ export const ADMIN_ROUTE = { manage: 'manage', detail: 'detail', write: 'write', + modification: 'modification', }; diff --git a/src/hooks/admin/useAdminNotice.ts b/src/hooks/admin/useAdminNotice.ts index c1b8b5b7..dbc4018b 100644 --- a/src/hooks/admin/useAdminNotice.ts +++ b/src/hooks/admin/useAdminNotice.ts @@ -64,10 +64,10 @@ export const useAdminNotice = ( const putNoticeMutate = useMutation< void, AxiosError, - { id: number; formData: WriteBody } + { id: string; formDataObj: WriteBody } >({ - mutationFn: ({ id, formData }: { id: number; formData: WriteBody }) => - putNotice(id, formData), + mutationFn: ({ id, formDataObj }: { id: string; formDataObj: WriteBody }) => + putNotice(id, formDataObj), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [CustomerService.notice], @@ -79,8 +79,8 @@ export const useAdminNotice = ( }, }); - const deleteNoticeMutate = useMutation({ - mutationFn: (id) => deleteNotice(id), + const deleteNoticeMutate = useMutation({ + mutationFn: (id: string) => deleteNotice(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [CustomerService.notice], diff --git a/src/hooks/user/useGetNoticeDetail.ts b/src/hooks/user/useGetNoticeDetail.ts index 5b662c2d..4aeee3ee 100644 --- a/src/hooks/user/useGetNoticeDetail.ts +++ b/src/hooks/user/useGetNoticeDetail.ts @@ -6,6 +6,7 @@ export const useGetNoticeDetail = (id: string) => { const { data: noticeDetailData, isLoading } = useQuery({ queryKey: [CustomerService.noticeDetail, id], queryFn: () => getNoticeDetail(id), + enabled: Boolean(id), }); return { noticeDetail: noticeDetailData, isLoading }; diff --git a/src/hooks/user/useMyInfo.ts b/src/hooks/user/useMyInfo.ts index 1096a30e..a73ec422 100644 --- a/src/hooks/user/useMyInfo.ts +++ b/src/hooks/user/useMyInfo.ts @@ -24,9 +24,10 @@ export const useMyProfileInfo = () => { const { data, isLoading } = useQuery({ queryKey: myInfoKey.myProfile, queryFn: () => getMyInfo(), - staleTime: Infinity, - gcTime: Infinity, + staleTime: 1000 * 60 * 30, + gcTime: 1000 * 60 * 60, refetchInterval: 1000 * 60 * 60, + refetchIntervalInBackground: false, enabled: isLoggedIn, }); diff --git a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx index 94495b76..376ef5ef 100644 --- a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx +++ b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx @@ -1,11 +1,12 @@ import { INQUIRY_MESSAGE } from '../../../../constants/user/customerService'; import * as S from './AdminNoticeWrite.styled'; -import React, { useState } from 'react'; -import { useLocation } from 'react-router-dom'; +import React, { useEffect, useState } from 'react'; +import { useLocation, useParams } from 'react-router-dom'; import { useModal } from '../../../../hooks/useModal'; import Modal from '../../../../components/common/modal/Modal'; import type { WriteBody } from '../../../../models/customerService'; import { useAdminNotice } from '../../../../hooks/admin/useAdminNotice'; +import { useGetNoticeDetail } from '../../../../hooks/user/useGetNoticeDetail'; export default function AdminNoticeWrite() { const location = useLocation(); @@ -15,7 +16,10 @@ export default function AdminNoticeWrite() { handleModalOpen, handleModalClose, } = useModal(); - const { postNoticeMutate } = useAdminNotice( + const { noticeId } = useParams(); + const id = String(noticeId) || ''; + const { noticeDetail, isLoading } = useGetNoticeDetail(id); + const { postNoticeMutate, putNoticeMutate } = useAdminNotice( handleModalOpen, location.state.from || '' ); @@ -24,6 +28,13 @@ export default function AdminNoticeWrite() { content: '', }); + useEffect(() => { + console.log('noticeDetail', noticeDetail); + + if (!noticeDetail) return; + setForm({ title: noticeDetail.title, content: noticeDetail.content }); + }, [noticeDetail]); + const handleSubmitInquiry = (e: React.FormEvent) => { e.preventDefault(); @@ -46,7 +57,11 @@ export default function AdminNoticeWrite() { return handleModalOpen(INQUIRY_MESSAGE.writeContent); } - postNoticeMutate.mutate(formDataObj); + if (id) { + return putNoticeMutate.mutate({ id, formDataObj }); + } else { + return postNoticeMutate.mutate(formDataObj); + } setForm({ title: '', content: '', diff --git a/src/routes/AdminRoutes.tsx b/src/routes/AdminRoutes.tsx index 1e8ae8c5..f9022ff2 100644 --- a/src/routes/AdminRoutes.tsx +++ b/src/routes/AdminRoutes.tsx @@ -53,6 +53,10 @@ export const AdminRoutes = () => { path: `${ADMIN_ROUTE.detail}/:noticeId`, element: , }, + { + path: `${ADMIN_ROUTE.detail}/:noticeId/${ADMIN_ROUTE.modification}`, + element: , + }, ], }, { From fbd88f42254f73a6a9e4b2aeca88ddf9fac35578 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 3 Jun 2025 10:53:38 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=EC=9E=91=EC=84=B1,=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=82=AD=EC=A0=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/NoticeDetailContent.tsx | 6 ++-- src/hooks/admin/useAdminNotice.ts | 14 +++++--- src/hooks/user/useGetNoticeDetail.ts | 6 +++- src/hooks/user/useMyInfo.ts | 10 +++--- src/main.tsx | 8 ++--- src/pages/admin/adminMain/AdminMain.tsx | 1 - .../adminNoticeWrite/AdminNoticeWrite.tsx | 35 ++++++++++++------- src/routes/AdminRoutes.tsx | 2 +- src/routes/ProtectAdminRoute.tsx | 6 +++- 9 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx b/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx index 7b42dc3f..a8c817ae 100644 --- a/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx +++ b/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx @@ -30,10 +30,8 @@ export default function NoticeDetailContent({ const { noticeId } = useParams() || ''; const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); const location = useLocation(); - const { deleteNoticeMutate } = useAdminNotice( - handleModalOpen, - `${ADMIN_ROUTE.admin}/${ADMIN_ROUTE.notice}` - ); + const pathname = `${ADMIN_ROUTE.admin}/${ADMIN_ROUTE.notice}`; + const { deleteNoticeMutate } = useAdminNotice({ handleModalOpen, pathname }); const handleClickDeleteNotice = (e: React.MouseEvent) => { e.preventDefault(); diff --git a/src/hooks/admin/useAdminNotice.ts b/src/hooks/admin/useAdminNotice.ts index dbc4018b..fb352eb2 100644 --- a/src/hooks/admin/useAdminNotice.ts +++ b/src/hooks/admin/useAdminNotice.ts @@ -12,10 +12,15 @@ import { WriteBody } from '../../models/customerService'; type State = 'success' | 'fail'; -export const useAdminNotice = ( - handleModalOpen: (message: string) => void, - pathname: string -) => { +export const useAdminNotice = ({ + handleModalOpen, + formDefault, + pathname, +}: { + handleModalOpen: (message: string) => void; + formDefault?: () => void; + pathname: string; +}) => { const queryClient = useQueryClient(); const navigate = useNavigate(); @@ -24,6 +29,7 @@ export const useAdminNotice = ( case 'success': if (!isDeleteApi) { handleModalOpen(ADMIN_MODAL_MESSAGE.writeSuccess); + formDefault?.(); } else { handleModalOpen(ADMIN_MODAL_MESSAGE.writeDeleteSuccess); } diff --git a/src/hooks/user/useGetNoticeDetail.ts b/src/hooks/user/useGetNoticeDetail.ts index 4aeee3ee..47193b7c 100644 --- a/src/hooks/user/useGetNoticeDetail.ts +++ b/src/hooks/user/useGetNoticeDetail.ts @@ -6,8 +6,12 @@ export const useGetNoticeDetail = (id: string) => { const { data: noticeDetailData, isLoading } = useQuery({ queryKey: [CustomerService.noticeDetail, id], queryFn: () => getNoticeDetail(id), - enabled: Boolean(id), + enabled: !!id, }); + if (!id) { + return { noticeDetail: undefined, isLoading: false }; + } + return { noticeDetail: noticeDetailData, isLoading }; }; diff --git a/src/hooks/user/useMyInfo.ts b/src/hooks/user/useMyInfo.ts index a73ec422..7b8affae 100644 --- a/src/hooks/user/useMyInfo.ts +++ b/src/hooks/user/useMyInfo.ts @@ -24,10 +24,12 @@ export const useMyProfileInfo = () => { const { data, isLoading } = useQuery({ queryKey: myInfoKey.myProfile, queryFn: () => getMyInfo(), - staleTime: 1000 * 60 * 30, - gcTime: 1000 * 60 * 60, - refetchInterval: 1000 * 60 * 60, - refetchIntervalInBackground: false, + staleTime: Infinity, + gcTime: Infinity, + // staleTime: 1000 * 60 * 30, + // gcTime: 1000 * 60 * 60, + // refetchInterval: 1000 * 60 * 60, + // refetchIntervalInBackground: false, enabled: isLoggedIn, }); diff --git a/src/main.tsx b/src/main.tsx index 32585982..fc6a5ab3 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,10 +2,10 @@ import { createRoot } from 'react-dom/client'; import App from './App.tsx'; async function mountApp() { - if (process.env.NODE_ENV === 'development') { - const { worker } = await import('./mock/browser'); - await worker.start(); - } + // if (process.env.NODE_ENV === 'development') { + // const { worker } = await import('./mock/browser'); + // await worker.start(); + // } createRoot(document.getElementById('root')!).render( <> diff --git a/src/pages/admin/adminMain/AdminMain.tsx b/src/pages/admin/adminMain/AdminMain.tsx index 85afe1dc..fd043238 100644 --- a/src/pages/admin/adminMain/AdminMain.tsx +++ b/src/pages/admin/adminMain/AdminMain.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import * as S from './AdminMain.styled'; import MainCard from '../../../components/admin/mainCard/MainCard'; import { cardList } from '../../../constants/admin/mainItems'; diff --git a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx index 376ef5ef..d816bd20 100644 --- a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx +++ b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx @@ -7,6 +7,7 @@ import Modal from '../../../../components/common/modal/Modal'; import type { WriteBody } from '../../../../models/customerService'; import { useAdminNotice } from '../../../../hooks/admin/useAdminNotice'; import { useGetNoticeDetail } from '../../../../hooks/user/useGetNoticeDetail'; +import Spinner from '../../../../components/user/mypage/Spinner'; export default function AdminNoticeWrite() { const location = useLocation(); @@ -17,20 +18,28 @@ export default function AdminNoticeWrite() { handleModalClose, } = useModal(); const { noticeId } = useParams(); - const id = String(noticeId) || ''; + const id = noticeId ? noticeId : ''; const { noticeDetail, isLoading } = useGetNoticeDetail(id); - const { postNoticeMutate, putNoticeMutate } = useAdminNotice( + const pathname = location.state.from || ''; + + const formDefault = () => { + setForm({ + title: '', + content: '', + }); + }; + + const { postNoticeMutate, putNoticeMutate } = useAdminNotice({ handleModalOpen, - location.state.from || '' - ); + formDefault, + pathname, + }); const [form, setForm] = useState({ title: '', content: '', }); useEffect(() => { - console.log('noticeDetail', noticeDetail); - if (!noticeDetail) return; setForm({ title: noticeDetail.title, content: noticeDetail.content }); }, [noticeDetail]); @@ -57,17 +66,17 @@ export default function AdminNoticeWrite() { return handleModalOpen(INQUIRY_MESSAGE.writeContent); } - if (id) { - return putNoticeMutate.mutate({ id, formDataObj }); - } else { + if (!id) { return postNoticeMutate.mutate(formDataObj); + } else { + return putNoticeMutate.mutate({ id, formDataObj }); } - setForm({ - title: '', - content: '', - }); }; + if (isLoading) { + return ; + } + return ( { children: [ { index: true, - element:
, + element: , }, { path: ADMIN_ROUTE.notice, diff --git a/src/routes/ProtectAdminRoute.tsx b/src/routes/ProtectAdminRoute.tsx index 6c4085e0..b2781d8d 100644 --- a/src/routes/ProtectAdminRoute.tsx +++ b/src/routes/ProtectAdminRoute.tsx @@ -5,6 +5,7 @@ import { ReactNode, useEffect } from 'react'; import { useModal } from '../hooks/useModal'; import Modal from '../components/common/modal/Modal'; import { MODAL_MESSAGE } from '../constants/user/modalMessage'; +import { useMyProfileInfo } from '../hooks/user/useMyInfo'; interface ProtectAdminRouteProps { children: ReactNode; @@ -21,13 +22,15 @@ export default function ProtectAdminRoute({ useAuthStore((state) => state.redirectAdmin) || false; const navigate = useNavigate(); const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); + const { myData } = useMyProfileInfo(); useEffect(() => { let timer: NodeJS.Timeout; + console.log(myData); const handleStorageChange = () => { const authStorage = localStorage.getItem('auth-storage'); - if (!authStorage) { + if (!authStorage || !myData) { handleModalOpen(MODAL_MESSAGE.needAuth); timer = setTimeout(() => { logout(); @@ -63,6 +66,7 @@ export default function ProtectAdminRoute({ if (timer) clearTimeout(timer); }; }, [ + myData, redirectAdminBool, isLoggedIn, isAdmin, From a808d8910beb31f4250228129e9d54c3f321aea3 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Wed, 4 Jun 2025 01:08:47 +0900 Subject: [PATCH 6/6] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/customerService.api.ts | 2 +- src/components/admin/searchBar/SearchBar.tsx | 20 ++++++++++--------- .../customerService/CustomerServiceHeader.tsx | 5 +++-- .../content/NoticeDetailContent.tsx | 8 +++++--- src/hooks/user/useMyInfo.ts | 10 ++++------ .../AdminNoticeWrite.styled.ts | 3 +++ .../adminNoticeWrite/AdminNoticeWrite.tsx | 20 +++++++++++-------- src/routes/ProtectAdminRoute.tsx | 5 ++--- 8 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/api/customerService.api.ts b/src/api/customerService.api.ts index d8264340..19483fb9 100644 --- a/src/api/customerService.api.ts +++ b/src/api/customerService.api.ts @@ -36,6 +36,6 @@ export const getNoticeDetail = async (id: string) => { return response.data.data; } catch (e) { console.error(e); - // throw e; + throw e; } }; diff --git a/src/components/admin/searchBar/SearchBar.tsx b/src/components/admin/searchBar/SearchBar.tsx index cb9b3119..cd5babc2 100644 --- a/src/components/admin/searchBar/SearchBar.tsx +++ b/src/components/admin/searchBar/SearchBar.tsx @@ -8,15 +8,16 @@ import Modal from '../../common/modal/Modal'; import { ADMIN_ROUTE } from '../../../constants/routes'; interface SearchBarProps { - onGetKeyword: (value: string) => void; + onGetKeyword: (keyword: string) => void; value: string; } export default function SearchBar({ onGetKeyword, value }: SearchBarProps) { - const [keyword, setKeyword] = useState(''); + const [inputValue, setInputValue] = useState(''); const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); const [searchParams, setSearchParams] = useSearchParams(); const location = useLocation(); + const keyword = inputValue ? inputValue : value; const handleKeyword = (inputValue: string) => { const newSearchParams = new URLSearchParams(searchParams); @@ -31,22 +32,22 @@ export default function SearchBar({ onGetKeyword, value }: SearchBarProps) { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (keyword.trim() === '') { + if (inputValue.trim() === '') { return handleModalOpen(MODAL_MESSAGE_CUSTOMER_SERVICE.noKeyword); } else { - onGetKeyword(keyword); - handleKeyword(keyword); + onGetKeyword(inputValue); + handleKeyword(inputValue); return; } }; const handleChangeKeyword = (e: React.ChangeEvent) => { const value = e.target.value; - setKeyword(value); + setInputValue(value); }; const handleClickSearchDefault = () => { - setKeyword(''); + setInputValue(''); onGetKeyword(''); handleKeyword(''); }; @@ -57,12 +58,13 @@ export default function SearchBar({ onGetKeyword, value }: SearchBarProps) { - {value && ( + {keyword && ( diff --git a/src/components/user/customerService/CustomerServiceHeader.tsx b/src/components/user/customerService/CustomerServiceHeader.tsx index 59e79f47..2105d4b7 100644 --- a/src/components/user/customerService/CustomerServiceHeader.tsx +++ b/src/components/user/customerService/CustomerServiceHeader.tsx @@ -15,12 +15,13 @@ interface CustomerServiceHeaderProps { export default function CustomerServiceHeader({ title, - keyword, + keyword: value, onGetKeyword, }: CustomerServiceHeaderProps) { const [inputValue, setInputValue] = useState(''); const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); const [searchParams, setSearchParams] = useSearchParams(); + const keyword = value ? value : inputValue; const handleKeyword = (inputValue: string) => { const newSearchParams = new URLSearchParams(searchParams); @@ -64,7 +65,7 @@ export default function CustomerServiceHeader({ diff --git a/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx b/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx index a8c817ae..66a375f0 100644 --- a/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx +++ b/src/components/user/customerService/noticeDetail/content/NoticeDetailContent.tsx @@ -27,7 +27,7 @@ export default function NoticeDetailContent({ viewCount, }: NoticeDetailContentProps) { const isAdmin = useAuthStore((state) => state.userData?.admin) ?? false; - const { noticeId } = useParams() || ''; + const { noticeId } = useParams() ?? ''; const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); const location = useLocation(); const pathname = `${ADMIN_ROUTE.admin}/${ADMIN_ROUTE.notice}`; @@ -35,7 +35,9 @@ export default function NoticeDetailContent({ const handleClickDeleteNotice = (e: React.MouseEvent) => { e.preventDefault(); - deleteNoticeMutate.mutate(noticeId as string); + if (noticeId) { + deleteNoticeMutate.mutate(noticeId); + } }; return ( @@ -74,7 +76,7 @@ export default function NoticeDetailContent({ > 수정하기 - + 삭제하기 diff --git a/src/hooks/user/useMyInfo.ts b/src/hooks/user/useMyInfo.ts index 7b8affae..a73ec422 100644 --- a/src/hooks/user/useMyInfo.ts +++ b/src/hooks/user/useMyInfo.ts @@ -24,12 +24,10 @@ export const useMyProfileInfo = () => { const { data, isLoading } = useQuery({ queryKey: myInfoKey.myProfile, queryFn: () => getMyInfo(), - staleTime: Infinity, - gcTime: Infinity, - // staleTime: 1000 * 60 * 30, - // gcTime: 1000 * 60 * 60, - // refetchInterval: 1000 * 60 * 60, - // refetchIntervalInBackground: false, + staleTime: 1000 * 60 * 30, + gcTime: 1000 * 60 * 60, + refetchInterval: 1000 * 60 * 60, + refetchIntervalInBackground: false, enabled: isLoggedIn, }); diff --git a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.styled.ts b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.styled.ts index 3cc98540..96d78368 100644 --- a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.styled.ts +++ b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.styled.ts @@ -10,6 +10,9 @@ import { SendButtonWrapper, SendButton, } from '../../../../components/user/customerService/inquiry/Inquiry.styled'; +import { SpinnerWrapperStyled } from '../../../../components/user/mypage/Spinner.styled'; + +export const SpinnerWrapper = styled(SpinnerWrapperStyled)``; export const AdminNoticeContainer = styled(Container)``; diff --git a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx index d816bd20..7c8789df 100644 --- a/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx +++ b/src/pages/admin/adminNotice/adminNoticeWrite/AdminNoticeWrite.tsx @@ -47,13 +47,6 @@ export default function AdminNoticeWrite() { const handleSubmitInquiry = (e: React.FormEvent) => { e.preventDefault(); - const formData = new FormData(e.currentTarget as HTMLFormElement); - - const formDataObj: WriteBody = { - title: formData.get('title') as string, - content: formData.get('content') as string, - }; - const isValid = { title: form.title.trim() !== '', content: form.content.trim() !== '', @@ -66,6 +59,13 @@ export default function AdminNoticeWrite() { return handleModalOpen(INQUIRY_MESSAGE.writeContent); } + const formData = new FormData(e.currentTarget as HTMLFormElement); + + const formDataObj: WriteBody = { + title: formData.get('title') as string, + content: formData.get('content') as string, + }; + if (!id) { return postNoticeMutate.mutate(formDataObj); } else { @@ -74,7 +74,11 @@ export default function AdminNoticeWrite() { }; if (isLoading) { - return ; + return ( + + + + ); } return ( diff --git a/src/routes/ProtectAdminRoute.tsx b/src/routes/ProtectAdminRoute.tsx index b2781d8d..660c2e69 100644 --- a/src/routes/ProtectAdminRoute.tsx +++ b/src/routes/ProtectAdminRoute.tsx @@ -22,15 +22,14 @@ export default function ProtectAdminRoute({ useAuthStore((state) => state.redirectAdmin) || false; const navigate = useNavigate(); const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); - const { myData } = useMyProfileInfo(); + const { myData, isLoading } = useMyProfileInfo(); useEffect(() => { let timer: NodeJS.Timeout; - console.log(myData); const handleStorageChange = () => { const authStorage = localStorage.getItem('auth-storage'); - if (!authStorage || !myData) { + if (!authStorage || (!myData && !isLoading)) { handleModalOpen(MODAL_MESSAGE.needAuth); timer = setTimeout(() => { logout();