Skip to content

Commit

Permalink
Merge pull request #61 from sos-sejong-opensource-software/feat-admin…
Browse files Browse the repository at this point in the history
…-faq

feat: 관리자페이지 - FAQ 목록 조회, 생성, 삭제, 수정
  • Loading branch information
joo-prog authored Feb 10, 2023
2 parents 5870054 + e8dad36 commit 56f4b48
Show file tree
Hide file tree
Showing 21 changed files with 399 additions and 12 deletions.
9 changes: 6 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import {
ClassContest,
ClassContestProblemList,
ClassStudentManagement,
AdminAllClasses,
AdminAllProblems,
AdminNewAnnouncement,
AdminEditAnnouncement,
AllProblemDetail,
Expand All @@ -29,6 +27,9 @@ import {
ProblemSubmission,
ProblemForm,
ClassEditContestList,
AdminFaqList,
AdminNewFaq,
AdminEditFaq,
} from '@/pages';
import { MainHeader } from '@/components';
import ResetPassword from './pages/User/ResetPassword';
Expand Down Expand Up @@ -79,7 +80,9 @@ export default function App() {
<Route path={PATH.ADMIN_ANNOUNCEMENTS_EDIT} element={<AdminEditAnnouncement />} />
<Route path={PATH.ADMIN_ANNOUNCEMENTS} element={<AdminAnnouncementList />} />

<Route path={PATH.ADMIN_FAQS} element={<div>FAQs</div>} />
<Route path={PATH.ADMIN_FAQS_NEW} element={<AdminNewFaq />} />
<Route path={PATH.ADMIN_FAQS_EDIT} element={<AdminEditFaq />} />
<Route path={PATH.ADMIN_FAQS} element={<AdminFaqList />} />
<Route path={PATH.ADMIN_USER_MANAGEMENT} element={<div>UserManagement</div>} />
</Route>

Expand Down
5 changes: 5 additions & 0 deletions src/constants/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
};

Expand All @@ -77,4 +79,7 @@ export const PAGE: { [key: string]: string } = {
ALL_CLASSES: '전체 수업 목록',
NEW_ANNOUNCEMENT: '공지사항 생성',
EDIT_ANNOUNCEMENT: '공지사항 수정',
ALL_FAQS: '전체 FAQ 목록',
NEW_FAQ: 'FAQ 생성',
EDIT_FAQ: 'FAQ 수정',
};
2 changes: 2 additions & 0 deletions src/constants/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
8 changes: 6 additions & 2 deletions src/pages/Admin/AdminEditAnnouncement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
};

Expand All @@ -19,7 +19,11 @@ export function AdminEditAnnouncement() {
<Heading as="h3" className="pageTitle">
{PAGE.EDIT_ANNOUNCEMENT}
</Heading>
<AdminCreateEditForm mode="announcement" data={data} onMutate={onMutate} />
<AdminCreateEditForm
mode="announcement"
data={data}
onAnnouncementMutate={onAnnouncementMutate}
/>
</div>
);
}
32 changes: 32 additions & 0 deletions src/pages/Admin/AdminEditFaq.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="container">
<Heading as="h3" className="pageTitle">
{PAGE.EDIT_FAQ}
</Heading>
<AdminCreateEditForm mode="faq" data={data} onFaqMutate={onFaqMutate} />
</div>
);
}
21 changes: 21 additions & 0 deletions src/pages/Admin/AdminFaqList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Button, Heading, Table } from '@/components';
import { PAGE, PATH } from '@/constants/paths';
import { useNavigate } from 'react-router-dom';
import { useAdminFaqsTable } from './hooks';

export function AdminFaqList() {
const navigate = useNavigate();
const { column, data } = useAdminFaqsTable();

return (
<>
<div className="flex flex-row justify-between">
<Heading as="h3" className="pageTitle">
{PAGE.ALL_FAQS}
<Button onClick={() => navigate(`${PATH.ADMIN}/${PATH.ADMIN_FAQS}/new`)}>글쓰기</Button>
</Heading>
</div>
<Table column={column} data={data} />
</>
);
}
4 changes: 2 additions & 2 deletions src/pages/Admin/AdminNewAnnouncement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};

Expand All @@ -15,7 +15,7 @@ export function AdminNewAnnouncement() {
<Heading as="h3" className="pageTitle">
{PAGE.NEW_ANNOUNCEMENT}
</Heading>
<AdminCreateEditForm mode="announcement" onMutate={onMutate} />
<AdminCreateEditForm mode="announcement" onAnnouncementMutate={onAnnouncementMutate} />
</div>
);
}
21 changes: 21 additions & 0 deletions src/pages/Admin/AdminNewFaq.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="container">
<Heading as="h3" className="pageTitle">
{PAGE.NEW_FAQ}
</Heading>
<AdminCreateEditForm mode="faq" onFaqMutate={onFaqMutate} />
</div>
);
}
30 changes: 30 additions & 0 deletions src/pages/Admin/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,30 @@ const getAnnouncementById = (
return api.get(`${API_URL}/announcements/${announcementId}/`);
};

const deleteFaq = (faqId: string) => {
return api.delete(`${API_URL}/faqs/${faqId}`);
};

const editFaqVisible = (payload: { id: number }) => {
return api.post(`${API_URL}/faqs/check/`, payload);
};

const getFaqs = (): Promise<AxiosResponse<AdminFaqListResponse>> => {
return api.get(`${API_URL}/faqs`);
};

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<AxiosResponse<AdminFaqResponse>> => {
return api.get(`${API_URL}/faqs/${faqId}/`);
};

export {
getUser,
editUserPrivilege,
Expand All @@ -73,4 +97,10 @@ export {
createAnnouncement,
editAnnouncement,
getAnnouncementById,
deleteFaq,
editFaqVisible,
getFaqs,
createFaq,
editFaq,
getFaqById,
};
32 changes: 27 additions & 5 deletions src/pages/Admin/components/AdminCreateEditForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
};

Expand All @@ -57,12 +71,20 @@ export function AdminCreateEditForm({ mode, data, onMutate }: AdminCreateEditFor
<div className="flex gap-4">
<div className="flex gap-2">
<Label>{LABEL.VISIBLE}</Label>
<Switch enabled={visible} name="visible" />
<Switch
key={`${mode}_${title}_visible_${String(visible)}`}
enabled={visible}
name="visible"
/>
</div>
{mode === 'announcement' && (
<div className="flex gap-2">
<Label>중요</Label>
<Switch enabled={important} name="important" />
<Switch
key={`${mode}_${title}_important_${String(important)}`}
enabled={important}
name="important"
/>
</div>
)}
</div>
Expand Down
1 change: 1 addition & 0 deletions src/pages/Admin/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './useSelectUserPrivilege';
export * from './useAdminAnnouncementsTable';
export * from './useAdminAllClassesTable';
export * from './useAdminAllProblemsTable';
export * from './useAdminFaqsTable';
6 changes: 6 additions & 0 deletions src/pages/Admin/hooks/query/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ export * from './useAdminAllProblemsQuery';
export * from './useCreateAnnouncementMutation';
export * from './useEditAnnouncementMutation';
export * from './useAdminAnnouncementQuery';
export * from './useDeleteFaqMutation';
export * from './useEditFaqVisibleMutation';
export * from './useAdminFaqListQuery';
export * from './useCreateFaqMutation';
export * from './useEditFaqMutation';
export * from './useAdminFaqQuery';
22 changes: 22 additions & 0 deletions src/pages/Admin/hooks/query/useAdminFaqListQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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 = (
options?: UseQueryOptions<AdminFaqListResponse, AxiosError, AdminFaqListResponse, string>
) => {
return useSuspenseQuery(
QUERY_KEYS.ADMIN_ALL_FAQS,
async () => {
const { data } = await getFaqs();
return data;
},
{
...options,
}
);
};
23 changes: 23 additions & 0 deletions src/pages/Admin/hooks/query/useAdminFaqQuery.ts
Original file line number Diff line number Diff line change
@@ -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<AdminFaqResponse, AxiosError, AdminFaqResponse, [string, string]>
) => {
return useSuspenseQuery(
[QUERY_KEYS.ADMIN_FAQ, faqId],
async ({ queryKey: [, faqId] }) => {
const { data } = await getFaqById(faqId);
return data;
},
{
...options,
}
);
};
19 changes: 19 additions & 0 deletions src/pages/Admin/hooks/query/useCreateFaqMutation.ts
Original file line number Diff line number Diff line change
@@ -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<AxiosResponse, AxiosError, CreateEditFaqRequest>
) => {
const navigate = useNavigate();

return useMutation((payload) => createFaq(payload), {
...options,
onSuccess: () => {
alert('생성되었습니다.');
navigate(`${PATH.ADMIN}/${PATH.ADMIN_FAQS}`);
},
});
};
14 changes: 14 additions & 0 deletions src/pages/Admin/hooks/query/useDeleteFaqMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { AxiosError, AxiosResponse } from 'axios';
import { useMutation, UseMutationOptions } from 'react-query';
import { deleteFaq } from '../../api';

export const useDeleteFaqMutation = (
options?: UseMutationOptions<AxiosResponse, AxiosError, string>
) => {
return useMutation((faqId) => deleteFaq(faqId), {
...options,
onSuccess: () => {
alert('삭제되었습니다.');
},
});
};
26 changes: 26 additions & 0 deletions src/pages/Admin/hooks/query/useEditFaqMutation.ts
Original file line number Diff line number Diff line change
@@ -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<AxiosResponse, AxiosError, useEditFaqMutationProps>
) => {
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}`);
},
});
};
Loading

0 comments on commit 56f4b48

Please sign in to comment.