From afe3ab5f60f4c46973c9c7d0401a0c0cdc5c9acd Mon Sep 17 00:00:00 2001 From: joo-prog <1114nhj@naver.com> Date: Fri, 10 Feb 2023 15:34:10 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20admin=20faq=20=EC=A1=B0=ED=9A=8C=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/App.tsx | 5 +- src/constants/paths.ts | 3 + src/constants/queryKeys.ts | 2 + src/pages/Admin/AdminFaqList.tsx | 31 +++++++ src/pages/Admin/api/index.ts | 15 ++++ .../Admin/components/AdminCreateEditForm.tsx | 12 ++- src/pages/Admin/hooks/index.ts | 1 + src/pages/Admin/hooks/query/index.ts | 3 + .../Admin/hooks/query/useAdminFaqListQuery.ts | 28 +++++++ .../Admin/hooks/query/useDeleteFaqMutation.ts | 14 ++++ .../hooks/query/useEditFaqVisibleMutation.ts | 22 +++++ src/pages/Admin/hooks/useAdminFaqsTable.tsx | 80 +++++++++++++++++++ src/pages/Admin/index.ts | 1 + src/types/admin.d.ts | 15 ++++ 14 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 src/pages/Admin/AdminFaqList.tsx create mode 100644 src/pages/Admin/hooks/query/useAdminFaqListQuery.ts create mode 100644 src/pages/Admin/hooks/query/useDeleteFaqMutation.ts create mode 100644 src/pages/Admin/hooks/query/useEditFaqVisibleMutation.ts create mode 100644 src/pages/Admin/hooks/useAdminFaqsTable.tsx diff --git a/src/App.tsx b/src/App.tsx index e0c5bb8..d587d1b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,8 +17,6 @@ import { ClassContest, ClassContestProblemList, ClassStudentManagement, - AdminAllClasses, - AdminAllProblems, AdminNewAnnouncement, AdminEditAnnouncement, AllProblemDetail, @@ -29,6 +27,7 @@ import { ProblemSubmission, ProblemForm, ClassEditContestList, + AdminFaqList, } from '@/pages'; import { MainHeader } from '@/components'; import ResetPassword from './pages/User/ResetPassword'; @@ -79,7 +78,7 @@ export default function App() { } /> } /> - FAQs} /> + } /> UserManagement} /> diff --git a/src/constants/paths.ts b/src/constants/paths.ts index 32a478e..837071b 100644 --- a/src/constants/paths.ts +++ b/src/constants/paths.ts @@ -77,4 +77,7 @@ export const PAGE: { [key: string]: string } = { ALL_CLASSES: '전체 수업 목록', NEW_ANNOUNCEMENT: '공지사항 생성', EDIT_ANNOUNCEMENT: '공지사항 수정', + ALL_FAQS: '전체 FAQ 목록', + NEW_FAQ: 'FAQ 생성', + EDIT_FAQ: 'FAQ 수정', }; diff --git a/src/constants/queryKeys.ts b/src/constants/queryKeys.ts index ccbf08d..77a0704 100644 --- a/src/constants/queryKeys.ts +++ b/src/constants/queryKeys.ts @@ -4,6 +4,8 @@ export const QUERY_KEYS = { ADMIN_ALL_ANNOUNCEMENTS: 'admin-all-announcements', ADMIN_ANNOUNCEMENT: 'admin-announcement', ADMIN_ALL_CLASSES: 'admin-all-classes', + ADMIN_ALL_FAQS: 'admin-all-faqs', + ADMIN_FAQ: 'admin-faq', FAQ: 'faq', ANNOUNCEMENT: 'announcement', CLASS: 'class', diff --git a/src/pages/Admin/AdminFaqList.tsx b/src/pages/Admin/AdminFaqList.tsx new file mode 100644 index 0000000..7284d5e --- /dev/null +++ b/src/pages/Admin/AdminFaqList.tsx @@ -0,0 +1,31 @@ +import { Button, Heading, Input, Table } from '@/components'; +import { PAGE, PATH } from '@/constants/paths'; +import { useSearch } from '@/hooks/useSearch'; +import { useNavigate } from 'react-router-dom'; +import { useAdminFaqsTable } from './hooks'; + +export function AdminFaqList() { + const navigate = useNavigate(); + const { keyword, onChange } = useSearch(); + const { column, data } = useAdminFaqsTable(keyword); + + return ( + <> +
+ + {PAGE.ALL_FAQS} + + +
+ +
+
+ + + ); +} diff --git a/src/pages/Admin/api/index.ts b/src/pages/Admin/api/index.ts index 1b221e2..51d9606 100644 --- a/src/pages/Admin/api/index.ts +++ b/src/pages/Admin/api/index.ts @@ -61,6 +61,18 @@ const getAnnouncementById = ( return api.get(`${API_URL}/announcements/${announcementId}/`); }; +const deleteFaq = (faqId: string) => { + return api.delete(`${API_URL}/faqs/${faqId}`); +}; + +const editFaqVisible = ({ faqId, payload }: { faqId: string; payload: { visible: boolean } }) => { + return api.put(`${API_URL}/faqs/${faqId}/check/`, payload); +}; + +const getFaqs = (keyword: string): Promise> => { + return api.get(`${API_URL}/faqs?keyword=${keyword}`); +}; + export { getUser, editUserPrivilege, @@ -73,4 +85,7 @@ export { createAnnouncement, editAnnouncement, getAnnouncementById, + deleteFaq, + editFaqVisible, + getFaqs, }; diff --git a/src/pages/Admin/components/AdminCreateEditForm.tsx b/src/pages/Admin/components/AdminCreateEditForm.tsx index e496350..1d4bc25 100644 --- a/src/pages/Admin/components/AdminCreateEditForm.tsx +++ b/src/pages/Admin/components/AdminCreateEditForm.tsx @@ -57,12 +57,20 @@ export function AdminCreateEditForm({ mode, data, onMutate }: AdminCreateEditFor
- +
{mode === 'announcement' && (
- +
)}
diff --git a/src/pages/Admin/hooks/index.ts b/src/pages/Admin/hooks/index.ts index 44a8e2d..a860a06 100644 --- a/src/pages/Admin/hooks/index.ts +++ b/src/pages/Admin/hooks/index.ts @@ -3,3 +3,4 @@ export * from './useSelectUserPrivilege'; export * from './useAdminAnnouncementsTable'; export * from './useAdminAllClassesTable'; export * from './useAdminAllProblemsTable'; +export * from './useAdminFaqsTable'; diff --git a/src/pages/Admin/hooks/query/index.ts b/src/pages/Admin/hooks/query/index.ts index a29e6cf..e896834 100644 --- a/src/pages/Admin/hooks/query/index.ts +++ b/src/pages/Admin/hooks/query/index.ts @@ -8,3 +8,6 @@ export * from './useAdminAllProblemsQuery'; export * from './useCreateAnnouncementMutation'; export * from './useEditAnnouncementMutation'; export * from './useAdminAnnouncementQuery'; +export * from './useDeleteFaqMutation'; +export * from './useEditFaqVisibleMutation'; +export * from './useAdminFaqListQuery'; diff --git a/src/pages/Admin/hooks/query/useAdminFaqListQuery.ts b/src/pages/Admin/hooks/query/useAdminFaqListQuery.ts new file mode 100644 index 0000000..e22b03e --- /dev/null +++ b/src/pages/Admin/hooks/query/useAdminFaqListQuery.ts @@ -0,0 +1,28 @@ +import { UseQueryOptions } from 'react-query'; +import { AxiosError } from 'axios'; + +import { QUERY_KEYS } from '@/constants'; +import { useSuspenseQuery } from '@/hooks/useSuspenseQuery'; + +import { getFaqs } from '../../api'; + +export const useAdminFaqListQuery = ( + keyword: string, + options?: UseQueryOptions< + AdminFaqListResponse, + AxiosError, + AdminFaqListResponse, + [string, string] + > +) => { + return useSuspenseQuery( + [QUERY_KEYS.ADMIN_ALL_FAQS, keyword], + async ({ queryKey: [, keyword] }) => { + const { data } = await getFaqs(keyword); + return data; + }, + { + ...options, + } + ); +}; diff --git a/src/pages/Admin/hooks/query/useDeleteFaqMutation.ts b/src/pages/Admin/hooks/query/useDeleteFaqMutation.ts new file mode 100644 index 0000000..6603200 --- /dev/null +++ b/src/pages/Admin/hooks/query/useDeleteFaqMutation.ts @@ -0,0 +1,14 @@ +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, UseMutationOptions } from 'react-query'; +import { deleteFaq } from '../../api'; + +export const useDeleteFaqMutation = ( + options?: UseMutationOptions +) => { + return useMutation((faqId) => deleteFaq(faqId), { + ...options, + onSuccess: () => { + alert('삭제되었습니다.'); + }, + }); +}; diff --git a/src/pages/Admin/hooks/query/useEditFaqVisibleMutation.ts b/src/pages/Admin/hooks/query/useEditFaqVisibleMutation.ts new file mode 100644 index 0000000..5137476 --- /dev/null +++ b/src/pages/Admin/hooks/query/useEditFaqVisibleMutation.ts @@ -0,0 +1,22 @@ +import { QUERY_KEYS } from '@/constants'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, UseMutationOptions, useQueryClient } from 'react-query'; +import { editFaqVisible } from '../../api'; + +type useEditFaqVisibleProps = { + faqId: string; + payload: { visible: boolean }; +}; + +export const useEditFaqVisibleMutation = ( + options?: UseMutationOptions +) => { + const queryClient = useQueryClient(); + + return useMutation(({ faqId, payload }) => editFaqVisible({ faqId, payload }), { + ...options, + onSuccess: async () => { + await queryClient.invalidateQueries(QUERY_KEYS.ADMIN_ALL_FAQS); + }, + }); +}; diff --git a/src/pages/Admin/hooks/useAdminFaqsTable.tsx b/src/pages/Admin/hooks/useAdminFaqsTable.tsx new file mode 100644 index 0000000..3e0e58a --- /dev/null +++ b/src/pages/Admin/hooks/useAdminFaqsTable.tsx @@ -0,0 +1,80 @@ +import { useNavigate } from 'react-router-dom'; +import { Button } from '@/components'; +import { PATH } from '@/constants/paths'; +import React from 'react'; +import { useAdminFaqListQuery, useDeleteFaqMutation, useEditFaqVisibleMutation } from './query'; +import { formatTime } from '@/utils/time'; +import { Switch } from '@/components/atom/Switch'; + +export const useAdminFaqsTable = (keyword: string) => { + const navigate = useNavigate(); + const { mutate: deleteFaq } = useDeleteFaqMutation(); + const { mutate: editFaqSwitch } = useEditFaqVisibleMutation(); + + const handleDeleteButtonClick = ({ + id, + question, + e, + }: { + question: string; + id: number; + e: React.MouseEvent; + }) => { + e.stopPropagation(); + const isConfirmed = confirm(`${question}을 삭제하시겠습니까?`); + if (isConfirmed) deleteFaq(`${id}`); + }; + + const handleSwitchButtonClick = ({ id, visible }: { id: number; visible: boolean }) => { + editFaqSwitch({ faqId: String(id), payload: { visible } }); + }; + + const column = [ + { Header: '#', accessor: 'id' }, + { Header: '질문', accessor: 'question' }, + { Header: '작성일', accessor: 'created_time' }, + { Header: '마지막 수정일', accessor: 'last_modified' }, + { Header: '공개', accessor: 'visible' }, + { Header: '편집', accessor: 'edit' }, + { Header: '삭제', accessor: 'delete' }, + ]; + + const { + data: { results }, + } = useAdminFaqListQuery(keyword); + + const data = results + .sort(({ id: prev }, { id: next }) => next - prev) + .map((_faq) => { + const { id, question, visible } = _faq; + const created_time = formatTime(_faq.created_time); + const last_modified = formatTime(_faq.last_modified); + + return { + ..._faq, + created_time, + last_modified, + visible: ( + { + handleSwitchButtonClick({ id, visible }); + }} + /> + ), + edit: ( + + ), + delete: , + }; + }); + + return { column, data }; +}; diff --git a/src/pages/Admin/index.ts b/src/pages/Admin/index.ts index a54afb1..f3db158 100644 --- a/src/pages/Admin/index.ts +++ b/src/pages/Admin/index.ts @@ -4,3 +4,4 @@ export * from './AdminAnnouncementList'; export * from './AdminAllClasses'; export * from './AdminNewAnnouncement'; export * from './AdminEditAnnouncement'; +export * from './AdminFaqList'; diff --git a/src/types/admin.d.ts b/src/types/admin.d.ts index 5ac0408..b229030 100644 --- a/src/types/admin.d.ts +++ b/src/types/admin.d.ts @@ -67,3 +67,18 @@ type AdminAnnouncementResponse = { created_time: string; last_modified: string; }; + +type AdminFaqs = { + id: number; + question: string; + created_time: string; + last_modified: string; + visible: boolean; +}; + +type AdminFaqListResponse = { + count: number; + next: number | null; + previous: number | nulle; + results: AdminFaqs[]; +}; From 9b238720e017d4d86f7431142110847598640676 Mon Sep 17 00:00:00 2001 From: joo-prog <1114nhj@naver.com> Date: Fri, 10 Feb 2023 16:44:54 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20admin=20faq=20visible=20=EC=8A=A4?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20url=20=EB=B0=8F=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Admin/api/index.ts | 9 +++++++-- src/pages/Admin/hooks/query/useEditFaqVisibleMutation.ts | 5 ++--- src/pages/Admin/hooks/useAdminFaqsTable.tsx | 9 +++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/pages/Admin/api/index.ts b/src/pages/Admin/api/index.ts index 51d9606..7792be9 100644 --- a/src/pages/Admin/api/index.ts +++ b/src/pages/Admin/api/index.ts @@ -65,14 +65,18 @@ const deleteFaq = (faqId: string) => { return api.delete(`${API_URL}/faqs/${faqId}`); }; -const editFaqVisible = ({ faqId, payload }: { faqId: string; payload: { visible: boolean } }) => { - return api.put(`${API_URL}/faqs/${faqId}/check/`, payload); +const editFaqVisible = (payload: { id: number }) => { + return api.post(`${API_URL}/faqs/check/`, payload); }; const getFaqs = (keyword: string): Promise> => { return api.get(`${API_URL}/faqs?keyword=${keyword}`); }; +const createFaq = (payload: CreateEditFaqRequest) => { + return api.post(`${API_URL}/faqs/`, payload); +}; + export { getUser, editUserPrivilege, @@ -88,4 +92,5 @@ export { deleteFaq, editFaqVisible, getFaqs, + createFaq, }; diff --git a/src/pages/Admin/hooks/query/useEditFaqVisibleMutation.ts b/src/pages/Admin/hooks/query/useEditFaqVisibleMutation.ts index 5137476..a790b43 100644 --- a/src/pages/Admin/hooks/query/useEditFaqVisibleMutation.ts +++ b/src/pages/Admin/hooks/query/useEditFaqVisibleMutation.ts @@ -4,8 +4,7 @@ import { useMutation, UseMutationOptions, useQueryClient } from 'react-query'; import { editFaqVisible } from '../../api'; type useEditFaqVisibleProps = { - faqId: string; - payload: { visible: boolean }; + id: number; }; export const useEditFaqVisibleMutation = ( @@ -13,7 +12,7 @@ export const useEditFaqVisibleMutation = ( ) => { const queryClient = useQueryClient(); - return useMutation(({ faqId, payload }) => editFaqVisible({ faqId, payload }), { + return useMutation((payload) => editFaqVisible(payload), { ...options, onSuccess: async () => { await queryClient.invalidateQueries(QUERY_KEYS.ADMIN_ALL_FAQS); diff --git a/src/pages/Admin/hooks/useAdminFaqsTable.tsx b/src/pages/Admin/hooks/useAdminFaqsTable.tsx index 3e0e58a..a444aac 100644 --- a/src/pages/Admin/hooks/useAdminFaqsTable.tsx +++ b/src/pages/Admin/hooks/useAdminFaqsTable.tsx @@ -9,7 +9,7 @@ import { Switch } from '@/components/atom/Switch'; export const useAdminFaqsTable = (keyword: string) => { const navigate = useNavigate(); const { mutate: deleteFaq } = useDeleteFaqMutation(); - const { mutate: editFaqSwitch } = useEditFaqVisibleMutation(); + const { mutate: editFaqVisible } = useEditFaqVisibleMutation(); const handleDeleteButtonClick = ({ id, @@ -25,8 +25,9 @@ export const useAdminFaqsTable = (keyword: string) => { if (isConfirmed) deleteFaq(`${id}`); }; - const handleSwitchButtonClick = ({ id, visible }: { id: number; visible: boolean }) => { - editFaqSwitch({ faqId: String(id), payload: { visible } }); + const handleSwitchButtonClick = (id: number) => { + const payload = { id }; + editFaqVisible(payload); }; const column = [ @@ -59,7 +60,7 @@ export const useAdminFaqsTable = (keyword: string) => { key={`${id}_visible_${String(visible)}`} enabled={visible} onClick={() => { - handleSwitchButtonClick({ id, visible }); + handleSwitchButtonClick(id); }} /> ), From f53e0b9a80cd772cfaaf2eba82365a60433bbe10 Mon Sep 17 00:00:00 2001 From: joo-prog <1114nhj@naver.com> Date: Fri, 10 Feb 2023 16:45:10 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20admin=20faq=20=EA=B8=80=EC=93=B0?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 ++ src/constants/paths.ts | 2 ++ src/pages/Admin/AdminEditAnnouncement.tsx | 8 +++++-- src/pages/Admin/AdminNewAnnouncement.tsx | 4 ++-- src/pages/Admin/AdminNewFaq.tsx | 21 +++++++++++++++++++ .../Admin/components/AdminCreateEditForm.tsx | 20 +++++++++++++++--- src/pages/Admin/hooks/query/index.ts | 1 + .../Admin/hooks/query/useCreateFaqMutation.ts | 19 +++++++++++++++++ src/pages/Admin/index.ts | 1 + src/types/admin.d.ts | 6 ++++++ 10 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 src/pages/Admin/AdminNewFaq.tsx create mode 100644 src/pages/Admin/hooks/query/useCreateFaqMutation.ts diff --git a/src/App.tsx b/src/App.tsx index d587d1b..7510ebf 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -28,6 +28,7 @@ import { ProblemForm, ClassEditContestList, AdminFaqList, + AdminNewFaq, } from '@/pages'; import { MainHeader } from '@/components'; import ResetPassword from './pages/User/ResetPassword'; @@ -78,6 +79,7 @@ export default function App() { } /> } /> + } /> } /> UserManagement} /> diff --git a/src/constants/paths.ts b/src/constants/paths.ts index 837071b..50d6af7 100644 --- a/src/constants/paths.ts +++ b/src/constants/paths.ts @@ -59,6 +59,8 @@ export const PATH: { [key: string]: string } = { ADMIN_ANNOUNCEMENTS_NEW: `${BASE_PATH.ADMIN}/${SUB_PATH.ANNOUNCEMENTS}/new`, ADMIN_ANNOUNCEMENTS_EDIT: `${BASE_PATH.ADMIN}/${SUB_PATH.ANNOUNCEMENTS}/:id/edit`, ADMIN_FAQS: `${SUB_PATH.FAQS}`, + ADMIN_FAQS_NEW: `${BASE_PATH.ADMIN}/${SUB_PATH.FAQS}/new`, + ADMIN_FAQS_EDIT: `${BASE_PATH.ADMIN}/${SUB_PATH.FAQS}/:id/edit`, ADMIN_USER_MANAGEMENT: `${SUB_PATH.USER_MANAGEMENT}`, }; diff --git a/src/pages/Admin/AdminEditAnnouncement.tsx b/src/pages/Admin/AdminEditAnnouncement.tsx index 17d8b28..4393b03 100644 --- a/src/pages/Admin/AdminEditAnnouncement.tsx +++ b/src/pages/Admin/AdminEditAnnouncement.tsx @@ -8,7 +8,7 @@ export function AdminEditAnnouncement() { const { id: announcementId } = useParams() as { id: string }; const { mutate: editAnnouncement } = useEditAnnouncementMutation(); - const onMutate = (data: CreateEditAnnouncementRequest) => { + const onAnnouncementMutate = (data: CreateEditAnnouncementRequest) => { editAnnouncement({ announcementId, payload: data }); }; @@ -19,7 +19,11 @@ export function AdminEditAnnouncement() { {PAGE.EDIT_ANNOUNCEMENT} - + ); } diff --git a/src/pages/Admin/AdminNewAnnouncement.tsx b/src/pages/Admin/AdminNewAnnouncement.tsx index 2efb3a6..a42fdee 100644 --- a/src/pages/Admin/AdminNewAnnouncement.tsx +++ b/src/pages/Admin/AdminNewAnnouncement.tsx @@ -6,7 +6,7 @@ import { useCreateAnnouncementMutation } from './hooks/query'; export function AdminNewAnnouncement() { const { mutate: createAnnouncement } = useCreateAnnouncementMutation(); - const onMutate = (data: CreateEditAnnouncementRequest) => { + const onAnnouncementMutate = (data: CreateEditAnnouncementRequest) => { createAnnouncement(data); }; @@ -15,7 +15,7 @@ export function AdminNewAnnouncement() { {PAGE.NEW_ANNOUNCEMENT} - + ); } diff --git a/src/pages/Admin/AdminNewFaq.tsx b/src/pages/Admin/AdminNewFaq.tsx new file mode 100644 index 0000000..4a75a27 --- /dev/null +++ b/src/pages/Admin/AdminNewFaq.tsx @@ -0,0 +1,21 @@ +import { Heading } from '@/components'; +import { PAGE } from '@/constants'; +import { AdminCreateEditForm } from './components'; +import { useCreateFaqMutation } from './hooks/query'; + +export function AdminNewFaq() { + const { mutate: createFaq } = useCreateFaqMutation(); + + const onFaqMutate = (data: CreateEditFaqRequest) => { + createFaq(data); + }; + + return ( +
+ + {PAGE.NEW_FAQ} + + +
+ ); +} diff --git a/src/pages/Admin/components/AdminCreateEditForm.tsx b/src/pages/Admin/components/AdminCreateEditForm.tsx index 1d4bc25..4166606 100644 --- a/src/pages/Admin/components/AdminCreateEditForm.tsx +++ b/src/pages/Admin/components/AdminCreateEditForm.tsx @@ -13,10 +13,16 @@ type AdminCreateEditFormProps = { visible: boolean; important?: boolean; }; - onMutate: (data: CreateEditAnnouncementRequest) => void; + onAnnouncementMutate?: (data: CreateEditAnnouncementRequest) => void; + onFaqMutate?: (data: CreateEditFaqRequest) => void; }; -export function AdminCreateEditForm({ mode, data, onMutate }: AdminCreateEditFormProps) { +export function AdminCreateEditForm({ + mode, + data, + onFaqMutate, + onAnnouncementMutate, +}: AdminCreateEditFormProps) { const LABEL = mode === 'announcement' ? ANNOUNCEMENT_LABEL : FAQ_LABEL; const navigate = useNavigate(); const [title, _context, visible, important] = data @@ -35,7 +41,15 @@ export function AdminCreateEditForm({ mode, data, onMutate }: AdminCreateEditFor visible: Boolean(formData.get('visible')) ?? false, important: Boolean(formData.get('important')) ?? false, }; - onMutate(data); + if (onAnnouncementMutate !== undefined) onAnnouncementMutate(data); + } + if (mode === 'faq') { + const data = { + question: String(formData.get('title')) ?? '', + answer: context, + visible: Boolean(formData.get('visible')) ?? false, + }; + if (onFaqMutate !== undefined) onFaqMutate(data); } }; diff --git a/src/pages/Admin/hooks/query/index.ts b/src/pages/Admin/hooks/query/index.ts index e896834..74635fb 100644 --- a/src/pages/Admin/hooks/query/index.ts +++ b/src/pages/Admin/hooks/query/index.ts @@ -11,3 +11,4 @@ export * from './useAdminAnnouncementQuery'; export * from './useDeleteFaqMutation'; export * from './useEditFaqVisibleMutation'; export * from './useAdminFaqListQuery'; +export * from './useCreateFaqMutation'; diff --git a/src/pages/Admin/hooks/query/useCreateFaqMutation.ts b/src/pages/Admin/hooks/query/useCreateFaqMutation.ts new file mode 100644 index 0000000..94ea970 --- /dev/null +++ b/src/pages/Admin/hooks/query/useCreateFaqMutation.ts @@ -0,0 +1,19 @@ +import { PATH } from '@/constants'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, UseMutationOptions } from 'react-query'; +import { useNavigate } from 'react-router-dom'; +import { createFaq } from '../../api'; + +export const useCreateFaqMutation = ( + options?: UseMutationOptions +) => { + const navigate = useNavigate(); + + return useMutation((payload) => createFaq(payload), { + ...options, + onSuccess: () => { + alert('생성되었습니다.'); + navigate(`${PATH.ADMIN}/${PATH.ADMIN_FAQS}`); + }, + }); +}; diff --git a/src/pages/Admin/index.ts b/src/pages/Admin/index.ts index f3db158..b84bcf7 100644 --- a/src/pages/Admin/index.ts +++ b/src/pages/Admin/index.ts @@ -5,3 +5,4 @@ export * from './AdminAllClasses'; export * from './AdminNewAnnouncement'; export * from './AdminEditAnnouncement'; export * from './AdminFaqList'; +export * from './AdminNewFaq'; diff --git a/src/types/admin.d.ts b/src/types/admin.d.ts index b229030..06b66fd 100644 --- a/src/types/admin.d.ts +++ b/src/types/admin.d.ts @@ -82,3 +82,9 @@ type AdminFaqListResponse = { previous: number | nulle; results: AdminFaqs[]; }; + +type CreateEditFaqRequest = { + question: string; + answer: string; + visible: boolean; +}; From 59bcc9b8e1e1cbfda3a9f1dc7433f5203b4feaff Mon Sep 17 00:00:00 2001 From: joo-prog <1114nhj@naver.com> Date: Fri, 10 Feb 2023 16:56:19 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20admin=20faq=20=ED=8E=B8=EC=A7=91=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/App.tsx | 2 ++ src/pages/Admin/AdminEditFaq.tsx | 32 +++++++++++++++++++ src/pages/Admin/api/index.ts | 10 ++++++ src/pages/Admin/hooks/query/index.ts | 2 ++ .../Admin/hooks/query/useAdminFaqQuery.ts | 23 +++++++++++++ .../Admin/hooks/query/useEditFaqMutation.ts | 26 +++++++++++++++ src/pages/Admin/index.ts | 1 + src/types/admin.d.ts | 10 ++++++ 8 files changed, 106 insertions(+) create mode 100644 src/pages/Admin/AdminEditFaq.tsx create mode 100644 src/pages/Admin/hooks/query/useAdminFaqQuery.ts create mode 100644 src/pages/Admin/hooks/query/useEditFaqMutation.ts diff --git a/src/App.tsx b/src/App.tsx index 7510ebf..bc8254c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -29,6 +29,7 @@ import { ClassEditContestList, AdminFaqList, AdminNewFaq, + AdminEditFaq, } from '@/pages'; import { MainHeader } from '@/components'; import ResetPassword from './pages/User/ResetPassword'; @@ -80,6 +81,7 @@ export default function App() { } /> } /> + } /> } /> UserManagement} /> diff --git a/src/pages/Admin/AdminEditFaq.tsx b/src/pages/Admin/AdminEditFaq.tsx new file mode 100644 index 0000000..63baf25 --- /dev/null +++ b/src/pages/Admin/AdminEditFaq.tsx @@ -0,0 +1,32 @@ +import { Heading } from '@/components'; +import { PAGE } from '@/constants'; +import { useParams } from 'react-router-dom'; +import { AdminCreateEditForm } from './components'; +import { useAdminFaqQuery, useEditFaqMutation } from './hooks/query'; + +export function AdminEditFaq() { + const { id: faqId } = useParams() as { id: string }; + const { mutate: editFaq } = useEditFaqMutation(); + + const onFaqMutate = (data: CreateEditFaqRequest) => { + editFaq({ faqId, payload: data }); + }; + + const { data: queryData } = useAdminFaqQuery(faqId); + + const { question, answer, ...res } = queryData; + const data = { + title: question, + context: answer, + ...res, + }; + + return ( +
+ + {PAGE.EDIT_Faq} + + +
+ ); +} diff --git a/src/pages/Admin/api/index.ts b/src/pages/Admin/api/index.ts index 7792be9..0114831 100644 --- a/src/pages/Admin/api/index.ts +++ b/src/pages/Admin/api/index.ts @@ -77,6 +77,14 @@ const createFaq = (payload: CreateEditFaqRequest) => { return api.post(`${API_URL}/faqs/`, payload); }; +const editFaq = (faqId: string, payload: CreateEditFaqRequest) => { + return api.patch(`${API_URL}/faqs/${faqId}/`, payload); +}; + +const getFaqById = (faqId: string): Promise> => { + return api.get(`${API_URL}/faqs/${faqId}/`); +}; + export { getUser, editUserPrivilege, @@ -93,4 +101,6 @@ export { editFaqVisible, getFaqs, createFaq, + editFaq, + getFaqById, }; diff --git a/src/pages/Admin/hooks/query/index.ts b/src/pages/Admin/hooks/query/index.ts index 74635fb..c567530 100644 --- a/src/pages/Admin/hooks/query/index.ts +++ b/src/pages/Admin/hooks/query/index.ts @@ -12,3 +12,5 @@ export * from './useDeleteFaqMutation'; export * from './useEditFaqVisibleMutation'; export * from './useAdminFaqListQuery'; export * from './useCreateFaqMutation'; +export * from './useEditFaqMutation'; +export * from './useAdminFaqQuery'; diff --git a/src/pages/Admin/hooks/query/useAdminFaqQuery.ts b/src/pages/Admin/hooks/query/useAdminFaqQuery.ts new file mode 100644 index 0000000..efc2b6c --- /dev/null +++ b/src/pages/Admin/hooks/query/useAdminFaqQuery.ts @@ -0,0 +1,23 @@ +import { UseQueryOptions } from 'react-query'; +import { AxiosError } from 'axios'; + +import { QUERY_KEYS } from '@/constants'; +import { useSuspenseQuery } from '@/hooks/useSuspenseQuery'; + +import { getFaqById } from '../../api'; + +export const useAdminFaqQuery = ( + faqId: string, + options?: UseQueryOptions +) => { + return useSuspenseQuery( + [QUERY_KEYS.ADMIN_FAQ, faqId], + async ({ queryKey: [, faqId] }) => { + const { data } = await getFaqById(faqId); + return data; + }, + { + ...options, + } + ); +}; diff --git a/src/pages/Admin/hooks/query/useEditFaqMutation.ts b/src/pages/Admin/hooks/query/useEditFaqMutation.ts new file mode 100644 index 0000000..5d7de5a --- /dev/null +++ b/src/pages/Admin/hooks/query/useEditFaqMutation.ts @@ -0,0 +1,26 @@ +import { PATH, QUERY_KEYS } from '@/constants'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, UseMutationOptions, useQueryClient } from 'react-query'; +import { useNavigate } from 'react-router-dom'; +import { editFaq } from '../../api'; + +type useEditFaqMutationProps = { + faqId: string; + payload: CreateEditFaqRequest; +}; + +export const useEditFaqMutation = ( + options?: UseMutationOptions +) => { + const navigate = useNavigate(); + const queryClient = useQueryClient(); + + return useMutation(({ faqId, payload }) => editFaq(faqId, payload), { + ...options, + onSuccess: async () => { + await queryClient.invalidateQueries(QUERY_KEYS.ADMIN_FAQ); + alert('수정되었습니다.'); + navigate(`${PATH.ADMIN}/${PATH.ADMIN_FAQS}`); + }, + }); +}; diff --git a/src/pages/Admin/index.ts b/src/pages/Admin/index.ts index b84bcf7..b2fa35b 100644 --- a/src/pages/Admin/index.ts +++ b/src/pages/Admin/index.ts @@ -6,3 +6,4 @@ export * from './AdminNewAnnouncement'; export * from './AdminEditAnnouncement'; export * from './AdminFaqList'; export * from './AdminNewFaq'; +export * from './AdminEditFaq'; diff --git a/src/types/admin.d.ts b/src/types/admin.d.ts index 06b66fd..554fd51 100644 --- a/src/types/admin.d.ts +++ b/src/types/admin.d.ts @@ -88,3 +88,13 @@ type CreateEditFaqRequest = { answer: string; visible: boolean; }; + +type AdminFaqResponse = { + question: string; + answer: string; + visible: boolean; + id: number; + created_user: string; + created_time: string; + last_modified: string; +}; From e3d6f2594bc5389adbf970214b2895ccb488cecd Mon Sep 17 00:00:00 2001 From: joo-prog <1114nhj@naver.com> Date: Fri, 10 Feb 2023 16:59:15 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20admin=20faq=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Admin/AdminFaqList.tsx | 14 ++------------ src/pages/Admin/api/index.ts | 4 ++-- .../Admin/hooks/query/useAdminFaqListQuery.ts | 14 ++++---------- src/pages/Admin/hooks/useAdminFaqsTable.tsx | 4 ++-- 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/src/pages/Admin/AdminFaqList.tsx b/src/pages/Admin/AdminFaqList.tsx index 7284d5e..c50db96 100644 --- a/src/pages/Admin/AdminFaqList.tsx +++ b/src/pages/Admin/AdminFaqList.tsx @@ -1,13 +1,11 @@ -import { Button, Heading, Input, Table } from '@/components'; +import { Button, Heading, Table } from '@/components'; import { PAGE, PATH } from '@/constants/paths'; -import { useSearch } from '@/hooks/useSearch'; import { useNavigate } from 'react-router-dom'; import { useAdminFaqsTable } from './hooks'; export function AdminFaqList() { const navigate = useNavigate(); - const { keyword, onChange } = useSearch(); - const { column, data } = useAdminFaqsTable(keyword); + const { column, data } = useAdminFaqsTable(); return ( <> @@ -16,14 +14,6 @@ export function AdminFaqList() { {PAGE.ALL_FAQS} -
- -
diff --git a/src/pages/Admin/api/index.ts b/src/pages/Admin/api/index.ts index 0114831..f2b18e7 100644 --- a/src/pages/Admin/api/index.ts +++ b/src/pages/Admin/api/index.ts @@ -69,8 +69,8 @@ const editFaqVisible = (payload: { id: number }) => { return api.post(`${API_URL}/faqs/check/`, payload); }; -const getFaqs = (keyword: string): Promise> => { - return api.get(`${API_URL}/faqs?keyword=${keyword}`); +const getFaqs = (): Promise> => { + return api.get(`${API_URL}/faqs`); }; const createFaq = (payload: CreateEditFaqRequest) => { diff --git a/src/pages/Admin/hooks/query/useAdminFaqListQuery.ts b/src/pages/Admin/hooks/query/useAdminFaqListQuery.ts index e22b03e..528e475 100644 --- a/src/pages/Admin/hooks/query/useAdminFaqListQuery.ts +++ b/src/pages/Admin/hooks/query/useAdminFaqListQuery.ts @@ -7,18 +7,12 @@ import { useSuspenseQuery } from '@/hooks/useSuspenseQuery'; import { getFaqs } from '../../api'; export const useAdminFaqListQuery = ( - keyword: string, - options?: UseQueryOptions< - AdminFaqListResponse, - AxiosError, - AdminFaqListResponse, - [string, string] - > + options?: UseQueryOptions ) => { return useSuspenseQuery( - [QUERY_KEYS.ADMIN_ALL_FAQS, keyword], - async ({ queryKey: [, keyword] }) => { - const { data } = await getFaqs(keyword); + QUERY_KEYS.ADMIN_ALL_FAQS, + async () => { + const { data } = await getFaqs(); return data; }, { diff --git a/src/pages/Admin/hooks/useAdminFaqsTable.tsx b/src/pages/Admin/hooks/useAdminFaqsTable.tsx index a444aac..83d7bf4 100644 --- a/src/pages/Admin/hooks/useAdminFaqsTable.tsx +++ b/src/pages/Admin/hooks/useAdminFaqsTable.tsx @@ -6,7 +6,7 @@ import { useAdminFaqListQuery, useDeleteFaqMutation, useEditFaqVisibleMutation } import { formatTime } from '@/utils/time'; import { Switch } from '@/components/atom/Switch'; -export const useAdminFaqsTable = (keyword: string) => { +export const useAdminFaqsTable = () => { const navigate = useNavigate(); const { mutate: deleteFaq } = useDeleteFaqMutation(); const { mutate: editFaqVisible } = useEditFaqVisibleMutation(); @@ -42,7 +42,7 @@ export const useAdminFaqsTable = (keyword: string) => { const { data: { results }, - } = useAdminFaqListQuery(keyword); + } = useAdminFaqListQuery(); const data = results .sort(({ id: prev }, { id: next }) => next - prev) From e8dad364c0169e930af4dd73eef0607753d46e98 Mon Sep 17 00:00:00 2001 From: joo-prog <1114nhj@naver.com> Date: Fri, 10 Feb 2023 17:04:13 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20page=20name=20=EB=8C=80=EC=86=8C?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=20=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Admin/AdminEditFaq.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Admin/AdminEditFaq.tsx b/src/pages/Admin/AdminEditFaq.tsx index 63baf25..899d279 100644 --- a/src/pages/Admin/AdminEditFaq.tsx +++ b/src/pages/Admin/AdminEditFaq.tsx @@ -24,7 +24,7 @@ export function AdminEditFaq() { return (
- {PAGE.EDIT_Faq} + {PAGE.EDIT_FAQ}