"use client"; import { useState, useEffect, useMemo, useCallback } from "react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Search, Database, RefreshCw, Settings, Plus, Activity, Trash2, Copy } from "lucide-react"; import { LoadingSpinner } from "@/components/common/LoadingSpinner"; import { toast } from "sonner"; import { useMultiLang } from "@/hooks/useMultiLang"; import { useAuth } from "@/hooks/useAuth"; import { TABLE_MANAGEMENT_KEYS } from "@/constants/tableManagement"; import { INPUT_TYPE_OPTIONS } from "@/types/input-types"; import { apiClient } from "@/lib/api/client"; import { commonCodeApi } from "@/lib/api/commonCode"; import { entityJoinApi, ReferenceTableColumn } from "@/lib/api/entityJoin"; import { ddlApi } from "@/lib/api/ddl"; import { getSecondLevelMenus, createColumnMapping, deleteColumnMappingsByColumn } from "@/lib/api/tableCategoryValue"; import { CreateTableModal } from "@/components/admin/CreateTableModal"; import { AddColumnModal } from "@/components/admin/AddColumnModal"; import { DDLLogViewer } from "@/components/admin/DDLLogViewer"; import { TableLogViewer } from "@/components/admin/TableLogViewer"; import { ScrollToTop } from "@/components/common/ScrollToTop"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; interface TableInfo { tableName: string; displayName: string; description: string; columnCount: number; } interface ColumnTypeInfo { columnName: string; displayName: string; inputType: string; // webType โ†’ inputType ๋ณ€๊ฒฝ detailSettings: string; description: string; isNullable: string; defaultValue?: string; maxLength?: number; numericPrecision?: number; numericScale?: number; codeCategory?: string; codeValue?: string; referenceTable?: string; referenceColumn?: string; displayColumn?: string; // ๐ŸŽฏ Entity ์กฐ์ธ์—์„œ ํ‘œ์‹œํ•  ์ปฌ๋Ÿผ๋ช… categoryMenus?: number[]; // ๐Ÿ†• Category ํƒ€์ž…: ์„ ํƒ๋œ 2๋ ˆ๋ฒจ ๋ฉ”๋‰ด OBJID ๋ฐฐ์—ด hierarchyRole?: "large" | "medium" | "small"; // ๐Ÿ†• ๊ณ„์ธต๊ตฌ์กฐ ์—ญํ•  } interface SecondLevelMenu { menuObjid: number; menuName: string; parentMenuName: string; screenCode?: string; } export default function TableManagementPage() { const { userLang, getText } = useMultiLang({ companyCode: "*" }); const { user } = useAuth(); const [tables, setTables] = useState([]); const [columns, setColumns] = useState([]); const [selectedTable, setSelectedTable] = useState(null); const [searchTerm, setSearchTerm] = useState(""); const [loading, setLoading] = useState(false); const [columnsLoading, setColumnsLoading] = useState(false); const [originalColumns, setOriginalColumns] = useState([]); // ์›๋ณธ ๋ฐ์ดํ„ฐ ์ €์žฅ const [uiTexts, setUiTexts] = useState>({}); // ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ƒํƒœ const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(9999); // ์ „์ฒด ์ปฌ๋Ÿผ ํ‘œ์‹œ const [totalColumns, setTotalColumns] = useState(0); // ํ…Œ์ด๋ธ” ๋ผ๋ฒจ ์ƒํƒœ const [tableLabel, setTableLabel] = useState(""); const [tableDescription, setTableDescription] = useState(""); // ๐ŸŽฏ Entity ์กฐ์ธ ๊ด€๋ จ ์ƒํƒœ const [referenceTableColumns, setReferenceTableColumns] = useState>({}); // DDL ๊ธฐ๋Šฅ ๊ด€๋ จ ์ƒํƒœ const [createTableModalOpen, setCreateTableModalOpen] = useState(false); const [addColumnModalOpen, setAddColumnModalOpen] = useState(false); const [ddlLogViewerOpen, setDdlLogViewerOpen] = useState(false); // ํ…Œ์ด๋ธ” ๋ณต์ œ ๊ด€๋ จ ์ƒํƒœ const [duplicateModalMode, setDuplicateModalMode] = useState<"create" | "duplicate">("create"); const [duplicateSourceTable, setDuplicateSourceTable] = useState(null); // ๐Ÿ†• Category ํƒ€์ž…์šฉ: 2๋ ˆ๋ฒจ ๋ฉ”๋‰ด ๋ชฉ๋ก const [secondLevelMenus, setSecondLevelMenus] = useState([]); // ๋กœ๊ทธ ๋ทฐ์–ด ์ƒํƒœ const [logViewerOpen, setLogViewerOpen] = useState(false); const [logViewerTableName, setLogViewerTableName] = useState(""); // ํ…Œ์ด๋ธ” ์‚ญ์ œ ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ ์ƒํƒœ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [tableToDelete, setTableToDelete] = useState(""); const [isDeleting, setIsDeleting] = useState(false); // ์„ ํƒ๋œ ํ…Œ์ด๋ธ” ๋ชฉ๋ก (์ฒดํฌ๋ฐ•์Šค) const [selectedTableIds, setSelectedTableIds] = useState>(new Set()); // ์ตœ๊ณ  ๊ด€๋ฆฌ์ž ์—ฌ๋ถ€ ํ™•์ธ (ํšŒ์‚ฌ์ฝ”๋“œ๊ฐ€ "*" AND userType์ด "SUPER_ADMIN") const isSuperAdmin = user?.companyCode === "*" && user?.userType === "SUPER_ADMIN"; // ๋‹ค๊ตญ์–ด ํ…์ŠคํŠธ ๋กœ๋“œ useEffect(() => { const loadTexts = async () => { if (!userLang) return; try { const response = await apiClient.post( "/multilang/batch", { langKeys: Object.values(TABLE_MANAGEMENT_KEYS), }, { params: { companyCode: "*", menuCode: "TABLE_MANAGEMENT", userLang: userLang, }, }, ); if (response.data.success) { setUiTexts(response.data.data); } } catch (error) { // console.error("๋‹ค๊ตญ์–ด ํ…์ŠคํŠธ ๋กœ๋“œ ์‹คํŒจ:", error); } }; loadTexts(); }, [userLang]); // ํ…์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ ํ•จ์ˆ˜ const getTextFromUI = (key: string, fallback?: string) => { return uiTexts[key] || fallback || key; }; // ๐ŸŽฏ ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ์ •๋ณด ๋กœ๋“œ const loadReferenceTableColumns = useCallback( async (tableName: string) => { if (!tableName) { return; } // ์ด๋ฏธ ๋กœ๋“œ๋œ ๊ฒฝ์šฐ์ด์ง€๋งŒ ๋นˆ ๋ฐฐ์—ด์ด ์•„๋‹Œ ๊ฒฝ์šฐ๋งŒ ์Šคํ‚ต const existingColumns = referenceTableColumns[tableName]; if (existingColumns && existingColumns.length > 0) { // console.log(`๐ŸŽฏ ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ์ด๋ฏธ ๋กœ๋“œ๋จ: ${tableName}`, existingColumns); return; } // console.log(`๐ŸŽฏ ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋กœ๋“œ ์‹œ์ž‘: ${tableName}`); try { const result = await entityJoinApi.getReferenceTableColumns(tableName); // console.log(`๐ŸŽฏ ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋กœ๋“œ ์„ฑ๊ณต: ${tableName}`, result.columns); setReferenceTableColumns((prev) => ({ ...prev, [tableName]: result.columns, })); } catch (error) { // console.error(`์ฐธ์กฐ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋กœ๋“œ ์‹คํŒจ: ${tableName}`, error); setReferenceTableColumns((prev) => ({ ...prev, [tableName]: [], })); } }, [], // ์˜์กด์„ฑ ๋ฐฐ์—ด์—์„œ referenceTableColumns ์ œ๊ฑฐ ); // ์ž…๋ ฅ ํƒ€์ž… ์˜ต์…˜ (8๊ฐœ ํ•ต์‹ฌ ํƒ€์ž…) const inputTypeOptions = INPUT_TYPE_OPTIONS.map((option) => ({ value: option.value, label: option.label, description: option.description, })); // ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ์ž…๋ ฅํƒ€์ž… ์˜ต์…˜ const memoizedInputTypeOptions = useMemo(() => inputTypeOptions, []); // ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์˜ต์…˜ (์‹ค์ œ ํ…Œ์ด๋ธ” ๋ชฉ๋ก์—์„œ ๊ฐ€์ ธ์˜ด) const referenceTableOptions = [ { value: "none", label: getTextFromUI(TABLE_MANAGEMENT_KEYS.LABEL_NONE, "์„ ํƒ ์•ˆํ•จ") }, ...tables.map((table) => ({ value: table.tableName, label: table.displayName || table.tableName })), ]; // ๊ณตํ†ต ์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ ๋ชฉ๋ก ์ƒํƒœ const [commonCodeCategories, setCommonCodeCategories] = useState>([]); // ๊ณตํ†ต ์ฝ”๋“œ ์˜ต์…˜ const commonCodeOptions = [ { value: "none", label: getTextFromUI(TABLE_MANAGEMENT_KEYS.SELECT_CODE_PLACEHOLDER, "์ฝ”๋“œ ์„ ํƒ") }, ...commonCodeCategories, ]; // ๊ณตํ†ต์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ ๋ชฉ๋ก ๋กœ๋“œ const loadCommonCodeCategories = async () => { try { const response = await commonCodeApi.categories.getList({ isActive: true }); // console.log("๐Ÿ” ๊ณตํ†ต์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ API ์‘๋‹ต:", response); if (response.success && response.data) { // console.log("๐Ÿ“‹ ๊ณตํ†ต์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ ๋ฐ์ดํ„ฐ:", response.data); const categories = response.data.map((category) => { // console.log("๐Ÿท๏ธ ์นดํ…Œ๊ณ ๋ฆฌ ํ•ญ๋ชฉ:", category); return { value: category.category_code, label: category.category_name || category.category_code, }; }); // console.log("โœ… ๋งคํ•‘๋œ ์นดํ…Œ๊ณ ๋ฆฌ ์˜ต์…˜:", categories); setCommonCodeCategories(categories); } } catch (error) { // console.error("๊ณตํ†ต์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ ๋กœ๋“œ ์‹คํŒจ:", error); // ์—๋Ÿฌ๋Š” ๋กœ๊ทธ๋งŒ ๋‚จ๊ธฐ๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” ์•Œ๋ฆฌ์ง€ ์•Š์Œ (์„ ํƒ์  ๊ธฐ๋Šฅ) } }; // ๐Ÿ†• 2๋ ˆ๋ฒจ ๋ฉ”๋‰ด ๋ชฉ๋ก ๋กœ๋“œ const loadSecondLevelMenus = async () => { try { const response = await getSecondLevelMenus(); if (response.success && response.data) { setSecondLevelMenus(response.data); } else { console.warn("โš ๏ธ 2๋ ˆ๋ฒจ ๋ฉ”๋‰ด ๋กœ๋“œ ์‹คํŒจ:", response); setSecondLevelMenus([]); // ๋นˆ ๋ฐฐ์—ด๋กœ ์„ค์ •ํ•˜์—ฌ ๋กœ๋”ฉ ์ƒํƒœ ํ•ด์ œ } } catch (error) { console.error("โŒ 2๋ ˆ๋ฒจ ๋ฉ”๋‰ด ๋กœ๋“œ ์—๋Ÿฌ:", error); setSecondLevelMenus([]); // ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ์—๋„ ๋นˆ ๋ฐฐ์—ด๋กœ ์„ค์ • } }; // ํ…Œ์ด๋ธ” ๋ชฉ๋ก ๋กœ๋“œ const loadTables = async () => { setLoading(true); try { const response = await apiClient.get("/table-management/tables"); // ์‘๋‹ต ์ƒํƒœ ํ™•์ธ if (response.data.success) { setTables(response.data.data); toast.success("ํ…Œ์ด๋ธ” ๋ชฉ๋ก์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋กœ๋“œํ–ˆ์Šต๋‹ˆ๋‹ค."); } else { toast.error(response.data.message || "ํ…Œ์ด๋ธ” ๋ชฉ๋ก ๋กœ๋“œ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); } } catch (error) { // console.error("ํ…Œ์ด๋ธ” ๋ชฉ๋ก ๋กœ๋“œ ์‹คํŒจ:", error); toast.error("ํ…Œ์ด๋ธ” ๋ชฉ๋ก ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } finally { setLoading(false); } }; // ์ปฌ๋Ÿผ ํƒ€์ž… ์ •๋ณด ๋กœ๋“œ (ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ ์šฉ) const loadColumnTypes = useCallback(async (tableName: string, page: number = 1, size: number = 50) => { setColumnsLoading(true); try { const response = await apiClient.get(`/table-management/tables/${tableName}/columns`, { params: { page, size }, }); // ์‘๋‹ต ์ƒํƒœ ํ™•์ธ if (response.data.success) { const data = response.data.data; console.log("๐Ÿ“ฅ ์›๋ณธ API ์‘๋‹ต:", { hasColumns: !!(data.columns || data), firstColumn: (data.columns || data)[0], statusColumn: (data.columns || data).find((col: any) => col.columnName === "status"), }); // ์ปฌ๋Ÿผ ๋ฐ์ดํ„ฐ์— ๊ธฐ๋ณธ๊ฐ’ ์„ค์ • const processedColumns = (data.columns || data).map((col: any) => { // detailSettings์—์„œ hierarchyRole ์ถ”์ถœ let hierarchyRole: "large" | "medium" | "small" | undefined = undefined; if (col.detailSettings && typeof col.detailSettings === "string") { try { const parsed = JSON.parse(col.detailSettings); if (parsed.hierarchyRole === "large" || parsed.hierarchyRole === "medium" || parsed.hierarchyRole === "small") { hierarchyRole = parsed.hierarchyRole; } } catch { // JSON ํŒŒ์‹ฑ ์‹คํŒจ ์‹œ ๋ฌด์‹œ } } return { ...col, inputType: col.inputType || "text", // ๊ธฐ๋ณธ๊ฐ’: text categoryMenus: col.categoryMenus || [], // ์นดํ…Œ๊ณ ๋ฆฌ ๋ฉ”๋‰ด ๋งคํ•‘ ์ •๋ณด hierarchyRole, // ๊ณ„์ธต๊ตฌ์กฐ ์—ญํ•  }; }); if (page === 1) { setColumns(processedColumns); setOriginalColumns(processedColumns); } else { // ํŽ˜์ด์ง€ ์ถ”๊ฐ€ ๋กœ๋“œ ์‹œ ๊ธฐ์กด ๋ฐ์ดํ„ฐ์— ์ถ”๊ฐ€ setColumns((prev) => [...prev, ...processedColumns]); } setTotalColumns(data.total || processedColumns.length); toast.success("์ปฌ๋Ÿผ ์ •๋ณด๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๋กœ๋“œํ–ˆ์Šต๋‹ˆ๋‹ค."); } else { toast.error(response.data.message || "์ปฌ๋Ÿผ ์ •๋ณด ๋กœ๋“œ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); } } catch (error) { // console.error("์ปฌ๋Ÿผ ํƒ€์ž… ์ •๋ณด ๋กœ๋“œ ์‹คํŒจ:", error); toast.error("์ปฌ๋Ÿผ ์ •๋ณด ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } finally { setColumnsLoading(false); } }, []); // ํ…Œ์ด๋ธ” ์„ ํƒ const handleTableSelect = useCallback( (tableName: string) => { setSelectedTable(tableName); setCurrentPage(1); setColumns([]); // ์„ ํƒ๋œ ํ…Œ์ด๋ธ” ์ •๋ณด์—์„œ ๋ผ๋ฒจ ์„ค์ • const tableInfo = tables.find((table) => table.tableName === tableName); setTableLabel(tableInfo?.displayName || tableName); setTableDescription(tableInfo?.description || ""); loadColumnTypes(tableName, 1, pageSize); }, [loadColumnTypes, pageSize, tables], ); // ์ž…๋ ฅ ํƒ€์ž… ๋ณ€๊ฒฝ const handleInputTypeChange = useCallback( (columnName: string, newInputType: string) => { setColumns((prev) => prev.map((col) => { if (col.columnName === columnName) { const inputTypeOption = memoizedInputTypeOptions.find((option) => option.value === newInputType); return { ...col, inputType: newInputType, detailSettings: inputTypeOption?.description || col.detailSettings, }; } return col; }), ); }, [memoizedInputTypeOptions], ); // ์ƒ์„ธ ์„ค์ • ๋ณ€๊ฒฝ (์ฝ”๋“œ/์—”ํ‹ฐํ‹ฐ ํƒ€์ž…์šฉ) const handleDetailSettingsChange = useCallback( (columnName: string, settingType: string, value: string) => { setColumns((prev) => prev.map((col) => { if (col.columnName === columnName) { let newDetailSettings = col.detailSettings; let codeCategory = col.codeCategory; let codeValue = col.codeValue; let referenceTable = col.referenceTable; let referenceColumn = col.referenceColumn; let displayColumn = col.displayColumn; let hierarchyRole = col.hierarchyRole; if (settingType === "code") { if (value === "none") { newDetailSettings = ""; codeCategory = undefined; codeValue = undefined; hierarchyRole = undefined; // ์ฝ”๋“œ ์„ ํƒ ํ•ด์ œ ์‹œ ๊ณ„์ธต ์—ญํ• ๋„ ์ดˆ๊ธฐํ™” } else { // ๊ธฐ์กด hierarchyRole ์œ ์ง€ํ•˜๋ฉด์„œ JSON ํ˜•์‹์œผ๋กœ ์ €์žฅ const existingHierarchyRole = hierarchyRole; newDetailSettings = JSON.stringify({ codeCategory: value, hierarchyRole: existingHierarchyRole }); codeCategory = value; codeValue = value; } } else if (settingType === "hierarchy_role") { // ๊ณ„์ธต๊ตฌ์กฐ ์—ญํ•  ๋ณ€๊ฒฝ - JSON ํ˜•์‹์œผ๋กœ ์ €์žฅ hierarchyRole = value === "none" ? undefined : (value as "large" | "medium" | "small"); // detailSettings๋ฅผ JSON์œผ๋กœ ์—…๋ฐ์ดํŠธ let existingSettings: Record = {}; if (typeof col.detailSettings === "string" && col.detailSettings.trim().startsWith("{")) { try { existingSettings = JSON.parse(col.detailSettings); } catch { existingSettings = {}; } } newDetailSettings = JSON.stringify({ ...existingSettings, hierarchyRole: hierarchyRole, }); } else if (settingType === "entity") { if (value === "none") { newDetailSettings = ""; referenceTable = undefined; referenceColumn = undefined; displayColumn = undefined; } else { const tableOption = referenceTableOptions.find((option) => option.value === value); newDetailSettings = tableOption ? `์ฐธ์กฐํ…Œ์ด๋ธ”: ${tableOption.label}` : ""; referenceTable = value; // ๐ŸŽฏ ์ฐธ์กฐ ์ปฌ๋Ÿผ์„ ์†Œ์Šค ์ปฌ๋Ÿผ๋ช…๊ณผ ๋™์ผํ•˜๊ฒŒ ์„ค์ • (์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ) // ์˜ˆ: user_info.dept_code -> dept_info.dept_code referenceColumn = col.columnName; // ์ฐธ์กฐ ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ ์ •๋ณด ๋กœ๋“œ loadReferenceTableColumns(value); } } else if (settingType === "entity_reference_column") { // ๐ŸŽฏ Entity ์ฐธ์กฐ ์ปฌ๋Ÿผ ๋ณ€๊ฒฝ (์กฐ์ธํ•  ์ปฌ๋Ÿผ) referenceColumn = value; const tableOption = referenceTableOptions.find((option) => option.value === col.referenceTable); newDetailSettings = tableOption ? `์ฐธ์กฐํ…Œ์ด๋ธ”: ${tableOption.label}` : ""; } else if (settingType === "entity_display_column") { // ๐ŸŽฏ Entity ํ‘œ์‹œ ์ปฌ๋Ÿผ ๋ณ€๊ฒฝ displayColumn = value; const tableOption = referenceTableOptions.find((option) => option.value === col.referenceTable); newDetailSettings = tableOption ? `์ฐธ์กฐํ…Œ์ด๋ธ”: ${tableOption.label} (${value})` : ""; } return { ...col, detailSettings: newDetailSettings, codeCategory, codeValue, referenceTable, referenceColumn, displayColumn, hierarchyRole, }; } return col; }), ); }, [commonCodeOptions, referenceTableOptions, loadReferenceTableColumns], ); // ๋ผ๋ฒจ ๋ณ€๊ฒฝ ํ•ธ๋“ค๋Ÿฌ ์ถ”๊ฐ€ const handleLabelChange = useCallback((columnName: string, newLabel: string) => { setColumns((prev) => prev.map((col) => { if (col.columnName === columnName) { return { ...col, displayName: newLabel, }; } return col; }), ); }, []); // ์ปฌ๋Ÿผ ๋ณ€๊ฒฝ ํ•ธ๋“ค๋Ÿฌ (์ธ๋ฑ์Šค ๊ธฐ๋ฐ˜) const handleColumnChange = useCallback((index: number, field: keyof ColumnTypeInfo, value: any) => { setColumns((prev) => prev.map((col, i) => { if (i === index) { return { ...col, [field]: value, }; } return col; }), ); }, []); // ๊ฐœ๋ณ„ ์ปฌ๋Ÿผ ์ €์žฅ const handleSaveColumn = async (column: ColumnTypeInfo) => { if (!selectedTable) return; try { // ๐ŸŽฏ Entity ํƒ€์ž…์ธ ๊ฒฝ์šฐ detailSettings์— ์—”ํ‹ฐํ‹ฐ ์„ค์ •์„ JSON์œผ๋กœ ํฌํ•จ let finalDetailSettings = column.detailSettings || ""; if (column.inputType === "entity" && column.referenceTable) { // ๊ธฐ์กด detailSettings๋ฅผ ํŒŒ์‹ฑํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ ์ƒ์„ฑ let existingSettings: Record = {}; if (typeof column.detailSettings === "string" && column.detailSettings.trim().startsWith("{")) { try { existingSettings = JSON.parse(column.detailSettings); } catch { existingSettings = {}; } } // ์—”ํ‹ฐํ‹ฐ ์„ค์ • ์ถ”๊ฐ€ const entitySettings = { ...existingSettings, entityTable: column.referenceTable, entityCodeColumn: column.referenceColumn || "id", entityLabelColumn: column.displayColumn || "name", placeholder: (existingSettings.placeholder as string) || "ํ•ญ๋ชฉ์„ ์„ ํƒํ•˜์„ธ์š”", searchable: existingSettings.searchable ?? true, }; finalDetailSettings = JSON.stringify(entitySettings); console.log("๐Ÿ”ง Entity ์„ค์ • JSON ์ƒ์„ฑ:", entitySettings); } // ๐ŸŽฏ Code ํƒ€์ž…์ธ ๊ฒฝ์šฐ hierarchyRole์„ detailSettings์— ํฌํ•จ if (column.inputType === "code" && column.hierarchyRole) { let existingSettings: Record = {}; if (typeof finalDetailSettings === "string" && finalDetailSettings.trim().startsWith("{")) { try { existingSettings = JSON.parse(finalDetailSettings); } catch { existingSettings = {}; } } const codeSettings = { ...existingSettings, hierarchyRole: column.hierarchyRole, }; finalDetailSettings = JSON.stringify(codeSettings); console.log("๐Ÿ”ง Code ๊ณ„์ธต ์—ญํ•  ์„ค์ • JSON ์ƒ์„ฑ:", codeSettings); } const columnSetting = { columnName: column.columnName, // ์‹ค์ œ DB ์ปฌ๋Ÿผ๋ช… (๋ณ€๊ฒฝ ๋ถˆ๊ฐ€) columnLabel: column.displayName, // ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ํ‘œ์‹œ๋ช… inputType: column.inputType || "text", detailSettings: finalDetailSettings, codeCategory: column.codeCategory || "", codeValue: column.codeValue || "", referenceTable: column.referenceTable || "", referenceColumn: column.referenceColumn || "", displayColumn: column.displayColumn || "", // ๐ŸŽฏ Entity ์กฐ์ธ์—์„œ ํ‘œ์‹œํ•  ์ปฌ๋Ÿผ๋ช… }; // console.log("์ €์žฅํ•  ์ปฌ๋Ÿผ ์„ค์ •:", columnSetting); console.log("๐Ÿ’พ ์ €์žฅํ•  ์ปฌ๋Ÿผ ์ •๋ณด:", { columnName: column.columnName, inputType: column.inputType, categoryMenus: column.categoryMenus, hasCategoryMenus: !!column.categoryMenus, categoryMenusLength: column.categoryMenus?.length || 0, }); const response = await apiClient.post(`/table-management/tables/${selectedTable}/columns/settings`, [ columnSetting, ]); if (response.data.success) { console.log("โœ… ์ปฌ๋Ÿผ ์„ค์ • ์ €์žฅ ์„ฑ๊ณต"); // ๐Ÿ†• Category ํƒ€์ž…์ธ ๊ฒฝ์šฐ ์ปฌ๋Ÿผ ๋งคํ•‘ ์ฒ˜๋ฆฌ console.log("๐Ÿ” ์นดํ…Œ๊ณ ๋ฆฌ ์กฐ๊ฑด ์ฒดํฌ:", { isCategory: column.inputType === "category", hasCategoryMenus: !!column.categoryMenus, length: column.categoryMenus?.length || 0, }); if (column.inputType === "category") { // 1. ๋จผ์ € ๊ธฐ์กด ๋งคํ•‘ ๋ชจ๋‘ ์‚ญ์ œ console.log("๐Ÿ—‘๏ธ ๊ธฐ์กด ์นดํ…Œ๊ณ ๋ฆฌ ๋ฉ”๋‰ด ๋งคํ•‘ ์‚ญ์ œ ์‹œ์ž‘:", { tableName: selectedTable, columnName: column.columnName, }); try { const deleteResponse = await deleteColumnMappingsByColumn(selectedTable, column.columnName); console.log("๐Ÿ—‘๏ธ ๊ธฐ์กด ๋งคํ•‘ ์‚ญ์ œ ๊ฒฐ๊ณผ:", deleteResponse); } catch (error) { console.error("โŒ ๊ธฐ์กด ๋งคํ•‘ ์‚ญ์ œ ์‹คํŒจ:", error); } // 2. ์ƒˆ๋กœ์šด ๋งคํ•‘ ์ถ”๊ฐ€ (์„ ํƒ๋œ ๋ฉ”๋‰ด๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ๋งŒ) if (column.categoryMenus && column.categoryMenus.length > 0) { console.log("๐Ÿ“ฅ ์นดํ…Œ๊ณ ๋ฆฌ ๋ฉ”๋‰ด ๋งคํ•‘ ์‹œ์ž‘:", { columnName: column.columnName, categoryMenus: column.categoryMenus, count: column.categoryMenus.length, }); let successCount = 0; let failCount = 0; for (const menuObjid of column.categoryMenus) { try { const mappingResponse = await createColumnMapping({ tableName: selectedTable, logicalColumnName: column.columnName, physicalColumnName: column.columnName, menuObjid, description: `${column.displayName} (๋ฉ”๋‰ด๋ณ„ ์นดํ…Œ๊ณ ๋ฆฌ)`, }); if (mappingResponse.success) { successCount++; } else { console.error("โŒ ๋งคํ•‘ ์ƒ์„ฑ ์‹คํŒจ:", mappingResponse); failCount++; } } catch (error) { console.error(`โŒ ๋ฉ”๋‰ด ${menuObjid}์— ๋Œ€ํ•œ ๋งคํ•‘ ์ƒ์„ฑ ์‹คํŒจ:`, error); failCount++; } } if (successCount > 0 && failCount === 0) { toast.success(`์ปฌ๋Ÿผ ์„ค์ • ๋ฐ ${successCount}๊ฐœ ๋ฉ”๋‰ด ๋งคํ•‘์ด ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`); } else if (successCount > 0 && failCount > 0) { toast.warning(`์ปฌ๋Ÿผ ์„ค์ • ์ €์žฅ ์„ฑ๊ณต. ${successCount}๊ฐœ ๋ฉ”๋‰ด ๋งคํ•‘ ์„ฑ๊ณต, ${failCount}๊ฐœ ์‹คํŒจ.`); } else if (failCount > 0) { toast.error("์ปฌ๋Ÿผ ์„ค์ • ์ €์žฅ ์„ฑ๊ณต. ๋ฉ”๋‰ด ๋งคํ•‘ ์ƒ์„ฑ ์‹คํŒจ."); } } else { toast.success("์ปฌ๋Ÿผ ์„ค์ •์ด ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (๋ฉ”๋‰ด ๋งคํ•‘ ์—†์Œ)"); } } else { toast.success("์ปฌ๋Ÿผ ์„ค์ •์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."); } // ์›๋ณธ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ setOriginalColumns((prev) => prev.map((col) => (col.columnName === column.columnName ? column : col))); // ์ €์žฅ ํ›„ ๋ฐ์ดํ„ฐ ํ™•์ธ์„ ์œ„ํ•ด ๋‹ค์‹œ ๋กœ๋“œ setTimeout(() => { loadColumnTypes(selectedTable); }, 1000); } else { toast.error(response.data.message || "์ปฌ๋Ÿผ ์„ค์ • ์ €์žฅ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); } } catch (error) { // console.error("์ปฌ๋Ÿผ ์„ค์ • ์ €์žฅ ์‹คํŒจ:", error); toast.error("์ปฌ๋Ÿผ ์„ค์ • ์ €์žฅ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } }; // ์ „์ฒด ์ €์žฅ (ํ…Œ์ด๋ธ” ๋ผ๋ฒจ + ๋ชจ๋“  ์ปฌ๋Ÿผ ์„ค์ •) const saveAllSettings = async () => { if (!selectedTable) return; try { // 1. ํ…Œ์ด๋ธ” ๋ผ๋ฒจ ์ €์žฅ (๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ์—๋งŒ) if (tableLabel !== selectedTable || tableDescription) { try { await apiClient.put(`/table-management/tables/${selectedTable}/label`, { displayName: tableLabel, description: tableDescription, }); } catch (error) { // console.warn("ํ…Œ์ด๋ธ” ๋ผ๋ฒจ ์ €์žฅ ์‹คํŒจ (API ๋ฏธ๊ตฌํ˜„ ๊ฐ€๋Šฅ):", error); } } // 2. ๋ชจ๋“  ์ปฌ๋Ÿผ ์„ค์ • ์ €์žฅ if (columns.length > 0) { const columnSettings = columns.map((column) => ({ columnName: column.columnName, // ์‹ค์ œ DB ์ปฌ๋Ÿผ๋ช… (๋ณ€๊ฒฝ ๋ถˆ๊ฐ€) columnLabel: column.displayName, // ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ํ‘œ์‹œ๋ช… inputType: column.inputType || "text", detailSettings: column.detailSettings || "", description: column.description || "", codeCategory: column.codeCategory || "", codeValue: column.codeValue || "", referenceTable: column.referenceTable || "", referenceColumn: column.referenceColumn || "", displayColumn: column.displayColumn || "", // ๐ŸŽฏ Entity ์กฐ์ธ์—์„œ ํ‘œ์‹œํ•  ์ปฌ๋Ÿผ๋ช… })); // console.log("์ €์žฅํ•  ์ „์ฒด ์„ค์ •:", { tableLabel, tableDescription, columnSettings }); // ์ „์ฒด ํ…Œ์ด๋ธ” ์„ค์ •์„ ํ•œ ๋ฒˆ์— ์ €์žฅ const response = await apiClient.post( `/table-management/tables/${selectedTable}/columns/settings`, columnSettings, ); if (response.data.success) { // ๐Ÿ†• Category ํƒ€์ž… ์ปฌ๋Ÿผ๋“ค์˜ ๋ฉ”๋‰ด ๋งคํ•‘ ์ฒ˜๋ฆฌ const categoryColumns = columns.filter((col) => col.inputType === "category"); console.log("๐Ÿ“ฅ ์ „์ฒด ์ €์žฅ: ์นดํ…Œ๊ณ ๋ฆฌ ์ปฌ๋Ÿผ ํ™•์ธ", { totalColumns: columns.length, categoryColumns: categoryColumns.length, categoryColumnsData: categoryColumns.map((col) => ({ columnName: col.columnName, categoryMenus: col.categoryMenus, })), }); if (categoryColumns.length > 0) { let totalSuccessCount = 0; let totalFailCount = 0; for (const column of categoryColumns) { // 1. ๋จผ์ € ๊ธฐ์กด ๋งคํ•‘ ๋ชจ๋‘ ์‚ญ์ œ console.log("๐Ÿ—‘๏ธ ๊ธฐ์กด ์นดํ…Œ๊ณ ๋ฆฌ ๋ฉ”๋‰ด ๋งคํ•‘ ์‚ญ์ œ:", { tableName: selectedTable, columnName: column.columnName, }); try { const deleteResponse = await deleteColumnMappingsByColumn(selectedTable, column.columnName); console.log("๐Ÿ—‘๏ธ ๊ธฐ์กด ๋งคํ•‘ ์‚ญ์ œ ๊ฒฐ๊ณผ:", deleteResponse); } catch (error) { console.error("โŒ ๊ธฐ์กด ๋งคํ•‘ ์‚ญ์ œ ์‹คํŒจ:", error); } // 2. ์ƒˆ๋กœ์šด ๋งคํ•‘ ์ถ”๊ฐ€ (์„ ํƒ๋œ ๋ฉ”๋‰ด๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ๋งŒ) if (column.categoryMenus && column.categoryMenus.length > 0) { for (const menuObjid of column.categoryMenus) { try { console.log("๐Ÿ”„ ๋งคํ•‘ API ํ˜ธ์ถœ:", { tableName: selectedTable, columnName: column.columnName, menuObjid, }); const mappingResponse = await createColumnMapping({ tableName: selectedTable, logicalColumnName: column.columnName, physicalColumnName: column.columnName, menuObjid, description: `${column.displayName} (๋ฉ”๋‰ด๋ณ„ ์นดํ…Œ๊ณ ๋ฆฌ)`, }); console.log("โœ… ๋งคํ•‘ API ์‘๋‹ต:", mappingResponse); if (mappingResponse.success) { totalSuccessCount++; } else { console.error("โŒ ๋งคํ•‘ ์ƒ์„ฑ ์‹คํŒจ:", mappingResponse); totalFailCount++; } } catch (error) { console.error(`โŒ ๋ฉ”๋‰ด ${menuObjid}์— ๋Œ€ํ•œ ๋งคํ•‘ ์ƒ์„ฑ ์‹คํŒจ:`, error); totalFailCount++; } } } } console.log("๐Ÿ“Š ์ „์ฒด ๋งคํ•‘ ๊ฒฐ๊ณผ:", { totalSuccessCount, totalFailCount }); if (totalSuccessCount > 0) { toast.success(`ํ…Œ์ด๋ธ” ์„ค์ • ๋ฐ ${totalSuccessCount}๊ฐœ ์นดํ…Œ๊ณ ๋ฆฌ ๋ฉ”๋‰ด ๋งคํ•‘์ด ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`); } else if (totalFailCount > 0) { toast.warning(`ํ…Œ์ด๋ธ” ์„ค์ •์€ ์ €์žฅ๋˜์—ˆ์œผ๋‚˜ ${totalFailCount}๊ฐœ ๋ฉ”๋‰ด ๋งคํ•‘ ์ƒ์„ฑ ์‹คํŒจ.`); } else { toast.success(`ํ…Œ์ด๋ธ” '${selectedTable}' ์„ค์ •์ด ๋ชจ๋‘ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`); } } else { toast.success(`ํ…Œ์ด๋ธ” '${selectedTable}' ์„ค์ •์ด ๋ชจ๋‘ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`); } // ์ €์žฅ ์„ฑ๊ณต ํ›„ ์›๋ณธ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ setOriginalColumns([...columns]); // ํ…Œ์ด๋ธ” ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ (๋ผ๋ฒจ ๋ณ€๊ฒฝ ๋ฐ˜์˜) loadTables(); // ์ €์žฅ ํ›„ ๋ฐ์ดํ„ฐ ๋‹ค์‹œ ๋กœ๋“œ setTimeout(() => { loadColumnTypes(selectedTable, 1, pageSize); }, 1000); } else { toast.error(response.data.message || "์„ค์ • ์ €์žฅ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); } } } catch (error) { // console.error("์„ค์ • ์ €์žฅ ์‹คํŒจ:", error); toast.error("์„ค์ • ์ €์žฅ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } }; // ํ•„ํ„ฐ๋ง๋œ ํ…Œ์ด๋ธ” ๋ชฉ๋ก (๋ฉ”๋ชจ์ด์ œ์ด์…˜) const filteredTables = useMemo( () => tables.filter( (table) => table.tableName.toLowerCase().includes(searchTerm.toLowerCase()) || table.displayName.toLowerCase().includes(searchTerm.toLowerCase()), ), [tables, searchTerm], ); // ์„ ํƒ๋œ ํ…Œ์ด๋ธ” ์ •๋ณด const selectedTableInfo = tables.find((table) => table.tableName === selectedTable); useEffect(() => { loadTables(); loadCommonCodeCategories(); loadSecondLevelMenus(); }, []); // ๐ŸŽฏ ์ปฌ๋Ÿผ ๋กœ๋“œ ํ›„ ์ด๋ฏธ ์„ค์ •๋œ ์ฐธ์กฐ ํ…Œ์ด๋ธ”๋“ค์˜ ์ปฌ๋Ÿผ ์ •๋ณด ๋กœ๋“œ useEffect(() => { if (columns.length > 0) { const entityColumns = columns.filter( (col) => col.inputType === "entity" && col.referenceTable && col.referenceTable !== "none", ); entityColumns.forEach((col) => { if (col.referenceTable) { // console.log(`๐ŸŽฏ ๊ธฐ์กด Entity ์ปฌ๋Ÿผ ๋ฐœ๊ฒฌ, ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋กœ๋“œ: ${col.columnName} -> ${col.referenceTable}`); loadReferenceTableColumns(col.referenceTable); } }); } }, [columns, loadReferenceTableColumns]); // ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ ๋กœ๋“œ const loadMoreColumns = useCallback(() => { if (selectedTable && columns.length < totalColumns && !columnsLoading) { const nextPage = Math.floor(columns.length / pageSize) + 1; loadColumnTypes(selectedTable, nextPage, pageSize); } }, [selectedTable, columns.length, totalColumns, columnsLoading, pageSize, loadColumnTypes]); // ํ…Œ์ด๋ธ” ์‚ญ์ œ ํ™•์ธ const handleDeleteTableClick = (tableName: string) => { setTableToDelete(tableName); setDeleteDialogOpen(true); }; // ํ…Œ์ด๋ธ” ์‚ญ์ œ ์‹คํ–‰ const handleDeleteTable = async () => { if (!tableToDelete) return; setIsDeleting(true); try { const result = await ddlApi.dropTable(tableToDelete); if (result.success) { toast.success(`ํ…Œ์ด๋ธ” '${tableToDelete}'์ด ์„ฑ๊ณต์ ์œผ๋กœ ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`); // ์‚ญ์ œ๋œ ํ…Œ์ด๋ธ”์ด ์„ ํƒ๋œ ํ…Œ์ด๋ธ”์ด์—ˆ๋‹ค๋ฉด ์„ ํƒ ํ•ด์ œ if (selectedTable === tableToDelete) { setSelectedTable(null); setColumns([]); } // ํ…Œ์ด๋ธ” ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ await loadTables(); } else { toast.error(result.message || "ํ…Œ์ด๋ธ” ์‚ญ์ œ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); } } catch (error: any) { toast.error(error?.response?.data?.message || "ํ…Œ์ด๋ธ” ์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } finally { setIsDeleting(false); setDeleteDialogOpen(false); setTableToDelete(""); } }; // ์ฒดํฌ๋ฐ•์Šค ์„ ํƒ ํ•ธ๋“ค๋Ÿฌ const handleTableCheck = (tableName: string, checked: boolean) => { setSelectedTableIds((prev) => { const newSet = new Set(prev); if (checked) { newSet.add(tableName); } else { newSet.delete(tableName); } return newSet; }); }; // ์ „์ฒด ์„ ํƒ/ํ•ด์ œ const handleSelectAll = (checked: boolean) => { if (checked) { const filteredTables = tables.filter( (table) => table.tableName.toLowerCase().includes(searchTerm.toLowerCase()) || (table.displayName && table.displayName.toLowerCase().includes(searchTerm.toLowerCase())), ); setSelectedTableIds(new Set(filteredTables.map((table) => table.tableName))); } else { setSelectedTableIds(new Set()); } }; // ์ผ๊ด„ ์‚ญ์ œ ํ™•์ธ const handleBulkDeleteClick = () => { if (selectedTableIds.size === 0) return; setDeleteDialogOpen(true); }; // ์ผ๊ด„ ์‚ญ์ œ ์‹คํ–‰ const handleBulkDelete = async () => { if (selectedTableIds.size === 0) return; setIsDeleting(true); try { const tablesToDelete = Array.from(selectedTableIds); let successCount = 0; let failCount = 0; for (const tableName of tablesToDelete) { try { const result = await ddlApi.dropTable(tableName); if (result.success) { successCount++; // ์‚ญ์ œ๋œ ํ…Œ์ด๋ธ”์ด ์„ ํƒ๋œ ํ…Œ์ด๋ธ”์ด์—ˆ๋‹ค๋ฉด ์„ ํƒ ํ•ด์ œ if (selectedTable === tableName) { setSelectedTable(null); setColumns([]); } } else { failCount++; } } catch (error) { failCount++; } } if (successCount > 0) { toast.success(`${successCount}๊ฐœ์˜ ํ…Œ์ด๋ธ”์ด ์„ฑ๊ณต์ ์œผ๋กœ ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`); } if (failCount > 0) { toast.error(`${failCount}๊ฐœ์˜ ํ…Œ์ด๋ธ” ์‚ญ์ œ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.`); } // ์„ ํƒ ์ดˆ๊ธฐํ™” ๋ฐ ํ…Œ์ด๋ธ” ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ setSelectedTableIds(new Set()); await loadTables(); } catch (error: any) { toast.error("ํ…Œ์ด๋ธ” ์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } finally { setIsDeleting(false); setDeleteDialogOpen(false); setTableToDelete(""); } }; return (
{/* ํŽ˜์ด์ง€ ํ—ค๋” */}

{getTextFromUI(TABLE_MANAGEMENT_KEYS.PAGE_TITLE, "ํ…Œ์ด๋ธ” ํƒ€์ž… ๊ด€๋ฆฌ")}

{getTextFromUI( TABLE_MANAGEMENT_KEYS.PAGE_DESCRIPTION, "๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”๊ณผ ์ปฌ๋Ÿผ์˜ ํƒ€์ž…์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค", )}

{isSuperAdmin && (

์ตœ๊ณ  ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์œผ๋กœ ์ƒˆ ํ…Œ์ด๋ธ” ์ƒ์„ฑ ๋ฐ ์ปฌ๋Ÿผ ์ถ”๊ฐ€๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

)}
{/* DDL ๊ธฐ๋Šฅ ๋ฒ„ํŠผ๋“ค (์ตœ๊ณ  ๊ด€๋ฆฌ์ž๋งŒ) */} {isSuperAdmin && ( <> {selectedTable && ( )} )}
{/* ์ขŒ์ธก ์‚ฌ์ด๋“œ๋ฐ”: ํ…Œ์ด๋ธ” ๋ชฉ๋ก (20%) */}
{/* ๊ฒ€์ƒ‰ */}
setSearchTerm(e.target.value)} className="h-10 pl-10 text-sm" />
{/* ํ…Œ์ด๋ธ” ๋ชฉ๋ก */}
{/* ์ „์ฒด ์„ ํƒ ๋ฐ ์ผ๊ด„ ์‚ญ์ œ (์ตœ๊ณ  ๊ด€๋ฆฌ์ž๋งŒ) */} {isSuperAdmin && (
table.tableName.toLowerCase().includes(searchTerm.toLowerCase()) || (table.displayName && table.displayName.toLowerCase().includes(searchTerm.toLowerCase())), ).length > 0 && tables .filter( (table) => table.tableName.toLowerCase().includes(searchTerm.toLowerCase()) || (table.displayName && table.displayName.toLowerCase().includes(searchTerm.toLowerCase())), ) .every((table) => selectedTableIds.has(table.tableName)) } onCheckedChange={handleSelectAll} aria-label="์ „์ฒด ์„ ํƒ" /> {selectedTableIds.size > 0 && `${selectedTableIds.size}๊ฐœ ์„ ํƒ๋จ`}
{selectedTableIds.size > 0 && ( )}
)} {loading ? (
{getTextFromUI(TABLE_MANAGEMENT_KEYS.MESSAGE_LOADING_TABLES, "ํ…Œ์ด๋ธ” ๋กœ๋”ฉ ์ค‘...")}
) : tables.length === 0 ? (
{getTextFromUI(TABLE_MANAGEMENT_KEYS.MESSAGE_NO_TABLES, "ํ…Œ์ด๋ธ”์ด ์—†์Šต๋‹ˆ๋‹ค")}
) : ( tables .filter( (table) => table.tableName.toLowerCase().includes(searchTerm.toLowerCase()) || (table.displayName && table.displayName.toLowerCase().includes(searchTerm.toLowerCase())), ) .map((table) => (
{/* ์ฒดํฌ๋ฐ•์Šค (์ตœ๊ณ  ๊ด€๋ฆฌ์ž๋งŒ) */} {isSuperAdmin && ( handleTableCheck(table.tableName, checked as boolean)} aria-label={`${table.displayName || table.tableName} ์„ ํƒ`} className="mt-0.5" onClick={(e) => e.stopPropagation()} /> )}
handleTableSelect(table.tableName)}>

{table.displayName || table.tableName}

{table.description || getTextFromUI(TABLE_MANAGEMENT_KEYS.TABLE_DESCRIPTION, "์„ค๋ช… ์—†์Œ")}

์ปฌ๋Ÿผ {table.columnCount}
)) )}
{/* ์šฐ์ธก ๋ฉ”์ธ ์˜์—ญ: ์ปฌ๋Ÿผ ํƒ€์ž… ๊ด€๋ฆฌ (80%) */}
{!selectedTable ? (

{getTextFromUI(TABLE_MANAGEMENT_KEYS.SELECT_TABLE_PLACEHOLDER, "ํ…Œ์ด๋ธ”์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”")}

) : ( <> {/* ํ…Œ์ด๋ธ” ๋ผ๋ฒจ ์„ค์ • + ์ €์žฅ ๋ฒ„ํŠผ (๊ณ ์ • ์˜์—ญ) */}
setTableLabel(e.target.value)} placeholder="ํ…Œ์ด๋ธ” ํ‘œ์‹œ๋ช…" className="h-10 text-sm" />
setTableDescription(e.target.value)} placeholder="ํ…Œ์ด๋ธ” ์„ค๋ช…" className="h-10 text-sm" />
{/* ์ €์žฅ ๋ฒ„ํŠผ (ํ•ญ์ƒ ๋ณด์ด๋„๋ก ์ƒ๋‹จ์— ๋ฐฐ์น˜) */}
{columnsLoading ? (
{getTextFromUI(TABLE_MANAGEMENT_KEYS.MESSAGE_LOADING_COLUMNS, "์ปฌ๋Ÿผ ์ •๋ณด ๋กœ๋”ฉ ์ค‘...")}
) : columns.length === 0 ? (
{getTextFromUI(TABLE_MANAGEMENT_KEYS.MESSAGE_NO_COLUMNS, "์ปฌ๋Ÿผ์ด ์—†์Šต๋‹ˆ๋‹ค")}
) : (
{/* ์ปฌ๋Ÿผ ํ—ค๋” (๊ณ ์ •) */}
์ปฌ๋Ÿผ๋ช…
๋ผ๋ฒจ
์ž…๋ ฅ ํƒ€์ž…
์„ค๋ช…
{/* ์ปฌ๋Ÿผ ๋ฆฌ์ŠคํŠธ (์Šคํฌ๋กค ์˜์—ญ) */}
{ const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; // ์Šคํฌ๋กค์ด ๋์— ๊ฐ€๊นŒ์›Œ์ง€๋ฉด ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ ๋กœ๋“œ if (scrollHeight - scrollTop <= clientHeight + 100) { loadMoreColumns(); } }} > {columns.map((column, index) => (
{column.columnName}
handleLabelChange(column.columnName, e.target.value)} placeholder={column.columnName} className="h-8 text-xs" />
{/* ์ž…๋ ฅ ํƒ€์ž… ์„ ํƒ */} {/* ์ž…๋ ฅ ํƒ€์ž…์ด 'code'์ธ ๊ฒฝ์šฐ ๊ณตํ†ต์ฝ”๋“œ ์„ ํƒ */} {column.inputType === "code" && ( <> {/* ๊ณ„์ธต๊ตฌ์กฐ ์—ญํ•  ์„ ํƒ */} {column.codeCategory && column.codeCategory !== "none" && ( )} )} {/* ์ž…๋ ฅ ํƒ€์ž…์ด 'category'์ธ ๊ฒฝ์šฐ 2๋ ˆ๋ฒจ ๋ฉ”๋‰ด ๋‹ค์ค‘ ์„ ํƒ */} {column.inputType === "category" && (
{secondLevelMenus.length === 0 ? (

2๋ ˆ๋ฒจ ๋ฉ”๋‰ด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋ฉ”๋‰ด๋ฅผ ์„ ํƒํ•˜์ง€ ์•Š์œผ๋ฉด ๋ชจ๋“  ๋ฉ”๋‰ด์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

) : ( secondLevelMenus.map((menu) => { // menuObjid๋ฅผ ์ˆซ์ž๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋น„๊ต const menuObjidNum = Number(menu.menuObjid); const isChecked = (column.categoryMenus || []).includes(menuObjidNum); return (
{ const currentMenus = column.categoryMenus || []; const newMenus = e.target.checked ? [...currentMenus, menuObjidNum] : currentMenus.filter((id) => id !== menuObjidNum); setColumns((prev) => prev.map((col) => col.columnName === column.columnName ? { ...col, categoryMenus: newMenus } : col, ), ); }} className="text-primary focus:ring-ring h-4 w-4 rounded border-gray-300 focus:ring-2" />
); }) )}
{column.categoryMenus && column.categoryMenus.length > 0 && (

{column.categoryMenus.length}๊ฐœ ๋ฉ”๋‰ด ์„ ํƒ๋จ

)}
)} {/* ์ž…๋ ฅ ํƒ€์ž…์ด 'entity'์ธ ๊ฒฝ์šฐ ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์„ ํƒ */} {column.inputType === "entity" && ( <> {/* ์ฐธ์กฐ ํ…Œ์ด๋ธ” */}
{/* ์กฐ์ธ ์ปฌ๋Ÿผ */} {column.referenceTable && column.referenceTable !== "none" && (
)} {/* ํ‘œ์‹œ ์ปฌ๋Ÿผ */} {column.referenceTable && column.referenceTable !== "none" && column.referenceColumn && column.referenceColumn !== "none" && (
)} {/* ์„ค์ • ์™„๋ฃŒ ํ‘œ์‹œ */} {column.referenceTable && column.referenceTable !== "none" && column.referenceColumn && column.referenceColumn !== "none" && column.displayColumn && column.displayColumn !== "none" && (
โœ“ ์„ค์ • ์™„๋ฃŒ
)} )}
handleColumnChange(index, "description", e.target.value)} placeholder="์„ค๋ช…" className="h-8 w-full text-xs" />
))} {/* ๋กœ๋”ฉ ํ‘œ์‹œ */} {columnsLoading && (
๋” ๋งŽ์€ ์ปฌ๋Ÿผ ๋กœ๋”ฉ ์ค‘...
)}
{/* ํŽ˜์ด์ง€ ์ •๋ณด (๊ณ ์ • ํ•˜๋‹จ) */}
{columns.length} / {totalColumns} ์ปฌ๋Ÿผ ํ‘œ์‹œ๋จ
)} )}
{/* DDL ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ๋“ค */} {isSuperAdmin && ( <> { setCreateTableModalOpen(false); setDuplicateModalMode("create"); setDuplicateSourceTable(null); }} onSuccess={async (result) => { const message = duplicateModalMode === "duplicate" ? "ํ…Œ์ด๋ธ”์ด ์„ฑ๊ณต์ ์œผ๋กœ ๋ณต์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!" : "ํ…Œ์ด๋ธ”์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!"; toast.success(message); // ํ…Œ์ด๋ธ” ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ await loadTables(); // ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ํ…Œ์ด๋ธ” ์ž๋™ ์„ ํƒ ๋ฐ ์ปฌ๋Ÿผ ๋กœ๋“œ if (result.data?.tableName) { setSelectedTable(result.data.tableName); setCurrentPage(1); setColumns([]); await loadColumnTypes(result.data.tableName, 1, pageSize); } // ์„ ํƒ ์ดˆ๊ธฐํ™” setSelectedTableIds(new Set()); // ์ƒํƒœ ์ดˆ๊ธฐํ™” setDuplicateModalMode("create"); setDuplicateSourceTable(null); }} mode={duplicateModalMode} sourceTableName={duplicateSourceTable || undefined} /> setAddColumnModalOpen(false)} tableName={selectedTable || ""} onSuccess={async (result) => { toast.success("์ปฌ๋Ÿผ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค!"); // ํ…Œ์ด๋ธ” ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ (์ปฌ๋Ÿผ ์ˆ˜ ์—…๋ฐ์ดํŠธ) await loadTables(); // ์„ ํƒ๋œ ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ - ํŽ˜์ด์ง€ ๋ฆฌ์…‹ if (selectedTable) { setCurrentPage(1); setColumns([]); // ๊ธฐ์กด ์ปฌ๋Ÿผ ๋ชฉ๋ก ์ดˆ๊ธฐํ™” await loadColumnTypes(selectedTable, 1, pageSize); } }} /> setDdlLogViewerOpen(false)} /> {/* ํ…Œ์ด๋ธ” ๋กœ๊ทธ ๋ทฐ์–ด */} {/* ํ…Œ์ด๋ธ” ์‚ญ์ œ ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ */} {selectedTableIds.size > 0 ? "ํ…Œ์ด๋ธ” ์ผ๊ด„ ์‚ญ์ œ ํ™•์ธ" : "ํ…Œ์ด๋ธ” ์‚ญ์ œ ํ™•์ธ"} {selectedTableIds.size > 0 ? ( <> ์„ ํƒ๋œ {selectedTableIds.size}๊ฐœ์˜ ํ…Œ์ด๋ธ”์„ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?
์ด ์ž‘์—…์€ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ) : ( <>์ •๋ง๋กœ ํ…Œ์ด๋ธ”์„ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์ด ์ž‘์—…์€ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. )}
{selectedTableIds.size === 0 && tableToDelete && (

๊ฒฝ๊ณ 

ํ…Œ์ด๋ธ” {tableToDelete}๊ณผ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ์˜๊ตฌ์ ์œผ๋กœ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.

)} {selectedTableIds.size > 0 && (

๊ฒฝ๊ณ 

๋‹ค์Œ ํ…Œ์ด๋ธ”๋“ค๊ณผ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ์˜๊ตฌ์ ์œผ๋กœ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค:

    {Array.from(selectedTableIds).map((tableName) => (
  • {tableName}
  • ))}
)}
)} {/* Scroll to Top ๋ฒ„ํŠผ */}
); }