195 lines
6.6 KiB
TypeScript
195 lines
6.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
|
|
|
|
import {
|
|
Button,
|
|
Dialog,
|
|
DialogContent,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
Input,
|
|
Label,
|
|
LoadingWithAnimation,
|
|
Textarea,
|
|
} from "@common/components";
|
|
|
|
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
|
|
import type {
|
|
ReviewerInfoRequest,
|
|
ReviewerInfoResponse,
|
|
ReviewSubmitRequest,
|
|
} from "@/entities/statistics/model/statistics-interface";
|
|
import { useAlertStore } from "@/shared/lib";
|
|
|
|
export interface ReviewSubmitDialogProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
context: ReviewerInfoRequest | null;
|
|
onSubmitted?: () => void | Promise<void>;
|
|
}
|
|
|
|
export function ReviewSubmitDialog({
|
|
open,
|
|
onOpenChange,
|
|
context,
|
|
onSubmitted,
|
|
}: ReviewSubmitDialogProps) {
|
|
const { setMessage: alert } = useAlertStore((state) => state);
|
|
const [reviewerInfo, setReviewerInfo] =
|
|
useState<ReviewerInfoResponse["data"] | null>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!open) {
|
|
setReviewerInfo(null);
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
if (context == null) {
|
|
setReviewerInfo(null);
|
|
return;
|
|
}
|
|
|
|
let cancelled = false;
|
|
|
|
const loadReviewerInfo = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
const response = await statisticsApi.getReviewerInfo(context);
|
|
|
|
if (cancelled) return;
|
|
|
|
if (response.status !== "success" || !response.data) {
|
|
setReviewerInfo(null);
|
|
return;
|
|
}
|
|
|
|
setReviewerInfo(response.data);
|
|
} catch {
|
|
if (!cancelled) {
|
|
setReviewerInfo(null);
|
|
}
|
|
} finally {
|
|
if (!cancelled) {
|
|
setIsLoading(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
void loadReviewerInfo();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [open, context, alert]);
|
|
|
|
const handleConfirm = async () => {
|
|
if (context == null) {
|
|
alert("제출 정보를 확인한 뒤 다시 시도해주세요.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setIsSubmitting(true);
|
|
|
|
const userInfo = await getUserInfo();
|
|
const submitParam: ReviewSubmitRequest = {
|
|
statsYr: context.statsYr,
|
|
sewerBzmnCd: context.sewerBzmnCd,
|
|
statsCd: context.statsCd,
|
|
mdfrId: userInfo.userUnqId,
|
|
splmntOpnnCn: reviewerInfo?.splmntOpnnCn ?? "",
|
|
wrterOpnnCn: reviewerInfo?.wrterOpnnCn ?? "",
|
|
};
|
|
|
|
const response = await statisticsApi.submitReview(submitParam);
|
|
if (response.status !== "success") {
|
|
alert(response.message || "제출에 실패했습니다.");
|
|
return;
|
|
}
|
|
|
|
alert(response.message || "제출되었습니다.");
|
|
onOpenChange(false);
|
|
await onSubmitted?.();
|
|
} catch {
|
|
alert("제출 중 오류가 발생했습니다.");
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-[64rem]" aria-describedby={undefined}>
|
|
<DialogHeader>
|
|
<DialogTitle>제출 정보</DialogTitle>
|
|
</DialogHeader>
|
|
{(isLoading || isSubmitting) && (
|
|
<div className="absolute inset-0 z-50 flex items-center justify-center rounded-lg p-10 [data-theme='black']:bg-transparent">
|
|
<div className="relative size-[5rem]">
|
|
<LoadingWithAnimation color="primary" />
|
|
</div>
|
|
</div>
|
|
)}
|
|
<div className="grid grid-cols-2 gap-4 py-2">
|
|
<div className="flex flex-col gap-2">
|
|
<Label className="text-label">검토자명</Label>
|
|
<Input value={reviewerInfo?.userNm ?? "-"} readOnly />
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<Label className="text-label">연락처</Label>
|
|
<Input value={reviewerInfo?.telOffrm ?? "-"} readOnly />
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<Label className="text-label">부서</Label>
|
|
<Input value={reviewerInfo?.dpmNm ?? "-"} readOnly />
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<Label className="text-label">직책</Label>
|
|
<Input value={reviewerInfo?.cps ?? "-"} readOnly />
|
|
</div>
|
|
<div className="col-span-2 flex flex-col gap-2">
|
|
<Label className="text-label">검토의견</Label>
|
|
<Textarea
|
|
value={reviewerInfo?.splmntOpnnCn ?? "-"}
|
|
readOnly
|
|
className="min-h-[10rem] p-2 resize-none !text-xl"
|
|
/>
|
|
</div>
|
|
<div className="col-span-2 flex flex-col gap-2">
|
|
<Label className="text-label">처리내역</Label>
|
|
<Textarea
|
|
value={reviewerInfo?.wrterOpnnCn ?? "-"}
|
|
readOnly
|
|
className="min-h-[10rem] p-2 resize-none !text-xl"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<DialogFooter>
|
|
<Button
|
|
type="button"
|
|
color="primary"
|
|
onClick={() => void handleConfirm()}
|
|
disabled={isLoading || isSubmitting}
|
|
>
|
|
제출
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
color="gray"
|
|
onClick={() => onOpenChange(false)}
|
|
disabled={isLoading || isSubmitting}
|
|
>
|
|
취소
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|