Files
frontend/apps/st/src/components/statistics/review/ReviewSubmitDialog.tsx
T

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>
);
}