diff --git a/backend-node/src/services/entityJoinService.ts b/backend-node/src/services/entityJoinService.ts index 25d96927..86762b64 100644 --- a/backend-node/src/services/entityJoinService.ts +++ b/backend-node/src/services/entityJoinService.ts @@ -334,6 +334,10 @@ export class EntityJoinService { ); }); + // ๐Ÿ”ง _label ๋ณ„์นญ ์ค‘๋ณต ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ Set + // ๊ฐ™์€ sourceColumn์—์„œ ์—ฌ๋Ÿฌ ์กฐ์ธ ์„ค์ •์ด ์žˆ์„ ๋•Œ _label์€ ์ฒซ ๋ฒˆ์งธ๋งŒ ์ƒ์„ฑ + const generatedLabelAliases = new Set(); + const joinColumns = joinConfigs .map((config) => { const aliasKey = `${config.referenceTable}:${config.sourceColumn}`; @@ -368,16 +372,26 @@ export class EntityJoinService { // _label ํ•„๋“œ๋„ ํ•จ๊ป˜ SELECT (ํ”„๋ก ํŠธ์—”๋“œ getColumnUniqueValues์šฉ) // sourceColumn_label ํ˜•์‹์œผ๋กœ ์ถ”๊ฐ€ - resultColumns.push( - `COALESCE(${alias}.${col}::TEXT, '') AS ${config.sourceColumn}_label` - ); + // ๐Ÿ”ง ์ค‘๋ณต ๋ฐฉ์ง€: ๊ฐ™์€ sourceColumn์—์„œ _label์€ ์ฒซ ๋ฒˆ์งธ๋งŒ ์ƒ์„ฑ + const labelAlias = `${config.sourceColumn}_label`; + if (!generatedLabelAliases.has(labelAlias)) { + resultColumns.push( + `COALESCE(${alias}.${col}::TEXT, '') AS ${labelAlias}` + ); + generatedLabelAliases.add(labelAlias); + } // ๐Ÿ†• referenceColumn (PK)๋„ ํ•ญ์ƒ SELECT (parentDataMapping์šฉ) // ์˜ˆ: customer_code, item_number ๋“ฑ // col๊ณผ ๋™์ผํ•ด๋„ ๋ณ„๋„์˜ alias๋กœ ์ถ”๊ฐ€ (customer_code as customer_code) - resultColumns.push( - `COALESCE(${alias}.${config.referenceColumn}::TEXT, '') AS ${config.referenceColumn}` - ); + // ๐Ÿ”ง ์ค‘๋ณต ๋ฐฉ์ง€: referenceColumn๋„ ํ•œ ๋ฒˆ๋งŒ ์ถ”๊ฐ€ + const refColAlias = config.referenceColumn; + if (!generatedLabelAliases.has(refColAlias)) { + resultColumns.push( + `COALESCE(${alias}.${config.referenceColumn}::TEXT, '') AS ${refColAlias}` + ); + generatedLabelAliases.add(refColAlias); + } } else { resultColumns.push( `COALESCE(main.${col}::TEXT, '') AS ${config.aliasColumn}` @@ -392,6 +406,11 @@ export class EntityJoinService { const individualAlias = `${config.sourceColumn}_${col}`; + // ๐Ÿ”ง ์ค‘๋ณต ๋ฐฉ์ง€: ๊ฐ™์€ alias๊ฐ€ ์ด๋ฏธ ์ƒ์„ฑ๋˜์—ˆ์œผ๋ฉด ์Šคํ‚ต + if (generatedLabelAliases.has(individualAlias)) { + return; + } + if (isJoinTableColumn) { // ์กฐ์ธ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ์€ ์กฐ์ธ ๋ณ„์นญ ์‚ฌ์šฉ resultColumns.push( @@ -403,6 +422,7 @@ export class EntityJoinService { `COALESCE(main.${col}::TEXT, '') AS ${individualAlias}` ); } + generatedLabelAliases.add(individualAlias); }); // ๐Ÿ†• referenceColumn (PK)๋„ ํ•จ๊ป˜ SELECT (parentDataMapping์šฉ) @@ -410,11 +430,13 @@ export class EntityJoinService { config.referenceTable && config.referenceTable !== tableName; if ( isJoinTableColumn && - !displayColumns.includes(config.referenceColumn) + !displayColumns.includes(config.referenceColumn) && + !generatedLabelAliases.has(config.referenceColumn) // ๐Ÿ”ง ์ค‘๋ณต ๋ฐฉ์ง€ ) { resultColumns.push( `COALESCE(${alias}.${config.referenceColumn}::TEXT, '') AS ${config.referenceColumn}` ); + generatedLabelAliases.add(config.referenceColumn); } } diff --git a/frontend/components/webtypes/RepeaterInput.tsx b/frontend/components/webtypes/RepeaterInput.tsx index 7cd4b279..050b386b 100644 --- a/frontend/components/webtypes/RepeaterInput.tsx +++ b/frontend/components/webtypes/RepeaterInput.tsx @@ -309,18 +309,32 @@ export const RepeaterInput: React.FC = ({ _subDataMaxValue: maxValue, }; - // ์„ ํƒ๋œ ํ•˜์œ„ ๋ฐ์ดํ„ฐ์˜ ํ•„๋“œ ๊ฐ’์„ ์ƒ์œ„ item์— ๋ณต์‚ฌ (์„ค์ •๋œ ๊ฒฝ์šฐ) - // ์˜ˆ: warehouse_code, location_code ๋“ฑ - if (subDataLookup.lookup.displayColumns) { - subDataLookup.lookup.displayColumns.forEach((col) => { - if (selectedItem[col] !== undefined) { - // ํ•„๋“œ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์œผ๋ฉด ๋ณต์‚ฌ - const fieldDef = fields.find((f) => f.name === col); - if (fieldDef || col.includes("_code") || col.includes("_id")) { - newItems[itemIndex][col] = selectedItem[col]; + // fieldMappings๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด ๋งคํ•‘์— ๋”ฐ๋ผ ๊ฐ’ ๋ณต์‚ฌ + if (subDataLookup.lookup.fieldMappings && subDataLookup.lookup.fieldMappings.length > 0) { + subDataLookup.lookup.fieldMappings.forEach((mapping) => { + if (mapping.targetField && mapping.targetField !== "") { + // ๋งคํ•‘๋œ ํƒ€๊ฒŸ ํ•„๋“œ์— ์†Œ์Šค ์ปฌ๋Ÿผ ๊ฐ’ ๋ณต์‚ฌ + const sourceValue = selectedItem[mapping.sourceColumn]; + if (sourceValue !== undefined) { + newItems[itemIndex][mapping.targetField] = sourceValue; } } }); + } else { + // fieldMappings๊ฐ€ ์—†์œผ๋ฉด ๊ธฐ์กด ๋กœ์ง (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ) + // ์„ ํƒ๋œ ํ•˜์œ„ ๋ฐ์ดํ„ฐ์˜ ํ•„๋“œ ๊ฐ’์„ ์ƒ์œ„ item์— ๋ณต์‚ฌ (์„ค์ •๋œ ๊ฒฝ์šฐ) + // ์˜ˆ: warehouse_code, location_code ๋“ฑ + if (subDataLookup.lookup.displayColumns) { + subDataLookup.lookup.displayColumns.forEach((col) => { + if (selectedItem[col] !== undefined) { + // ํ•„๋“œ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์œผ๋ฉด ๋ณต์‚ฌ + const fieldDef = fields.find((f) => f.name === col); + if (fieldDef || col.includes("_code") || col.includes("_id")) { + newItems[itemIndex][col] = selectedItem[col]; + } + } + }); + } } setItems(newItems); @@ -893,6 +907,10 @@ export const RepeaterInput: React.FC = ({ const renderGridLayout = () => { // ํ•˜์œ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์„ค์ •์ด ์žˆ์œผ๋ฉด ์—ฐ๊ฒฐ ์ปฌ๋Ÿผ ์ฐพ๊ธฐ const linkColumn = subDataLookup?.lookup?.linkColumn; + + // hidden์ด ์•„๋‹Œ ํ•„๋“œ๋งŒ ํ‘œ์‹œ + // isHidden์ด true์ด๊ฑฐ๋‚˜ displayMode๊ฐ€ hidden์ธ ํ•„๋“œ๋Š” ์ œ์™ธ (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ ์œ ์ง€) + const visibleFields = fields.filter((f) => !f.isHidden && f.displayMode !== "hidden"); return (
@@ -905,7 +923,7 @@ export const RepeaterInput: React.FC = ({ {allowReorder && ( )} - {fields.map((field) => ( + {visibleFields.map((field) => ( {field.label} {field.required && *} @@ -944,8 +962,8 @@ export const RepeaterInput: React.FC = ({ )} - {/* ํ•„๋“œ๋“ค */} - {fields.map((field) => ( + {/* ํ•„๋“œ๋“ค (hidden ์ œ์™ธ) */} + {visibleFields.map((field) => ( {renderField(field, itemIndex, item[field.name])} @@ -973,7 +991,7 @@ export const RepeaterInput: React.FC = ({ @@ -1002,6 +1020,10 @@ export const RepeaterInput: React.FC = ({ const renderCardLayout = () => { // ํ•˜์œ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์„ค์ •์ด ์žˆ์œผ๋ฉด ์—ฐ๊ฒฐ ์ปฌ๋Ÿผ ์ฐพ๊ธฐ const linkColumn = subDataLookup?.lookup?.linkColumn; + + // hidden์ด ์•„๋‹Œ ํ•„๋“œ๋งŒ ํ‘œ์‹œ + // isHidden์ด true์ด๊ฑฐ๋‚˜ displayMode๊ฐ€ hidden์ธ ํ•„๋“œ๋Š” ์ œ์™ธ (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ ์œ ์ง€) + const visibleFields = fields.filter((f) => !f.isHidden && f.displayMode !== "hidden"); return ( <> @@ -1070,7 +1092,7 @@ export const RepeaterInput: React.FC = ({ {!isCollapsed && (
- {fields.map((field) => ( + {visibleFields.map((field) => (