diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/sewagePump.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/sewagePump.tsx index 651ee05b6..5dcde5f5e 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/sewagePump.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/sewagePump.tsx @@ -4,7 +4,7 @@ import React, { useState, useEffect } from 'react' const exclnInputColumn = [ { id: "swtrmtZoneNm", title: "배수구역", type: "input" }, { id: "mltvDtdvNm", title: "배수분구", type: "input" }, - { id: "designFrqYr", title: "설계빈도(년)", type: "input" }, + { id: "designFrqYr", title: "설계빈도(년)", type: "input", placeholder: "예: 10", isNumber: true, length: 4 }, ] // 오수펌프장 자체 입력 정보 const drtwInputColumn = [ diff --git a/apps/sn/app/components/ui/verticalTable.tsx b/apps/sn/app/components/ui/verticalTable.tsx index 499636903..dc024e068 100644 --- a/apps/sn/app/components/ui/verticalTable.tsx +++ b/apps/sn/app/components/ui/verticalTable.tsx @@ -1,6 +1,8 @@ import UiPageNations from "@common/business_components/ui/table/uiPageNations"; import { Table, TableBody, TableCell, TableRow, Button } from "@common/components"; import { useEffect, useState } from "react"; +import { confirmToast, showConfirmToast } from '@/components/ui/toast' +import { toast, Toaster } from "react-hot-toast"; type UiTableProps = { headName: React.ReactNode | undefined; @@ -39,6 +41,8 @@ type UiHeadProps = { type?: string; options?: string[]; placeholder?: string; + isNumber?: boolean; + length?: number; }; export function VerticalTable({ @@ -63,6 +67,16 @@ export function VerticalTable({ }: UiTableProps) { const [cur, setCur] = useState(1); const [formData, setFormData] = useState<{ [key: string]: string | number | boolean }>({}); + const [errors, setErrors] = useState<{ [key: string]: string }>({}); // 에러 상태 추가 + + // 컴포넌트 언마운트 시 confirmToast 정리 + useEffect(() => { + return () => { + // 컴포넌트가 언마운트될 때 global-confirm-toast ID를 가진 Toast 제거 + toast.dismiss("global-confirm-toast"); + }; + }, []); + useEffect(() => { if (resetPageNo) { setCur(1); @@ -88,16 +102,33 @@ export function VerticalTable({ console.log("New form data:", newFormData); // 디버깅용 setFormData(newFormData); + // 에러 메시지 초기화 (입력이 성공하면 에러 제거) + if (errors[id]) { + setErrors(prev => { + const newErrors = { ...prev }; + delete newErrors[id]; + return newErrors; + }); + } + // 폼 데이터가 변경될 때마다 부모에게 알림 if (onFormDataChange) { onFormDataChange(newFormData); } }; + const handlePageChange = (page: number) => { setCur(page); onPageChange(page); }; + const showError = (fieldId: string, message: string) => { + setErrors(prev => ({ + ...prev, + [fieldId]: message + })); + }; + return ( <> {headName ? ( @@ -142,13 +173,55 @@ export function VerticalTable({ ))} ) : head.type === "input" ? ( - handleInputChange(head.id, e.target.value)} - className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" - /> +
+ { + if (head.isNumber) { + const newValue = e.target.value; + + if (newValue === "") { + handleInputChange(head.id, newValue); + return; + } + + const isValidNumber = /^[0-9]+$/.test(newValue); + if (!isValidNumber) { + showError(head.id, `${head.title}는 숫자만 입력 가능합니다.`); + return; + } + + if (head.length && newValue.length > head.length) { + showError(head.id, `${head.title}는 최대 ${head.length}자까지 입력 가능합니다.`); + return; + } + + const numericValue = parseInt(newValue); + if (isNaN(numericValue) || numericValue < 0) return; + + handleInputChange(head.id, numericValue); + } else { + const newValue = e.target.value; + + if (head.length && newValue.length > head.length) { + showError(head.id, `${head.title}는 최대 ${head.length}자까지 입력 가능합니다.`); + return; + } + + handleInputChange(head.id, e.target.value); + } + }} + className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" + placeholder={head.placeholder} + /> + {errors[head.id] && ( +
+ {errors[head.id]} +
+ )} +
) : ( {formData[head.id] ?? "-"} ) @@ -189,14 +262,55 @@ export function VerticalTable({ ))} ) : head.type === "input" ? ( - handleInputChange(head.id, e.target.value)} - className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" - placeholder={head.placeholder} - /> +
+ { + if (head.isNumber) { + const newValue = e.target.value; + + if (newValue === "") { + handleInputChange(head.id, newValue); + return; + } + + const isValidNumber = /^[0-9]+$/.test(newValue); + if (!isValidNumber) { + showError(head.id, `${head.title}는 숫자만 입력 가능합니다.`); + return; + } + + if (head.length && newValue.length > head.length) { + showError(head.id, `${head.title}는 최대 ${head.length}자까지 입력 가능합니다.`); + return; + } + + const numericValue = parseInt(newValue); + if (isNaN(numericValue) || numericValue < 0) return; + + handleInputChange(head.id, numericValue); + } else { + const newValue = e.target.value; + + if (head.length && newValue.length > head.length) { + showError(head.id, `${head.title}는 최대 ${head.length}자까지 입력 가능합니다.`); + return; + } + + handleInputChange(head.id, e.target.value); + } + }} + className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" + placeholder={head.placeholder} + /> + {errors[head.id] && ( +
+ {errors[head.id]} +
+ )} +
) : ( {formData[head.id] ?? "-"} ) @@ -218,6 +332,7 @@ export function VerticalTable({ /> )} + ); } \ No newline at end of file