Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/graduate/src/pages/admin/all/constants/excel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ export const TABLE_HEADER = {

export type TableHeader = (typeof TABLE_HEADER)[keyof typeof TABLE_HEADER];
export type SubmissionStage = '신청서' | '중간보고서' | '최종보고서';

22 changes: 17 additions & 5 deletions apps/graduate/src/pages/admin/notice/model/notices.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
export interface NoticeItem {
export type NoticeItem = {
noticeId: number;
category: string;
title: string;
author: string;
description: string;
content?: string;
views: number;
hasAttachment: boolean;
isPinned: boolean;
createdAt: string;
updatedAt?: string;
}
};

export type NoticeDetailItem = {
noticeId: number;
category: string;
title: string;
author: string;
content: string;
isPinned: boolean;
file?: {
physicalPath: string;
};
createdAt: string;
};

export type NoticeFormItem = {
title: string;
content: string;
isPinned: boolean;
uploadedFiles?: string[];
category: 'GRADUATION';
fileId?: number;
};
116 changes: 100 additions & 16 deletions apps/graduate/src/pages/admin/notice/ui/NoticeAdminCreatePage.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { useNavigate } from '@tanstack/react-router';
import { Button, Checkbox, Divider, Input, Upload } from 'antd';
import type { UploadProps } from 'antd';
import { useEffect } from 'react';
import type { UploadFile, UploadProps } from 'antd';
import { useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';

import { uploadNoticeFile } from '~/shared/api/file';
import { TextEditor } from '~/shared/components';
import { ROUTE } from '~/shared/constants';
import { API_ADMIN_URL, ROUTE } from '~/shared/constants';
import { useNoticeDetail, useToast } from '~/shared/hooks';

import { useCreateNotice, useUpdateNotice, useDeleteNotice } from '../hooks';
import type { NoticeFormItem } from '../model/notices';
import * as style from '../styles/NoticeAdminCreatePage.css';

Expand All @@ -22,9 +24,15 @@ export default function NoticeAdminCreatePage({
const isEditMode = !!noticeId;
const { toast, confirm } = useToast();

const [fileList, setFileList] = useState<UploadFile[]>([]);
const [uploadedFileId, setUploadedFileId] = useState<number | undefined>();

const { data: notice, isPending } = useNoticeDetail(noticeId ?? 0);
const { mutate: createNotice } = useCreateNotice();
const { mutate: updateNotice } = useUpdateNotice(noticeId ?? 0);
const { mutate: deleteNotice } = useDeleteNotice();

const { createdAt, updatedAt } = notice ?? {};
const { createdAt } = notice ?? {};

const {
control,
Expand All @@ -36,7 +44,7 @@ export default function NoticeAdminCreatePage({
title: '',
content: '',
isPinned: false,
uploadedFiles: [],
category: 'GRADUATION',
},
});

Expand All @@ -46,33 +54,110 @@ export default function NoticeAdminCreatePage({
title: notice.title ?? '',
content: notice.content ?? '',
isPinned: notice.isPinned ?? false,
uploadedFiles: [],
category: 'GRADUATION',
});

if (notice.file) {
const fileName =
notice.file.physicalPath.split('/').pop() || '첨부파일';
setFileList([
{
uid: '-1',
name: fileName,
status: 'done',
url: `${API_ADMIN_URL}${notice.file.physicalPath}`,
},
]);
}
}
}, [notice, isEditMode, reset]);

const uploadProps: UploadProps = {
name: 'file',
multiple: true,
fileList: fileList,
maxCount: 1,
beforeUpload: file => {
toast.info(`${file.name} 파일이 선택되었습니다.`);
return false;
},
customRequest: async ({ file, onSuccess, onError }) => {
try {
const response = await uploadNoticeFile(file as File);
const { id } = response.data;

setUploadedFileId(id);

toast.success(`${(file as File).name} 파일이 업로드되었습니다.`);
onSuccess?.(response.data);
} catch (error) {
toast.error('파일 업로드에 실패했습니다.');
onError?.(error as Error);
}
},
onChange: ({ fileList: newFileList }) => {
setFileList(newFileList);
},
onRemove: () => {
setUploadedFileId(undefined);
},
};

const onSubmit = () => {
toast.success(
isEditMode ? '공지사항이 수정되었습니다.' : '공지사항이 작성되었습니다.',
);
handleGoBack();
const onSubmit = (data: NoticeFormItem) => {
if (isEditMode) {
updateNotice(
{
title: data.title,
content: data.content,
isPinned: data.isPinned,
fileId: uploadedFileId,
},
{
onSuccess: () => {
toast.success('공지사항이 수정되었습니다.');
handleGoBack();
},
onError: () => {
toast.error('공지사항 수정에 실패했습니다.');
},
},
);
} else {
createNotice(
{
title: data.title,
content: data.content,
isPinned: data.isPinned,
category: data.category,
fileId: uploadedFileId,
},
{
onSuccess: () => {
toast.success('공지사항이 작성되었습니다.');
handleGoBack();
},
onError: () => {
toast.error('공지사항 작성에 실패했습니다.');
},
},
);
}
};

const handleDelete = () => {
if (!noticeId) return;

confirm({
title: '정말 삭제하시겠습니까?',
onOk: () => {
toast.success('공지사항이 삭제되었습니다.');
handleGoBack();
deleteNotice(noticeId, {
onSuccess: () => {
toast.success('공지사항이 삭제되었습니다.');
handleGoBack();
},
onError: () => {
toast.error('공지사항 삭제에 실패했습니다.');
},
});
},
});
};
Expand Down Expand Up @@ -112,7 +197,6 @@ export default function NoticeAdminCreatePage({
{isEditMode && (
<div className={style.metaInfo}>
<div className={style.metaItem}>작성일: {createdAt}</div>
<div className={style.metaItem}>수정일: {updatedAt}</div>
</div>
)}

Expand Down Expand Up @@ -192,7 +276,7 @@ export default function NoticeAdminCreatePage({
<div className={style.actionSection}>
<div className={style.leftActions}>
{isEditMode && (
<Button onClick={handleDelete} size='large'>
<Button onClick={handleDelete} size='large' danger>
삭제
</Button>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default function NoticeAdminPage() {
page: currentPage - 1,
size: 10,
keywords: searchText ? [searchText] : undefined,
category: 'GRADUATION',
});

const noticeList = noticeResponse?.contents || [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export const thesisColumns: ReadonlyArray<Column<ThesisRow>> = [
{ key: 'advisor', header: '지도교수', width: 50, cell: r => r.advisor },
{ key: 'gradTerm', header: '졸업 년도', width: 90, cell: r => r.gradTerm },
{ key: 'status', header: '상태', width: 130, cell: r => r.status },
{ key: 'submissionStatus', header: '제출 상태', width: 70, cell: r => r.submissionStatus },
{
key: 'submissionStatus',
header: '제출 상태',
width: 70,
cell: r => r.submissionStatus,
},
{ key: 'approved', header: '승인 여부', width: 70, cell: r => r.approved },
] as const;
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ export default function ThesisAdminPage() {
await approveGraduationUsers(selectedIds);
toast.success(APPROVE_SUCCESS);
} catch (error) {
toast.error(
error instanceof Error ? error.message : APPROVE_FAILED,
);
toast.error(error instanceof Error ? error.message : APPROVE_FAILED);
}
},
});
Expand Down
Loading
Loading