diff --git a/apps/admin/src/apis/controller/notice/deleteNotice.ts b/apps/admin/src/apis/controller/notice/deleteNotice.ts new file mode 100644 index 00000000..9a6dc73a --- /dev/null +++ b/apps/admin/src/apis/controller/notice/deleteNotice.ts @@ -0,0 +1,7 @@ +import { $api } from '@apis'; + +const deleteNotice = () => { + return $api.useMutation('delete', '/api/admin/notice/{id}'); +}; + +export default deleteNotice; diff --git a/apps/admin/src/apis/controller/notice/index.ts b/apps/admin/src/apis/controller/notice/index.ts index fa21aee1..82972643 100644 --- a/apps/admin/src/apis/controller/notice/index.ts +++ b/apps/admin/src/apis/controller/notice/index.ts @@ -1,6 +1,7 @@ +import deleteNotice from './deleteNotice'; import getNotice from './getNotice'; import getNoticeAvailable from './getNoticeAvailable'; import postNotice from './postNotice'; import putNotice from './putNotice'; -export { getNotice, getNoticeAvailable, postNotice, putNotice }; \ No newline at end of file +export { deleteNotice, getNotice, getNoticeAvailable, postNotice, putNotice }; diff --git a/apps/admin/src/components/common/Modals/NoticeListModal.tsx b/apps/admin/src/components/common/Modals/NoticeListModal.tsx index 357c236a..51f45f4d 100644 --- a/apps/admin/src/components/common/Modals/NoticeListModal.tsx +++ b/apps/admin/src/components/common/Modals/NoticeListModal.tsx @@ -1,8 +1,10 @@ -import { Button, IconButton } from '@components'; -import { getNotice } from '@apis'; +import { Button, IconButton, Modal, TwoButtonModalTemplate } from '@components'; +import { deleteNotice, getNotice } from '@apis'; import { components } from '@schema'; import dayjs from 'dayjs'; import { useState } from 'react'; +import { useInvalidate, useModal } from '@hooks'; +import { Slide, toast, ToastContainer } from 'react-toastify'; interface Props { selectedStudent: components['schemas']['StudentResp'] | null; @@ -13,6 +15,9 @@ const NoticeListModal = ({ selectedStudent, onClose }: Props) => { const { data: noticeData } = getNotice( selectedStudent ? { studentId: selectedStudent.id } : { studentId: 0 } ); + const { mutate: mutateDeleteNotice } = deleteNotice(); + const { invalidateAll } = useInvalidate(); + const { isOpen, openModal, closeModal } = useModal(); const notices = noticeData?.data ?? []; @@ -20,8 +25,34 @@ const NoticeListModal = ({ selectedStudent, onClose }: Props) => { null ); + const [selectedNoticeToDelete, setSelectedNoticeToDelete] = useState< + components['schemas']['NoticeResp'] | null + >(null); + const handleDeleteNotice = (noticeId: number) => { - alert(`(${noticeId}) 공지 삭제 API가 없어용`); + mutateDeleteNotice( + { + params: { + path: { + id: noticeId, + }, + }, + }, + { + onSuccess: () => { + if (selectedNoticeToDelete?.id === selectedNotice?.id) { + setSelectedNotice(null); + } + setSelectedNoticeToDelete(null); + invalidateAll(); + closeModal(); + toast.success('공지가 삭제되었습니다.'); + }, + onError: (error: unknown) => { + toast.error((error as { message?: string })?.message || '공지 삭제에 실패했습니다.'); + }, + } + ); }; if (!selectedStudent) { @@ -41,99 +72,126 @@ const NoticeListModal = ({ selectedStudent, onClose }: Props) => { } return ( -
-

공지 목록

+ <> + +
+

공지 목록

- {/* 메인 콘텐츠 */} -
- {notices.length === 0 ? ( -
-

등록된 공지가 없습니다

-
- ) : ( -
-
-
-
- {notices.map((notice) => ( -
setSelectedNotice(notice)}> -
-
-
-
- - {dayjs(notice.startAt).format('YY.MM.DD')} ~{' '} - {dayjs(notice.endAt).format('YY.MM.DD')} - + {/* 메인 콘텐츠 */} +
+ {notices.length === 0 ? ( +
+

등록된 공지가 없습니다

+
+ ) : ( +
+
+
+
+ {notices.map((notice) => ( +
setSelectedNotice(notice)}> +
+
+
+
+ + {dayjs(notice.startAt).format('YY.MM.DD')} ~{' '} + {dayjs(notice.endAt).format('YY.MM.DD')} + +
+

+ {notice.content} +

+
+
+ { + e.stopPropagation(); + setSelectedNoticeToDelete(notice); + openModal(); + }} + />
-

- {notice.content} -

-
-
- { - e.stopPropagation(); - handleDeleteNotice(notice.id); - }} - />
-
- ))} + ))} +
-
- {/* 공지 내용 */} -
-
- {selectedNotice ? ( -
-
-
- 공지 기간 -
-

- {dayjs(selectedNotice.startAt).format('YYYY년 M월 D일')} ~{' '} - {dayjs(selectedNotice.endAt).format('YYYY년 M월 D일')} -

-
- 공지 내용 + {/* 공지 내용 */} +
+
+ {selectedNotice ? ( +
+
+
+ 공지 기간 +
+

+ {dayjs(selectedNotice.startAt).format('YYYY년 M월 D일')} ~{' '} + {dayjs(selectedNotice.endAt).format('YYYY년 M월 D일')} +

+
+ 공지 내용 +
+

+ {selectedNotice.content} +

-

- {selectedNotice.content} +

+ ) : ( +
+

+ 자세히 볼 공지를 좌측에서 선택해주세요

-
- ) : ( -
-

- 자세히 볼 공지를 좌측에서 선택해주세요 -

-
- )} + )} +
-
- )} -
+ )} +
- {/* 푸터 */} -
- + {/* 푸터 */} +
+ +
-
+ + handleDeleteNotice(selectedNoticeToDelete?.id ?? -1)} + /> + + ); }; diff --git a/apps/admin/src/types/api/schema.d.ts b/apps/admin/src/types/api/schema.d.ts index 323fa315..f129263e 100644 --- a/apps/admin/src/types/api/schema.d.ts +++ b/apps/admin/src/types/api/schema.d.ts @@ -33,7 +33,8 @@ export interface paths { /** 수정 */ put: operations['update']; post?: never; - delete?: never; + /** 삭제 */ + delete: operations['delete']; options?: never; head?: never; patch?: never; @@ -52,7 +53,7 @@ export interface paths { put: operations['update_1']; post?: never; /** Q&A 삭제 */ - delete: operations['delete']; + delete: operations['delete_1']; options?: never; head?: never; patch?: never; @@ -177,24 +178,7 @@ export interface paths { put: operations['update_4']; post?: never; /** 삭제 */ - delete: operations['delete_1']; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/admin/problem-set/{id}/status': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - /** 수정 */ - put: operations['update_5']; - post?: never; - delete?: never; + delete: operations['delete_2']; options?: never; head?: never; patch?: never; @@ -226,10 +210,10 @@ export interface paths { }; get?: never; /** 수정 */ - put: operations['update_6']; + put: operations['update_5']; post?: never; /** 삭제 */ - delete: operations['delete_2']; + delete: operations['delete_3']; options?: never; head?: never; patch?: never; @@ -244,9 +228,10 @@ export interface paths { }; get?: never; /** 수정 */ - put: operations['update_7']; + put: operations['update_6']; post?: never; - delete?: never; + /** 삭제 */ + delete: operations['delete_4']; options?: never; head?: never; patch?: never; @@ -261,10 +246,10 @@ export interface paths { }; get?: never; /** 개념태그 수정 */ - put: operations['update_8']; + put: operations['update_7']; post?: never; /** 개념태그 삭제 */ - delete: operations['delete_3']; + delete: operations['delete_5']; options?: never; head?: never; patch?: never; @@ -1092,7 +1077,7 @@ export interface paths { put?: never; post?: never; /** 삭제 */ - delete: operations['delete_4']; + delete: operations['delete_6']; options?: never; head?: never; patch?: never; @@ -1160,7 +1145,7 @@ export interface paths { put?: never; post?: never; /** 삭제 */ - delete: operations['delete_5']; + delete: operations['delete_7']; options?: never; head?: never; patch?: never; @@ -1177,7 +1162,7 @@ export interface paths { put?: never; post?: never; /** 선생님 삭제 */ - delete: operations['delete_6']; + delete: operations['delete_8']; options?: never; head?: never; patch?: never; @@ -1531,10 +1516,6 @@ export interface components { firstProblem: components['schemas']['ProblemMetaResp']; problems: components['schemas']['ProblemSetItemResp'][]; }; - ProblemSetUpdateStatusReq: { - /** @enum {string} */ - status: 'CONFIRMED' | 'DOING'; - }; Request: { /** Format: int32 */ year: number; @@ -2083,6 +2064,26 @@ export interface operations { }; }; }; + delete: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; getById: { parameters: { query?: never; @@ -2131,7 +2132,7 @@ export interface operations { }; }; }; - delete: { + delete_1: { parameters: { query?: never; header?: never; @@ -2433,7 +2434,7 @@ export interface operations { }; }; }; - delete_1: { + delete_2: { parameters: { query?: never; header?: never; @@ -2453,32 +2454,6 @@ export interface operations { }; }; }; - update_5: { - parameters: { - query?: never; - header?: never; - path: { - id: number; - }; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': components['schemas']['ProblemSetUpdateStatusReq']; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - '*/*': components['schemas']['ProblemSetResp']; - }; - }; - }; - }; toggleStatus: { parameters: { query?: never; @@ -2501,7 +2476,7 @@ export interface operations { }; }; }; - update_6: { + update_5: { parameters: { query?: never; header?: never; @@ -2527,7 +2502,7 @@ export interface operations { }; }; }; - delete_2: { + delete_3: { parameters: { query?: never; header?: never; @@ -2547,7 +2522,7 @@ export interface operations { }; }; }; - update_7: { + update_6: { parameters: { query?: never; header?: never; @@ -2573,7 +2548,27 @@ export interface operations { }; }; }; - update_8: { + delete_4: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + update_7: { parameters: { query?: never; header?: never; @@ -2599,7 +2594,7 @@ export interface operations { }; }; }; - delete_3: { + delete_5: { parameters: { query?: never; header?: never; @@ -3989,7 +3984,7 @@ export interface operations { }; }; }; - delete_4: { + delete_6: { parameters: { query?: never; header?: never; @@ -4069,7 +4064,7 @@ export interface operations { }; }; }; - delete_5: { + delete_7: { parameters: { query?: never; header?: never; @@ -4089,7 +4084,7 @@ export interface operations { }; }; }; - delete_6: { + delete_8: { parameters: { query?: never; header?: never;