From 2645d627daf608fb204acde791ff6143fb6b0d5e Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Thu, 8 Jan 2026 12:25:35 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=EC=88=98=EC=A3=BC=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EB=AA=A8=EB=8B=AC=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20-=20UPDATE=20?= =?UTF-8?q?=ED=8F=B4=EB=B0=B1=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80,?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B3=91=ED=95=A9=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C=20=EC=88=98=EC=A0=95,=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=88=9C=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20id=20=ED=83=80=EC=9E=85=20=EB=B9=84?= =?UTF-8?q?=EA=B5=90=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/lib/api/dynamicForm.ts | 7 ++ .../UniversalFormModalComponent.tsx | 14 ++-- frontend/lib/utils/buttonActions.ts | 68 +++++++++++-------- 3 files changed, 56 insertions(+), 33 deletions(-) diff --git a/frontend/lib/api/dynamicForm.ts b/frontend/lib/api/dynamicForm.ts index a5a3b2eb..d2433c48 100644 --- a/frontend/lib/api/dynamicForm.ts +++ b/frontend/lib/api/dynamicForm.ts @@ -93,10 +93,15 @@ export class DynamicFormApi { ): Promise> { try { console.log("๐Ÿ”„ ํผ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ์š”์ฒญ:", { id, formData }); + console.log("๐ŸŒ API URL:", `/dynamic-form/${id}`); + console.log("๐Ÿ“ฆ ์š”์ฒญ ๋ณธ๋ฌธ:", JSON.stringify(formData, null, 2)); const response = await apiClient.put(`/dynamic-form/${id}`, formData); console.log("โœ… ํผ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ์„ฑ๊ณต:", response.data); + console.log("๐Ÿ“Š ์‘๋‹ต ์ƒํƒœ:", response.status); + console.log("๐Ÿ“‹ ์‘๋‹ต ํ—ค๋”:", response.headers); + return { success: true, data: response.data, @@ -104,6 +109,8 @@ export class DynamicFormApi { }; } catch (error: any) { console.error("โŒ ํผ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ์‹คํŒจ:", error); + console.error("๐Ÿ“Š ์—๋Ÿฌ ์‘๋‹ต:", error.response?.data); + console.error("๐Ÿ“Š ์—๋Ÿฌ ์ƒํƒœ:", error.response?.status); const errorMessage = error.response?.data?.message || error.message || "๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."; diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx index 4ee024a4..3e043331 100644 --- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx +++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx @@ -333,6 +333,14 @@ export function UniversalFormModalComponent({ } } + // ๐Ÿ†• ํ…Œ์ด๋ธ” ์„น์…˜ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ (ํ’ˆ๋ชฉ ๋ฆฌ์ŠคํŠธ ๋“ฑ) + for (const [key, value] of Object.entries(formData)) { + if (key.startsWith("_tableSection_") && Array.isArray(value)) { + event.detail.formData[key] = value; + console.log(`[UniversalFormModal] ํ…Œ์ด๋ธ” ์„น์…˜ ๋ณ‘ํ•ฉ: ${key}, ${value.length}๊ฐœ ํ•ญ๋ชฉ`); + } + } + // ๐Ÿ†• ์ˆ˜์ • ๋ชจ๋“œ: ์›๋ณธ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์ „๋‹ฌ (UPDATE/DELETE ์ถ”์ ์šฉ) if (originalGroupedData.length > 0) { event.detail.formData._originalGroupedData = originalGroupedData; @@ -355,15 +363,9 @@ export function UniversalFormModalComponent({ // ํ…Œ์ด๋ธ” ํƒ€์ž… ์„น์…˜ ์ฐพ๊ธฐ const tableSection = config.sections.find((s) => s.type === "table"); if (!tableSection) { - // console.log("[UniversalFormModal] ํ…Œ์ด๋ธ” ์„น์…˜ ์—†์Œ - _groupedData ๋ฌด์‹œ"); return; } - // console.log("[UniversalFormModal] ์ˆ˜์ • ๋ชจ๋“œ - ํ…Œ์ด๋ธ” ์„น์…˜ ์ดˆ๊ธฐํ™”:", { - // sectionId: tableSection.id, - // itemCount: _groupedData.length, - // }); - // ์›๋ณธ ๋ฐ์ดํ„ฐ ์ €์žฅ (์ˆ˜์ •/์‚ญ์ œ ์ถ”์ ์šฉ) setOriginalGroupedData(JSON.parse(JSON.stringify(_groupedData))); diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 4c9d638b..7512d6c0 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -724,11 +724,16 @@ export class ButtonActionExecutor { // originalData๋Š” ์ˆ˜์ • ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ editData๋กœ ์ „๋‹ฌ๋˜์–ด context.originalData๋กœ ์„ค์ •๋จ // ๋นˆ ๊ฐ์ฒด {}๋„ truthy์ด๋ฏ€๋กœ Object.keys๋กœ ์‹ค์ œ ๋ฐ์ดํ„ฐ ์œ ๋ฌด ํ™•์ธ const hasRealOriginalData = originalData && Object.keys(originalData).length > 0; - const isUpdate = hasRealOriginalData && !!primaryKeyValue; + + // ๐Ÿ†• ํด๋ฐฑ ๋กœ์ง: originalData๊ฐ€ ์—†์–ด๋„ formData์— id๊ฐ€ ์žˆ์œผ๋ฉด UPDATE๋กœ ํŒ๋‹จ + // ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๋“ฑ์—์„œ originalData ์ „๋‹ฌ์ด ๋ˆ„๋ฝ๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌ + const hasIdInFormData = formData.id !== undefined && formData.id !== null && formData.id !== ""; + const isUpdate = (hasRealOriginalData || hasIdInFormData) && !!primaryKeyValue; console.log("๐Ÿ” [handleSave] INSERT/UPDATE ํŒ๋‹จ:", { hasOriginalData: !!originalData, hasRealOriginalData, + hasIdInFormData, originalDataKeys: originalData ? Object.keys(originalData) : [], primaryKeyValue, isUpdate, @@ -741,18 +746,18 @@ export class ButtonActionExecutor { // UPDATE ์ฒ˜๋ฆฌ - ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ ์‚ฌ์šฉ (์›๋ณธ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ) console.log("๐Ÿ”„ UPDATE ๋ชจ๋“œ๋กœ ์ €์žฅ:", { primaryKeyValue, - formData, - originalData, hasOriginalData: !!originalData, + hasIdInFormData, + updateReason: hasRealOriginalData ? "originalData ์กด์žฌ" : "formData.id ์กด์žฌ (ํด๋ฐฑ)", }); - if (originalData) { + if (hasRealOriginalData) { // ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ: ๋ณ€๊ฒฝ๋œ ํ•„๋“œ๋งŒ ์—…๋ฐ์ดํŠธ console.log("๐Ÿ“ ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ ์‹คํ–‰ (๋ณ€๊ฒฝ๋œ ํ•„๋“œ๋งŒ)"); saveResult = await DynamicFormApi.updateFormDataPartial(primaryKeyValue, originalData, formData, tableName); } else { - // ์ „์ฒด ์—…๋ฐ์ดํŠธ (๊ธฐ์กด ๋ฐฉ์‹) - console.log("๐Ÿ“ ์ „์ฒด ์—…๋ฐ์ดํŠธ ์‹คํ–‰ (๋ชจ๋“  ํ•„๋“œ)"); + // ์ „์ฒด ์—…๋ฐ์ดํŠธ (originalData ์—†์ด id๋กœ UPDATE ํŒ๋‹จ๋œ ๊ฒฝ์šฐ) + console.log("๐Ÿ“ ์ „์ฒด ์—…๋ฐ์ดํŠธ ์‹คํ–‰ (originalData ์—†์Œ - ํด๋ฐฑ ๋ชจ๋“œ)"); saveResult = await DynamicFormApi.updateFormData(primaryKeyValue, { tableName, data: formData, @@ -1862,37 +1867,45 @@ export class ButtonActionExecutor { const originalItem = originalGroupedData.find((orig) => orig.id === item.id); if (!originalItem) { - console.warn(`โš ๏ธ [UPDATE] ์›๋ณธ ๋ฐ์ดํ„ฐ ์—†์Œ - INSERT๋กœ ์ฒ˜๋ฆฌ: id=${item.id}`); - // ์›๋ณธ์ด ์—†์œผ๋ฉด ์‹ ๊ทœ๋กœ ์ฒ˜๋ฆฌ - const rowToSave = { ...commonFieldsData, ...item, ...userInfo }; - Object.keys(rowToSave).forEach((key) => { + // ๐Ÿ†• ํด๋ฐฑ ๋กœ์ง: ์›๋ณธ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์–ด๋„ id๊ฐ€ ์žˆ์œผ๋ฉด UPDATE ์‹œ๋„ + // originalGroupedData ์ „๋‹ฌ์ด ๋ˆ„๋ฝ๋œ ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌ + console.warn(`โš ๏ธ [UPDATE] ์›๋ณธ ๋ฐ์ดํ„ฐ ์—†์Œ - id๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ UPDATE ์‹œ๋„ (ํด๋ฐฑ): id=${item.id}`); + + // โš ๏ธ ์ค‘์š”: commonFieldsData๊ฐ€ item๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์•„์•ผ ํ•จ + // item์— ์žˆ๋Š” ๊ธฐ์กด ๊ฐ’(์˜ˆ: manager_id=123)์ด commonFieldsData์˜ ์ƒˆ ๊ฐ’(manager_id=234)์„ ๋ฎ์–ด์“ฐ์ง€ ์•Š๋„๋ก + // ์ˆœ์„œ: item(๊ธฐ์กด) โ†’ commonFieldsData(์ƒˆ๋กœ ์ž…๋ ฅ) โ†’ userInfo(๋ฉ”ํƒ€๋ฐ์ดํ„ฐ) + const rowToUpdate = { ...item, ...commonFieldsData, ...userInfo }; + Object.keys(rowToUpdate).forEach((key) => { if (key.startsWith("_")) { - delete rowToSave[key]; + delete rowToUpdate[key]; } }); - delete rowToSave.id; // id ์ œ๊ฑฐํ•˜์—ฌ INSERT - // ๐Ÿ†• ๋ฉ”์ธ ๋ ˆ์ฝ”๋“œ ID ์—ฐ๊ฒฐ (๋ณ„๋„ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•˜๋Š” ๊ฒฝ์šฐ) - if (targetTableName && mainRecordId && saveConfig.primaryKeyColumn) { - rowToSave[saveConfig.primaryKeyColumn] = mainRecordId; - } - - const saveResult = await DynamicFormApi.saveFormData({ - screenId: screenId!, + console.log("๐Ÿ“ [UPDATE ํด๋ฐฑ] ์ €์žฅํ•  ๋ฐ์ดํ„ฐ:", { + id: item.id, tableName: saveTableName, - data: rowToSave, + commonFieldsData, + itemFields: Object.keys(item).filter(k => !k.startsWith("_")), + rowToUpdate, }); - if (!saveResult.success) { - throw new Error(saveResult.message || "ํ’ˆ๋ชฉ ์ €์žฅ ์‹คํŒจ"); + // id๋ฅผ ์œ ์ง€ํ•˜๊ณ  UPDATE ์‹คํ–‰ + const updateResult = await DynamicFormApi.updateFormData(item.id, { + tableName: saveTableName, + data: rowToUpdate, + }); + + if (!updateResult.success) { + throw new Error(updateResult.message || "ํ’ˆ๋ชฉ ์ˆ˜์ • ์‹คํŒจ"); } - insertedCount++; + updatedCount++; continue; } // ๋ณ€๊ฒฝ ์‚ฌํ•ญ ํ™•์ธ (๊ณตํ†ต ํ•„๋“œ ํฌํ•จ) - const currentDataWithCommon = { ...commonFieldsData, ...item }; + // โš ๏ธ ์ค‘์š”: commonFieldsData๊ฐ€ item๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์•„์•ผ ํ•จ (์ƒˆ๋กœ ์ž…๋ ฅํ•œ ๊ฐ’์ด ๊ธฐ์กด ๊ฐ’์„ ๋ฎ์–ด์”€) + const currentDataWithCommon = { ...item, ...commonFieldsData }; const hasChanges = this.checkForChanges(originalItem, currentDataWithCommon); if (hasChanges) { @@ -1917,13 +1930,14 @@ export class ButtonActionExecutor { } // 3๏ธโƒฃ ์‚ญ์ œ๋œ ํ’ˆ๋ชฉ DELETE (์›๋ณธ์—๋Š” ์žˆ์ง€๋งŒ ํ˜„์žฌ์—๋Š” ์—†๋Š” ํ•ญ๋ชฉ) - const currentIds = new Set(currentItems.map((item) => item.id).filter(Boolean)); - const deletedItems = originalGroupedData.filter((orig) => orig.id && !currentIds.has(orig.id)); + // โš ๏ธ id ํƒ€์ž… ํ†ต์ผ: ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋น„๊ต (์ˆซ์ž vs ๋ฌธ์ž์—ด ๋ถˆ์ผ์น˜ ๋ฐฉ์ง€) + const currentIds = new Set(currentItems.map((item) => String(item.id)).filter(Boolean)); + const deletedItems = originalGroupedData.filter((orig) => orig.id && !currentIds.has(String(orig.id))); for (const deletedItem of deletedItems) { console.log(`๐Ÿ—‘๏ธ [DELETE] ํ’ˆ๋ชฉ ์‚ญ์ œ: id=${deletedItem.id}, tableName=${saveTableName}`); - const deleteResult = await DynamicFormApi.deleteFormDataFromTable(saveTableName, deletedItem.id); + const deleteResult = await DynamicFormApi.deleteFormDataFromTable(deletedItem.id, saveTableName); if (!deleteResult.success) { throw new Error(deleteResult.message || "ํ’ˆ๋ชฉ ์‚ญ์ œ ์‹คํŒจ"); From 3e9bf29bcf97fa0d311386319d449f4612d2a833 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Thu, 8 Jan 2026 14:13:19 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20SplitPanelLayout=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20groupByColumns=20?= =?UTF-8?q?=EA=B8=B0=EC=A4=80=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=EB=A9=80?= =?UTF-8?q?=ED=8B=B0=ED=85=8C=EB=84=8C=EC=8B=9C=20=EB=B3=B4=ED=98=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(=EC=98=81=EC=97=85=EA=B4=80=EB=A6=AC=5F?= =?UTF-8?q?=EA=B1=B0=EB=9E=98=EC=B2=98=EB=B3=84=20=ED=92=88=EB=AA=A9=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=93=B1=EC=97=90=EC=84=9C,,)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend-node/src/routes/dataRoutes.ts | 6 +- backend-node/src/services/dataService.ts | 46 +++++++- .../SplitPanelLayoutComponent.tsx | 102 ++++++++++++------ 3 files changed, 119 insertions(+), 35 deletions(-) diff --git a/backend-node/src/routes/dataRoutes.ts b/backend-node/src/routes/dataRoutes.ts index f87aa5d6..f9d88d92 100644 --- a/backend-node/src/routes/dataRoutes.ts +++ b/backend-node/src/routes/dataRoutes.ts @@ -698,6 +698,7 @@ router.post( try { const { tableName } = req.params; const filterConditions = req.body; + const userCompany = req.user?.companyCode; if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) { return res.status(400).json({ @@ -706,11 +707,12 @@ router.post( }); } - console.log(`๐Ÿ—‘๏ธ ๊ทธ๋ฃน ์‚ญ์ œ:`, { tableName, filterConditions }); + console.log(`๐Ÿ—‘๏ธ ๊ทธ๋ฃน ์‚ญ์ œ:`, { tableName, filterConditions, userCompany }); const result = await dataService.deleteGroupRecords( tableName, - filterConditions + filterConditions, + userCompany // ํšŒ์‚ฌ ์ฝ”๋“œ ์ „๋‹ฌ ); if (!result.success) { diff --git a/backend-node/src/services/dataService.ts b/backend-node/src/services/dataService.ts index a1a494f2..75c57673 100644 --- a/backend-node/src/services/dataService.ts +++ b/backend-node/src/services/dataService.ts @@ -1189,6 +1189,13 @@ class DataService { [tableName] ); + console.log(`๐Ÿ” ํ…Œ์ด๋ธ” ${tableName}์˜ Primary Key ์กฐํšŒ ๊ฒฐ๊ณผ:`, { + pkColumns: pkResult.map((r) => r.attname), + pkCount: pkResult.length, + inputId: typeof id === "object" ? JSON.stringify(id).substring(0, 200) + "..." : id, + inputIdType: typeof id, + }); + let whereClauses: string[] = []; let params: any[] = []; @@ -1216,17 +1223,31 @@ class DataService { params.push(typeof id === "object" ? id[pkColumn] : id); } - const queryText = `DELETE FROM "${tableName}" WHERE ${whereClauses.join(" AND ")}`; + const queryText = `DELETE FROM "${tableName}" WHERE ${whereClauses.join(" AND ")} RETURNING *`; console.log(`๐Ÿ—‘๏ธ ์‚ญ์ œ ์ฟผ๋ฆฌ:`, queryText, params); const result = await query(queryText, params); + // ์‚ญ์ œ๋œ ํ–‰์ด ์—†์œผ๋ฉด ์‹คํŒจ ์ฒ˜๋ฆฌ + if (result.length === 0) { + console.warn( + `โš ๏ธ ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ ์‹คํŒจ: ${tableName}, ํ•ด๋‹น ์กฐ๊ฑด์— ๋งž๋Š” ๋ ˆ์ฝ”๋“œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.`, + { whereClauses, params } + ); + return { + success: false, + message: "์‚ญ์ œํ•  ๋ ˆ์ฝ”๋“œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ ์‚ญ์ œ๋˜์—ˆ๊ฑฐ๋‚˜ ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.", + error: "RECORD_NOT_FOUND", + }; + } + console.log( `โœ… ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ ์™„๋ฃŒ: ${tableName}, ์˜ํ–ฅ๋ฐ›์€ ํ–‰: ${result.length}` ); return { success: true, + data: result[0], // ์‚ญ์ œ๋œ ๋ ˆ์ฝ”๋“œ ์ •๋ณด ๋ฐ˜ํ™˜ }; } catch (error) { console.error(`๋ ˆ์ฝ”๋“œ ์‚ญ์ œ ์˜ค๋ฅ˜ (${tableName}):`, error); @@ -1240,10 +1261,14 @@ class DataService { /** * ์กฐ๊ฑด์— ๋งž๋Š” ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ (๊ทธ๋ฃน ์‚ญ์ œ) + * @param tableName ํ…Œ์ด๋ธ”๋ช… + * @param filterConditions ์‚ญ์ œ ์กฐ๊ฑด + * @param userCompany ์‚ฌ์šฉ์ž ํšŒ์‚ฌ ์ฝ”๋“œ (๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ ํ•„ํ„ฐ๋ง) */ async deleteGroupRecords( tableName: string, - filterConditions: Record + filterConditions: Record, + userCompany?: string ): Promise> { try { const validation = await this.validateTableAccess(tableName); @@ -1255,6 +1280,7 @@ class DataService { const whereValues: any[] = []; let paramIndex = 1; + // ์‚ฌ์šฉ์ž ํ•„ํ„ฐ ์กฐ๊ฑด ์ถ”๊ฐ€ for (const [key, value] of Object.entries(filterConditions)) { whereConditions.push(`"${key}" = $${paramIndex}`); whereValues.push(value); @@ -1269,10 +1295,24 @@ class DataService { }; } + // ๐Ÿ”’ ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ: company_code ํ•„ํ„ฐ๋ง (์ตœ๊ณ  ๊ด€๋ฆฌ์ž ์ œ์™ธ) + const hasCompanyCode = await this.checkColumnExists(tableName, "company_code"); + if (hasCompanyCode && userCompany && userCompany !== "*") { + whereConditions.push(`"company_code" = $${paramIndex}`); + whereValues.push(userCompany); + paramIndex++; + console.log(`๐Ÿ”’ ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ ํ•„ํ„ฐ ์ ์šฉ: company_code = ${userCompany}`); + } + const whereClause = whereConditions.join(" AND "); const deleteQuery = `DELETE FROM "${tableName}" WHERE ${whereClause} RETURNING *`; - console.log(`๐Ÿ—‘๏ธ ๊ทธ๋ฃน ์‚ญ์ œ:`, { tableName, conditions: filterConditions }); + console.log(`๐Ÿ—‘๏ธ ๊ทธ๋ฃน ์‚ญ์ œ:`, { + tableName, + conditions: filterConditions, + userCompany, + whereClause, + }); const result = await pool.query(deleteQuery, whereValues); diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index ad7f5302..afc5c13e 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -1613,47 +1613,89 @@ export const SplitPanelLayoutComponent: React.FC try { console.log("๐Ÿ—‘๏ธ ๋ฐ์ดํ„ฐ ์‚ญ์ œ:", { tableName, primaryKey }); - // ๐Ÿ” ์ค‘๋ณต ์ œ๊ฑฐ ์„ค์ • ๋””๋ฒ„๊น… - console.log("๐Ÿ” ์ค‘๋ณต ์ œ๊ฑฐ ๋””๋ฒ„๊น…:", { + // ๐Ÿ” ๊ทธ๋ฃน ์‚ญ์ œ ์„ค์ • ํ™•์ธ (editButton.groupByColumns ๋˜๋Š” deduplication) + const groupByColumns = componentConfig.rightPanel?.editButton?.groupByColumns || []; + const deduplication = componentConfig.rightPanel?.dataFilter?.deduplication; + + console.log("๐Ÿ” ์‚ญ์ œ ์„ค์ • ๋””๋ฒ„๊น…:", { panel: deleteModalPanel, - dataFilter: componentConfig.rightPanel?.dataFilter, - deduplication: componentConfig.rightPanel?.dataFilter?.deduplication, - enabled: componentConfig.rightPanel?.dataFilter?.deduplication?.enabled, + groupByColumns, + deduplication, + deduplicationEnabled: deduplication?.enabled, }); let result; - // ๐Ÿ”ง ์ค‘๋ณต ์ œ๊ฑฐ๊ฐ€ ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ, groupByColumn ๊ธฐ์ค€์œผ๋กœ ๋ชจ๋“  ๊ด€๋ จ ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ - if (deleteModalPanel === "right" && componentConfig.rightPanel?.dataFilter?.deduplication?.enabled) { - const deduplication = componentConfig.rightPanel.dataFilter.deduplication; - const groupByColumn = deduplication.groupByColumn; - - if (groupByColumn && deleteModalItem[groupByColumn]) { - const groupValue = deleteModalItem[groupByColumn]; - console.log(`๐Ÿ”— ์ค‘๋ณต ์ œ๊ฑฐ ํ™œ์„ฑํ™”: ${groupByColumn} = ${groupValue} ๊ธฐ์ค€์œผ๋กœ ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ`); - - // groupByColumn ๊ฐ’์œผ๋กœ ํ•„ํ„ฐ๋งํ•˜์—ฌ ์‚ญ์ œ - const filterConditions: Record = { - [groupByColumn]: groupValue, - }; - - // ์ขŒ์ธก ํŒจ๋„์˜ ์„ ํƒ๋œ ํ•ญ๋ชฉ ์ •๋ณด๋„ ํฌํ•จ (customer_id ๋“ฑ) - if (selectedLeftItem && componentConfig.rightPanel?.mode === "join") { - const leftColumn = componentConfig.rightPanel.join.leftColumn; - const rightColumn = componentConfig.rightPanel.join.rightColumn; - filterConditions[rightColumn] = selectedLeftItem[leftColumn]; + // ๐Ÿ”ง ์šฐ์ธก ํŒจ๋„ ์‚ญ์ œ ์‹œ ๊ทธ๋ฃน ์‚ญ์ œ ์กฐ๊ฑด ํ™•์ธ + if (deleteModalPanel === "right") { + // 1. groupByColumns๊ฐ€ ์„ค์ •๋œ ๊ฒฝ์šฐ (ํŒจ๋„ ์„ค์ •์—์„œ ์„ ํƒ๋œ ์ปฌ๋Ÿผ๋“ค) + if (groupByColumns.length > 0) { + const filterConditions: Record = {}; + + // ์„ ํƒ๋œ ์ปฌ๋Ÿผ๋“ค์˜ ๊ฐ’์„ ํ•„ํ„ฐ ์กฐ๊ฑด์œผ๋กœ ์ถ”๊ฐ€ + for (const col of groupByColumns) { + if (deleteModalItem[col] !== undefined && deleteModalItem[col] !== null) { + filterConditions[col] = deleteModalItem[col]; + } } - console.log("๐Ÿ—‘๏ธ ๊ทธ๋ฃน ์‚ญ์ œ ์กฐ๊ฑด:", filterConditions); + // ๐Ÿ”’ ์•ˆ์ „์žฅ์น˜: ์กฐ์ธ ๋ชจ๋“œ์—์„œ ์ขŒ์ธก ํŒจ๋„์˜ ํ‚ค ๊ฐ’๋„ ํ•„ํ„ฐ ์กฐ๊ฑด์— ํฌํ•จ + // (๋‹ค๋ฅธ ๊ฑฐ๋ž˜์ฒ˜์˜ ๊ฐ™์€ ํ’ˆ๋ชฉ์ด ์‚ญ์ œ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€) + if (selectedLeftItem && componentConfig.rightPanel?.mode === "join") { + const leftColumn = componentConfig.rightPanel.join?.leftColumn; + const rightColumn = componentConfig.rightPanel.join?.rightColumn; + if (leftColumn && rightColumn && selectedLeftItem[leftColumn]) { + // rightColumn์ด filterConditions์— ์—†์œผ๋ฉด ์ถ”๊ฐ€ + if (!filterConditions[rightColumn]) { + filterConditions[rightColumn] = selectedLeftItem[leftColumn]; + console.log(`๐Ÿ”’ ์•ˆ์ „์žฅ์น˜: ${rightColumn} = ${selectedLeftItem[leftColumn]} ์ถ”๊ฐ€`); + } + } + } - // ๊ทธ๋ฃน ์‚ญ์ œ API ํ˜ธ์ถœ - result = await dataApi.deleteGroupRecords(tableName, filterConditions); - } else { - // ๋‹จ์ผ ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ + // ํ•„ํ„ฐ ์กฐ๊ฑด์ด ์žˆ์œผ๋ฉด ๊ทธ๋ฃน ์‚ญ์ œ + if (Object.keys(filterConditions).length > 0) { + console.log(`๐Ÿ”— ๊ทธ๋ฃน ์‚ญ์ œ (groupByColumns): ${groupByColumns.join(", ")} ๊ธฐ์ค€`); + console.log("๐Ÿ—‘๏ธ ๊ทธ๋ฃน ์‚ญ์ œ ์กฐ๊ฑด:", filterConditions); + + result = await dataApi.deleteGroupRecords(tableName, filterConditions); + } else { + // ํ•„ํ„ฐ ์กฐ๊ฑด์ด ์—†์œผ๋ฉด ๋‹จ์ผ ์‚ญ์ œ + console.log("โš ๏ธ groupByColumns ๊ฐ’์ด ์—†์–ด ๋‹จ์ผ ์‚ญ์ œ๋กœ ์ „ํ™˜"); + result = await dataApi.deleteRecord(tableName, primaryKey); + } + } + // 2. ์ค‘๋ณต ์ œ๊ฑฐ(deduplication)๊ฐ€ ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ + else if (deduplication?.enabled && deduplication?.groupByColumn) { + const groupByColumn = deduplication.groupByColumn; + const groupValue = deleteModalItem[groupByColumn]; + + if (groupValue) { + console.log(`๐Ÿ”— ์ค‘๋ณต ์ œ๊ฑฐ ํ™œ์„ฑํ™”: ${groupByColumn} = ${groupValue} ๊ธฐ์ค€์œผ๋กœ ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ`); + + const filterConditions: Record = { + [groupByColumn]: groupValue, + }; + + // ์ขŒ์ธก ํŒจ๋„์˜ ์„ ํƒ๋œ ํ•ญ๋ชฉ ์ •๋ณด๋„ ํฌํ•จ (customer_id ๋“ฑ) + if (selectedLeftItem && componentConfig.rightPanel?.mode === "join") { + const leftColumn = componentConfig.rightPanel.join.leftColumn; + const rightColumn = componentConfig.rightPanel.join.rightColumn; + filterConditions[rightColumn] = selectedLeftItem[leftColumn]; + } + + console.log("๐Ÿ—‘๏ธ ๊ทธ๋ฃน ์‚ญ์ œ ์กฐ๊ฑด:", filterConditions); + result = await dataApi.deleteGroupRecords(tableName, filterConditions); + } else { + result = await dataApi.deleteRecord(tableName, primaryKey); + } + } + // 3. ๊ทธ ์™ธ: ๋‹จ์ผ ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ + else { result = await dataApi.deleteRecord(tableName, primaryKey); } } else { - // ๋‹จ์ผ ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ + // ์ขŒ์ธก ํŒจ๋„: ๋‹จ์ผ ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ result = await dataApi.deleteRecord(tableName, primaryKey); }