From 8f4060d414c4e7b4250b0588289c2079c92d07e3 Mon Sep 17 00:00:00 2001 From: hynseok Date: Thu, 29 Feb 2024 23:34:16 +0900 Subject: [PATCH 01/24] =?UTF-8?q?chore:=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.production | 1 + Dockerfile | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 .env.production diff --git a/.env.production b/.env.production new file mode 100644 index 00000000..443ea5f7 --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +NEXT_PUBLIC_API_ENDPOINT = https://qa.ice.scg.skku.ac.kr/v1/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index cb34f829..20232b93 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ COPY . . RUN yarn install - RUN yarn build CMD ["yarn", "start"] \ No newline at end of file From 75d6fb2321341b9d1bddcf1dd497668f00d6619d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=84=9D?= Date: Thu, 7 Mar 2024 18:33:05 +0900 Subject: [PATCH 02/24] Update qa.yml --- .github/workflows/qa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 0f1691dd..10a05644 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -34,7 +34,7 @@ jobs: - name: Docker 준비(3/4) - buildx 설정 uses: docker/setup-buildx-action@v3 - name: Docker 준비(4/4) - 레지스트리 로그인 - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ secrets.HARBOR_REGISTRY }} username: ${{ secrets.HARBOR_USERNAME }} From 186ee9a82eb81da159651e534ee7f8c85618940c Mon Sep 17 00:00:00 2001 From: hynseok Date: Thu, 18 Apr 2024 16:16:15 +0900 Subject: [PATCH 03/24] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=95=88=EB=82=B4=EB=AC=B8=EA=B5=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/pages/LoginForm/LoginForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/pages/LoginForm/LoginForm.tsx b/src/components/pages/LoginForm/LoginForm.tsx index 55dfa627..2852a284 100644 --- a/src/components/pages/LoginForm/LoginForm.tsx +++ b/src/components/pages/LoginForm/LoginForm.tsx @@ -114,7 +114,7 @@ function LoginForm() { {type === "student" && "아이디는 학번, 비밀번호는 생년월일입니다."} - {type === "professor" && "아이디와 비밀번호는 내선번호입니다."} + {type === "professor" && "아이디는 킹고아이디, 비밀번호는 생년월일입니다."} {type === "admin" && "관리자 계정은 행정실에 문의해주세요."} From 8e6247eb7647964282f4c20a43398239b919a3e4 Mon Sep 17 00:00:00 2001 From: hynseok Date: Tue, 23 Apr 2024 16:55:12 +0900 Subject: [PATCH 04/24] =?UTF-8?q?chore:=20=EC=84=9C=EB=AA=85=20=EB=AF=B8?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=84=A4=EB=AA=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/prof/final/[id]/page.tsx | 5 ++++- src/app/prof/review/[id]/page.tsx | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/prof/final/[id]/page.tsx b/src/app/prof/final/[id]/page.tsx index d674c0e6..1730fb53 100644 --- a/src/app/prof/final/[id]/page.tsx +++ b/src/app/prof/final/[id]/page.tsx @@ -44,7 +44,10 @@ export default async function ProfessorFinalPage({ return ( <> - + {!within && ( diff --git a/src/app/prof/review/[id]/page.tsx b/src/app/prof/review/[id]/page.tsx index 3a6b4bd6..52caf8c4 100644 --- a/src/app/prof/review/[id]/page.tsx +++ b/src/app/prof/review/[id]/page.tsx @@ -44,7 +44,10 @@ export default async function ProfessorReviewPage({ return ( <> - + {!within && ( From 8ba0384acef6a6219543847b5d878a35f41d30b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=84=9D?= Date: Mon, 29 Apr 2024 17:47:23 +0900 Subject: [PATCH 05/24] =?UTF-8?q?qa=20=EB=B0=B0=ED=8F=AC=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=EB=A8=B8=EC=A7=80=ED=95=A9=EB=8B=88?= =?UTF-8?q?=EB=8B=A4.=20(#137)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(pages): QA 오류 수정 * feat(pages): 관리자가 수정지시사항 확인 여부를 수정할 수 있도록 변경 * fix: 무한 요청 오류 수정 * fix: showNotificationSuccess 위치 변경 * fix: (chore) issn 옵셔널로 변경후 undefined여서 생기는 오류 수정 * fix: 학생등록 폼 논문제목/연락처/이메일 옵셔널 변경 및 학과로 필터링 제거 * fix: 교수 등록 폼 수정 및 학생/교수 로그인하기 기능 오류 수정 디벨롭 브랜치와 머지, #118 브랜치에서 이어서 작업 (메인에서 작업해서..ㅠㅠ) * fix: 본심 전환 모달 오류 수정 * chore: 포맷팅 * fix: 심사결과페이지 심사 중에도 확인 가능하도록 수정 * fix: 논문투고 페이지 예심/본심 뱃지 추가 * fix: 논문투고 페이지 일정 예심/본심에 따라 불러오도록 수정 * fix: 학생 심사 결과 페이지 본심 정보도 불러오도록 수정 * fix: AchievementForm isAdmin 수정 * fix: 논문정보 수정 api 스펠링 변경 저희쪽에서 변경하는게 빠를 것 같아서 수정하였습니다. * fix: 심사 정보 수정 심사의견 및 파일 업로드가능하도록 수정 * fix: 심사의견/심사의견파일 둘중 하나만 선택하도록 수정 * feat: 교수/학생 일괄 삭제 기능 추가 * feat: main workflow 추가 * chore: 주석달기 * chore: 코드 중복 제거 * fix: 테이블 헤더 수정 * fix: 로그인 안내문구 수정 * chore: 서명 미업로드 설명 추가 --------- Co-authored-by: lhwdev --- .github/workflows/main.yml | 83 +++++++ .github/workflows/qa.yml | 1 - src/api/_types/reviews.ts | 2 +- src/api/apiRoute.ts | 4 +- .../results/[thesisId]/AdminReviewContent.tsx | 93 ++++++-- .../[thesisId]/AdminReviewListContent.tsx | 86 +++++-- .../prof/final/[id]/ProfessorFinalForm.tsx | 7 +- src/app/prof/final/[id]/page.tsx | 5 +- .../prof/review/[id]/ProfessorReviewForm.tsx | 7 +- src/app/prof/review/[id]/page.tsx | 5 +- .../prof/revision/[id]/RevisionCheckForm.tsx | 10 +- src/app/student/achievement/[id]/page.tsx | 2 +- src/app/student/result/page.tsx | 40 ++-- src/app/student/write/page.tsx | 13 +- .../rows/CommentTypeRow/CommentTypeRow.tsx | 23 ++ .../rows/FileUploadRow/FileUploadRow.tsx | 3 + src/components/common/rows/index.ts | 1 + .../AdminExcelRegister/AdminExcelRegister.tsx | 5 +- .../pages/AdminProfForm/AdminProfForm.tsx | 18 +- .../AdminStudentForm/AdminStudentForm.tsx | 35 ++- .../_hooks/useReviewersAssign.tsx | 10 +- .../_sections/AssignReviewerSection.tsx | 213 +++++++----------- .../_sections/BasicInfoSection.tsx | 27 ++- .../_sections/MainRegisterModal.tsx | 18 +- .../_sections/ThesisInfoSection.tsx | 39 ++-- .../_types/AdminStudentForm.ts | 1 - src/components/pages/LoginForm/LoginForm.tsx | 2 +- .../achievement/AchievementEditSection.tsx | 10 +- .../pages/lists/_constants/table.ts | 2 +- .../AchievementListSection.tsx | 2 +- .../ProfessorListSection.tsx | 49 +++- .../StudentListSection/StudentListSection.tsx | 49 +++- .../pages/review/Review/FinalReview.tsx | 19 +- .../pages/review/Review/ProfessorReview.tsx | 15 +- .../pages/review/Review/ReviewList.tsx | 58 +++-- .../review/ThesisInfo/AdminThesisInfo.tsx | 9 + .../PaperSubmissionForm.tsx | 17 +- 37 files changed, 680 insertions(+), 303 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 src/components/common/rows/CommentTypeRow/CommentTypeRow.tsx diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..f3625743 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,83 @@ +name: Docker Image CI + +on: + push: + branches: ["main"] +env: + dockerimage_tag: ${{ github.sha }} + dockerimage_name: harbor.k8s.scg.skku.ac.kr/library/ice-gs-thesis-fe +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + steps: + - name: --------------- Code Repo --------------- + run: echo "Code Repo" + - name: Code Repo 불러오기 + uses: actions/checkout@v4 + - name: Docker 준비(1/4) - 메타데이터 생성 + id: meta + uses: docker/metadata-action@v5.5.1 + with: + images: | + ${{ env.dockerimage_name }} + tags: | + ${{ env.dockerimage_tag }} + latest + flavor: | + latest=true + - name: Docker 준비(2/4) - QEMU 설정 + uses: docker/setup-qemu-action@v3 + - name: Docker 준비(3/4) - buildx 설정 + uses: docker/setup-buildx-action@v3 + - name: Docker 준비(4/4) - 레지스트리 로그인 + uses: docker/login-action@v2 + with: + registry: ${{ secrets.HARBOR_REGISTRY }} + username: ${{ secrets.HARBOR_USERNAME }} + password: ${{ secrets.HARBOR_PASSWORD }} + - name: env 파일 생성 + run: | + echo NEXT_PUBLIC_API_ENDPOINT=${{secrets.API_ENDPOINT_PROD}} >> .env + cat .env + - name: Docker 이미지 빌드+푸시 + id: build-and-push + uses: docker/build-push-action@v5.1.0 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + provenance: false + - name: --------------- Config Repo --------------- + run: echo "[Config Repo]" + - name: Config Repo 불러오기 + uses: actions/checkout@v4 + with: + repository: SystemConsultantGroup/ice-grad-thesis-config + ref: main + token: ${{ secrets.ACTION_TOKEN }} + path: ice-grad-thesis-config + - name: Kustomize 준비 + uses: imranismail/setup-kustomize@v2.0.0 + - name: Config Repo 이미지 값 업데이트 (Kustomize) + run: | + cd ice-grad-thesis-config/overlays/prod/fe/ + kustomize edit set image ${{ env.dockerimage_name }}:${{ env.dockerimage_tag }} + cat kustomization.yaml + - name: Config Repo 변경사항 푸시 + run: | + cd ice-grad-thesis-config + git config --global user.email "wefwef12e@naver.com" + git config --global user.name "hynseok" + git commit -am "Update image tag" + git push -u origin main + - name: --------------- Clean Up --------------- + run: echo "Clean Up" diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 10a05644..6754077c 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -42,7 +42,6 @@ jobs: - name: env 파일 생성 run: | echo NEXT_PUBLIC_API_ENDPOINT=${{secrets.API_ENDPOINT}} >> .env - echo NEXT_PUBLIC_REVIEW_API_ENDPOINT=${{secrets.REVIEW_API_ENDPOINT}} >> .env cat .env - name: Docker 이미지 빌드+푸시 id: build-and-push diff --git a/src/api/_types/reviews.ts b/src/api/_types/reviews.ts index 09c49b51..52f43ef8 100644 --- a/src/api/_types/reviews.ts +++ b/src/api/_types/reviews.ts @@ -76,7 +76,7 @@ export interface FinalReviewResponse extends CommonApiResponse { export interface UpdateReviewRequestBody { contentStatus: Status; presentationStatus?: Status | null; - comment: string; + comment?: string; fileUUID?: string; } diff --git a/src/api/apiRoute.ts b/src/api/apiRoute.ts index d426145c..ee30445c 100644 --- a/src/api/apiRoute.ts +++ b/src/api/apiRoute.ts @@ -56,6 +56,7 @@ export const API_ROUTES = { `/students/${studentId}/headReviewer/${reviewerId}`, // PUT: 학생 심사위원장 배정 deleteReviewer: (studentId: ApiId, reviewerId: ApiId) => `/students/${studentId}/reviewers/${reviewerId}`, // PUT: 학생 심사위원 배정 취소 + delete: () => "/students", // DELETE: 학생 삭제 }, review: { // 학생 본인의 논문 조회 @@ -90,7 +91,8 @@ export const API_ROUTES = { }, }, thesis: { - put: (thesisId: ApiId) => `/thesis/${thesisId}`, + // 백엔드 api 경로 오타로 theses로 설정 + put: (thesisId: ApiId) => `/theses/${thesisId}`, }, achievement: { get: (achievementId?: ApiId) => `/achievements/${achievementId ?? ""}`, diff --git a/src/app/admin/results/[thesisId]/AdminReviewContent.tsx b/src/app/admin/results/[thesisId]/AdminReviewContent.tsx index 3a27fd63..093e3171 100644 --- a/src/app/admin/results/[thesisId]/AdminReviewContent.tsx +++ b/src/app/admin/results/[thesisId]/AdminReviewContent.tsx @@ -1,5 +1,7 @@ "use client"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; import { ClientAxios } from "@/api/ClientAxios"; import { Status } from "@/api/_types/common"; import { AdminReviewResponse, ThesisReview, UpdateReviewRequestBody } from "@/api/_types/reviews"; @@ -8,11 +10,19 @@ import { API_ROUTES } from "@/api/apiRoute"; import { ApiDownloadButton } from "@/components/common/Buttons"; import { showNotificationSuccess } from "@/components/common/Notifications"; import SectionTitle from "@/components/common/SectionTitle"; -import { BasicRow, ButtonRow, RowGroup, TitleRow } from "@/components/common/rows"; +import { + BasicRow, + ButtonRow, + CommentTypeRow, + FileUploadRow, + RowGroup, + TextAreaRow, + TitleRow, +} from "@/components/common/rows"; import { AdminReviewList } from "@/components/pages/review/Review/ReviewList"; import { StatusButtons } from "@/components/pages/review/Review/StatusButtons"; import { Badge, Button, Modal, Space, Stack } from "@mantine/core"; -import { useState } from "react"; +import { uploadFile } from "@/api/_utils/uploadFile"; export function AdminReviewListContent({ data }: { data: AdminReviewResponse }) { const [open, setOpen] = useState(false); @@ -33,7 +43,7 @@ export function AdminReviewListContent({ data }: { data: AdminReviewResponse }) opened={!!open} onClose={() => setOpen(false)} centered - size="lg" + size="xl" padding="lg" radius="lg" withCloseButton={false} @@ -107,7 +117,7 @@ export function ReviewReportAdminEditable({ opened={!!open} onClose={() => setOpen(false)} centered - size="lg" + size="xl" padding="lg" radius="lg" withCloseButton={false} @@ -129,6 +139,15 @@ function ModalContent({ open, setOpen, data, current }: ModalProps) { const [loading, setLoading] = useState(false); const [thesis, setThesis] = useState(current.contentStatus); const [presentation, setPresentation] = useState(current.presentationStatus); + const [comment, setComment] = useState(current.comment); + const [reviewFile, setReviewFile] = useState(); + const [commentType, setCommentType] = useState(); + + const handleCommentChange = (event: React.ChangeEvent) => { + setComment(event.currentTarget.value); + }; + + const router = useRouter(); return (
setLoading(false)); + let fileUUID; + if (reviewFile) { + fileUUID = (await uploadFile(reviewFile)).uuid; + } else if (current.file) { + fileUUID = current.file.uuid ?? undefined; + } + await ClientAxios.put( current.isFinal ? API_ROUTES.review.final.put(current.id) - : API_ROUTES.review.put(current.id), + : data.stage === "REVISION" + ? API_ROUTES.review.revision.put(current.id) + : API_ROUTES.review.put(current.id), { - comment: current.comment, + ...(commentType === "심사 의견" ? { comment } : {}), contentStatus: thesis, - presentationStatus: presentation, + ...(current.isFinal ? {} : { presentationStatus: presentation }), + ...(commentType === "심사 의견 파일" ? { fileUUID } : {}), } satisfies UpdateReviewRequestBody, { baseURL: process.env.NEXT_PUBLIC_REVIEW_API_ENDPOINT } ); showNotificationSuccess({ message: current.isFinal - ? "최종심사 결과를 수정했습니다." - : `${current.reviewer.name} 교수의 심사를 수정했습니다.`, + ? "최종심사 결과를 수정했습니다." // NOTE: 심사위원장은 수정지시사항 확인을 안한다고 가정 + : `${current.reviewer.name} 교수의 ${ + data.stage === "REVISION" ? "확인여부를" : "심사를" + } 수정했습니다.`, }); + setOpen(false); + router.refresh(); })(); }} > @@ -177,17 +210,21 @@ function ModalContent({ open, setOpen, data, current }: ModalProps) { {current.reviewer.name} - - - - + {data.stage === "REVISION" ? ( + + + + ) : ( + + + + )} - {!current.isFinal && data.stage === "MAIN" ? ( @@ -199,7 +236,25 @@ function ModalContent({ open, setOpen, data, current }: ModalProps) { ) : null} - + {data.stage !== "REVISION" && ( + <> + + + + setReviewFile(file)} + disabled={commentType !== "심사 의견 파일"} + /> + + + )} diff --git a/src/app/admin/reviews/[thesisId]/AdminReviewListContent.tsx b/src/app/admin/reviews/[thesisId]/AdminReviewListContent.tsx index 34975f16..2644ac30 100644 --- a/src/app/admin/reviews/[thesisId]/AdminReviewListContent.tsx +++ b/src/app/admin/reviews/[thesisId]/AdminReviewListContent.tsx @@ -1,16 +1,26 @@ "use client"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; import { ClientAxios } from "@/api/ClientAxios"; import { AdminReviewResponse, ThesisReview, UpdateReviewRequestBody } from "@/api/_types/reviews"; import { transactionTask } from "@/api/_utils/task"; import { API_ROUTES } from "@/api/apiRoute"; import { showNotificationSuccess } from "@/components/common/Notifications"; import SectionTitle from "@/components/common/SectionTitle"; -import { BasicRow, ButtonRow, RowGroup, TitleRow } from "@/components/common/rows"; +import { + BasicRow, + ButtonRow, + CommentTypeRow, + FileUploadRow, + RowGroup, + TextAreaRow, + TitleRow, +} from "@/components/common/rows"; import { AdminReviewList } from "@/components/pages/review/Review/ReviewList"; import { StatusButtons } from "@/components/pages/review/Review/StatusButtons"; import { Badge, Button, Modal, Space, Stack } from "@mantine/core"; -import { useState } from "react"; +import { uploadFile } from "@/api/_utils/uploadFile"; export function AdminReviewListContent({ data }: { data: AdminReviewResponse }) { const [open, setOpen] = useState(false); @@ -31,7 +41,7 @@ export function AdminReviewListContent({ data }: { data: AdminReviewResponse }) opened={!!open} onClose={() => setOpen(false)} centered - size="lg" + size="xl" padding="lg" radius="lg" withCloseButton={false} @@ -61,6 +71,15 @@ function ModalContent({ open, setOpen, data, current }: ModalProps) { const [loading, setLoading] = useState(false); const [thesis, setThesis] = useState(current.contentStatus); const [presentation, setPresentation] = useState(current.presentationStatus); + const [comment, setComment] = useState(current.comment); + const [reviewFile, setReviewFile] = useState(); + const [commentType, setCommentType] = useState(); + + const handleCommentChange = (event: React.ChangeEvent) => { + setComment(event.currentTarget.value); + }; + + const router = useRouter(); return ( setLoading(false)); + let fileUUID; + if (reviewFile) { + fileUUID = (await uploadFile(reviewFile)).uuid; + } else if (current.file) { + fileUUID = current.file.uuid ?? undefined; + } + await ClientAxios.put( - API_ROUTES.review.put(current.id), + data.stage === "REVISION" + ? API_ROUTES.review.revision.put(current.id) + : API_ROUTES.review.put(current.id), { - comment: current.comment, + ...(commentType === "심사 의견" ? { comment } : {}), contentStatus: thesis, presentationStatus: presentation, + ...(commentType === "심사 의견 파일" ? { fileUUID } : {}), } satisfies UpdateReviewRequestBody, { baseURL: process.env.NEXT_PUBLIC_REVIEW_API_ENDPOINT } ); showNotificationSuccess({ - message: `${current.reviewer.name} 교수의 심사를 수정했습니다.`, + message: `${current.reviewer.name} 교수의 ${ + data.stage === "REVISION" ? "확인여부를" : "심사를" + } 수정했습니다.`, }); + setOpen(false); + router.refresh(); })(); }} > @@ -107,15 +140,20 @@ function ModalContent({ open, setOpen, data, current }: ModalProps) { - - - + {data.stage === "REVISION" ? ( + + + + ) : ( + + + + )} - {!current.isFinal && data.stage === "MAIN" ? ( @@ -127,7 +165,25 @@ function ModalContent({ open, setOpen, data, current }: ModalProps) { ) : null} - + {data.stage !== "REVISION" && ( + <> + + + + setReviewFile(file)} + disabled={commentType !== "심사 의견 파일"} + /> + + + )} diff --git a/src/app/prof/final/[id]/ProfessorFinalForm.tsx b/src/app/prof/final/[id]/ProfessorFinalForm.tsx index c22e6d10..ef494c99 100644 --- a/src/app/prof/final/[id]/ProfessorFinalForm.tsx +++ b/src/app/prof/final/[id]/ProfessorFinalForm.tsx @@ -53,6 +53,7 @@ export function ProfessorFinalForm({ const { values } = form; const [showConfirmDialog, setShowConfirmDialog] = useState(false); const [currentState, setCurrentState] = useState(null); + const [commentType, setCommentType] = useState(); const handleSubmit = transactionTask(async (task, input: FormInput) => { setCurrentState("pending"); @@ -70,8 +71,8 @@ export function ProfessorFinalForm({ API_ROUTES.review.final.put(reviewId), { contentStatus: input.status, - comment: input.comment, - fileUUID, + ...(commentType === "심사 의견" ? { comment: input.comment } : {}), + ...(commentType === "심사 의견 파일" ? { fileUUID } : {}), } satisfies UpdateReviewRequestBody, { baseURL: process.env.NEXT_PUBLIC_REVIEW_API_ENDPOINT } ); @@ -113,6 +114,8 @@ export function ProfessorFinalForm({ form={form} previousCommentFile={previous.reviewFile ?? undefined} currentState={currentState} + commentType={commentType} + setCommentType={setCommentType} /> - + {!within && ( diff --git a/src/app/prof/review/[id]/ProfessorReviewForm.tsx b/src/app/prof/review/[id]/ProfessorReviewForm.tsx index f3955d9e..e7dd44f6 100644 --- a/src/app/prof/review/[id]/ProfessorReviewForm.tsx +++ b/src/app/prof/review/[id]/ProfessorReviewForm.tsx @@ -61,6 +61,7 @@ export function ProfessorReviewForm({ const { values } = form; const [showConfirmDialog, setShowConfirmDialog] = useState(false); const [currentState, setCurrentState] = useState(null); + const [commentType, setCommentType] = useState(); const handleSubmit = transactionTask(async (task, input: FormInput) => { setCurrentState("pending"); @@ -79,8 +80,8 @@ export function ProfessorReviewForm({ { contentStatus: input.thesis, presentationStatus: input.presentation, - comment: input.comment, - fileUUID, + ...(commentType === "심사 의견" ? { comment: input.comment } : {}), + ...(commentType === "심사 의견 파일" ? { fileUUID } : {}), } satisfies UpdateReviewRequestBody, { baseURL: process.env.NEXT_PUBLIC_REVIEW_API_ENDPOINT } ); @@ -125,6 +126,8 @@ export function ProfessorReviewForm({ form={form} previousCommentFile={previous?.reviewFile ?? undefined} currentState={currentState} + commentType={commentType} + setCommentType={setCommentType} /> - + {!within && ( diff --git a/src/app/prof/revision/[id]/RevisionCheckForm.tsx b/src/app/prof/revision/[id]/RevisionCheckForm.tsx index dbb0a343..88fd0874 100644 --- a/src/app/prof/revision/[id]/RevisionCheckForm.tsx +++ b/src/app/prof/revision/[id]/RevisionCheckForm.tsx @@ -37,13 +37,9 @@ export function RevisionCheckForm({ const handleSubmit = async (input: FormInput) => { setPending(true); try { - await ClientAxios.put( - API_ROUTES.review.revision.put(revisionId), - { - contentStatus: input.checked ? "PASS" : "FAIL", - } satisfies { contentStatus: Status }, - { baseURL: process.env.NEXT_PUBLIC_REVIEW_API_ENDPOINT } - ); + await ClientAxios.put(API_ROUTES.review.revision.put(revisionId), { + contentStatus: input.checked ? "PASS" : "FAIL", + } satisfies { contentStatus: Status }); showNotificationSuccess({ message: input.checked diff --git a/src/app/student/achievement/[id]/page.tsx b/src/app/student/achievement/[id]/page.tsx index 48bf2373..3b17cbc7 100644 --- a/src/app/student/achievement/[id]/page.tsx +++ b/src/app/student/achievement/[id]/page.tsx @@ -15,7 +15,7 @@ async function StudentAchievementPage({ params: { id } }: Props) { <>
- +
); diff --git a/src/app/student/result/page.tsx b/src/app/student/result/page.tsx index 2ae9adaa..9ad71187 100644 --- a/src/app/student/result/page.tsx +++ b/src/app/student/result/page.tsx @@ -7,50 +7,44 @@ import { fetcher } from "@/api/fetcher"; import { API_ROUTES } from "@/api/apiRoute"; import { MyReviewResponse } from "@/api/_types/reviews"; import { ThesisInfoData } from "@/components/pages/review/ThesisInfo/ThesisInfo"; -import { checkPhase } from "@/api/_utils/checkPhase"; -import { PhaseReady } from "@/components/pages/PhaseReady"; -import { formatTime } from "@/components/common/Clock/date/format"; +import { UserResponse } from "@/api/_types/user"; export default async function StudentResultPage() { const { token } = await AuthSSR({ userType: "STUDENT" }); - const { "0": result } = (await fetcher({ url: API_ROUTES.review.getMe(), token })) as { + const user = (await fetcher({ url: API_ROUTES.user.get(), token })) as UserResponse; + const { "0": pre, "1": main } = (await fetcher({ url: API_ROUTES.review.getMe(), token })) as { "0": MyReviewResponse; + "1": MyReviewResponse; }; + const thesisRes: MyReviewResponse = user.currentPhase === "PRELIMINARY" ? pre : main; const thesisInfo: ThesisInfoData = { - title: result.title, - stage: result.stage, + title: thesisRes.title, + stage: thesisRes.stage, studentInfo: { - name: result.student, - department: { name: result.department }, + name: thesisRes.student, + department: { name: thesisRes.department }, }, - abstract: result.abstract, - thesisFile: result.thesisFiles.find((file) => file.type === "THESIS")?.file, - presentationFile: result.thesisFiles.find((file) => file.type === "PRESENTATION")?.file, + abstract: thesisRes.abstract, + thesisFile: thesisRes.thesisFiles.find((file) => file.type === "THESIS")?.file, + presentationFile: thesisRes.thesisFiles.find((file) => file.type === "PRESENTATION")?.file, }; - const { end, after } = await checkPhase({ - title: thesisInfo.stage === "MAIN" ? "본심 최종 심사" : "예심 최종 심사", - token, - }); - - return after ? ( + return ( <> review.isFinal)} + stage={thesisRes.stage} + review={thesisRes.reviews.find((review) => review.isFinal)} /> !review.isFinal)} + stage={thesisRes.stage} + reviews={thesisRes.reviews.filter((review) => !review.isFinal)} /> - ) : ( - ); } diff --git a/src/app/student/write/page.tsx b/src/app/student/write/page.tsx index fcea2edf..ead83b9e 100644 --- a/src/app/student/write/page.tsx +++ b/src/app/student/write/page.tsx @@ -1,5 +1,8 @@ import { AuthSSR } from "@/api/AuthSSR"; +import { UserResponse } from "@/api/_types/user"; import { checkPhase } from "@/api/_utils/checkPhase"; +import { API_ROUTES } from "@/api/apiRoute"; +import { fetcher } from "@/api/fetcher"; import { formatTime } from "@/components/common/Clock/date/format"; import PageHeader from "@/components/common/PageHeader"; import { Section } from "@/components/common/Section"; @@ -8,7 +11,15 @@ import PaperSubmissionForm from "@/components/pages/write/PaperSubmissionForm/Pa export default async function StudentWritePage() { const { token } = await AuthSSR({ userType: "STUDENT" }); - const { within, start, end } = await checkPhase({ title: "논문 제출", token }); + const user = (await fetcher({ url: API_ROUTES.user.get(), token })) as UserResponse; + + const { within, start, end } = await checkPhase({ + title: + user.currentPhase === "MAIN" || user.currentPhase === "REVISION" + ? "본심 논문 제출" + : "예심 논문 제출", + token, + }); return within ? ( <> diff --git a/src/components/common/rows/CommentTypeRow/CommentTypeRow.tsx b/src/components/common/rows/CommentTypeRow/CommentTypeRow.tsx new file mode 100644 index 00000000..b9600afd --- /dev/null +++ b/src/components/common/rows/CommentTypeRow/CommentTypeRow.tsx @@ -0,0 +1,23 @@ +import { Dispatch, SetStateAction } from "react"; +import { Group, Radio, RadioGroup } from "@mantine/core"; +import RowGroup from "@/components/common/rows/RowGroup/RowGroup"; + +interface Props { + commentType?: string; + setCommentType: Dispatch>; +} + +function CommentTypeRow({ commentType, setCommentType }: Props) { + return ( + + + + + + + + + ); +} + +export default CommentTypeRow; diff --git a/src/components/common/rows/FileUploadRow/FileUploadRow.tsx b/src/components/common/rows/FileUploadRow/FileUploadRow.tsx index 2e2a7e2f..e0953912 100644 --- a/src/components/common/rows/FileUploadRow/FileUploadRow.tsx +++ b/src/components/common/rows/FileUploadRow/FileUploadRow.tsx @@ -26,6 +26,7 @@ interface Props { /* eslint-disable @typescript-eslint/no-explicit-any */ form?: UseFormReturnType; formKey?: string; + disabled?: boolean; } /** @@ -43,6 +44,7 @@ function FileUploadRow({ form, formKey = "file", fieldSize = "md", + disabled, }: Props) { const [file, setFile] = useState(null); @@ -76,6 +78,7 @@ function FileUploadRow({ placeholder={usePrevious ? previousFile.name : "파일 업로드..."} {...form?.getInputProps(formKey)} value={form ? formValue || null : file} + disabled={disabled} /> {(!required || usePrevious) && ( @@ -330,30 +299,22 @@ function AssignReviewerSection({
- { setSelectedHeadReviewer((prev) => ({ ...prev, - deptId: value, + profId: value, })); }} - value={selectedHeadReviewer.deptId} /> - {selectedHeadReviewer.deptId ? ( - { - setSelectedHeadReviewer((prev) => ({ - ...prev, - profId: value, - })); - }} - style={{ marginLeft: "10px" }} - value={selectedHeadReviewer.profId} - /> - ) : ( - department.modificationFlag) + .map((department) => ({ + label: department.name, + value: String(department.id), + }))} + {...getExcludeInputProps("id")} + /> + + + + + + +
+ + + department.modificationFlag) - .map((department) => ({ - label: department.name, - value: String(department.id), - }))} - {...getExcludeInputProps("id")} - /> - - - -
-
- -
- - - department.modificationFlag) + .map((department) => ({ + label: department.name, + value: String(department.id), + }))} + {...getExcludeInputProps("id")} + /> + + + +
+
+ +
+ + + department.modificationFlag) - .map((department) => ({ - label: department.name, - value: String(department.id), - }))} - {...getExcludeInputProps("id")} - /> - - - -
-
- -
- - -