From a489e2c1553b3e24bb104b23e2efeaf108cdf001 Mon Sep 17 00:00:00 2001 From: leeheejin Date: Thu, 11 Dec 2025 15:46:05 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=EC=B2=A8=EB=B6=80=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/lib/api/file.ts | 7 ++-- .../file-upload/FileUploadComponent.tsx | 22 +++++++++++- .../table-list/TableListComponent.tsx | 34 ++++++++++++++++--- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/frontend/lib/api/file.ts b/frontend/lib/api/file.ts index 318f26fb..4908b381 100644 --- a/frontend/lib/api/file.ts +++ b/frontend/lib/api/file.ts @@ -32,7 +32,7 @@ export const uploadFiles = async (params: { files: FileList | File[]; tableName?: string; fieldName?: string; - recordId?: string; + recordId?: string | number; docType?: string; docTypeName?: string; targetObjid?: string; @@ -43,6 +43,7 @@ export const uploadFiles = async (params: { columnName?: string; isVirtualFileColumn?: boolean; companyCode?: string; // ๐Ÿ”’ ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ: ํšŒ์‚ฌ ์ฝ”๋“œ + isRecordMode?: boolean; // ๐Ÿ†• ๋ ˆ์ฝ”๋“œ ๋ชจ๋“œ ํ”Œ๋ž˜๊ทธ }): Promise => { const formData = new FormData(); @@ -55,7 +56,7 @@ export const uploadFiles = async (params: { // ์ถ”๊ฐ€ ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค ์ถ”๊ฐ€ if (params.tableName) formData.append("tableName", params.tableName); if (params.fieldName) formData.append("fieldName", params.fieldName); - if (params.recordId) formData.append("recordId", params.recordId); + if (params.recordId) formData.append("recordId", String(params.recordId)); if (params.docType) formData.append("docType", params.docType); if (params.docTypeName) formData.append("docTypeName", params.docTypeName); if (params.targetObjid) formData.append("targetObjid", params.targetObjid); @@ -66,6 +67,8 @@ export const uploadFiles = async (params: { if (params.columnName) formData.append("columnName", params.columnName); if (params.isVirtualFileColumn !== undefined) formData.append("isVirtualFileColumn", params.isVirtualFileColumn.toString()); if (params.companyCode) formData.append("companyCode", params.companyCode); // ๐Ÿ”’ ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ + // ๐Ÿ†• ๋ ˆ์ฝ”๋“œ ๋ชจ๋“œ ํ”Œ๋ž˜๊ทธ ์ถ”๊ฐ€ (๋ฐฑ์—”๋“œ์—์„œ attachments ์ปฌ๋Ÿผ ์ž๋™ ์—…๋ฐ์ดํŠธ์šฉ) + if (params.isRecordMode !== undefined) formData.append("isRecordMode", params.isRecordMode.toString()); const response = await apiClient.post("/files/upload", formData, { headers: { diff --git a/frontend/lib/registry/components/file-upload/FileUploadComponent.tsx b/frontend/lib/registry/components/file-upload/FileUploadComponent.tsx index 805fe755..dd4f4c6b 100644 --- a/frontend/lib/registry/components/file-upload/FileUploadComponent.tsx +++ b/frontend/lib/registry/components/file-upload/FileUploadComponent.tsx @@ -603,10 +603,16 @@ const FileUploadComponent: React.FC = ({ targetObjid, }); + // ๐Ÿ”‘ ๋ ˆ์ฝ”๋“œ ๋ชจ๋“œ์ผ ๋•Œ๋Š” effectiveTableName์„ ์šฐ์„  ์‚ฌ์šฉ + // formData.linkedTable์ด 'screen_files' ๊ฐ™์€ ๊ธฐ๋ณธ๊ฐ’์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ ˆ์ฝ”๋“œ ๋ชจ๋“œ์—์„œ๋Š” ๋ฌด์‹œ + const finalLinkedTable = effectiveIsRecordMode + ? effectiveTableName + : (formData?.linkedTable || effectiveTableName); + const uploadData = { // ๐ŸŽฏ formData์—์„œ ๋ฐฑ์—”๋“œ API ์„ค์ • ๊ฐ€์ ธ์˜ค๊ธฐ autoLink: formData?.autoLink || true, - linkedTable: formData?.linkedTable || effectiveTableName, + linkedTable: finalLinkedTable, recordId: effectiveRecordId || `temp_${component.id}`, columnName: effectiveColumnName, isVirtualFileColumn: formData?.isVirtualFileColumn || true, @@ -620,13 +626,27 @@ const FileUploadComponent: React.FC = ({ // ๐Ÿ†• ๋ ˆ์ฝ”๋“œ ๋ชจ๋“œ ํ”Œ๋ž˜๊ทธ isRecordMode: effectiveIsRecordMode, }; + + console.log("๐Ÿ“ค [FileUploadComponent] uploadData ์ตœ์ข…:", { + isRecordMode: effectiveIsRecordMode, + linkedTable: finalLinkedTable, + recordId: effectiveRecordId, + columnName: effectiveColumnName, + targetObjid, + }); + console.log("๐Ÿš€ [FileUploadComponent] uploadFiles API ํ˜ธ์ถœ ์ง์ „:", { + filesCount: filesToUpload.length, + uploadData, + }); + const response = await uploadFiles({ files: filesToUpload, ...uploadData, }); + console.log("๐Ÿ“ฅ [FileUploadComponent] uploadFiles API ์‘๋‹ต:", response); if (response.success) { // FileUploadResponse ํƒ€์ž…์— ๋งž๊ฒŒ files ๋ฐฐ์—ด ์‚ฌ์šฉ diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index f68b8383..a12bf494 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -1683,6 +1683,19 @@ export const TableListComponent: React.FC = ({ // console.log(` - uniqueItemNumbers: ${JSON.stringify(uniqueItemNumbers)}`); // console.log(` - isDuplicated: ${itemNumbers.length !== uniqueItemNumbers.length}`); + // ๐Ÿ” ๋””๋ฒ„๊ทธ: attachments ์ปฌ๋Ÿผ ๋ฐ์ดํ„ฐ ํ™•์ธ + if (response.data && response.data.length > 0) { + const firstRow = response.data[0]; + if ('attachments' in firstRow) { + console.log("๐Ÿ“Ž [TableList] attachments ๋ฐ์ดํ„ฐ ํ™•์ธ:", { + tableName: tableConfig.selectedTable, + firstRowAttachments: firstRow.attachments, + type: typeof firstRow.attachments, + isArray: Array.isArray(firstRow.attachments), + }); + } + } + setData(response.data || []); setTotalPages(response.totalPages || 0); setTotalItems(response.total || 0); @@ -3971,17 +3984,30 @@ export const TableListComponent: React.FC = ({ } // ๐Ÿ“Ž ์ฒจ๋ถ€ํŒŒ์ผ ํƒ€์ž…: ํŒŒ์ผ ์•„์ด์ฝ˜๊ณผ ๊ฐœ์ˆ˜ ํ‘œ์‹œ - if (inputType === "file" || inputType === "attachment" || column.columnName === "attachments") { + // ์ปฌ๋Ÿผ๋ช…์ด 'attachments'๋ฅผ ํฌํ•จํ•˜๊ฑฐ๋‚˜, inputType์ด file/attachment์ธ ๊ฒฝ์šฐ + const isAttachmentColumn = + inputType === "file" || + inputType === "attachment" || + column.columnName === "attachments" || + column.columnName?.toLowerCase().includes("attachment") || + column.columnName?.toLowerCase().includes("file"); + + if (isAttachmentColumn) { // JSONB ๋ฐฐ์—ด ๋˜๋Š” JSON ๋ฌธ์ž์—ด ํŒŒ์‹ฑ let files: any[] = []; try { - if (typeof value === "string") { - files = JSON.parse(value); + if (typeof value === "string" && value.trim()) { + const parsed = JSON.parse(value); + files = Array.isArray(parsed) ? parsed : []; } else if (Array.isArray(value)) { files = value; + } else if (value && typeof value === "object") { + // ๋‹จ์ผ ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ + files = [value]; } - } catch { + } catch (e) { // ํŒŒ์‹ฑ ์‹คํŒจ ์‹œ ๋นˆ ๋ฐฐ์—ด + console.warn("๐Ÿ“Ž [TableList] ์ฒจ๋ถ€ํŒŒ์ผ ํŒŒ์‹ฑ ์‹คํŒจ:", { columnName: column.columnName, value, error: e }); } if (!files || files.length === 0) { From 84bd1ce154f6672d64454e88392c32d02e72cf56 Mon Sep 17 00:00:00 2001 From: leeheejin Date: Thu, 11 Dec 2025 15:50:28 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=EC=B2=A8=EB=B6=80=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9C=BC=EB=A1=9C=20=EB=82=98=EC=98=A4?= =?UTF-8?q?=EA=B2=8C=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table-list/TableListComponent.tsx | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index a12bf494..c9d41dce 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -4014,13 +4014,24 @@ export const TableListComponent: React.FC = ({ return -; } - // ํŒŒ์ผ ๊ฐœ์ˆ˜์™€ ์•„์ด์ฝ˜ ํ‘œ์‹œ + // ํŒŒ์ผ ์ด๋ฆ„ ํ‘œ์‹œ (์—ฌ๋Ÿฌ ๊ฐœ๋ฉด ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„) const { Paperclip } = require("lucide-react"); + const fileNames = files.map((f: any) => f.realFileName || f.real_file_name || f.name || "ํŒŒ์ผ").join(", "); + return ( -
- - {files.length} - ๊ฐœ +
+ + + {fileNames} + + {files.length > 1 && ( + + ({files.length}) + + )}
); } From 30361e0f45b84b29ba2a83987093473c9dd93cfd Mon Sep 17 00:00:00 2001 From: leeheejin Date: Thu, 11 Dec 2025 16:07:16 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=EB=8F=94=20=EC=97=90=EB=9F=AC=20=EB=9C=A8?= =?UTF-8?q?=EB=8D=98=EA=B1=B0=20=EA=B3=A0=EC=B9=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/accordion-basic/AccordionBasicComponent.tsx | 3 +++ .../registry/components/divider-line/DividerLineComponent.tsx | 3 +++ .../lib/registry/components/text-input/TextInputComponent.tsx | 3 +++ 3 files changed, 9 insertions(+) diff --git a/frontend/lib/registry/components/accordion-basic/AccordionBasicComponent.tsx b/frontend/lib/registry/components/accordion-basic/AccordionBasicComponent.tsx index c25cb7bd..78951f7e 100644 --- a/frontend/lib/registry/components/accordion-basic/AccordionBasicComponent.tsx +++ b/frontend/lib/registry/components/accordion-basic/AccordionBasicComponent.tsx @@ -602,6 +602,9 @@ export const AccordionBasicComponent: React.FC = ( isInModal: _isInModal, isPreview: _isPreview, originalData: _originalData, + _originalData: __originalData, + _initialData: __initialData, + _groupedData: __groupedData, allComponents: _allComponents, selectedRows: _selectedRows, selectedRowsData: _selectedRowsData, diff --git a/frontend/lib/registry/components/divider-line/DividerLineComponent.tsx b/frontend/lib/registry/components/divider-line/DividerLineComponent.tsx index d2b61c90..ea4428ca 100644 --- a/frontend/lib/registry/components/divider-line/DividerLineComponent.tsx +++ b/frontend/lib/registry/components/divider-line/DividerLineComponent.tsx @@ -86,6 +86,9 @@ export const DividerLineComponent: React.FC = ({ isInModal: _isInModal, readonly: _readonly, originalData: _originalData, + _originalData: __originalData, + _initialData: __initialData, + _groupedData: __groupedData, allComponents: _allComponents, onUpdateLayout: _onUpdateLayout, selectedRows: _selectedRows, diff --git a/frontend/lib/registry/components/text-input/TextInputComponent.tsx b/frontend/lib/registry/components/text-input/TextInputComponent.tsx index 72dabb61..ad37f19f 100644 --- a/frontend/lib/registry/components/text-input/TextInputComponent.tsx +++ b/frontend/lib/registry/components/text-input/TextInputComponent.tsx @@ -224,6 +224,9 @@ export const TextInputComponent: React.FC = ({ isInModal: _isInModal, isPreview: _isPreview, originalData: _originalData, + _originalData: __originalData, + _initialData: __initialData, + _groupedData: __groupedData, allComponents: _allComponents, selectedRows: _selectedRows, selectedRowsData: _selectedRowsData, From 563acb7c0019cc463bc9605f96c256db8c2ad4dc Mon Sep 17 00:00:00 2001 From: leeheejin Date: Thu, 11 Dec 2025 16:09:58 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=EB=B4=84=20=EC=97=90=EB=9F=AC=20=EB=9C=A8?= =?UTF-8?q?=EB=8D=98=EA=B1=B0=20=EA=B3=A0=EC=B9=A82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/button-primary/ButtonPrimaryComponent.tsx | 3 +++ .../repeat-screen-modal/RepeatScreenModalComponent.tsx | 8 ++++++-- .../universal-form-modal/UniversalFormModalComponent.tsx | 4 +++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx index 1942d268..ae6a01d8 100644 --- a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx +++ b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx @@ -984,6 +984,9 @@ export const ButtonPrimaryComponent: React.FC = ({ flowSelectedStepId: _flowSelectedStepId, // ํ”Œ๋กœ์šฐ ์„ ํƒ ์Šคํ… ID ํ•„ํ„ฐ๋ง onFlowRefresh: _onFlowRefresh, // ํ”Œ๋กœ์šฐ ์ƒˆ๋กœ๊ณ ์นจ ์ฝœ๋ฐฑ ํ•„ํ„ฐ๋ง originalData: _originalData, // ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ์šฉ ์›๋ณธ ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง + _originalData: __originalData, // DOM ํ•„ํ„ฐ๋ง + _initialData: __initialData, // DOM ํ•„ํ„ฐ๋ง + _groupedData: __groupedData, // DOM ํ•„ํ„ฐ๋ง refreshKey: _refreshKey, // ํ•„ํ„ฐ๋ง ์ถ”๊ฐ€ isInModal: _isInModal, // ํ•„ํ„ฐ๋ง ์ถ”๊ฐ€ mode: _mode, // ํ•„ํ„ฐ๋ง ์ถ”๊ฐ€ diff --git a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalComponent.tsx b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalComponent.tsx index 980bbfe9..4b4809a1 100644 --- a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalComponent.tsx +++ b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalComponent.tsx @@ -52,11 +52,15 @@ export function RepeatScreenModalComponent({ config, className, groupedData: propsGroupedData, // EditModal์—์„œ ์ „๋‹ฌ๋ฐ›๋Š” ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ + // DynamicComponentRenderer์—์„œ ์ „๋‹ฌ๋˜๋Š” props (DOM ์ „๋‹ฌ ๋ฐฉ์ง€) + _initialData, + _originalData: _propsOriginalData, + _groupedData, ...props -}: RepeatScreenModalComponentProps) { +}: RepeatScreenModalComponentProps & { _initialData?: any; _originalData?: any; _groupedData?: any }) { // props์—์„œ๋„ groupedData๋ฅผ ์ถ”์ถœ (DynamicWebTypeRenderer์—์„œ ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ์Œ) // DynamicComponentRenderer์—์„œ๋Š” _groupedData๋กœ ์ „๋‹ฌ๋จ - const groupedData = propsGroupedData || (props as any).groupedData || (props as any)._groupedData; + const groupedData = propsGroupedData || (props as any).groupedData || _groupedData; const componentConfig = { ...config, ...component?.config, diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx index 89838c67..4ac31b12 100644 --- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx +++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx @@ -126,11 +126,13 @@ export function UniversalFormModalComponent({ initialData: propInitialData, // DynamicComponentRenderer์—์„œ ์ „๋‹ฌ๋˜๋Š” props (DOM ์ „๋‹ฌ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด _ prefix ์‚ฌ์šฉ) _initialData, + _originalData, + _groupedData, onSave, onCancel, onChange, ...restProps // ๋‚˜๋จธ์ง€ props๋Š” DOM์— ์ „๋‹ฌํ•˜์ง€ ์•Š์Œ -}: UniversalFormModalComponentProps & { _initialData?: any }) { +}: UniversalFormModalComponentProps & { _initialData?: any; _originalData?: any; _groupedData?: any }) { // initialData ์šฐ์„ ์ˆœ์œ„: ์ง์ ‘ ์ „๋‹ฌ๋œ prop > DynamicComponentRenderer์—์„œ ์ „๋‹ฌ๋œ prop const initialData = propInitialData || _initialData; // ์„ค์ • ๋ณ‘ํ•ฉ