[st] feat: 통계 검증 승인 기능 작업
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
Input,
|
||||
Label,
|
||||
Textarea,
|
||||
} from "@common/components";
|
||||
|
||||
export interface ApprovalFormData {
|
||||
rjctOpnnCn: string;
|
||||
lastCfmtnPicNm: string;
|
||||
lastCfmtnPicTelNo: string;
|
||||
lastCfmtnPicDeptNm: string;
|
||||
lastCfmtnPicJbgdNm: string;
|
||||
}
|
||||
|
||||
export interface ApprovalInputDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onConfirm?: (data: ApprovalFormData) => void | Promise<void>;
|
||||
}
|
||||
|
||||
export function ApprovalInputDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
onConfirm,
|
||||
}: ApprovalInputDialogProps) {
|
||||
const [rjctOpnnCn, setRjctOpnnCn] = useState("");
|
||||
const [lastCfmtnPicNm, setLastCfmtnPicNm] = useState("");
|
||||
const [lastCfmtnPicTelNo, setLastCfmtnPicTelNo] = useState("");
|
||||
const [lastCfmtnPicDeptNm, setLastCfmtnPicDeptNm] = useState("");
|
||||
const [lastCfmtnPicJbgdNm, setLastCfmtnPicJbgdNm] = useState("");
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
setRjctOpnnCn("");
|
||||
setLastCfmtnPicNm("");
|
||||
setLastCfmtnPicTelNo("");
|
||||
setLastCfmtnPicDeptNm("");
|
||||
setLastCfmtnPicJbgdNm("");
|
||||
};
|
||||
|
||||
const handleDialogOpenChange = (nextOpen: boolean) => {
|
||||
if (!nextOpen) resetForm();
|
||||
onOpenChange(nextOpen);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
resetForm();
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const handleConfirm = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const formData: ApprovalFormData = {
|
||||
rjctOpnnCn: rjctOpnnCn.trim(),
|
||||
lastCfmtnPicNm: lastCfmtnPicNm.trim(),
|
||||
lastCfmtnPicTelNo: lastCfmtnPicTelNo.trim(),
|
||||
lastCfmtnPicDeptNm: lastCfmtnPicDeptNm.trim(),
|
||||
lastCfmtnPicJbgdNm: lastCfmtnPicJbgdNm.trim(),
|
||||
};
|
||||
await onConfirm?.(formData);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleDialogOpenChange}>
|
||||
<DialogContent
|
||||
className="max-w-[56rem]"
|
||||
aria-describedby={undefined}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>승인 정보 입력</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-4 py-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-label">최종 확인 담당자명</Label>
|
||||
<Input
|
||||
value={lastCfmtnPicNm}
|
||||
onChange={(e) => {
|
||||
setLastCfmtnPicNm(e.target.value);
|
||||
}}
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="담당자명을 입력하세요."
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-label">
|
||||
최종 확인 담당자 전화번호
|
||||
</Label>
|
||||
<Input
|
||||
value={lastCfmtnPicTelNo}
|
||||
onChange={(e) => {
|
||||
setLastCfmtnPicTelNo(e.target.value);
|
||||
}}
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="전화번호를 입력하세요."
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-label">
|
||||
최종 확인 담당자 부서명
|
||||
</Label>
|
||||
<Input
|
||||
value={lastCfmtnPicDeptNm}
|
||||
onChange={(e) => {
|
||||
setLastCfmtnPicDeptNm(e.target.value);
|
||||
}}
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="부서명을 입력하세요."
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-label">
|
||||
최종 확인 담당자 직급명
|
||||
</Label>
|
||||
<Input
|
||||
value={lastCfmtnPicJbgdNm}
|
||||
onChange={(e) => {
|
||||
setLastCfmtnPicJbgdNm(e.target.value);
|
||||
}}
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="직급명을 입력하세요."
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-label">반려 의견 내용</Label>
|
||||
<Textarea
|
||||
value={rjctOpnnCn}
|
||||
onChange={(e) => setRjctOpnnCn(e.target.value)}
|
||||
className="min-h-[10rem] resize-y !text-[1.3rem] !leading-6"
|
||||
placeholder="반려 의견 내용을 입력하세요. (선택사항)"
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
type="button"
|
||||
color="gray"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
취소
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
disabled={isSubmitting}
|
||||
onClick={() => void handleConfirm()}
|
||||
>
|
||||
확인
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -46,6 +46,10 @@ import {
|
||||
} from "@common/components";
|
||||
|
||||
import DataGrid from "@/components/module/DataGrid";
|
||||
import {
|
||||
type ApprovalFormData,
|
||||
ApprovalInputDialog,
|
||||
} from "@/components/statistics/verification/ApprovalInputDialog";
|
||||
import type { CellEditedCallbackParams } from "@/components/type/DataGrid.type";
|
||||
import { toGridRows } from "@/components/util/grid/gridFunction";
|
||||
|
||||
@@ -167,6 +171,7 @@ function StatisticsVerificationReport() {
|
||||
const [selectedResultRows, setSelectedResultRows] = useState<
|
||||
VerifyResultItem[]
|
||||
>([]);
|
||||
const [approvalDialogOpen, setApprovalDialogOpen] = useState(false);
|
||||
const editedResultRowMapRef = useRef<Map<number, VerifyResultItem>>(
|
||||
new Map(),
|
||||
);
|
||||
@@ -405,24 +410,27 @@ function StatisticsVerificationReport() {
|
||||
handleSearch(DEFAULT_FILTERS, DEFAULT_SUB_FILTERS);
|
||||
}
|
||||
|
||||
async function handleApprove() {
|
||||
function handleApprove() {
|
||||
if (selectedReportRows.length === 0) return;
|
||||
setApprovalDialogOpen(true);
|
||||
}
|
||||
|
||||
async function handleApprovalConfirm(formData: ApprovalFormData) {
|
||||
const commonParam = {
|
||||
statsYr: filters.statsYr,
|
||||
sewerBzmnCd: filters.sewerBzmnCd ?? "",
|
||||
statsCd: filters.statsCd ?? "",
|
||||
mdfrId: userInfo?.userUnqId ?? "",
|
||||
};
|
||||
const approvalRequests = selectedReportRows.map((row) => ({
|
||||
...commonParam,
|
||||
rjctOpnnCn: row.rjctOpnnCn ?? "",
|
||||
lastCfmtnPicNm: row.laePicNm ?? "",
|
||||
lastCfmtnPicTelNo: row.laePicTelNo ?? "",
|
||||
lastCfmtnPicDeptNm: row.laeKbrdrDeptNm ?? "",
|
||||
lastCfmtnPicJbgdNm: row.laeKbrdrJbgdNm ?? "",
|
||||
...formData,
|
||||
statsCd: row.statsCd ?? "",
|
||||
sewerBzmnCd: row.sewerBzmnCd ?? "",
|
||||
}));
|
||||
|
||||
try {
|
||||
if (await confirm("승인하시겠습니까?")) {
|
||||
await onApproveStats(approvalRequests);
|
||||
setApprovalDialogOpen(false);
|
||||
}
|
||||
} catch {
|
||||
alert("승인 중 오류가 발생했습니다.");
|
||||
@@ -474,6 +482,11 @@ function StatisticsVerificationReport() {
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<ApprovalInputDialog
|
||||
open={approvalDialogOpen}
|
||||
onOpenChange={setApprovalDialogOpen}
|
||||
onConfirm={handleApprovalConfirm}
|
||||
/>
|
||||
<div className="flex justify-between p-2">
|
||||
<div className="flex items-center gap-6">
|
||||
<FilterSelect
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface StatsInputStatusList {
|
||||
ctpvNm?: string;
|
||||
sggNm?: string;
|
||||
statsCd?: string;
|
||||
sewerBzmnCd?: string;
|
||||
menuTabName?: string;
|
||||
locgStatusNm?: string;
|
||||
inputRate?: number;
|
||||
|
||||
Reference in New Issue
Block a user