From 10f44bba3cd7b234f740923e1b0d709eb062b38c Mon Sep 17 00:00:00 2001 From: swcho Date: Wed, 13 Aug 2025 14:51:25 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=B9=97=EB=AC=BC=ED=8E=8C=ED=94=84?= =?UTF-8?q?=EC=9E=A5=20=EC=9E=90=EC=B2=B4=20=EC=9E=85=EB=A0=A5=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EC=97=90=EC=84=9C=20'=EC=84=A4=EA=B3=84=EB=B9=88?= =?UTF-8?q?=EB=8F=84(=EB=85=84)'=EC=97=90=20=EC=88=AB=EC=9E=90=EB=A7=8C=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=EB=90=98=EA=B2=8C=EB=81=94=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(placeholder=20=EC=B6=94=EA=B0=80,=20alert,=20valid?= =?UTF-8?q?ation)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/popupComponent/sewagePump.tsx | 2 +- apps/sn/app/components/ui/verticalTable.tsx | 145 ++++++++++++++++-- 2 files changed, 131 insertions(+), 16 deletions(-) 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