diff --git a/frontend/app/(main)/admin/systemMng/dataflow/edit/[diagramId]/page.tsx b/frontend/app/(main)/admin/systemMng/dataflow/edit/[diagramId]/page.tsx index f3a8a895..54e4adf8 100644 --- a/frontend/app/(main)/admin/systemMng/dataflow/edit/[diagramId]/page.tsx +++ b/frontend/app/(main)/admin/systemMng/dataflow/edit/[diagramId]/page.tsx @@ -80,8 +80,8 @@ export default function DataFlowEditPage() {
diff --git a/frontend/app/api/admin/layouts/list/route.ts b/frontend/app/api/admin/layouts/list/route.ts index dc57eecd..f5748cb1 100644 --- a/frontend/app/api/admin/layouts/list/route.ts +++ b/frontend/app/api/admin/layouts/list/route.ts @@ -10,14 +10,14 @@ export async function GET(request: NextRequest) { const codeLayouts = LayoutRegistry.getAllLayouts().map((layout) => ({ id: layout.id, name: layout.name, - nameEng: layout.nameEng, + nameEng: layout.name_eng, description: layout.description, category: layout.category, type: "code", // 코드로 생성된 레이아웃 - isActive: layout.isActive !== false, + isActive: layout.is_active !== false, tags: layout.tags || [], metadata: layout.metadata, - zones: layout.defaultZones?.length || 0, + zones: layout.default_zones?.length || 0, })); // 레지스트리 통계 diff --git a/frontend/app/test-type-safety/simple-test.tsx b/frontend/app/test-type-safety/simple-test.tsx index f9387d8a..b49c207b 100644 --- a/frontend/app/test-type-safety/simple-test.tsx +++ b/frontend/app/test-type-safety/simple-test.tsx @@ -92,11 +92,11 @@ export default function SimpleTypeSafetyTest() { const testWidget: WidgetComponent = { id: "test-widget", type: "widget", - widgetType: "text", + widget_type: "text", position: { x: 0, y: 0 }, size: { width: 200, height: 40 }, label: "테스트", - webTypeConfig: {}, + web_type_config: {}, }; const testContainer = { @@ -128,38 +128,38 @@ export default function SimpleTypeSafetyTest() { { id: "userName", type: "widget", - widgetType: "text", + widget_type: "text", position: { x: 0, y: 0 }, size: { width: 200, height: 40 }, label: "사용자명", - columnName: "user_name", - webTypeConfig: {}, + column_name: "user_name", + web_type_config: {}, }, { id: "isActive", type: "widget", - widgetType: "checkbox", + widget_type: "checkbox", position: { x: 0, y: 50 }, size: { width: 200, height: 40 }, label: "활성화", - columnName: "is_active", - webTypeConfig: {}, + column_name: "is_active", + web_type_config: {}, }, ]; const processedData: Record = {}; formComponents.forEach((component) => { const fieldValue = formData[component.id as keyof typeof formData]; - if (fieldValue !== undefined && component.columnName) { - switch (component.widgetType) { + if (fieldValue !== undefined && component.column_name) { + switch (component.widget_type) { case "text": - processedData[component.columnName] = String(fieldValue); + processedData[component.column_name] = String(fieldValue); break; case "checkbox": - processedData[component.columnName] = booleanToYN(Boolean(fieldValue)); + processedData[component.column_name] = booleanToYN(Boolean(fieldValue)); break; default: - processedData[component.columnName] = fieldValue; + processedData[component.column_name] = fieldValue; } } }); diff --git a/frontend/components/GlobalFileViewer.tsx b/frontend/components/GlobalFileViewer.tsx index 0fb9c2ca..806c7594 100644 --- a/frontend/components/GlobalFileViewer.tsx +++ b/frontend/components/GlobalFileViewer.tsx @@ -98,12 +98,12 @@ export const GlobalFileViewer: React.FC = ({ // 탭별 필터링 if (tab === "images") { filtered = files.filter(file => { - const ext = file.realFileName?.split('.').pop()?.toLowerCase() || ''; + const ext = file.real_file_name?.split('.').pop()?.toLowerCase() || ''; return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'].includes(ext); }); } else if (tab === "documents") { filtered = files.filter(file => { - const ext = file.realFileName?.split('.').pop()?.toLowerCase() || ''; + const ext = file.real_file_name?.split('.').pop()?.toLowerCase() || ''; return ['txt', 'md', 'doc', 'docx', 'pdf', 'rtf', 'hwp', 'hwpx'].includes(ext); }); } else if (tab === "recent") { @@ -116,8 +116,8 @@ export const GlobalFileViewer: React.FC = ({ if (query.trim()) { const lowerQuery = query.toLowerCase(); filtered = filtered.filter(file => - file.realFileName?.toLowerCase().includes(lowerQuery) || - file.savedFileName?.toLowerCase().includes(lowerQuery) || + file.real_file_name?.toLowerCase().includes(lowerQuery) || + file.saved_file_name?.toLowerCase().includes(lowerQuery) || file.uploadPage?.toLowerCase().includes(lowerQuery) ); } @@ -129,10 +129,11 @@ export const GlobalFileViewer: React.FC = ({ const handleDownload = async (file: GlobalFileInfo) => { try { await downloadFile({ - fileId: file.objid, - originalName: file.realFileName || file.savedFileName || "download", + fileId: file.id, + server_filename: file.server_filename || "", + original_name: file.real_file_name || file.saved_file_name || "download", }); - toast.success(`파일 다운로드 시작: ${file.realFileName}`); + toast.success(`파일 다운로드 시작: ${file.real_file_name}`); } catch (error) { console.error("파일 다운로드 오류:", error); showErrorToast("파일 다운로드에 실패했습니다", error, { guidance: "파일이 존재하는지 확인하고 다시 시도해 주세요." }); @@ -147,9 +148,9 @@ export const GlobalFileViewer: React.FC = ({ // 파일 접근 불가능하게 설정 (삭제 대신) const handleRemove = (file: GlobalFileInfo) => { - GlobalFileManager.setFileAccessible(file.objid, false); + GlobalFileManager.setFileAccessible(file.id, false); refreshFiles(); - toast.success(`파일이 목록에서 제거되었습니다: ${file.realFileName}`); + toast.success(`파일이 목록에서 제거되었습니다: ${file.real_file_name}`); }; // 초기 로드 및 검색/탭 변경 시 필터링 @@ -224,16 +225,16 @@ export const GlobalFileViewer: React.FC = ({
) : ( filteredFiles.map((file) => ( - +
- {getFileIcon(file.realFileName || file.savedFileName || "", 20)} + {getFileIcon(file.real_file_name || file.saved_file_name || "", 20)}
- {file.realFileName || file.savedFileName} + {file.real_file_name || file.saved_file_name}
- {formatFileSize(file.fileSize)} + {formatFileSize(file.size)}
{new Date(file.uploadTime).toLocaleDateString()} diff --git a/frontend/components/admin/CodeDetailPanel.tsx b/frontend/components/admin/CodeDetailPanel.tsx index bab3ad9f..ffdb9361 100644 --- a/frontend/components/admin/CodeDetailPanel.tsx +++ b/frontend/components/admin/CodeDetailPanel.tsx @@ -176,7 +176,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { })), }); }, - getItemId: (code: CodeInfo) => code.code_value, + getItemId: (code: CodeInfo) => code.code_value || "", }); // 새 코드 생성 @@ -213,7 +213,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { try { await deleteCodeMutation.mutateAsync({ categoryCode, - codeValue: deletingCode.code_value, + codeValue: deletingCode.code_value || "", }); setShowDeleteModal(false); @@ -298,7 +298,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { <> code.code_value)} + items={visibleCodes.map((code) => code.code_value || "")} strategy={verticalListSortingStrategy} > {visibleCodes.map((code, index) => { diff --git a/frontend/components/admin/CodeFormModal.tsx b/frontend/components/admin/CodeFormModal.tsx index b5a8847b..3e1e9737 100644 --- a/frontend/components/admin/CodeFormModal.tsx +++ b/frontend/components/admin/CodeFormModal.tsx @@ -64,7 +64,7 @@ export function CodeFormModal({ categoryCode, "codeName", validationStates.codeName.value, - isEditing ? editingCode?.codeValue || editingCode?.code_value : undefined, + isEditing ? editingCode?.code_value : undefined, validationStates.codeName.enabled, ); @@ -96,22 +96,22 @@ export function CodeFormModal({ if (isOpen) { if (isEditing && editingCode) { // 수정 모드: 기존 데이터 로드 (codeValue는 표시용으로만 설정) - const parentValue = editingCode.parentCodeValue || editingCode.parent_code_value || ""; + const parentValue = editingCode.parent_code_value || ""; form.reset({ - codeName: editingCode.codeName || editingCode.code_name, - codeNameEng: editingCode.codeNameEng || editingCode.code_name_eng || "", + codeName: editingCode.code_name, + codeNameEng: editingCode.code_name_eng || "", description: editingCode.description || "", - sortOrder: editingCode.sortOrder || editingCode.sort_order, - isActive: (editingCode.isActive || editingCode.is_active) as "Y" | "N", + sortOrder: editingCode.sort_order, + isActive: editingCode.is_active as "Y" | "N", parentCodeValue: parentValue, }); // codeValue는 별도로 설정 (표시용) - form.setValue("codeValue" as any, editingCode.codeValue || editingCode.code_value); + form.setValue("codeValue" as any, editingCode.code_value); } else { // 새 코드 모드: 자동 순서 계산 - const maxSortOrder = codes.length > 0 ? Math.max(...codes.map((c) => c.sortOrder || c.sort_order || 0)) : 0; + const maxSortOrder = codes.length > 0 ? Math.max(...codes.map((c) => c.sort_order || 0)) : 0; // 기본 부모 코드가 있으면 설정 (하위 코드 추가 시) const parentValue = defaultParentCode || ""; @@ -137,7 +137,7 @@ export function CodeFormModal({ // 수정 await updateCodeMutation.mutateAsync({ categoryCode, - codeValue: editingCode.codeValue || editingCode.code_value || "", + codeValue: editingCode.code_value || "", data: data as UpdateCodeData, }); } else { @@ -252,9 +252,9 @@ export function CodeFormModal({
{(() => { - const parentCode = codes.find((c) => (c.codeValue || c.code_value) === defaultParentCode); + const parentCode = codes.find((c) => c.code_value === defaultParentCode); return parentCode - ? `${parentCode.codeName || parentCode.code_name} (${defaultParentCode})` + ? `${parentCode.code_name} (${defaultParentCode})` : defaultParentCode; })()}
diff --git a/frontend/components/admin/CollectionConfigModal.tsx b/frontend/components/admin/CollectionConfigModal.tsx index ea099bfa..fa25402f 100644 --- a/frontend/components/admin/CollectionConfigModal.tsx +++ b/frontend/components/admin/CollectionConfigModal.tsx @@ -5,8 +5,8 @@ import { Dialog, DialogContent, DialogHeader, - - + DialogTitle, + DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; diff --git a/frontend/components/admin/ColumnDefinitionTable.tsx b/frontend/components/admin/ColumnDefinitionTable.tsx index 5ad02693..dee7761a 100644 --- a/frontend/components/admin/ColumnDefinitionTable.tsx +++ b/frontend/components/admin/ColumnDefinitionTable.tsx @@ -106,12 +106,12 @@ export function ColumnDefinitionTable({ columns, onChange, disabled = false }: C } // 입력타입 검증 - if (!column.inputType) { + if (!column.input_type) { errors.push("입력타입을 선택해주세요"); } // 길이 검증 (길이를 지원하는 타입인 경우) - const inputTypeOption = INPUT_TYPE_OPTIONS.find((opt) => opt.value === column.inputType); + const inputTypeOption = INPUT_TYPE_OPTIONS.find((opt) => opt.value === column.input_type) as any; if (inputTypeOption?.supportsLength && column.length !== undefined) { if (column.length < VALIDATION_RULES.columnLength.min || column.length > VALIDATION_RULES.columnLength.max) { errors.push(VALIDATION_RULES.columnLength.errorMessage); @@ -131,8 +131,8 @@ export function ColumnDefinitionTable({ columns, onChange, disabled = false }: C * 입력타입 변경 시 길이 기본값 설정 */ const handleInputTypeChange = (index: number, inputType: string) => { - const inputTypeOption = INPUT_TYPE_OPTIONS.find((opt) => opt.value === inputType); - const updates: Partial = { inputType: inputType as any }; + const inputTypeOption = INPUT_TYPE_OPTIONS.find((opt) => opt.value === inputType) as any; + const updates: Partial = { input_type: inputType as any }; // 길이를 지원하는 타입이고 현재 길이가 없으면 기본값 설정 if (inputTypeOption?.supportsLength && !columns[index].length && inputTypeOption.defaultLength) { @@ -183,7 +183,7 @@ export function ColumnDefinitionTable({ columns, onChange, disabled = false }: C {columns.map((column, index) => { - const inputTypeOption = INPUT_TYPE_OPTIONS.find((opt) => opt.value === column.inputType); + const inputTypeOption = INPUT_TYPE_OPTIONS.find((opt) => opt.value === column.input_type) as any; const rowErrors = validationErrors[index] || []; const hasRowError = rowErrors.length > 0; @@ -220,7 +220,7 @@ export function ColumnDefinitionTable({ columns, onChange, disabled = false }: C updateColumn(index, { defaultValue: e.target.value })} + value={column.default_value || ""} + onChange={(e) => updateColumn(index, { default_value: e.target.value })} placeholder="기본값" disabled={disabled} className="text-sm" diff --git a/frontend/components/admin/CompanyDeleteDialog.tsx b/frontend/components/admin/CompanyDeleteDialog.tsx index 7f8a9145..cbd7143e 100644 --- a/frontend/components/admin/CompanyDeleteDialog.tsx +++ b/frontend/components/admin/CompanyDeleteDialog.tsx @@ -37,9 +37,9 @@ export function CompanyDeleteDialog({ const [isDeleting, setIsDeleting] = useState(false); // 다이얼로그가 열려있지 않으면 렌더링하지 않음 - if (!deleteState.isOpen || !deleteState.targetCompany) return null; + if (!deleteState.isOpen || !deleteState.target_company) return null; - const { targetCompany } = deleteState; + const targetCompany = deleteState.target_company; // 삭제 확인 처리 const handleConfirm = async () => { diff --git a/frontend/components/admin/CompanyFormModal.tsx b/frontend/components/admin/CompanyFormModal.tsx index 74c9a1bb..8830836c 100644 --- a/frontend/components/admin/CompanyFormModal.tsx +++ b/frontend/components/admin/CompanyFormModal.tsx @@ -42,7 +42,7 @@ export function CompanyFormModal({ // 모달이 열려있지 않으면 렌더링하지 않음 if (!modalState.isOpen) return null; - const { mode, formData, selectedCompany } = modalState; + const { mode, form_data: formData, selected_company: selectedCompany } = modalState; const isEditMode = mode === "edit"; // 사업자등록번호 변경 처리 @@ -112,17 +112,9 @@ export function CompanyFormModal({ return ( - {isEditMode ? "회사 정보 수정" : "새 회사 등록"} @@ -237,18 +229,18 @@ export function CompanyFormModal({ )} {/* 수정 모드일 때 추가 정보 표시 */} - {isEditMode && modalState.selectedCompany && ( + {isEditMode && modalState.selected_company && (

- 회사 코드: {modalState.selectedCompany.company_code} + 회사 코드: {modalState.selected_company.company_code}

- 등록자: {modalState.selectedCompany.writer} + 등록자: {modalState.selected_company.writer}

등록일:{" "} - {new Date(modalState.selectedCompany.regdate).toLocaleDateString("ko-KR")} + {new Date(modalState.selected_company.regdate).toLocaleDateString("ko-KR")}

diff --git a/frontend/components/admin/CompanyTable.tsx b/frontend/components/admin/CompanyTable.tsx index e20fb806..86049d4b 100644 --- a/frontend/components/admin/CompanyTable.tsx +++ b/frontend/components/admin/CompanyTable.tsx @@ -24,7 +24,7 @@ export function CompanyTable({ companies, isLoading, onEdit, onDelete }: Company // 디스크 사용량 포맷팅 함수 const formatDiskUsage = (company: Company) => { - if (!company.diskUsage) { + if (!company.disk_usage) { return (
@@ -33,7 +33,7 @@ export function CompanyTable({ companies, isLoading, onEdit, onDelete }: Company ); } - const { fileCount, totalSizeMB } = company.diskUsage; + const { file_count: fileCount, total_size_mb: totalSizeMB } = company.disk_usage; return (
diff --git a/frontend/components/admin/CreateTableModal.tsx b/frontend/components/admin/CreateTableModal.tsx index a237f47a..c50c8e0a 100644 --- a/frontend/components/admin/CreateTableModal.tsx +++ b/frontend/components/admin/CreateTableModal.tsx @@ -33,12 +33,12 @@ import { RESERVED_WORDS, } from "../../types/ddl"; -export function CreateTableModal({ - isOpen, - onClose, - onSuccess, +export function CreateTableModal({ + isOpen, + onClose, + onSuccess, mode = "create", - sourceTableName + source_table_name: sourceTableName }: CreateTableModalProps) { const isDuplicateMode = mode === "duplicate" && sourceTableName; @@ -48,7 +48,7 @@ export function CreateTableModal({ { name: "", label: "", - inputType: "text", + input_type: "text", nullable: true, order: 1, }, @@ -69,7 +69,7 @@ export function CreateTableModal({ { name: "", label: "", - inputType: "text", + input_type: "text", nullable: true, order: 1, }, @@ -123,10 +123,10 @@ export function CreateTableModal({ // 2. 컬럼 정보 변환 const loadedColumns: CreateColumnDefinition[] = columnsList.map((col, idx) => ({ - name: col.columnName, - label: col.displayName || col.columnName, - inputType: col.webType || col.inputType || "text", - nullable: col.isNullable === "YES", + name: col.column_name, + label: col.display_name || col.column_name, + input_type: col.web_type || col.input_type || "text", + nullable: col.is_nullable === "YES", order: idx + 1, description: col.description, })); @@ -215,7 +215,7 @@ export function CreateTableModal({ { name: "", label: "", - inputType: "text", + input_type: "text", nullable: true, order: columns.length + 1, }, @@ -231,7 +231,7 @@ export function CreateTableModal({ return; } - const validColumns = columns.filter((col) => col.name && col.inputType); + const validColumns = columns.filter((col) => col.name && col.input_type); if (validColumns.length === 0) { toast.error("최소 1개의 유효한 컬럼이 필요합니다."); return; @@ -240,7 +240,7 @@ export function CreateTableModal({ setValidating(true); try { const result = await ddlApi.validateTableCreation({ - tableName, + table_name: tableName, columns: validColumns, description, }); @@ -269,7 +269,7 @@ export function CreateTableModal({ return; } - const validColumns = columns.filter((col) => col.name && col.inputType); + const validColumns = columns.filter((col) => col.name && col.input_type); if (validColumns.length === 0) { toast.error("최소 1개의 유효한 컬럼이 필요합니다."); return; @@ -278,7 +278,7 @@ export function CreateTableModal({ setLoading(true); try { const result = await ddlApi.createTable({ - tableName, + table_name: tableName, columns: validColumns, description, }); @@ -318,7 +318,7 @@ export function CreateTableModal({ /** * 폼 유효성 확인 */ - const isFormValid = !tableNameError && tableName && columns.some((col) => col.name && col.inputType); + const isFormValid = !tableNameError && tableName && columns.some((col) => col.name && col.input_type); return ( diff --git a/frontend/components/admin/DDLLogViewer.tsx b/frontend/components/admin/DDLLogViewer.tsx index e8949be4..d698d62f 100644 --- a/frontend/components/admin/DDLLogViewer.tsx +++ b/frontend/components/admin/DDLLogViewer.tsx @@ -144,8 +144,8 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) { * 성공률 계산 */ const getSuccessRate = (stats: DDLStatistics) => { - if (stats.totalExecutions === 0) return 0; - return Math.round((stats.successfulExecutions / stats.totalExecutions) * 100); + if (stats.total_executions === 0) return 0; + return Math.round((stats.successful_executions / stats.total_executions) * 100); }; return ( @@ -314,7 +314,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) { 전체 실행 -
{statistics.totalExecutions}
+
{statistics.total_executions}
@@ -323,7 +323,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) { 성공 -
{statistics.successfulExecutions}
+
{statistics.successful_executions}
@@ -332,7 +332,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) { 실패 -
{statistics.failedExecutions}
+
{statistics.failed_executions}
@@ -353,7 +353,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) { DDL 타입별 실행 횟수 - {Object.entries(statistics.byDDLType).map(([type, count]) => ( + {Object.entries(statistics.by_ddl_type).map(([type, count]) => (
{type} {count}회 @@ -367,7 +367,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) { 사용자별 실행 횟수 - {Object.entries(statistics.byUser).map(([user, count]) => ( + {Object.entries(statistics.by_user).map(([user, count]) => (
{user} {count}회 @@ -378,7 +378,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) {
{/* 최근 실패 로그 */} - {statistics.recentFailures.length > 0 && ( + {statistics.recent_failures.length > 0 && ( 최근 실패 로그 @@ -386,7 +386,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) {
- {statistics.recentFailures.map((failure, index) => ( + {statistics.recent_failures.map((failure, index) => (
diff --git a/frontend/components/admin/DiskUsageSummary.tsx b/frontend/components/admin/DiskUsageSummary.tsx index 55bc1918..04e114ec 100644 --- a/frontend/components/admin/DiskUsageSummary.tsx +++ b/frontend/components/admin/DiskUsageSummary.tsx @@ -42,8 +42,8 @@ export function DiskUsageSummary({ diskUsageInfo, isLoading, onRefresh }: DiskUs ); } - const { summary, lastChecked } = diskUsageInfo; - const lastCheckedDate = new Date(lastChecked); + const { summary, last_checked } = diskUsageInfo; + const lastCheckedDate = new Date(last_checked); return (
@@ -70,7 +70,7 @@ export function DiskUsageSummary({ diskUsageInfo, isLoading, onRefresh }: DiskUs

총 회사

-

{summary.totalCompanies}개

+

{summary.total_companies}개

@@ -79,7 +79,7 @@ export function DiskUsageSummary({ diskUsageInfo, isLoading, onRefresh }: DiskUs

총 파일

-

{summary.totalFiles.toLocaleString()}개

+

{summary.total_files.toLocaleString()}개

@@ -88,7 +88,7 @@ export function DiskUsageSummary({ diskUsageInfo, isLoading, onRefresh }: DiskUs

총 용량

-

{summary.totalSizeMB.toFixed(1)} MB

+

{summary.total_size_mb.toFixed(1)} MB

@@ -114,9 +114,9 @@ export function DiskUsageSummary({ diskUsageInfo, isLoading, onRefresh }: DiskUs
저장소 상태 1000 ? "destructive" : summary.totalSizeMB > 500 ? "secondary" : "default"} + variant={summary.total_size_mb > 1000 ? "destructive" : summary.total_size_mb > 500 ? "secondary" : "default"} > - {summary.totalSizeMB > 1000 ? "용량 주의" : summary.totalSizeMB > 500 ? "보통" : "여유"} + {summary.total_size_mb > 1000 ? "용량 주의" : summary.total_size_mb > 500 ? "보통" : "여유"}
@@ -124,10 +124,10 @@ export function DiskUsageSummary({ diskUsageInfo, isLoading, onRefresh }: DiskUs
1000 ? "bg-destructive" : summary.totalSizeMB > 500 ? "bg-primary/60" : "bg-primary" + summary.total_size_mb > 1000 ? "bg-destructive" : summary.total_size_mb > 500 ? "bg-primary/60" : "bg-primary" }`} style={{ - width: `${Math.min((summary.totalSizeMB / 2000) * 100, 100)}%`, + width: `${Math.min((summary.total_size_mb / 2000) * 100, 100)}%`, }} />
diff --git a/frontend/components/admin/LangKeyModal.tsx b/frontend/components/admin/LangKeyModal.tsx index cf960915..c91f4772 100644 --- a/frontend/components/admin/LangKeyModal.tsx +++ b/frontend/components/admin/LangKeyModal.tsx @@ -42,9 +42,9 @@ export default function LangKeyModal({ isOpen, onClose, onSave, keyData, compani } else { // 새 키 추가 모드 - 기본값 설정 setFormData({ - companyCode: "", - menuName: "", - langKey: "", + company_code: "", + menu_name: "", + lang_key: "", description: "", }); } diff --git a/frontend/components/admin/LayoutFormModal.tsx b/frontend/components/admin/LayoutFormModal.tsx index 3a5b3b19..098ad635 100644 --- a/frontend/components/admin/LayoutFormModal.tsx +++ b/frontend/components/admin/LayoutFormModal.tsx @@ -9,10 +9,10 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Dialog, DialogContent, - - + DialogDescription, + DialogFooter, DialogHeader, - + DialogTitle, } from "@/components/ui/dialog"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; diff --git a/frontend/components/admin/MenuFormModal.tsx b/frontend/components/admin/MenuFormModal.tsx index 79d6c61d..c97b745a 100644 --- a/frontend/components/admin/MenuFormModal.tsx +++ b/frontend/components/admin/MenuFormModal.tsx @@ -146,7 +146,7 @@ export const MenuFormModal: React.FC = ({ // }); // ScreenDefinition에서는 screenId 필드를 사용 - const actualScreenId = screen['screenId'] || screen.id; + const actualScreenId = screen.screen_id; if (!actualScreenId) { console.error("❌ 화면 ID를 찾을 수 없습니다:", screen); @@ -170,14 +170,14 @@ export const MenuFormModal: React.FC = ({ setFormData((prev) => ({ ...prev, menuUrl: screenUrl, - screenCode: screen.screenCode, // 화면 코드도 함께 저장 + screenCode: screen.screen_code, // 화면 코드도 함께 저장 })); // console.log("🖥️ 화면 선택 완료:", { // screenId: screen['screenId'], // legacyId: screen.id, // actualScreenId, - // screenName: screen.screenName, + // screenName: screen.screen_name, // menuType: menuType, // formDataMenuType: formData.menu_type, // isAdminMenu, @@ -205,7 +205,7 @@ export const MenuFormModal: React.FC = ({ // POP 화면 선택 시 URL 자동 설정 const handlePopScreenSelect = (screen: ScreenDefinition) => { - const actualScreenId = screen['screenId'] || screen.id; + const actualScreenId = screen.screen_id; if (!actualScreenId) { toast.error("화면 ID를 찾을 수 없습니다."); return; @@ -244,7 +244,7 @@ export const MenuFormModal: React.FC = ({ } else if (type === "pop") { setSelectedScreen(null); if (selectedPopScreen) { - const actualScreenId = selectedPopScreen['screenId'] || selectedPopScreen.id; + const actualScreenId = selectedPopScreen.screen_id; setFormData((prev) => ({ ...prev, menuUrl: `/pop/screens/${actualScreenId}`, @@ -258,7 +258,7 @@ export const MenuFormModal: React.FC = ({ } else if (type === "screen") { setSelectedPopScreen(null); if (selectedScreen) { - const actualScreenId = selectedScreen['screenId'] || selectedScreen.id; + const actualScreenId = selectedScreen.screen_id; let screenUrl = `/screens/${actualScreenId}`; const isAdminMenu = menuType === "0" || menuType === "admin" || formData.menu_type === "0"; if (isAdminMenu) { @@ -267,7 +267,7 @@ export const MenuFormModal: React.FC = ({ setFormData((prev) => ({ ...prev, menuUrl: screenUrl, - screenCode: selectedScreen.screenCode, + screenCode: selectedScreen.screen_code, })); } else { setFormData((prev) => ({ @@ -372,7 +372,7 @@ export const MenuFormModal: React.FC = ({ // 화면 설정 함수 const setScreenFromId = () => { - const screen = screens.find((s) => s['screenId'].toString() === screenId || s.id?.toString() === screenId); + const screen = screens.find((s) => s.screen_id.toString() === screenId); if (screen) { setSelectedScreen(screen); // console.log("🖥️ 기존 메뉴의 할당된 화면 설정:", { @@ -414,7 +414,7 @@ export const MenuFormModal: React.FC = ({ const popScreenId = menuUrl.match(/\/pop\/screens\/(\d+)/)?.[1]; if (popScreenId) { const setPopScreenFromId = () => { - const screen = screens.find((s) => s['screenId'].toString() === popScreenId || s.id?.toString() === popScreenId); + const screen = screens.find((s) => s.screen_id.toString() === popScreenId); if (screen) { setSelectedPopScreen(screen); } @@ -576,12 +576,12 @@ export const MenuFormModal: React.FC = ({ const screenId = menuUrl.match(/\/screens\/(\d+)/)?.[1]; if (screenId && !selectedScreen) { console.log("🔄 화면 목록 로드 완료 - 기존 할당 화면 자동 설정"); - const screen = screens.find((s) => s['screenId'].toString() === screenId || s.id?.toString() === screenId); + const screen = screens.find((s) => s.screen_id.toString() === screenId); if (screen) { setSelectedScreen(screen); // console.log("✅ 기존 메뉴의 할당된 화면 자동 설정 완료:", { // screenId, - // screenName: screen.screenName, + // screenName: screen.screen_name, // menuUrl, // }); } @@ -614,7 +614,7 @@ export const MenuFormModal: React.FC = ({ if (menuUrl.startsWith("/pop/screens/")) { const popScreenId = menuUrl.match(/\/pop\/screens\/(\d+)/)?.[1]; if (popScreenId && !selectedPopScreen) { - const screen = screens.find((s) => s['screenId'].toString() === popScreenId || s.id?.toString() === popScreenId); + const screen = screens.find((s) => s.screen_id.toString() === popScreenId); if (screen) { setSelectedPopScreen(screen); } @@ -738,7 +738,7 @@ export const MenuFormModal: React.FC = ({ if (menuObjid > 0) { console.log("📋 화면-메뉴 관계 테이블 업데이트 시작:", { - screenId: selectedScreen['screenId'], + screenId: selectedScreen.screen_id, menuObjid, }); @@ -750,10 +750,10 @@ export const MenuFormModal: React.FC = ({ // 2. 기존 화면들 모두 제거 for (const existingScreen of existingScreens) { try { - await menuScreenApi.unassignScreenFromMenu(existingScreen['screenId'], menuObjid); - console.log(`✅ 기존 화면 제거 완료: ${existingScreen.screenName}`); + await menuScreenApi.unassignScreenFromMenu(existingScreen.screen_id, menuObjid); + console.log(`✅ 기존 화면 제거 완료: ${existingScreen.screen_name}`); } catch (unassignError) { - console.warn(`⚠️ 기존 화면 제거 실패: ${existingScreen.screenName}`, unassignError); + console.warn(`⚠️ 기존 화면 제거 실패: ${existingScreen.screen_name}`, unassignError); } } } catch (getError) { @@ -761,7 +761,7 @@ export const MenuFormModal: React.FC = ({ } // 3. 새 화면 할당 - await menuScreenApi.assignScreenToMenu(selectedScreen['screenId'], menuObjid); + await menuScreenApi.assignScreenToMenu(selectedScreen.screen_id, menuObjid); console.log("✅ 새 화면 할당 완료"); } } catch (assignError) { @@ -1009,7 +1009,7 @@ export const MenuFormModal: React.FC = ({ className="w-full justify-between" > - {selectedScreen ? selectedScreen.screenName : "화면을 선택하세요"} + {selectedScreen ? selectedScreen.screen_name : "화면을 선택하세요"} @@ -1034,28 +1034,28 @@ export const MenuFormModal: React.FC = ({ {screens .filter( (screen) => - screen.screenName.toLowerCase().includes(screenSearchText.toLowerCase()) || - screen.screenCode.toLowerCase().includes(screenSearchText.toLowerCase()), + screen.screen_name.toLowerCase().includes(screenSearchText.toLowerCase()) || + screen.screen_code.toLowerCase().includes(screenSearchText.toLowerCase()), ) .map((screen, index) => (
handleScreenSelect(screen)} className="cursor-pointer border-b px-3 py-2 last:border-b-0 hover:bg-muted" >
-
{screen.screenName}
-
{screen.screenCode}
+
{screen.screen_name}
+
{screen.screen_code}
-
ID: {screen['screenId'] || screen.id || "N/A"}
+
ID: {screen.screen_id || "N/A"}
))} {screens.filter( (screen) => - screen.screenName.toLowerCase().includes(screenSearchText.toLowerCase()) || - screen.screenCode.toLowerCase().includes(screenSearchText.toLowerCase()), + screen.screen_name.toLowerCase().includes(screenSearchText.toLowerCase()) || + screen.screen_code.toLowerCase().includes(screenSearchText.toLowerCase()), ).length === 0 &&
검색 결과가 없습니다.
}
@@ -1065,8 +1065,8 @@ export const MenuFormModal: React.FC = ({ {/* 선택된 화면 정보 표시 */} {selectedScreen && (
-
{selectedScreen.screenName}
-
코드: {selectedScreen.screenCode}
+
{selectedScreen.screen_name}
+
코드: {selectedScreen.screen_code}
생성된 URL: {formData.menuUrl}
)} @@ -1165,7 +1165,7 @@ export const MenuFormModal: React.FC = ({ className="w-full justify-between" > - {selectedPopScreen ? selectedPopScreen.screenName : "POP 화면을 선택하세요"} + {selectedPopScreen ? selectedPopScreen.screen_name : "POP 화면을 선택하세요"} @@ -1188,28 +1188,28 @@ export const MenuFormModal: React.FC = ({ {screens .filter( (screen) => - screen.screenName.toLowerCase().includes(popScreenSearchText.toLowerCase()) || - screen.screenCode.toLowerCase().includes(popScreenSearchText.toLowerCase()), + screen.screen_name.toLowerCase().includes(popScreenSearchText.toLowerCase()) || + screen.screen_code.toLowerCase().includes(popScreenSearchText.toLowerCase()), ) .map((screen, index) => (
handlePopScreenSelect(screen)} className="cursor-pointer border-b px-3 py-2 last:border-b-0 hover:bg-gray-100" >
-
{screen.screenName}
-
{screen.screenCode}
+
{screen.screen_name}
+
{screen.screen_code}
-
ID: {screen['screenId'] || screen.id || "N/A"}
+
ID: {screen.screen_id || "N/A"}
))} {screens.filter( (screen) => - screen.screenName.toLowerCase().includes(popScreenSearchText.toLowerCase()) || - screen.screenCode.toLowerCase().includes(popScreenSearchText.toLowerCase()), + screen.screen_name.toLowerCase().includes(popScreenSearchText.toLowerCase()) || + screen.screen_code.toLowerCase().includes(popScreenSearchText.toLowerCase()), ).length === 0 &&
검색 결과가 없습니다.
}
@@ -1218,8 +1218,8 @@ export const MenuFormModal: React.FC = ({ {selectedPopScreen && (
-
{selectedPopScreen.screenName}
-
코드: {selectedPopScreen.screenCode}
+
{selectedPopScreen.screen_name}
+
코드: {selectedPopScreen.screen_code}
생성된 URL: {formData.menuUrl}
)} diff --git a/frontend/components/admin/MenuPermissionsTable.tsx b/frontend/components/admin/MenuPermissionsTable.tsx index 1210a4d1..dbefd921 100644 --- a/frontend/components/admin/MenuPermissionsTable.tsx +++ b/frontend/components/admin/MenuPermissionsTable.tsx @@ -54,7 +54,7 @@ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGro const [menuTypeFilter, setMenuTypeFilter] = useState("all"); // 최고 관리자 여부 확인 - const isSuperAdmin = currentUser?.['companyCode'] === "*" && currentUser?.['userType'] === "SUPER_ADMIN"; + const isSuperAdmin = currentUser?.company_code === "*" && currentUser?.user_type === "SUPER_ADMIN"; // 회사 정보 가져오기 useEffect(() => { @@ -97,9 +97,9 @@ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGro console.log("🔍 [MenuPermissionsTable] 전체 메뉴 로드 시작", { currentUser: { - userId: currentUser['userId'], - companyCode: currentUser['companyCode'], - userType: currentUser['userType'], + userId: currentUser.user_id, + companyCode: currentUser.company_code, + userType: currentUser.user_type, }, isSuperAdmin, roleGroupCompanyCode: roleGroup.company_code, diff --git a/frontend/components/admin/SortableCodeItem.tsx b/frontend/components/admin/SortableCodeItem.tsx index ade1a49e..839f5398 100644 --- a/frontend/components/admin/SortableCodeItem.tsx +++ b/frontend/components/admin/SortableCodeItem.tsx @@ -38,7 +38,7 @@ export function SortableCodeItem({ onToggleExpand, }: SortableCodeItemProps) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ - id: code.codeValue || code.code_value || "", + id: code.code_value || "", disabled: isDragOverlay, }); const updateCodeMutation = useUpdateCode(); @@ -51,7 +51,7 @@ export function SortableCodeItem({ // 활성/비활성 토글 핸들러 const handleToggleActive = async (checked: boolean) => { try { - const codeValue = code.codeValue || code.code_value; + const codeValue = code.code_value; if (!codeValue) { return; } @@ -60,10 +60,10 @@ export function SortableCodeItem({ categoryCode, codeValue: codeValue, data: { - codeName: code.codeName || code.code_name, - codeNameEng: code.codeNameEng || code.code_name_eng || "", + codeName: code.code_name, + codeNameEng: code.code_name_eng || "", description: code.description || "", - sortOrder: code.sortOrder || code.sort_order, + sortOrder: code.sort_order, isActive: checked ? "Y" : "N", }, }); @@ -75,7 +75,7 @@ export function SortableCodeItem({ // 계층구조 깊이에 따른 들여쓰기 const depth = code.depth || 1; const indentLevel = (depth - 1) * 28; // 28px per level - const hasParent = !!(code.parentCodeValue || code.parent_code_value); + const hasParent = !!code.parent_code_value; return (
@@ -122,7 +122,7 @@ export function SortableCodeItem({ {isExpanded ? : } )} -

{code.codeName || code.code_name}

+

{code.code_name}

{/* 접힌 상태에서 자식 개수 표시 */} {hasChildren && !isExpanded && ({childCount})} {/* 깊이 표시 배지 */} @@ -150,7 +150,7 @@ export function SortableCodeItem({ )} e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} > - {code.isActive === "Y" || code.is_active === "Y" ? "활성" : "비활성"} + {code.is_active === "Y" ? "활성" : "비활성"}
-

{code.codeValue || code.code_value}

+

{code.code_value}

{/* 부모 코드 표시 */} {hasParent && (

- 상위: {code.parentCodeValue || code.parent_code_value} + 상위: {code.parent_code_value}

)} {code.description &&

{code.description}

} diff --git a/frontend/components/admin/UserHistoryModal.tsx b/frontend/components/admin/UserHistoryModal.tsx index d7686213..964cf8cb 100644 --- a/frontend/components/admin/UserHistoryModal.tsx +++ b/frontend/components/admin/UserHistoryModal.tsx @@ -78,7 +78,7 @@ export function UserHistoryModal({ isOpen, onClose, userId, userName }: UserHist setHistoryList(mappedHistoryList); setTotalItems(responseTotal); - setMaxPageSize(response.maxPageSize || 1); + setMaxPageSize(response.max_page_size || 1); } else if (response && response.success && (!response.data || response.data.length === 0)) { // 데이터가 비어있는 경우 console.log("📋 변경이력이 없습니다."); diff --git a/frontend/components/admin/UserToolbar.tsx b/frontend/components/admin/UserToolbar.tsx index f4b8241c..b77b62ff 100644 --- a/frontend/components/admin/UserToolbar.tsx +++ b/frontend/components/admin/UserToolbar.tsx @@ -28,7 +28,7 @@ export function UserToolbar({ // 통합 검색어 변경 const handleV2SearchChange = (value: string) => { onSearchChange({ - searchValue: value, + search_value: value, // 통합 검색 시 고급 검색 필드들 클리어 searchType: undefined, search_sabun: undefined, @@ -78,7 +78,7 @@ export function UserToolbar({ /> handleV2SearchChange(e.target.value)} disabled={isAdvancedSearchMode} className={`h-10 pl-10 text-sm ${ diff --git a/frontend/components/admin/dashboard/DashboardSaveModal.tsx b/frontend/components/admin/dashboard/DashboardSaveModal.tsx index aea22c6d..30bb0f05 100644 --- a/frontend/components/admin/dashboard/DashboardSaveModal.tsx +++ b/frontend/components/admin/dashboard/DashboardSaveModal.tsx @@ -83,9 +83,9 @@ export function DashboardSaveModal({ try { const [adminData, userData] = await Promise.all([menuApi.getAdminMenus(), menuApi.getUserMenus()]); - // API 응답이 배열인지 확인하고 처리 - const adminMenuList = Array.isArray(adminData) ? adminData : adminData?.data || []; - const userMenuList = Array.isArray(userData) ? userData : userData?.data || []; + // API 응답에서 data 배열 추출 + const adminMenuList = (adminData?.data || []) as any[]; + const userMenuList = (userData?.data || []) as any[]; setAdminMenus(adminMenuList); setUserMenus(userMenuList); diff --git a/frontend/components/admin/dashboard/MultiChartConfigPanel.tsx b/frontend/components/admin/dashboard/MultiChartConfigPanel.tsx index 1c332dcd..b867694d 100644 --- a/frontend/components/admin/dashboard/MultiChartConfigPanel.tsx +++ b/frontend/components/admin/dashboard/MultiChartConfigPanel.tsx @@ -29,6 +29,7 @@ export function MultiChartConfigPanel({ xAxis: string; yAxis: string[]; label?: string; + chartType?: "bar" | "line" | "area"; }> >(config.dataSourceConfigs || []); diff --git a/frontend/components/admin/dashboard/data-sources/ApiConfig.tsx b/frontend/components/admin/dashboard/data-sources/ApiConfig.tsx index 562ff80d..7768fd99 100644 --- a/frontend/components/admin/dashboard/data-sources/ApiConfig.tsx +++ b/frontend/components/admin/dashboard/data-sources/ApiConfig.tsx @@ -147,7 +147,7 @@ export function ApiConfig({ dataSource, onChange, onTestResult }: ApiConfigProps } // 외부 커넥션 ID 저장 (백엔드에서 인증 정보 조회용) - updates.external_connection_id = connection.id; + updates.externalConnectionId = connection.id; console.log("최종 업데이트:", updates); @@ -190,10 +190,10 @@ export function ApiConfig({ dataSource, onChange, onTestResult }: ApiConfigProps // 쿼리 파라미터를 배열로 정규화 (객체 형식 호환) const normalizeQueryParams = (): KeyValuePair[] => { - if (!dataSource.query_params) return []; - if (Array.isArray(dataSource.query_params)) return dataSource.query_params; + if (!dataSource.queryParams) return []; + if (Array.isArray(dataSource.queryParams)) return dataSource.queryParams; // 객체 형식이면 배열로 변환 - return Object.entries(dataSource.query_params as Record).map(([key, value]) => ({ + return Object.entries(dataSource.queryParams as Record).map(([key, value]) => ({ id: `param_${Date.now()}_${Math.random()}`, key, value, @@ -204,21 +204,21 @@ export function ApiConfig({ dataSource, onChange, onTestResult }: ApiConfigProps const addQueryParam = () => { const queryParams = normalizeQueryParams(); onChange({ - query_params: [...queryParams, { id: `param_${Date.now()}`, key: "", value: "" }], + queryParams: [...queryParams, { id: `param_${Date.now()}`, key: "", value: "" }], }); }; // 쿼리 파라미터 제거 const removeQueryParam = (id: string) => { const queryParams = normalizeQueryParams(); - onChange({ query_params: queryParams.filter((p) => p.id !== id) }); + onChange({ queryParams: queryParams.filter((p) => p.id !== id) }); }; // 쿼리 파라미터 업데이트 const updateQueryParam = (id: string, updates: Partial) => { const queryParams = normalizeQueryParams(); onChange({ - query_params: queryParams.map((p) => (p.id === id ? { ...p, ...updates } : p)), + queryParams: queryParams.map((p) => (p.id === id ? { ...p, ...updates } : p)), }); }; @@ -288,7 +288,7 @@ export function ApiConfig({ dataSource, onChange, onTestResult }: ApiConfigProps headers: headers, query_params: params, body: requestBody, - external_connection_id: dataSource.external_connection_id, // DB 토큰 등 인증 정보 조회용 + external_connection_id: dataSource.externalConnectionId, // DB 토큰 등 인증 정보 조회용 }), }); @@ -335,26 +335,26 @@ export function ApiConfig({ dataSource, onChange, onTestResult }: ApiConfigProps // JSON Path 처리 let data = apiData; - if (dataSource.json_path) { - const paths = dataSource.json_path.split("."); + if (dataSource.jsonPath) { + const paths = dataSource.jsonPath.split("."); for (const path of paths) { // 배열인 경우 인덱스 접근, 객체인 경우 키 접근 if (data === null || data === undefined) { - throw new Error(`JSON Path "${dataSource.json_path}"에서 데이터를 찾을 수 없습니다 (null/undefined)`); + throw new Error(`JSON Path "${dataSource.jsonPath}"에서 데이터를 찾을 수 없습니다 (null/undefined)`); } - + if (Array.isArray(data)) { // 배열인 경우 숫자 인덱스로 접근 시도 const index = parseInt(path); if (!isNaN(index) && index >= 0 && index < data.length) { data = data[index]; } else { - throw new Error(`JSON Path "${dataSource.json_path}"에서 배열 인덱스 "${path}"를 찾을 수 없습니다`); + throw new Error(`JSON Path "${dataSource.jsonPath}"에서 배열 인덱스 "${path}"를 찾을 수 없습니다`); } } else if (typeof data === "object" && path in data) { data = (data as Record)[path]; } else { - throw new Error(`JSON Path "${dataSource.json_path}"에서 "${path}" 키를 찾을 수 없습니다`); + throw new Error(`JSON Path "${dataSource.jsonPath}"에서 "${path}" 키를 찾을 수 없습니다`); } } } @@ -614,8 +614,8 @@ export function ApiConfig({ dataSource, onChange, onTestResult }: ApiConfigProps onChange({ json_path: e.target.value })} + value={dataSource.jsonPath || ""} + onChange={(e) => onChange({ jsonPath: e.target.value })} />

JSON 응답에서 데이터 배열의 경로 (예: data.results, items, response.data) @@ -628,8 +628,8 @@ export function ApiConfig({ dataSource, onChange, onTestResult }: ApiConfigProps

update({ fontColor: e.target.value })} + value={selected.font_color || "#000000"} + onChange={(e) => update({ font_color: e.target.value })} className="h-9 w-20 p-1" />
@@ -258,8 +258,8 @@ export function BarcodeDesignerRightPanel() {
- {selected.barcodeType === "QR" && ( + {selected.barcode_type === "QR" && ( )}
- + update({ barcodeValue: e.target.value })} - placeholder={selected.barcodeType === "QR" ? '{"part_no":"","part_name":"","spec":""}' : "123456789"} + value={selected.barcode_value || ""} + onChange={(e) => update({ barcode_value: e.target.value })} + placeholder={selected.barcode_type === "QR" ? '{"part_no":"","part_name":"","spec":""}' : "123456789"} />
update({ showBarcodeText: v })} + checked={selected.show_barcode_text !== false} + onCheckedChange={(v) => update({ show_barcode_text: v })} />
@@ -301,16 +301,16 @@ export function BarcodeDesignerRightPanel() { update({ lineWidth: Number(e.target.value) || 1 })} + value={selected.line_width || 1} + onChange={(e) => update({ line_width: Number(e.target.value) || 1 })} />
update({ lineColor: e.target.value })} + value={selected.line_color || "#000000"} + onChange={(e) => update({ line_color: e.target.value })} className="h-9 w-20 p-1" />
@@ -324,16 +324,16 @@ export function BarcodeDesignerRightPanel() { update({ lineWidth: Number(e.target.value) || 0 })} + value={selected.line_width ?? 1} + onChange={(e) => update({ line_width: Number(e.target.value) || 0 })} />
update({ lineColor: e.target.value })} + value={selected.line_color || "#000000"} + onChange={(e) => update({ line_color: e.target.value })} className="h-9 w-20 p-1" />
@@ -341,8 +341,8 @@ export function BarcodeDesignerRightPanel() { update({ backgroundColor: e.target.value })} + value={selected.background_color || "#ffffff"} + onChange={(e) => update({ background_color: e.target.value })} className="h-9 w-20 p-1" />
@@ -353,8 +353,8 @@ export function BarcodeDesignerRightPanel() {
update({ imageUrl: e.target.value })} + value={selected.image_url || ""} + onChange={(e) => update({ image_url: e.target.value })} placeholder="https://..." />

또는 나중에 업로드 기능 연동

diff --git a/frontend/components/barcode/designer/BarcodeDesignerToolbar.tsx b/frontend/components/barcode/designer/BarcodeDesignerToolbar.tsx index a7b0f598..6f57e7d9 100644 --- a/frontend/components/barcode/designer/BarcodeDesignerToolbar.tsx +++ b/frontend/components/barcode/designer/BarcodeDesignerToolbar.tsx @@ -71,7 +71,7 @@ export function BarcodeDesignerToolbar() { setCreating(true); try { const createRes = await barcodeApi.createLabel({ - labelNameKor: name, + label_name_kor: name, }); if (!createRes.success || !createRes.data?.labelId) throw new Error(createRes.message || "생성 실패"); const newId = createRes.data.labelId; @@ -79,7 +79,7 @@ export function BarcodeDesignerToolbar() { await barcodeApi.saveLayout(newId, { width_mm: widthMm, height_mm: heightMm, - components: components.map((c, i) => ({ ...c, zIndex: i })), + components: components.map((c, i) => ({ ...c, z_index: i })), }); toast({ title: "저장됨", description: "라벨이 생성되었습니다." }); @@ -145,7 +145,7 @@ export function BarcodeDesignerToolbar() { layout={{ width_mm: widthMm, height_mm: heightMm, - components: components.map((c, i) => ({ ...c, zIndex: i })), + components: components.map((c, i) => ({ ...c, z_index: i })), }} labelName={labelMaster?.label_name_kor || "라벨"} /> diff --git a/frontend/components/barcode/designer/BarcodeLabelCanvasComponent.tsx b/frontend/components/barcode/designer/BarcodeLabelCanvasComponent.tsx index 3567d916..b19e40cd 100644 --- a/frontend/components/barcode/designer/BarcodeLabelCanvasComponent.tsx +++ b/frontend/components/barcode/designer/BarcodeLabelCanvasComponent.tsx @@ -147,12 +147,12 @@ export function BarcodeLabelCanvasComponent({ component }: Props) { top: component.y, width: component.width, height: component.height, - zIndex: component.zIndex, + zIndex: component.z_index, }; const border = selected ? "2px solid #2563eb" : "1px solid transparent"; const isBarcode = component.type === "barcode"; - const isQR = component.barcodeType === "QR"; + const isQR = component.barcode_type === "QR"; const content = () => { switch (component.type) { @@ -160,9 +160,9 @@ export function BarcodeLabelCanvasComponent({ component }: Props) { return (
); } return ( ); case "image": - return component.imageUrl ? ( + return component.image_url ? ( ) : ( @@ -211,9 +211,9 @@ export function BarcodeLabelCanvasComponent({ component }: Props) {
); @@ -223,8 +223,8 @@ export function BarcodeLabelCanvasComponent({ component }: Props) { style={{ width: "100%", height: "100%", - backgroundColor: component.backgroundColor || "transparent", - border: `${component.lineWidth || 1}px solid ${component.lineColor || "#000"}`, + backgroundColor: component.background_color || "transparent", + border: `${component.line_width || 1}px solid ${component.line_color || "#000"}`, }} /> ); diff --git a/frontend/components/common/CascadingDropdown.tsx b/frontend/components/common/CascadingDropdown.tsx index 026c8868..54776822 100644 --- a/frontend/components/common/CascadingDropdown.tsx +++ b/frontend/components/common/CascadingDropdown.tsx @@ -98,27 +98,27 @@ export function CascadingDropdown({ // 부모 값 변경 시 자동 초기화 useEffect(() => { - if (config.clearOnParentChange !== false) { - if (prevParentValueRef.current !== undefined && - prevParentValueRef.current !== parentValue && + if (config.clear_on_parent_change !== false) { + if (prevParentValueRef.current !== undefined && + prevParentValueRef.current !== parentValue && value) { // 부모 값이 변경되면 현재 값 초기화 onChange?.(""); } } prevParentValueRef.current = parentValue; - }, [parentValue, config.clearOnParentChange, value, onChange]); + }, [parentValue, config.clear_on_parent_change, value, onChange]); // 부모 값이 없을 때 메시지 const getPlaceholder = () => { if (!parentValue) { - return config.emptyParentMessage || "상위 항목을 먼저 선택하세요"; + return config.empty_parent_message || "상위 항목을 먼저 선택하세요"; } if (loading) { - return config.loadingMessage || "로딩 중..."; + return config.loading_message || "로딩 중..."; } if (options.length === 0) { - return config.noOptionsMessage || "선택 가능한 항목이 없습니다"; + return config.no_options_message || "선택 가능한 항목이 없습니다"; } return placeholder || "선택하세요"; }; @@ -152,7 +152,7 @@ export function CascadingDropdown({
- {config.loadingMessage || "로딩 중..."} + {config.loading_message || "로딩 중..."}
) : ( @@ -163,8 +163,8 @@ export function CascadingDropdown({ {options.length === 0 ? (
{!parentValue - ? config.emptyParentMessage || "상위 항목을 먼저 선택하세요" - : config.noOptionsMessage || "선택 가능한 항목이 없습니다"} + ? config.empty_parent_message || "상위 항목을 먼저 선택하세요" + : config.no_options_message || "선택 가능한 항목이 없습니다"}
) : ( options.map((option) => ( diff --git a/frontend/components/common/ExcelUploadModal.tsx b/frontend/components/common/ExcelUploadModal.tsx index 087df420..6f1316c8 100644 --- a/frontend/components/common/ExcelUploadModal.tsx +++ b/frontend/components/common/ExcelUploadModal.tsx @@ -257,7 +257,7 @@ export const ExcelUploadModal: React.FC = ({ console.log("🔍 엔티티 데이터 조회:", refTable); const response = await DynamicFormApi.getTableData(refTable, { page: 1, - pageSize: 1000, + page_size: 1000, }); console.log("🔍 엔티티 데이터 응답:", response); // getTableData는 { success, data: [...] } 형식으로 반환 @@ -521,12 +521,12 @@ export const ExcelUploadModal: React.FC = ({ const typeResponse = await getTableColumns(tbl); if (typeResponse.success && typeResponse.data?.columns) { for (const tc of typeResponse.data.columns) { - if (tc.inputType === "numbering") { + if (tc.input_type === "numbering") { try { - const settings = typeof tc.detailSettings === "string" - ? JSON.parse(tc.detailSettings) : tc.detailSettings; + const settings = typeof tc.detail_settings === "string" + ? JSON.parse(tc.detail_settings) : tc.detail_settings; if (settings?.numberingRuleId) { - numberingColSet.add(tc.columnName); + numberingColSet.add(tc.column_name); } } catch { /* 파싱 실패 무시 */ } } @@ -672,7 +672,7 @@ export const ExcelUploadModal: React.FC = ({ } const categoryColumns = colResponse.data.columns.filter( - (col: any) => col.inputType === "category" + (col: any) => col.input_type === "category" ); if (categoryColumns.length === 0) { @@ -693,13 +693,13 @@ export const ExcelUploadModal: React.FC = ({ : mapping.systemColumn; const catCol = categoryColumns.find( - (cc: any) => (cc.columnName || cc.column_name) === rawName + (cc: any) => cc.column_name === rawName ); if (catCol) { mappedCategoryColumns.push({ systemCol: rawName, excelCol: mapping.excelColumn, - displayName: catCol.displayName || catCol.display_name || rawName, + displayName: catCol.display_name || rawName, }); } } @@ -968,18 +968,18 @@ export const ExcelUploadModal: React.FC = ({ if (response.success && response.data?.columns) { for (const col of response.data.columns) { - if (col.inputType === "numbering") { + if (col.input_type === "numbering") { try { const settings = - typeof col.detailSettings === "string" - ? JSON.parse(col.detailSettings) - : col.detailSettings; + typeof col.detail_settings === "string" + ? JSON.parse(col.detail_settings) + : col.detail_settings; if (settings?.numberingRuleId) { console.log( - `✅ 채번 컬럼 자동 감지: ${col.columnName} → 규칙 ID: ${settings.numberingRuleId}` + `✅ 채번 컬럼 자동 감지: ${col.column_name} → 규칙 ID: ${settings.numberingRuleId}` ); return { - columnName: col.columnName, + columnName: col.column_name, numberingRuleId: settings.numberingRuleId, }; } @@ -1173,7 +1173,7 @@ export const ExcelUploadModal: React.FC = ({ // DynamicFormApi.getTableData 사용 const existingResponse = await DynamicFormApi.getTableData(tableName, { page: 1, - pageSize: 10000, + page_size: 10000, }); console.log("📊 중복 체크용 기존 데이터 조회 결과:", existingResponse); diff --git a/frontend/components/common/MultiColumnHierarchySelect.tsx b/frontend/components/common/MultiColumnHierarchySelect.tsx index 1face82e..372174d3 100644 --- a/frontend/components/common/MultiColumnHierarchySelect.tsx +++ b/frontend/components/common/MultiColumnHierarchySelect.tsx @@ -275,8 +275,8 @@ export function MultiColumnHierarchySelect({ {largeOptions.map((code) => { - const codeValue = code.codeValue || code.code_value || ""; - const codeName = code.codeName || code.code_name || ""; + const codeValue = code.code_value || ""; + const codeName = code.code_name || ""; return ( {codeName} @@ -321,8 +321,8 @@ export function MultiColumnHierarchySelect({
) : ( mediumOptions.map((code) => { - const codeValue = code.codeValue || code.code_value || ""; - const codeName = code.codeName || code.code_name || ""; + const codeValue = code.code_value || ""; + const codeName = code.code_name || ""; return ( {codeName} @@ -368,8 +368,8 @@ export function MultiColumnHierarchySelect({
) : ( smallOptions.map((code) => { - const codeValue = code.codeValue || code.code_value || ""; - const codeName = code.codeName || code.code_name || ""; + const codeValue = code.code_value || ""; + const codeName = code.code_name || ""; return ( {codeName} diff --git a/frontend/components/common/MultiTableExcelUploadModal.tsx b/frontend/components/common/MultiTableExcelUploadModal.tsx index 586ee0d7..21166b48 100644 --- a/frontend/components/common/MultiTableExcelUploadModal.tsx +++ b/frontend/components/common/MultiTableExcelUploadModal.tsx @@ -367,14 +367,14 @@ export const MultiTableExcelUploadModal: React.FC col.inputType === "category" + (col: any) => col.input_type === "category" ); if (categoryColumns.length === 0) continue; // 매핑된 컬럼 중 카테고리 타입인 것 찾기 for (const catCol of categoryColumns) { - const catColName = catCol.columnName || catCol.column_name; - const catDisplayName = catCol.displayName || catCol.display_name || catColName; + const catColName = catCol.column_name; + const catDisplayName = catCol.display_name || catColName; // level.columns에서 해당 dbColumn 찾기 const levelCol = level.columns.find((lc) => lc.dbColumn === catColName); diff --git a/frontend/components/common/ScreenModal.tsx b/frontend/components/common/ScreenModal.tsx index 1d330d5b..00cd9b70 100644 --- a/frontend/components/common/ScreenModal.tsx +++ b/frontend/components/common/ScreenModal.tsx @@ -46,7 +46,7 @@ export const ScreenModal: React.FC = ({ className }) => { const { userId, userName, user } = useAuth(); const splitPanelContext = useSplitPanelContext(); const tabId = useTabId(); - const activeTabId = useTabStore((s) => s[s.mode].activeTabId); + const activeTabId = useTabStore((s) => s[s.mode].active_tab_id); const [modalState, setModalState] = useState({ isOpen: false, @@ -112,12 +112,12 @@ export const ScreenModal: React.FC = ({ className }) => { const bindingUpdates: Record = {}; for (const comp of screenData.components) { const db = - comp.componentConfig?.dataBinding || + comp.component_config?.dataBinding || (comp as any).dataBinding; if (!db?.sourceComponentId || !db?.sourceColumn) continue; if (db.sourceComponentId !== detail.source) continue; - const colName = (comp as any).columnName || comp.componentConfig?.columnName; + const colName = (comp as any).columnName || comp.component_config?.columnName; if (!colName) continue; const selectedRow = detail.data[0]; @@ -209,7 +209,7 @@ export const ScreenModal: React.FC = ({ className }) => { const handleOpenModal = (event: CustomEvent) => { // 활성 탭에서만 이벤트 처리 (다른 탭의 ScreenModal 인스턴스는 무시) const storeState = useTabStore.getState(); - const currentActiveTabId = storeState[storeState.mode].activeTabId; + const currentActiveTabId = storeState[storeState.mode].active_tab_id; if (tabId && tabId !== currentActiveTabId) return; const { @@ -290,7 +290,7 @@ export const ScreenModal: React.FC = ({ className }) => { // 예: 설비의 manufacturer가 소모품의 manufacturer로 들어감 // parentDataMapping에서 명시된 필드만 추출 - const parentDataMapping = splitPanelContext?.parentDataMapping || []; + const parentDataMapping = splitPanelContext?.parent_data_mapping || []; // 부모 데이터 소스 // 🔧 수정: 여러 소스를 병합 (우선순위: splitPanelParentData > selectedLeftData > 기존 formData의 링크 필드) @@ -298,7 +298,7 @@ export const ScreenModal: React.FC = ({ className }) => { // - splitPanelParentData: item_info 데이터 (screen 226에서 전달) // - selectedLeftData: customer_mng 데이터 (SplitPanel 좌측 선택) // - 기존 formData: 이전 모달에서 설정된 link 필드 (customer_code 등) - const contextData = splitPanelContext?.selectedLeftData || {}; + const contextData = splitPanelContext?.selected_left_data || {}; const eventData = splitPanelParentData && Object.keys(splitPanelParentData).length > 0 ? splitPanelParentData : {}; @@ -342,9 +342,9 @@ export const ScreenModal: React.FC = ({ className }) => { // parentDataMapping에 정의된 필드만 전달 for (const mapping of parentDataMapping) { - const sourceValue = rawParentData[mapping.sourceColumn]; + const sourceValue = rawParentData[mapping.source_column]; if (sourceValue !== undefined && sourceValue !== null) { - parentData[mapping.targetColumn] = sourceValue; + parentData[mapping.target_column] = sourceValue; } } @@ -524,7 +524,7 @@ export const ScreenModal: React.FC = ({ className }) => { const urlParams = new URLSearchParams(window.location.search); const mode = urlParams.get("mode"); const editId = urlParams.get("editId"); - const tableName = urlParams.get("tableName") || screenInfo.tableName; + const tableName = urlParams.get("tableName") || screenInfo.table_name; const groupByColumnsParam = urlParams.get("groupByColumns"); const primaryKeyColumn = urlParams.get("primaryKeyColumn"); // 🆕 Primary Key 컬럼명 @@ -723,7 +723,7 @@ export const ScreenModal: React.FC = ({ className }) => { console.log("[ScreenModal] 조건부 레이어 로드 완료:", layerDefs.length, "개", layerDefs.map((l) => ({ - id: l.id, name: l.name, conditionValue: l.conditionValue, + id: l.id, name: l.name, conditionValue: l.condition_value, componentCount: l.components.length, condition: l.condition, })) @@ -744,7 +744,7 @@ export const ScreenModal: React.FC = ({ className }) => { conditionalLayers.forEach((layer) => { if (!layer.condition) return; - const { targetComponentId, operator, value } = layer.condition; + const { target_component_id: targetComponentId, operator, value } = layer.condition; if (!targetComponentId) return; // V2 레이아웃: overrides.columnName 우선 @@ -813,7 +813,7 @@ export const ScreenModal: React.FC = ({ className }) => { const currentActiveLayerIds = conditionalLayers .filter((layer) => { if (!layer.condition) return false; - const { targetComponentId, operator, value } = layer.condition; + const { target_component_id: targetComponentId, operator, value } = layer.condition; if (!targetComponentId) return false; const allComponents = screenData?.components || []; @@ -1075,8 +1075,8 @@ export const ScreenModal: React.FC = ({ className }) => {
) : screenData ? ( diff --git a/frontend/components/dashboard/widgets/CargoListWidget.tsx b/frontend/components/dashboard/widgets/CargoListWidget.tsx index 98eda797..6d7787fb 100644 --- a/frontend/components/dashboard/widgets/CargoListWidget.tsx +++ b/frontend/components/dashboard/widgets/CargoListWidget.tsx @@ -54,8 +54,8 @@ export default function CargoListWidget({ element }: CargoListWidgetProps) { }, body: JSON.stringify({ query: element.dataSource.query, - connection_type: element.dataSource.connection_type || "current", - connectionId: element.dataSource.connectionId, + connection_type: element.dataSource.connectionType || "current", + connectionId: element.dataSource.externalConnectionId, }), }); diff --git a/frontend/components/dashboard/widgets/ChartTestWidget.tsx b/frontend/components/dashboard/widgets/ChartTestWidget.tsx index 8e53587a..4fa7e4f3 100644 --- a/frontend/components/dashboard/widgets/ChartTestWidget.tsx +++ b/frontend/components/dashboard/widgets/ChartTestWidget.tsx @@ -111,8 +111,8 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) { } const queryParams: Record = {}; - if (source.query_params) { - source.query_params.forEach((param) => { + if (source.queryParams) { + source.queryParams.forEach((param) => { if (param.key && param.value) { queryParams[param.key] = param.value; } @@ -150,15 +150,15 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) { } let apiData = result.data; - if (source.json_path) { - const pathParts = source.json_path.split("."); + if (source.jsonPath) { + const pathParts = source.jsonPath.split("."); for (const part of pathParts) { apiData = apiData?.[part]; } } const rows = Array.isArray(apiData) ? apiData : [apiData]; - return applyColumnMapping(rows, source.column_mapping); + return applyColumnMapping(rows, source.columnMapping); }; // Database 데이터 로딩 @@ -168,9 +168,9 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) { } let result; - if (source.connection_type === "external" && source.external_connection_id) { + if (source.connectionType === "external" && source.externalConnectionId) { const { ExternalDbConnectionAPI } = await import("@/lib/api/externalDbConnection"); - result = await ExternalDbConnectionAPI.executeQuery(parseInt(source.external_connection_id), source.query); + result = await ExternalDbConnectionAPI.executeQuery(parseInt(source.externalConnectionId), source.query); } else { const { dashboardApi } = await import("@/lib/api/dashboard"); @@ -190,8 +190,8 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) { throw new Error(result.message || "쿼리 실패"); } - const rows = result.rows || result.data || []; - return applyColumnMapping(rows, source.column_mapping); + const rows = (result as any).rows || result.data || []; + return applyColumnMapping(rows, source.columnMapping); }; // 초기 로드 @@ -206,7 +206,7 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) { if (!dataSources || dataSources.length === 0) return; const intervals = dataSources - .map((ds) => ds.refresh_interval) + .map((ds) => ds.refreshInterval) .filter((interval): interval is number => typeof interval === "number" && interval > 0); if (intervals.length === 0) return; diff --git a/frontend/components/dashboard/widgets/CustomerIssuesWidget.tsx b/frontend/components/dashboard/widgets/CustomerIssuesWidget.tsx index 892bed18..33b47f15 100644 --- a/frontend/components/dashboard/widgets/CustomerIssuesWidget.tsx +++ b/frontend/components/dashboard/widgets/CustomerIssuesWidget.tsx @@ -55,8 +55,8 @@ export default function CustomerIssuesWidget({ element }: CustomerIssuesWidgetPr }, body: JSON.stringify({ query: element.dataSource.query, - connection_type: element.dataSource.connection_type || "current", - connectionId: element.dataSource.connectionId, + connection_type: element.dataSource.connectionType || "current", + connectionId: element.dataSource.externalConnectionId, }), }); diff --git a/frontend/components/dashboard/widgets/DeliveryStatusSummaryWidget.tsx b/frontend/components/dashboard/widgets/DeliveryStatusSummaryWidget.tsx index 090e3036..6186d2bc 100644 --- a/frontend/components/dashboard/widgets/DeliveryStatusSummaryWidget.tsx +++ b/frontend/components/dashboard/widgets/DeliveryStatusSummaryWidget.tsx @@ -49,7 +49,7 @@ export default function DeliveryStatusSummaryWidget({ element }: DeliveryStatusS body: JSON.stringify({ query: element.dataSource.query, connectionType: element.dataSource.connectionType || "current", - connectionId: element.dataSource.connectionId, + connectionId: element.dataSource.externalConnectionId, }), }); diff --git a/frontend/components/dashboard/widgets/DeliveryTodayStatsWidget.tsx b/frontend/components/dashboard/widgets/DeliveryTodayStatsWidget.tsx index 6655bc11..a4d70fee 100644 --- a/frontend/components/dashboard/widgets/DeliveryTodayStatsWidget.tsx +++ b/frontend/components/dashboard/widgets/DeliveryTodayStatsWidget.tsx @@ -50,7 +50,7 @@ export default function DeliveryTodayStatsWidget({ element }: DeliveryTodayStats body: JSON.stringify({ query: element.dataSource.query, connectionType: element.dataSource.connectionType || "current", - connectionId: element.dataSource.connectionId, + connectionId: element.dataSource.externalConnectionId, }), }); diff --git a/frontend/components/dashboard/widgets/ExchangeWidget.tsx b/frontend/components/dashboard/widgets/ExchangeWidget.tsx index d9d9b63b..415c2b01 100644 --- a/frontend/components/dashboard/widgets/ExchangeWidget.tsx +++ b/frontend/components/dashboard/widgets/ExchangeWidget.tsx @@ -270,7 +270,7 @@ export default function ExchangeWidget({ {/* 데이터 출처 */}
-

출처: {exchangeRate.source}

+

출처: 환율 API

); diff --git a/frontend/components/dashboard/widgets/WorkHistoryWidget.tsx b/frontend/components/dashboard/widgets/WorkHistoryWidget.tsx index 0e62abef..cff82241 100644 --- a/frontend/components/dashboard/widgets/WorkHistoryWidget.tsx +++ b/frontend/components/dashboard/widgets/WorkHistoryWidget.tsx @@ -43,7 +43,7 @@ export default function WorkHistoryWidget({ element, refreshInterval = 60000 }: body: JSON.stringify({ query: element.dataSource.query, connectionType: element.dataSource.connectionType || "current", - connectionId: element.dataSource.connectionId, + connectionId: element.dataSource.externalConnectionId, }), }); diff --git a/frontend/components/dataflow/ConnectionSetupModal.tsx b/frontend/components/dataflow/ConnectionSetupModal.tsx index d2ddf035..d1ba0e2b 100644 --- a/frontend/components/dataflow/ConnectionSetupModal.tsx +++ b/frontend/components/dataflow/ConnectionSetupModal.tsx @@ -12,6 +12,7 @@ import { import { AlertDialog, AlertDialogAction, + AlertDialogFooter, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, @@ -43,15 +44,15 @@ import { toast } from "sonner"; export const ConnectionSetupModal: React.FC = ({ isOpen, connection, - companyCode, + company_code: companyCode, onConfirm, onCancel, }) => { const [config, setConfig] = useState({ - relationshipName: "", - connectionType: "simple-key", - fromColumnName: "", - toColumnName: "", + relationship_name: "", + connection_type: "simple-key", + from_column_name: "", + to_column_name: "", settings: {}, }); @@ -125,28 +126,28 @@ export const ConnectionSetupModal: React.FC = ({ actions: actionsData.map((action: Record) => ({ id: (action.id as string) || `action-${Date.now()}`, name: (action.name as string) || "새 액션", - actionType: (action.actionType as "insert" | "update" | "delete" | "upsert") || "insert", + action_type: (action.action_type as "insert" | "update" | "delete" | "upsert") || "insert", conditions: Array.isArray(action.conditions) ? (action.conditions as ConditionNode[]).map((condition) => ({ ...condition, operator: condition.operator || "=", // 기본값 보장 })) : [], - fieldMappings: Array.isArray(action.fieldMappings) - ? action.fieldMappings.map((mapping: Record) => ({ - sourceTable: (mapping.sourceTable as string) || "", - sourceField: (mapping.sourceField as string) || "", - targetTable: (mapping.targetTable as string) || "", - targetField: (mapping.targetField as string) || "", - defaultValue: (mapping.defaultValue as string) || "", - transformFunction: (mapping.transformFunction as string) || "", + field_mappings: Array.isArray(action.field_mappings) + ? action.field_mappings.map((mapping: Record) => ({ + source_table: (mapping.source_table as string) || "", + source_field: (mapping.source_field as string) || "", + target_table: (mapping.target_table as string) || "", + target_field: (mapping.target_field as string) || "", + default_value: (mapping.default_value as string) || "", + transform_function: (mapping.transformFunction as string) || "", })) : [], - splitConfig: action.splitConfig + split_config: action.split_config ? { - sourceField: ((action.splitConfig as Record).sourceField as string) || "", - delimiter: ((action.splitConfig as Record).delimiter as string) || ",", - targetField: ((action.splitConfig as Record).targetField as string) || "", + source_field: ((action.split_config as Record).source_field as string) || "", + delimiter: ((action.split_config as Record).delimiter as string) || ",", + target_field: ((action.split_config as Record).target_field as string) || "", } : undefined, })), @@ -178,8 +179,8 @@ export const ConnectionSetupModal: React.FC = ({ } setExternalCallSettings({ - configId: (externalCallData.configId as number) || undefined, - configName: (externalCallData.configName as string) || undefined, + config_id: (externalCallData.config_id as number) || undefined, + config_name: (externalCallData.config_name as string) || undefined, message: (externalCallData.message as string) || "", }); } @@ -210,25 +211,25 @@ export const ConnectionSetupModal: React.FC = ({ // 모달이 열릴 때마다 캐시 초기화 (라벨 업데이트 반영) setTableColumnsCache({}); - const fromTableName = connection.fromNode.tableName; - const toTableName = connection.toNode.tableName; - const fromDisplayName = connection.fromNode.displayName; - const toDisplayName = connection.toNode.displayName; + const fromTableName = connection.from_node.table_name; + const toTableName = connection.to_node.table_name; + const fromDisplayName = connection.from_node.display_name; + const toDisplayName = connection.to_node.display_name; // 테이블 선택 설정 setSelectedFromTable(fromTableName); setSelectedToTable(toTableName); // 기존 관계 정보가 있으면 사용, 없으면 기본값 설정 - const existingRel = connection.existingRelationship; + const existingRel = connection.existing_relationship; const connectionType = - (existingRel?.connectionType as "simple-key" | "data-save" | "external-call") || "simple-key"; + (existingRel?.connection_type as "simple-key" | "data-save" | "external-call") || "simple-key"; setConfig({ - relationshipName: existingRel?.relationshipName || `${fromDisplayName} → ${toDisplayName}`, - connectionType, - fromColumnName: "", - toColumnName: "", + relationship_name: existingRel?.relationship_name || `${fromDisplayName} → ${toDisplayName}`, + connection_type: connectionType, + from_column_name: "", + to_column_name: "", settings: existingRel?.settings || {}, }); @@ -248,17 +249,17 @@ export const ConnectionSetupModal: React.FC = ({ setSelectedToColumns([]); // 선택된 컬럼 정보가 있다면 설정 - if (connection.selectedColumnsData) { - const fromColumns = connection.selectedColumnsData[fromTableName]?.columns || []; - const toColumns = connection.selectedColumnsData[toTableName]?.columns || []; + if (connection.selected_columns_data) { + const fromColumns = connection.selected_columns_data[fromTableName]?.columns || []; + const toColumns = connection.selected_columns_data[toTableName]?.columns || []; setSelectedFromColumns(fromColumns); setSelectedToColumns(toColumns); setConfig((prev) => ({ ...prev, - fromColumnName: fromColumns.join(", "), - toColumnName: toColumns.join(", "), + from_column_name: fromColumns.join(", "), + to_column_name: toColumns.join(", "), })); } } @@ -302,8 +303,8 @@ export const ConnectionSetupModal: React.FC = ({ useEffect(() => { setConfig((prev) => ({ ...prev, - fromColumnName: selectedFromColumns.join(", "), - toColumnName: selectedToColumns.join(", "), + from_column_name: selectedFromColumns.join(", "), + to_column_name: selectedToColumns.join(", "), })); }, [selectedFromColumns, selectedToColumns]); @@ -333,12 +334,12 @@ export const ConnectionSetupModal: React.FC = ({ // 필드 매핑에서 사용되는 모든 테이블 수집 dataSaveSettings.actions?.forEach((action) => { - action.fieldMappings?.forEach((mapping) => { - if (mapping.sourceTable && !tableColumnsCache[mapping.sourceTable]) { - tablesToLoad.add(mapping.sourceTable); + action.field_mappings?.forEach((mapping) => { + if (mapping.source_table && !tableColumnsCache[mapping.source_table]) { + tablesToLoad.add(mapping.source_table); } - if (mapping.targetTable && !tableColumnsCache[mapping.targetTable]) { - tablesToLoad.add(mapping.targetTable); + if (mapping.target_table && !tableColumnsCache[mapping.target_table]) { + tablesToLoad.add(mapping.target_table); } }); }); @@ -353,7 +354,7 @@ export const ConnectionSetupModal: React.FC = ({ }, [dataSaveSettings.actions, tableColumnsCache]); // eslint-disable-line react-hooks/exhaustive-deps const handleConfirm = () => { - if (!config.relationshipName || !connection) { + if (!config.relationship_name || !connection) { toast.error("필수 정보를 모두 입력해주세요."); return; } @@ -362,7 +363,7 @@ export const ConnectionSetupModal: React.FC = ({ let settings = {}; let plan = {}; // plan 변수 선언 - switch (config.connectionType) { + switch (config.connection_type) { case "simple-key": settings = simpleKeySettings; break; @@ -371,10 +372,10 @@ export const ConnectionSetupModal: React.FC = ({ // INSERT가 아닌 액션 타입에 대한 실행조건 필수 검증 for (const action of dataSaveSettings.actions) { - if (action.actionType !== "insert") { + if (action.action_type !== "insert") { if (!action.conditions || action.conditions.length === 0) { toast.error( - `${action.actionType.toUpperCase()} 액션은 실행조건이 필수입니다. '${action.name}' 액션에 실행조건을 추가해주세요.`, + `${action.action_type.toUpperCase()} 액션은 실행조건이 필수입니다. '${action.name}' 액션에 실행조건을 추가해주세요.`, ); return; } @@ -393,7 +394,7 @@ export const ConnectionSetupModal: React.FC = ({ if (!hasValidConditions) { toast.error( - `${action.actionType.toUpperCase()} 액션은 완전한 실행조건이 필요합니다. '${action.name}' 액션에 필드, 연산자, 값을 모두 설정해주세요.`, + `${action.action_type.toUpperCase()} 액션은 완전한 실행조건이 필요합니다. '${action.name}' 액션에 필드, 연산자, 값을 모두 설정해주세요.`, ); return; } @@ -410,7 +411,7 @@ export const ConnectionSetupModal: React.FC = ({ // 기존 설정 호환성 유지 plan = { externalCall: { - configId: externalCallSettings.configId, + configId: externalCallSettings.config_id, configName: externalCallSettings.configName, message: externalCallSettings.message, }, @@ -421,7 +422,7 @@ export const ConnectionSetupModal: React.FC = ({ } // 단순 키값 연결일 때만 컬럼 선택 검증 - if (config.connectionType === "simple-key") { + if (config.connection_type === "simple-key") { if (selectedFromColumns.length === 0 || selectedToColumns.length === 0) { toast.error("선택된 컬럼이 없습니다. From과 To 테이블에서 각각 최소 1개 이상의 컬럼을 선택해주세요."); return; @@ -429,26 +430,26 @@ export const ConnectionSetupModal: React.FC = ({ } // 선택된 테이블과 컬럼 정보 사용 - const fromTableName = selectedFromTable || connection.fromNode.tableName; - const toTableName = selectedToTable || connection.toNode.tableName; + const fromTableName = selectedFromTable || connection.from_node.table_name; + const toTableName = selectedToTable || connection.to_node.table_name; // 조건부 연결 설정 데이터 준비 - const conditionalSettings = isConditionalConnection(config.connectionType) + const conditionalSettings = isConditionalConnection(config.connection_type) ? { control: { triggerType: "insert", conditionTree: conditions.length > 0 ? conditions : null, }, category: { - type: config.connectionType, + type: config.connection_type, }, plan: { sourceTable: fromTableName, targetActions: - config.connectionType === "data-save" + config.connection_type === "data-save" ? dataSaveSettings.actions.map((action) => ({ id: action.id, - actionType: action.actionType, + actionType: action.action_type, enabled: true, conditions: action.conditions?.map((condition) => { @@ -459,15 +460,15 @@ export const ConnectionSetupModal: React.FC = ({ } return baseCondition; }) || [], - fieldMappings: action.fieldMappings.map((mapping) => ({ - sourceTable: mapping.sourceTable, - sourceField: mapping.sourceField, - targetTable: mapping.targetTable, - targetField: mapping.targetField, - defaultValue: mapping.defaultValue, - transformFunction: mapping.transformFunction, + fieldMappings: action.field_mappings.map((mapping) => ({ + sourceTable: mapping.source_table, + sourceField: mapping.source_field, + targetTable: mapping.target_table, + targetField: mapping.target_field, + defaultValue: mapping.default_value, + transformFunction: mapping.transform_function, })), - splitConfig: action.splitConfig, + splitConfig: action.split_config, })) : [], }, @@ -475,17 +476,17 @@ export const ConnectionSetupModal: React.FC = ({ : {}; // 컬럼 정보는 단순 키값 연결일 때만 사용 - const finalFromColumns = config.connectionType === "simple-key" ? selectedFromColumns : []; - const finalToColumns = config.connectionType === "simple-key" ? selectedToColumns : []; + const finalFromColumns = config.connection_type === "simple-key" ? selectedFromColumns : []; + const finalToColumns = config.connection_type === "simple-key" ? selectedToColumns : []; // 메모리 기반 시스템: 관계 데이터만 생성하여 부모로 전달 const relationshipData: TableRelationship = { - relationship_name: config.relationshipName, + relationship_name: config.relationship_name, from_table_name: fromTableName, to_table_name: toTableName, from_column_name: finalFromColumns.join(","), // 여러 컬럼을 콤마로 구분 to_column_name: finalToColumns.join(","), // 여러 컬럼을 콤마로 구분 - connection_type: config.connectionType, + connection_type: config.connection_type, company_code: companyCode, settings: { ...settings, @@ -495,17 +496,17 @@ export const ConnectionSetupModal: React.FC = ({ }; // 성공 모달 표시를 위한 상태 설정 - setCreatedConnectionName(config.relationshipName); + setCreatedConnectionName(config.relationship_name); setPendingRelationshipData(relationshipData); setShowSuccessModal(true); }; const handleCancel = () => { setConfig({ - relationshipName: "", - connectionType: "simple-key", - fromColumnName: "", - toColumnName: "", + relationship_name: "", + connection_type: "simple-key", + from_column_name: "", + to_column_name: "", }); onCancel(); }; @@ -525,10 +526,10 @@ export const ConnectionSetupModal: React.FC = ({ // 연결 종류별 설정 패널 렌더링 const renderConnectionTypeSettings = () => { - console.log("🔍 [ConnectionSetupModal] renderConnectionTypeSettings - connectionType:", config.connectionType); + console.log("🔍 [ConnectionSetupModal] renderConnectionTypeSettings - connectionType:", config.connection_type); console.log("🔍 [ConnectionSetupModal] externalCallConfig:", externalCallConfig); - switch (config.connectionType) { + switch (config.connection_type) { case "simple-key": return ( = ({ console.log("🚀 [ConnectionSetupModal] Rendering ExternalCallPanel"); return ( ); @@ -577,11 +578,11 @@ export const ConnectionSetupModal: React.FC = ({ const isButtonDisabled = () => { // 공통 검증: 관계 이름은 필수 - const hasRelationshipName = !!config.relationshipName?.trim(); + const hasRelationshipName = !!config.relationship_name?.trim(); if (!hasRelationshipName) return true; // 연결 타입별 검증 - switch (config.connectionType) { + switch (config.connection_type) { case "simple-key": // 단순 키값 연결: From과 To 컬럼이 모두 선택되어야 함 const hasFromColumns = selectedFromColumns.length > 0; @@ -594,32 +595,32 @@ export const ConnectionSetupModal: React.FC = ({ // DELETE 액션은 필드 매핑이 필요 없음 const allActionsHaveMappings = dataSaveSettings.actions.every((action) => { - if (action.actionType === "delete") { + if (action.action_type === "delete") { return true; // DELETE는 필드 매핑 불필요 } - return action.fieldMappings.length > 0; + return action.field_mappings.length > 0; }); const allMappingsComplete = dataSaveSettings.actions.every((action) => { - if (action.actionType === "delete") { + if (action.action_type === "delete") { return true; // DELETE는 필드 매핑 검증 생략 } // INSERT 액션의 경우 최소 하나의 매핑이 있으면 됨 (모든 컬럼 매핑 필수 조건 제거) - if (action.actionType === "insert") { + if (action.action_type === "insert") { return true; // 필드 매핑이 있으면 충분함 } - return action.fieldMappings.every((mapping) => { + return action.field_mappings.every((mapping) => { // 타겟은 항상 필요 - if (!mapping.targetTable || !mapping.targetField) return false; + if (!mapping.target_table || !mapping.target_field) return false; // 소스와 기본값 중 하나는 있어야 함 - const hasSource = mapping.sourceTable && mapping.sourceField; - const hasDefault = mapping.defaultValue && mapping.defaultValue.trim(); + const hasSource = mapping.source_table && mapping.source_field; + const hasDefault = mapping.default_value && mapping.default_value.trim(); // FROM 테이블이 비어있으면 기본값이 필요 - if (!mapping.sourceTable) { + if (!mapping.source_table) { return !!hasDefault; } @@ -630,7 +631,7 @@ export const ConnectionSetupModal: React.FC = ({ // INSERT가 아닌 액션 타입에 대한 실행조건 필수 검증 const allRequiredConditionsMet = dataSaveSettings.actions.every((action) => { - if (action.actionType === "insert") { + if (action.action_type === "insert") { return true; // INSERT는 조건 불필요 } @@ -659,9 +660,9 @@ export const ConnectionSetupModal: React.FC = ({ case "external-call": // 외부 호출: 새로운 설정이 있으면 API URL 검증, 없으면 기존 설정 검증 if (externalCallConfig) { - return !externalCallConfig.restApiSettings?.apiUrl?.trim(); + return !externalCallConfig.rest_api_settings?.apiUrl?.trim(); } else { - return !externalCallSettings.configId || !externalCallSettings.message?.trim(); + return !externalCallSettings.config_id || !externalCallSettings.message?.trim(); } default: @@ -689,7 +690,7 @@ export const ConnectionSetupModal: React.FC = ({ setConfig({ ...config, relationshipName: e.target.value })} placeholder="employee_id_department_id_연결" className="text-sm" @@ -701,11 +702,11 @@ export const ConnectionSetupModal: React.FC = ({ {/* 조건부 연결을 위한 조건 설정 */} - {isConditionalConnection(config.connectionType) && ( + {isConditionalConnection(config.connection_type) && ( > = ({ +export const CustomEdge: React.FC>> = ({ id, sourceX, sourceY, diff --git a/frontend/components/dataflow/DataFlowDesigner.tsx b/frontend/components/dataflow/DataFlowDesigner.tsx index 111ac773..27bc8402 100644 --- a/frontend/components/dataflow/DataFlowDesigner.tsx +++ b/frontend/components/dataflow/DataFlowDesigner.tsx @@ -33,8 +33,8 @@ const nodeTypes = { const edgeTypes = {}; export const DataFlowDesigner: React.FC = ({ - companyCode: propCompanyCode = "*", - diagramId, + company_code: propCompanyCode = "*", + diagram_id: diagramId, }) => { const { user: authUser } = useAuth(); @@ -113,6 +113,7 @@ export const DataFlowDesigner: React.FC = ({ fromColumns: Array.isArray(rel.fromColumns) ? rel.fromColumns : [], toColumns: Array.isArray(rel.toColumns) ? rel.toColumns : [], connectionType: rel.connectionType || "simple-key", + connection_type: (rel.connectionType || "simple-key") as "simple-key" | "data-save" | "external-call", relationshipName: rel.relationshipName || "", note: rel.note || "", // 🔥 연결 설명 로드 })); @@ -140,8 +141,8 @@ export const DataFlowDesigner: React.FC = ({ }, data: { table: { - tableName, - displayName: tableName, + table_name: tableName, + display_name: tableName, description: "", columns: Array.isArray(columns) ? columns.map((col) => ({ @@ -152,8 +153,8 @@ export const DataFlowDesigner: React.FC = ({ : [], }, onColumnClick: handleColumnClick, - selectedColumns: [], - connectedColumns: {}, + selected_columns: [], + connected_columns: {}, }, selected: false, }; @@ -168,14 +169,14 @@ export const DataFlowDesigner: React.FC = ({ }, data: { table: { - tableName, - displayName: tableName, + table_name: tableName, + display_name: tableName, description: "", columns: [], }, onColumnClick: handleColumnClick, - selectedColumns: [], - connectedColumns: {}, + selected_columns: [], + connected_columns: {}, }, selected: false, }; @@ -279,24 +280,20 @@ export const DataFlowDesigner: React.FC = ({ position: { x: Math.random() * 300, y: Math.random() * 200 }, data: { table: { - tableName: table.tableName, - displayName: table.displayName || table.tableName, + table_name: table.tableName, + display_name: table.displayName || table.tableName, description: "", // 새로 추가된 노드는 description 없이 통일 columns: Array.isArray(table.columns) ? table.columns.map((col) => ({ - columnName: col.columnName || "unknown", - name: col.columnName || "unknown", // 호환성을 위해 유지 - displayName: col.displayName, // 한국어 라벨 - columnLabel: col.columnLabel, // 한국어 라벨 + name: col.columnName || "unknown", type: col.dataType || "varchar", - dataType: col.dataType || "varchar", description: col.description || "", })) : [], }, onColumnClick: handleColumnClick, - selectedColumns: selectedColumns[table.tableName] || [], - connectedColumns: {}, // 새로 추가된 노드는 연결 정보 없음 + selected_columns: selectedColumns[table.tableName] || [], + connected_columns: {}, // 새로 추가된 노드는 연결 정보 없음 }, }; @@ -470,15 +467,15 @@ export const DataFlowDesigner: React.FC = ({ if (!firstNode || !secondNode) return; setPendingConnection({ - fromNode: { + from_node: { id: firstNode.id, - tableName: firstNode.data.table.tableName, - displayName: firstNode.data.table.displayName, + table_name: firstNode.data.table.table_name, + display_name: firstNode.data.table.display_name, }, - toNode: { + to_node: { id: secondNode.id, - tableName: secondNode.data.table.tableName, - displayName: secondNode.data.table.displayName, + table_name: secondNode.data.table.table_name, + display_name: secondNode.data.table.display_name, }, }); }; @@ -497,6 +494,7 @@ export const DataFlowDesigner: React.FC = ({ fromColumns: relationshipData.from_column_name ? relationshipData.from_column_name.split(",") : [], toColumns: relationshipData.to_column_name ? relationshipData.to_column_name.split(",") : [], connectionType: relationshipData.connection_type as "simple-key" | "data-save" | "external-call", + connection_type: relationshipData.connection_type as "simple-key" | "data-save" | "external-call", relationshipName: relationshipData.relationship_name, note: (relationshipData.settings as any)?.notes || "", // 🔥 notes를 note로 변환 settings: relationshipData.settings || {}, @@ -537,6 +535,7 @@ export const DataFlowDesigner: React.FC = ({ fromColumns: relationshipData.from_column_name ? relationshipData.from_column_name.split(",") : [], toColumns: relationshipData.to_column_name ? relationshipData.to_column_name.split(",") : [], connectionType: relationshipData.connection_type as "simple-key" | "data-save" | "external-call", + connection_type: relationshipData.connection_type as "simple-key" | "data-save" | "external-call", relationshipName: relationshipData.relationship_name, note: (relationshipData.settings as any)?.notes || "", // 🔥 notes를 note로 변환 settings: relationshipData.settings || {}, @@ -650,8 +649,8 @@ export const DataFlowDesigner: React.FC = ({ .filter((rel) => rel.settings?.control) .map((rel) => ({ id: rel.id, - triggerType: rel.settings?.control?.triggerType || "insert", - conditions: (rel.settings?.control?.conditionTree || []).map((condition: Record) => ({ + triggerType: rel.settings?.control?.trigger_type || "insert", + conditions: (rel.settings?.control?.condition_tree || []).map((condition: Record) => ({ ...condition, logicalOperator: condition.logicalOperator === "AND" || condition.logicalOperator === "OR" @@ -909,12 +908,12 @@ export const DataFlowDesigner: React.FC = ({ @@ -933,41 +932,41 @@ export const DataFlowDesigner: React.FC = ({ onEdit={() => { if (selectedEdgeInfo) { // 기존 관계 찾기 - const existingRelationship = tempRelationships.find((rel) => rel.id === selectedEdgeInfo.relationshipId); + const existingRelationship = tempRelationships.find((rel) => rel.id === selectedEdgeInfo.relationship_id); if (existingRelationship) { // 편집 모드로 설정 - setEditingRelationshipId(selectedEdgeInfo.relationshipId); + setEditingRelationshipId(selectedEdgeInfo.relationship_id); // 연결 설정 모달 열기 - const fromTable = nodes.find((node) => node.data?.table?.tableName === selectedEdgeInfo.fromTable); - const toTable = nodes.find((node) => node.data?.table?.tableName === selectedEdgeInfo.toTable); + const fromTable = nodes.find((node) => node.data?.table?.table_name === selectedEdgeInfo.from_table); + const toTable = nodes.find((node) => node.data?.table?.table_name === selectedEdgeInfo.to_table); if (fromTable && toTable) { setPendingConnection({ - fromNode: { + from_node: { id: fromTable.id, - tableName: selectedEdgeInfo.fromTable, - displayName: fromTable.data?.table?.displayName || selectedEdgeInfo.fromTable, + table_name: selectedEdgeInfo.from_table, + display_name: fromTable.data?.table?.display_name || selectedEdgeInfo.from_table, }, - toNode: { + to_node: { id: toTable.id, - tableName: selectedEdgeInfo.toTable, - displayName: toTable.data?.table?.displayName || selectedEdgeInfo.toTable, + table_name: selectedEdgeInfo.to_table, + display_name: toTable.data?.table?.display_name || selectedEdgeInfo.to_table, }, - selectedColumnsData: { - [selectedEdgeInfo.fromTable]: { - displayName: fromTable.data?.table?.displayName || selectedEdgeInfo.fromTable, - columns: selectedEdgeInfo.fromColumns || [], + selected_columns_data: { + [selectedEdgeInfo.from_table]: { + display_name: fromTable.data?.table?.display_name || selectedEdgeInfo.from_table, + columns: selectedEdgeInfo.from_columns || [], }, - [selectedEdgeInfo.toTable]: { - displayName: toTable.data?.table?.displayName || selectedEdgeInfo.toTable, - columns: selectedEdgeInfo.toColumns || [], + [selectedEdgeInfo.to_table]: { + display_name: toTable.data?.table?.display_name || selectedEdgeInfo.to_table, + columns: selectedEdgeInfo.to_columns || [], }, }, - existingRelationship: { - relationshipName: existingRelationship.relationshipName, - connectionType: existingRelationship.connectionType, + existing_relationship: { + relationship_name: existingRelationship.relationshipName, + connection_type: existingRelationship.connectionType, settings: existingRelationship.settings, }, }); @@ -983,10 +982,10 @@ export const DataFlowDesigner: React.FC = ({ onDelete={() => { if (selectedEdgeInfo) { // 관계 삭제 - setTempRelationships((prev) => prev.filter((rel) => rel.id !== selectedEdgeInfo.relationshipId)); + setTempRelationships((prev) => prev.filter((rel) => rel.id !== selectedEdgeInfo.relationship_id)); // 엣지 삭제 - setEdges((prev) => prev.filter((edge) => edge.data?.relationshipId !== selectedEdgeInfo.relationshipId)); + setEdges((prev) => prev.filter((edge) => edge.data?.relationshipId !== selectedEdgeInfo.relationship_id)); // 변경사항 표시 setHasUnsavedChanges(true); diff --git a/frontend/components/dataflow/DataFlowSidebar.tsx b/frontend/components/dataflow/DataFlowSidebar.tsx index 0b9162f6..2e0db80f 100644 --- a/frontend/components/dataflow/DataFlowSidebar.tsx +++ b/frontend/components/dataflow/DataFlowSidebar.tsx @@ -7,7 +7,7 @@ import { ExtendedJsonRelationship } from "@/types/dataflowTypes"; interface DataFlowSidebarProps { companyCode: string; - nodes: Array<{ id: string; data: { table: { tableName: string } } }>; + nodes: Array<{ id: string; data: { table: { table_name: string } } }>; edges: Array<{ id: string }>; tempRelationships: ExtendedJsonRelationship[]; hasUnsavedChanges: boolean; diff --git a/frontend/components/dataflow/EdgeInfoPanel.tsx b/frontend/components/dataflow/EdgeInfoPanel.tsx index 676755a2..f478b8a5 100644 --- a/frontend/components/dataflow/EdgeInfoPanel.tsx +++ b/frontend/components/dataflow/EdgeInfoPanel.tsx @@ -39,7 +39,7 @@ export const EdgeInfoPanel: React.FC = ({ 🔗
-
{edgeInfo.relationshipName}
+
{edgeInfo.relationship_name}
데이터 관계 정보
@@ -57,7 +57,7 @@ export const EdgeInfoPanel: React.FC = ({
연결 유형
- {edgeInfo.connectionType} + {edgeInfo.connection_type}
@@ -68,10 +68,10 @@ export const EdgeInfoPanel: React.FC = ({ {/* From 테이블 */}
FROM
-
{edgeInfo.fromTable}
+
{edgeInfo.from_table}
- {edgeInfo.fromColumns.map((column, index) => ( + {edgeInfo.from_columns.map((column, index) => ( = ({ {/* To 테이블 */}
TO
-
{edgeInfo.toTable}
+
{edgeInfo.to_table}
- {edgeInfo.toColumns.map((column, index) => ( + {edgeInfo.to_columns.map((column, index) => ( = ({ } // 연결 설정 모달 열기 - const fromTable = nodes.find((node) => node.data?.table?.tableName === relationship.fromTable); - const toTable = nodes.find((node) => node.data?.table?.tableName === relationship.toTable); + const fromTable = nodes.find((node) => node.data?.table?.table_name === relationship.fromTable); + const toTable = nodes.find((node) => node.data?.table?.table_name === relationship.toTable); if (fromTable && toTable) { onSetPendingConnection({ fromNode: { id: fromTable.id, tableName: relationship.fromTable, - displayName: fromTable.data?.table?.displayName || relationship.fromTable, + displayName: fromTable.data?.table?.display_name || relationship.fromTable, }, toNode: { id: toTable.id, tableName: relationship.toTable, - displayName: toTable.data?.table?.displayName || relationship.toTable, + displayName: toTable.data?.table?.display_name || relationship.toTable, }, selectedColumnsData: { [relationship.fromTable]: { - displayName: fromTable.data?.table?.displayName || relationship.fromTable, + displayName: fromTable.data?.table?.display_name || relationship.fromTable, columns: relationship.fromColumns || [], }, [relationship.toTable]: { - displayName: toTable.data?.table?.displayName || relationship.toTable, + displayName: toTable.data?.table?.display_name || relationship.toTable, columns: relationship.toColumns || [], }, }, diff --git a/frontend/components/dataflow/SaveDiagramModal.tsx b/frontend/components/dataflow/SaveDiagramModal.tsx index 53d67afa..5fae22e3 100644 --- a/frontend/components/dataflow/SaveDiagramModal.tsx +++ b/frontend/components/dataflow/SaveDiagramModal.tsx @@ -12,6 +12,7 @@ import { import { AlertDialog, AlertDialogAction, + AlertDialogFooter, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, diff --git a/frontend/components/dataflow/SelectedTablesPanel.tsx b/frontend/components/dataflow/SelectedTablesPanel.tsx index 319051e4..67c99490 100644 --- a/frontend/components/dataflow/SelectedTablesPanel.tsx +++ b/frontend/components/dataflow/SelectedTablesPanel.tsx @@ -57,7 +57,7 @@ export const SelectedTablesPanel: React.FC = ({ const node = nodes.find((n) => n.id === nodeId); if (!node) return null; - const { tableName, displayName } = node.data.table; + const { table_name, display_name } = node.data.table; return (
{/* 테이블 정보 */} @@ -76,7 +76,7 @@ export const SelectedTablesPanel: React.FC = ({ index === 0 ? "text-success" : index === 1 ? "text-primary" : "text-foreground" }`} > - {displayName} + {display_name}
{selectedNodes.length === 2 && (
= ({
)}
-
{tableName}
+
{table_name}
{/* 연결 화살표 (마지막이 아닌 경우) */} diff --git a/frontend/components/dataflow/connection/ActionConditionsSection.tsx b/frontend/components/dataflow/connection/ActionConditionsSection.tsx index e57050f8..a5a2a417 100644 --- a/frontend/components/dataflow/connection/ActionConditionsSection.tsx +++ b/frontend/components/dataflow/connection/ActionConditionsSection.tsx @@ -33,7 +33,7 @@ export const ActionConditionsSection: React.FC = ( const { addActionGroupStart, addActionGroupEnd, getActionCurrentGroupLevel } = useActionConditionHelpers(); // INSERT가 아닌 액션 타입인지 확인 - const isConditionRequired = action.actionType !== "insert"; + const isConditionRequired = action.action_type !== "insert"; // 유효한 조건이 있는지 확인 (group-start, group-end만 있는 경우 제외) const hasValidConditions = @@ -161,7 +161,7 @@ export const ActionConditionsSection: React.FC = (
실행조건이 필요합니다
- {action.actionType.toUpperCase()} 액션은 언제 실행될지 결정하는 조건이 반드시 필요합니다. + {action.action_type.toUpperCase()} 액션은 언제 실행될지 결정하는 조건이 반드시 필요합니다.
필드, 연산자, 값을 모두 입력해야 합니다.
diff --git a/frontend/components/dataflow/connection/ActionFieldMappings.tsx b/frontend/components/dataflow/connection/ActionFieldMappings.tsx index 4e13a86e..0dfa2632 100644 --- a/frontend/components/dataflow/connection/ActionFieldMappings.tsx +++ b/frontend/components/dataflow/connection/ActionFieldMappings.tsx @@ -44,10 +44,10 @@ export const ActionFieldMappings: React.FC = ({ enableMultiConnection = false, }) => { // 🆕 다중 커넥션 상태 관리 - const [fromConnectionId, setFromConnectionId] = useState(action.fromConnection?.connectionId); - const [toConnectionId, setToConnectionId] = useState(action.toConnection?.connectionId); - const [selectedFromTable, setSelectedFromTable] = useState(action.fromTable || fromTableName); - const [selectedToTable, setSelectedToTable] = useState(action.targetTable || toTableName); + const [fromConnectionId, setFromConnectionId] = useState((action as any).fromConnection?.connectionId); + const [toConnectionId, setToConnectionId] = useState((action as any).toConnection?.connectionId); + const [selectedFromTable, setSelectedFromTable] = useState((action as any).fromTable || fromTableName); + const [selectedToTable, setSelectedToTable] = useState((action as any).targetTable || toTableName); // 다중 커넥션이 활성화된 경우 새로운 UI 렌더링 if (enableMultiConnection) { @@ -55,7 +55,7 @@ export const ActionFieldMappings: React.FC = ({ } // 기존 INSERT 액션 처리 (단일 커넥션) - if (action.actionType === "insert" && fromTableColumns.length > 0 && toTableColumns.length > 0) { + if (action.action_type === "insert" && fromTableColumns.length > 0 && toTableColumns.length > 0) { return ( = ({ const handleToTableChange = (tableName: string) => { setSelectedToTable(tableName); - updateActionTable("targetTable", tableName); + updateActionTable("target_table", tableName); }; // 액션 커넥션 정보 업데이트 const updateActionConnection = (type: "fromConnection" | "toConnection", connectionId: number) => { const newActions = [...settings.actions]; - if (!newActions[actionIndex][type]) { - newActions[actionIndex][type] = {}; + if (!(newActions[actionIndex] as any)[type]) { + (newActions[actionIndex] as any)[type] = {}; } - newActions[actionIndex][type]!.connectionId = connectionId; + (newActions[actionIndex] as any)[type]!.connectionId = connectionId; onSettingsChange({ ...settings, actions: newActions }); }; // 액션 테이블 정보 업데이트 - const updateActionTable = (type: "fromTable" | "targetTable", tableName: string) => { + const updateActionTable = (type: "fromTable" | "target_table", tableName: string) => { const newActions = [...settings.actions]; - newActions[actionIndex][type] = tableName; + (newActions[actionIndex] as any)[type] = tableName; onSettingsChange({ ...settings, actions: newActions }); }; @@ -124,7 +124,7 @@ export const ActionFieldMappings: React.FC = ({ toConnectionId={toConnectionId} onFromConnectionChange={handleFromConnectionChange} onToConnectionChange={handleToConnectionChange} - actionType={action.actionType} + actionType={action.action_type} allowSameConnection={true} /> @@ -137,7 +137,7 @@ export const ActionFieldMappings: React.FC = ({ selectedToTable={selectedToTable} onFromTableChange={handleFromTableChange} onToTableChange={handleToTableChange} - actionType={action.actionType} + actionType={action.action_type} allowSameTable={true} showSameTableWarning={true} /> @@ -151,7 +151,7 @@ export const ActionFieldMappings: React.FC = ({ // 액션 타입별 패널 렌더링 function renderActionSpecificPanel() { - switch (action.actionType) { + switch (action.action_type) { case "insert": return ( = ({ } const addFieldMapping = () => { const newActions = [...settings.actions]; - newActions[actionIndex].fieldMappings.push({ - sourceTable: "", - sourceField: "", - targetTable: "", - targetField: "", - defaultValue: "", - transformFunction: "", + newActions[actionIndex].field_mappings.push({ + source_table: "", + source_field: "", + target_table: "", + target_field: "", + default_value: "", }); onSettingsChange({ ...settings, actions: newActions }); }; const updateFieldMapping = (mappingIndex: number, field: string, value: string) => { const newActions = [...settings.actions]; - (newActions[actionIndex].fieldMappings[mappingIndex] as any)[field] = value; + (newActions[actionIndex].field_mappings[mappingIndex] as any)[field] = value; onSettingsChange({ ...settings, actions: newActions }); }; const removeFieldMapping = (mappingIndex: number) => { const newActions = [...settings.actions]; - newActions[actionIndex].fieldMappings = newActions[actionIndex].fieldMappings.filter((_, i) => i !== mappingIndex); + newActions[actionIndex].field_mappings = newActions[actionIndex].field_mappings.filter((_: unknown, i: number) => i !== mappingIndex); onSettingsChange({ ...settings, actions: newActions }); }; @@ -236,9 +235,9 @@ export const ActionFieldMappings: React.FC = ({
- {action.fieldMappings.map((mapping, mappingIndex) => ( + {action.field_mappings.map((mapping, mappingIndex) => (
{/* 컴팩트한 매핑 표시 */} @@ -246,16 +245,16 @@ export const ActionFieldMappings: React.FC = ({ {/* 소스 */}
- {mapping.sourceTable && ( + {mapping.source_table && (