"use client"; import React, { useState, useEffect, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Switch } from "@/components/ui/switch"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { Separator } from "@/components/ui/separator"; import { Check, ChevronsUpDown, Plus, Pencil, Trash2, Search, RefreshCw, ArrowRight, X, GripVertical, } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { cascadingAutoFillApi, AutoFillGroup, AutoFillMapping } from "@/lib/api/cascadingAutoFill"; import { tableManagementApi } from "@/lib/api/tableManagement"; interface TableColumn { columnName: string; columnLabel?: string; dataType?: string; } export default function AutoFillTab() { // 목록 상태 const [groups, setGroups] = useState([]); const [loading, setLoading] = useState(true); const [searchText, setSearchText] = useState(""); // 모달 상태 const [isModalOpen, setIsModalOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [editingGroup, setEditingGroup] = useState(null); const [deletingGroupCode, setDeletingGroupCode] = useState(null); // 테이블/컬럼 목록 const [tableList, setTableList] = useState>([]); const [masterColumns, setMasterColumns] = useState([]); // 폼 데이터 const [formData, setFormData] = useState({ groupName: "", description: "", masterTable: "", masterValueColumn: "", masterLabelColumn: "", isActive: "Y", }); // 매핑 데이터 const [mappings, setMappings] = useState([]); // 테이블 Combobox 상태 const [tableComboOpen, setTableComboOpen] = useState(false); // 목록 로드 const loadGroups = useCallback(async () => { setLoading(true); try { const response = await cascadingAutoFillApi.getGroups(); if (response.success && response.data) { setGroups(response.data); } } catch (error) { console.error("그룹 목록 로드 실패:", error); toast.error("그룹 목록을 불러오는데 실패했습니다."); } finally { setLoading(false); } }, []); // 테이블 목록 로드 const loadTableList = useCallback(async () => { try { const response = await tableManagementApi.getTableList(); if (response.success && response.data) { setTableList(response.data); } } catch (error) { console.error("테이블 목록 로드 실패:", error); } }, []); // 테이블 컬럼 로드 const loadColumns = useCallback(async (tableName: string) => { if (!tableName) { setMasterColumns([]); return; } try { const response = await tableManagementApi.getColumnList(tableName); if (response.success && response.data?.columns) { setMasterColumns( response.data.columns.map((col: any) => ({ columnName: col.columnName || col.column_name, columnLabel: col.columnLabel || col.column_label || col.columnName, dataType: col.dataType || col.data_type, })), ); } } catch (error) { console.error("컬럼 목록 로드 실패:", error); setMasterColumns([]); } }, []); useEffect(() => { loadGroups(); loadTableList(); }, [loadGroups, loadTableList]); // 테이블 변경 시 컬럼 로드 useEffect(() => { if (formData.masterTable) { loadColumns(formData.masterTable); } }, [formData.masterTable, loadColumns]); // 필터된 목록 const filteredGroups = groups.filter( (g) => g.groupCode.toLowerCase().includes(searchText.toLowerCase()) || g.groupName.toLowerCase().includes(searchText.toLowerCase()) || g.masterTable?.toLowerCase().includes(searchText.toLowerCase()), ); // 모달 열기 (생성) const handleOpenCreate = () => { setEditingGroup(null); setFormData({ groupName: "", description: "", masterTable: "", masterValueColumn: "", masterLabelColumn: "", isActive: "Y", }); setMappings([]); setMasterColumns([]); setIsModalOpen(true); }; // 모달 열기 (수정) const handleOpenEdit = async (group: AutoFillGroup) => { setEditingGroup(group); // 상세 정보 로드 const detailResponse = await cascadingAutoFillApi.getGroupDetail(group.groupCode); if (detailResponse.success && detailResponse.data) { const detail = detailResponse.data; // 컬럼 먼저 로드 if (detail.masterTable) { await loadColumns(detail.masterTable); } setFormData({ groupCode: detail.groupCode, groupName: detail.groupName, description: detail.description || "", masterTable: detail.masterTable, masterValueColumn: detail.masterValueColumn, masterLabelColumn: detail.masterLabelColumn || "", isActive: detail.isActive || "Y", }); // 매핑 데이터 변환 (snake_case → camelCase) const convertedMappings = (detail.mappings || []).map((m: any) => ({ sourceColumn: m.source_column || m.sourceColumn, targetField: m.target_field || m.targetField, targetLabel: m.target_label || m.targetLabel || "", isEditable: m.is_editable || m.isEditable || "Y", isRequired: m.is_required || m.isRequired || "N", defaultValue: m.default_value || m.defaultValue || "", sortOrder: m.sort_order || m.sortOrder || 0, })); setMappings(convertedMappings); } setIsModalOpen(true); }; // 삭제 확인 const handleDeleteConfirm = (groupCode: string) => { setDeletingGroupCode(groupCode); setIsDeleteDialogOpen(true); }; // 삭제 실행 const handleDelete = async () => { if (!deletingGroupCode) return; try { const response = await cascadingAutoFillApi.deleteGroup(deletingGroupCode); if (response.success) { toast.success("자동 입력 그룹이 삭제되었습니다."); loadGroups(); } else { toast.error(response.error || "삭제에 실패했습니다."); } } catch (error) { toast.error("삭제 중 오류가 발생했습니다."); } finally { setIsDeleteDialogOpen(false); setDeletingGroupCode(null); } }; // 저장 const handleSave = async () => { // 유효성 검사 if (!formData.groupName || !formData.masterTable || !formData.masterValueColumn) { toast.error("필수 항목을 모두 입력해주세요."); return; } try { const saveData = { ...formData, mappings, }; let response; if (editingGroup) { response = await cascadingAutoFillApi.updateGroup(editingGroup.groupCode!, saveData); } else { response = await cascadingAutoFillApi.createGroup(saveData); } if (response.success) { toast.success(editingGroup ? "수정되었습니다." : "생성되었습니다."); setIsModalOpen(false); loadGroups(); } else { toast.error(response.error || "저장에 실패했습니다."); } } catch (error) { toast.error("저장 중 오류가 발생했습니다."); } }; // 매핑 추가 const handleAddMapping = () => { setMappings([ ...mappings, { sourceColumn: "", targetField: "", targetLabel: "", isEditable: "Y", isRequired: "N", defaultValue: "", sortOrder: mappings.length + 1, }, ]); }; // 매핑 삭제 const handleRemoveMapping = (index: number) => { setMappings(mappings.filter((_, i) => i !== index)); }; // 매핑 수정 const handleMappingChange = (index: number, field: keyof AutoFillMapping, value: any) => { const updated = [...mappings]; updated[index] = { ...updated[index], [field]: value }; setMappings(updated); }; return (
{/* 검색 및 액션 */}
setSearchText(e.target.value)} className="pl-10" />
{/* 목록 */}
자동 입력 그룹 마스터 선택 시 여러 필드를 자동으로 입력합니다. (총 {filteredGroups.length}개)
{loading ? (
로딩 중...
) : filteredGroups.length === 0 ? (
{searchText ? "검색 결과가 없습니다." : "등록된 자동 입력 그룹이 없습니다."}
) : ( 그룹 코드 그룹명 마스터 테이블 매핑 수 상태 작업 {filteredGroups.map((group) => ( {group.groupCode} {group.groupName} {group.masterTable} {group.mappingCount || 0}개 {group.isActive === "Y" ? "활성" : "비활성"} ))}
)}
{/* 생성/수정 모달 */} {editingGroup ? "자동 입력 그룹 수정" : "자동 입력 그룹 생성"} 마스터 데이터 선택 시 자동으로 입력할 필드들을 설정합니다.
{/* 기본 정보 */}

기본 정보

setFormData({ ...formData, groupName: e.target.value })} placeholder="예: 고객사 정보 자동입력" />