"use client"; import React, { useState, useEffect } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { toast } from "sonner"; import { EnhancedInteractiveScreenViewer } from "@/components/screen/EnhancedInteractiveScreenViewer"; import { FormValidationIndicator } from "@/components/common/FormValidationIndicator"; import { useFormValidation } from "@/hooks/useFormValidation"; import { enhancedFormService } from "@/lib/services/enhancedFormService"; import { tableManagementApi } from "@/lib/api/tableManagement"; import { ComponentData, WidgetComponent, ColumnInfo, ScreenDefinition } from "@/types/screen"; import { normalizeWebType } from "@/types/unified-web-types"; // 테스트용 화면 정의 const TEST_SCREEN_DEFINITION: ScreenDefinition = { id: 999, screenName: "validation-demo", tableName: "test_users", // 테스트용 테이블 screenResolution: { width: 800, height: 600 }, gridSettings: { size: 20, color: "#e0e0e0", opacity: 0.5 }, description: "검증 시스템 데모 화면", }; // 테스트용 컴포넌트 데이터 const TEST_COMPONENTS: ComponentData[] = [ { id: "container-1", type: "container", x: 0, y: 0, width: 800, height: 600, parentId: null, children: ["widget-1", "widget-2", "widget-3", "widget-4", "widget-5", "widget-6"], }, { id: "widget-1", type: "widget", x: 20, y: 20, width: 200, height: 40, parentId: "container-1", label: "사용자명", widgetType: "text", columnName: "user_name", required: true, style: { labelFontSize: "14px", labelColor: "#374151", labelFontWeight: "500", }, } as WidgetComponent, { id: "widget-2", type: "widget", x: 20, y: 80, width: 200, height: 40, parentId: "container-1", label: "이메일", widgetType: "email", columnName: "email", required: true, style: { labelFontSize: "14px", labelColor: "#374151", labelFontWeight: "500", }, } as WidgetComponent, { id: "widget-3", type: "widget", x: 20, y: 140, width: 200, height: 40, parentId: "container-1", label: "나이", widgetType: "number", columnName: "age", required: false, webTypeConfig: { min: 0, max: 120, }, style: { labelFontSize: "14px", labelColor: "#374151", labelFontWeight: "500", }, } as WidgetComponent, { id: "widget-4", type: "widget", x: 20, y: 200, width: 200, height: 40, parentId: "container-1", label: "생년월일", widgetType: "date", columnName: "birth_date", required: false, style: { labelFontSize: "14px", labelColor: "#374151", labelFontWeight: "500", }, } as WidgetComponent, { id: "widget-5", type: "widget", x: 20, y: 260, width: 200, height: 40, parentId: "container-1", label: "전화번호", widgetType: "tel", columnName: "phone", required: false, style: { labelFontSize: "14px", labelColor: "#374151", labelFontWeight: "500", }, } as WidgetComponent, { id: "widget-6", type: "widget", x: 20, y: 320, width: 100, height: 40, parentId: "container-1", label: "저장", widgetType: "button", columnName: "save_button", required: false, webTypeConfig: { actionType: "save", text: "저장하기", }, style: { labelFontSize: "14px", labelColor: "#374151", labelFontWeight: "500", }, } as WidgetComponent, ]; // 테스트용 테이블 컬럼 정보 const TEST_TABLE_COLUMNS: ColumnInfo[] = [ { tableName: "test_users", columnName: "id", columnLabel: "ID", dataType: "integer", webType: "number", widgetType: "number", inputType: "auto", isNullable: "N", required: false, isPrimaryKey: true, isVisible: false, displayOrder: 0, description: "기본키", }, { tableName: "test_users", columnName: "user_name", columnLabel: "사용자명", dataType: "character varying", webType: "text", widgetType: "text", inputType: "direct", isNullable: "N", required: true, characterMaximumLength: 50, isVisible: true, displayOrder: 1, description: "사용자 이름", }, { tableName: "test_users", columnName: "email", columnLabel: "이메일", dataType: "character varying", webType: "email", widgetType: "email", inputType: "direct", isNullable: "N", required: true, characterMaximumLength: 100, isVisible: true, displayOrder: 2, description: "이메일 주소", }, { tableName: "test_users", columnName: "age", columnLabel: "나이", dataType: "integer", webType: "number", widgetType: "number", inputType: "direct", isNullable: "Y", required: false, isVisible: true, displayOrder: 3, description: "나이", }, { tableName: "test_users", columnName: "birth_date", columnLabel: "생년월일", dataType: "date", webType: "date", widgetType: "date", inputType: "direct", isNullable: "Y", required: false, isVisible: true, displayOrder: 4, description: "생년월일", }, { tableName: "test_users", columnName: "phone", columnLabel: "전화번호", dataType: "character varying", webType: "tel", widgetType: "tel", inputType: "direct", isNullable: "Y", required: false, characterMaximumLength: 20, isVisible: true, displayOrder: 5, description: "전화번호", }, ]; export default function ValidationDemoPage() { const [formData, setFormData] = useState>({}); const [selectedTable, setSelectedTable] = useState("test_users"); const [availableTables, setAvailableTables] = useState([]); const [tableColumns, setTableColumns] = useState(TEST_TABLE_COLUMNS); const [isLoading, setIsLoading] = useState(false); // 폼 검증 훅 사용 const { validationState, saveState, validateForm, saveForm, canSave, getFieldError, hasFieldError, isFieldValid } = useFormValidation( formData, TEST_COMPONENTS.filter((c) => c.type === "widget") as WidgetComponent[], tableColumns, TEST_SCREEN_DEFINITION, { enableRealTimeValidation: true, validationDelay: 300, enableAutoSave: false, showToastMessages: true, validateOnMount: false, }, ); // 테이블 목록 로드 useEffect(() => { const loadTables = async () => { try { const response = await tableManagementApi.getTableList(); if (response.success && response.data) { setAvailableTables(response.data.map((table) => table.tableName)); } } catch (error) { console.error("테이블 목록 로드 실패:", error); } }; loadTables(); }, []); // 선택된 테이블의 컬럼 정보 로드 useEffect(() => { if (selectedTable && selectedTable !== "test_users") { const loadTableColumns = async () => { setIsLoading(true); try { const response = await tableManagementApi.getColumnList(selectedTable); if (response.success && response.data) { setTableColumns(response.data.columns || []); } } catch (error) { console.error("테이블 컬럼 정보 로드 실패:", error); toast.error("테이블 컬럼 정보를 불러오는데 실패했습니다."); } finally { setIsLoading(false); } }; loadTableColumns(); } else { setTableColumns(TEST_TABLE_COLUMNS); } }, [selectedTable]); const handleFormDataChange = (fieldName: string, value: any) => { setFormData((prev) => ({ ...prev, [fieldName]: value })); }; const handleTestFormSubmit = async () => { const result = await saveForm(); if (result) { toast.success("폼 데이터가 성공적으로 저장되었습니다!"); } }; const handleManualValidation = async () => { const result = await validateForm(); toast.info( `검증 완료: ${result.isValid ? "성공" : "실패"} (오류 ${result.errors.length}개, 경고 ${result.warnings.length}개)`, ); }; const generateTestData = () => { setFormData({ user_name: "테스트 사용자", email: "test@example.com", age: 25, birth_date: "1999-01-01", phone: "010-1234-5678", }); toast.info("테스트 데이터가 입력되었습니다."); }; const generateInvalidData = () => { setFormData({ user_name: "", // 필수 필드 누락 email: "invalid-email", // 잘못된 이메일 형식 age: -5, // 음수 나이 birth_date: "invalid-date", // 잘못된 날짜 phone: "123", // 잘못된 전화번호 형식 }); toast.info("잘못된 테스트 데이터가 입력되었습니다."); }; const clearForm = () => { setFormData({}); toast.info("폼이 초기화되었습니다."); }; return (
{/* 헤더 */}

검증 시스템 데모

개선된 폼 검증 시스템을 테스트해보세요

개발 버전 {validationState.isValid ? "검증 통과" : "검증 실패"}
데모 폼 검증 상태 설정
{/* 폼 영역 */} 테스트 폼 실시간 검증이 적용된 폼입니다. 입력하면서 검증 결과를 확인해보세요.
{/* 컨트롤 패널 */} 컨트롤 패널 테스트 기능들을 사용해보세요
검증 상태 상세 현재 폼의 검증 상태를 자세히 확인할 수 있습니다

폼 데이터

                  {JSON.stringify(formData, null, 2)}
                

검증 통계

{Object.values(validationState.fieldStates).filter((f) => f.status === "valid").length}
유효한 필드
{validationState.errors.length}
오류 개수
테스트 설정 검증 동작을 조정할 수 있습니다
{isLoading && (
테이블 정보를 불러오는 중...
)}

테이블 컬럼 정보

{tableColumns.map((column) => ( ))}
컬럼명 타입 필수
{column.columnName} {column.webType} {column.required ? ( 필수 ) : ( 선택 )}
); }