Files
frontend/apps/st/src/components/statistics/input/SubmitDailog.tsx
T

191 lines
8.2 KiB
TypeScript

"use client";
import { useEffect } from "react";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { statisticsInputSubmitInfoSchema, type StatisticsInputSubmitInfoFormData } from "@/entities/statistics/model/statistics-input-submit-info-schema";
import type { SewerageStatusInfo } from "@/entities/statistics/model/statistics-interface";
import {
Button,
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
Input,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
LoadingWithAnimation,
} from "@common/components";
/** 숫자만 추출 후 하이픈 포맷 (010-1234-5678, 02-123-4567 등) */
function formatPhoneWithHyphen(value: string): string {
const digits = value.replace(/\D/g, "");
if (digits.length <= 3) return digits;
if (digits.startsWith("010")) {
if (digits.length <= 7) return `010-${digits.slice(3)}`;
return `010-${digits.slice(3, 7)}-${digits.slice(7, 11)}`;
}
if (digits.startsWith("02")) {
if (digits.length <= 5) return `02-${digits.slice(2)}`;
if (digits.length <= 9) return `02-${digits.slice(2, 5)}-${digits.slice(5, 9)}`;
return `02-${digits.slice(2, 6)}-${digits.slice(6, 10)}`;
}
if (digits.length <= 6) return `${digits.slice(0, 3)}-${digits.slice(3)}`;
return `${digits.slice(0, 3)}-${digits.slice(3, 6)}-${digits.slice(6, 10)}`;
}
const PHONE_FIELDS: (keyof StatisticsInputSubmitInfoFormData)[] = ["laePicTelNo", "laeIdfrTelNo"];
interface SubmitDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
statusInfo: SewerageStatusInfo | null;
/** 다이얼로그에서 저장 클릭 시 호출. 제출 정보를 넘기면 부모에서 requestData에 포함해 제출 API 호출 */
onConfirm?: (data: StatisticsInputSubmitInfoFormData) => void | Promise<void>;
/** 예외 제출로 다이얼로그를 연 경우: 일반 제출(onConfirm) 대신 호출 */
onExceptionConfirm?: (data: StatisticsInputSubmitInfoFormData) => void | Promise<void>;
/** 'normal': onConfirm / 'exception': onExceptionConfirm */
submitMode?: "normal" | "exception";
isLoading?: boolean;
}
const defaultValues: StatisticsInputSubmitInfoFormData = {
laePicNm: "",
laeKbrdrJbgdNm: "",
laeKbrdrDeptNm: "",
laePicTelNo: "",
laeIdfrNm: "",
laeIdfrJbgdNm: "",
laeIdfrDeptNm: "",
laeIdfrTelNo: "",
};
function statusInfoToFormData(info: SewerageStatusInfo): StatisticsInputSubmitInfoFormData {
return {
laePicNm: info.laePicNm ?? "",
laeKbrdrJbgdNm: info.laeKbrdrJbgdNm ?? "",
laeKbrdrDeptNm: info.laeKbrdrDeptNm ?? "",
laePicTelNo: info.laePicTelNo ?? "",
laeIdfrNm: info.laeIdfrNm ?? "",
laeIdfrJbgdNm: info.laeIdfrJbgdNm ?? "",
laeIdfrDeptNm: info.laeIdfrDeptNm ?? "",
laeIdfrTelNo: info.laeIdfrTelNo ?? "",
};
};
const SubmitDailog = ({
open,
onOpenChange,
statusInfo,
onConfirm,
onExceptionConfirm,
submitMode = "normal",
isLoading,
}: SubmitDialogProps) => {
const { control, handleSubmit, reset, formState: { errors } } = useForm<StatisticsInputSubmitInfoFormData>({
resolver: zodResolver(statisticsInputSubmitInfoSchema),
defaultValues,
});
useEffect(() => {
if (open) {
reset(statusInfo ? statusInfoToFormData(statusInfo) : defaultValues);
}
}, [open, statusInfo, reset]);
async function onSubmit(data: StatisticsInputSubmitInfoFormData) {
if (submitMode === "exception") {
await onExceptionConfirm?.(data);
} else {
await onConfirm?.(data);
}
onOpenChange(false);
reset(defaultValues);
}
function handleCancel() {
onOpenChange(false);
reset(defaultValues);
}
const rows: { name: keyof StatisticsInputSubmitInfoFormData; label: string; placeholder: string }[] = [
{ name: "laePicNm", label: "작성자 명", placeholder: "작성자 명 입력" },
{ name: "laeKbrdrJbgdNm", label: "작성자 직제", placeholder: "작성자 직제 입력" },
{ name: "laeKbrdrDeptNm", label: "작성자 부서", placeholder: "작성자 부서 입력" },
{ name: "laePicTelNo", label: "작성자 연락처", placeholder: "작성자 연락처 입력" },
{ name: "laeIdfrNm", label: "승인자명", placeholder: "승인자명 입력" },
{ name: "laeIdfrJbgdNm", label: "승인자 직제", placeholder: "승인자 직제 입력" },
{ name: "laeIdfrDeptNm", label: "승인자 부서", placeholder: "승인자 부서 입력" },
{ name: "laeIdfrTelNo", label: "승인자 연락처", placeholder: "승인자 연락처" },
];
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-[800px]">
<DialogHeader>
<DialogTitle>제출 정보</DialogTitle>
</DialogHeader>
{isLoading && <div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>}
<form onSubmit={handleSubmit(onSubmit)}>
<Table variant="secondary">
<TableBody>
{rows.map(({ name, label, placeholder }) => (
<TableRow key={name}>
<TableHead className="w-[150px] bg-muted font-medium" data-required>
{label}
</TableHead>
<TableCell>
<Controller
name={name}
control={control}
render={({ field }) => {
const isPhone = PHONE_FIELDS.includes(name);
return (
<div className="space-y-1">
<Input
{...field}
value={field.value ?? ""}
placeholder={placeholder}
onChange={(e) => {
const v = e.target.value;
field.onChange(isPhone ? formatPhoneWithHyphen(v) : v);
}}
/>
{errors[name]?.message && (
<p className="text-sm text-destructive ml-1">
{errors[name]?.message}
</p>
)}
</div>
);
}}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<DialogFooter className="mt-6">
<Button type="submit" color="primary">
{submitMode === 'normal' ? '제출' : '예외 제출'}
</Button>
<Button type="button" color="gray" onClick={handleCancel}>
취소
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
};
export default SubmitDailog;