diff --git a/backend-node/src/routes/cascadingAutoFillRoutes.ts b/backend-node/src/routes/cascadingAutoFillRoutes.ts index 5d922dd6..de4eb913 100644 --- a/backend-node/src/routes/cascadingAutoFillRoutes.ts +++ b/backend-node/src/routes/cascadingAutoFillRoutes.ts @@ -50,3 +50,4 @@ router.get("/data/:groupCode", getAutoFillData); export default router; + diff --git a/backend-node/src/routes/cascadingConditionRoutes.ts b/backend-node/src/routes/cascadingConditionRoutes.ts index 813dbff1..c2f12782 100644 --- a/backend-node/src/routes/cascadingConditionRoutes.ts +++ b/backend-node/src/routes/cascadingConditionRoutes.ts @@ -46,3 +46,4 @@ router.get("/filtered-options/:relationCode", getFilteredOptions); export default router; + diff --git a/backend-node/src/routes/cascadingHierarchyRoutes.ts b/backend-node/src/routes/cascadingHierarchyRoutes.ts index be37da49..71e6c418 100644 --- a/backend-node/src/routes/cascadingHierarchyRoutes.ts +++ b/backend-node/src/routes/cascadingHierarchyRoutes.ts @@ -62,3 +62,4 @@ router.get("/:groupCode/options/:levelOrder", getLevelOptions); export default router; + diff --git a/backend-node/src/routes/cascadingMutualExclusionRoutes.ts b/backend-node/src/routes/cascadingMutualExclusionRoutes.ts index 46bbf427..d92d7d72 100644 --- a/backend-node/src/routes/cascadingMutualExclusionRoutes.ts +++ b/backend-node/src/routes/cascadingMutualExclusionRoutes.ts @@ -50,3 +50,4 @@ router.get("/options/:exclusionCode", getExcludedOptions); export default router; + diff --git a/backend-node/src/services/dynamicFormService.ts b/backend-node/src/services/dynamicFormService.ts index 65efcd1b..7ec95626 100644 --- a/backend-node/src/services/dynamicFormService.ts +++ b/backend-node/src/services/dynamicFormService.ts @@ -903,7 +903,7 @@ export class DynamicFormService { return `${key} = $${index + 1}::numeric`; } else if (dataType === "boolean") { return `${key} = $${index + 1}::boolean`; - } else if (dataType === 'jsonb' || dataType === 'json') { + } else if (dataType === "jsonb" || dataType === "json") { // ๐Ÿ†• JSONB/JSON ํƒ€์ž…์€ ๋ช…์‹œ์  ์บ์ŠคํŒ… return `${key} = $${index + 1}::jsonb`; } else { @@ -917,9 +917,13 @@ export class DynamicFormService { const values: any[] = Object.keys(changedFields).map((key) => { const value = changedFields[key]; const dataType = columnTypes[key]; - + // JSONB/JSON ํƒ€์ž…์ด๊ณ  ๋ฐฐ์—ด/๊ฐ์ฒด์ธ ๊ฒฝ์šฐ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ - if ((dataType === 'jsonb' || dataType === 'json') && (Array.isArray(value) || (typeof value === 'object' && value !== null))) { + if ( + (dataType === "jsonb" || dataType === "json") && + (Array.isArray(value) || + (typeof value === "object" && value !== null)) + ) { return JSON.stringify(value); } return value; @@ -1588,6 +1592,7 @@ export class DynamicFormService { /** * ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ (ํ™”๋ฉด์— ์„ค์ •๋œ ๊ฒฝ์šฐ) + * ๋‹ค์ค‘ ์ œ์–ด๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์ˆœ์ฐจ ์‹คํ–‰ ์ง€์› */ private async executeDataflowControlIfConfigured( screenId: number, @@ -1629,105 +1634,67 @@ export class DynamicFormService { hasDataflowConfig: !!properties?.webTypeConfig?.dataflowConfig, hasDiagramId: !!properties?.webTypeConfig?.dataflowConfig?.selectedDiagramId, + hasFlowControls: + !!properties?.webTypeConfig?.dataflowConfig?.flowControls, }); // ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ์ด๊ณ  ์ €์žฅ ์•ก์…˜์ด๋ฉฐ ์ œ์–ด๊ด€๋ฆฌ๊ฐ€ ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ if ( properties?.componentType === "button-primary" && properties?.componentConfig?.action?.type === "save" && - properties?.webTypeConfig?.enableDataflowControl === true && - properties?.webTypeConfig?.dataflowConfig?.selectedDiagramId + properties?.webTypeConfig?.enableDataflowControl === true ) { - controlConfigFound = true; - const diagramId = - properties.webTypeConfig.dataflowConfig.selectedDiagramId; - const relationshipId = - properties.webTypeConfig.dataflowConfig.selectedRelationshipId; + const dataflowConfig = properties?.webTypeConfig?.dataflowConfig; - console.log(`๐ŸŽฏ ์ œ์–ด๊ด€๋ฆฌ ์„ค์ • ๋ฐœ๊ฒฌ:`, { - componentId: layout.component_id, - diagramId, - relationshipId, - triggerType, - }); + // ๋‹ค์ค‘ ์ œ์–ด ์„ค์ • ํ™•์ธ (flowControls ๋ฐฐ์—ด) + const flowControls = dataflowConfig?.flowControls || []; - // ๋…ธ๋“œ ํ”Œ๋กœ์šฐ ์‹คํ–‰ (relationshipId๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋…ธ๋“œ ํ”Œ๋กœ์šฐ๋กœ ๊ฐ„์ฃผ) - let controlResult: any; + // flowControls๊ฐ€ ์žˆ์œผ๋ฉด ๋‹ค์ค‘ ์ œ์–ด ์‹คํ–‰, ์—†์œผ๋ฉด ๊ธฐ์กด ๋‹จ์ผ ์ œ์–ด ์‹คํ–‰ + if (flowControls.length > 0) { + controlConfigFound = true; + console.log(`๐ŸŽฏ ๋‹ค์ค‘ ์ œ์–ด๊ด€๋ฆฌ ์„ค์ • ๋ฐœ๊ฒฌ: ${flowControls.length}๊ฐœ`); - if (!relationshipId) { - // ๋…ธ๋“œ ํ”Œ๋กœ์šฐ ์‹คํ–‰ - console.log(`๐Ÿš€ ๋…ธ๋“œ ํ”Œ๋กœ์šฐ ์‹คํ–‰ (flowId: ${diagramId})`); - const { NodeFlowExecutionService } = await import( - "./nodeFlowExecutionService" + // ์ˆœ์„œ๋Œ€๋กœ ์ •๋ ฌ + const sortedControls = [...flowControls].sort( + (a: any, b: any) => (a.order || 0) - (b.order || 0) ); - const executionResult = await NodeFlowExecutionService.executeFlow( + // ๋‹ค์ค‘ ์ œ์–ด ์ˆœ์ฐจ ์‹คํ–‰ + await this.executeMultipleFlowControls( + sortedControls, + savedData, + screenId, + tableName, + triggerType, + userId, + companyCode + ); + } else if (dataflowConfig?.selectedDiagramId) { + // ๊ธฐ์กด ๋‹จ์ผ ์ œ์–ด ์‹คํ–‰ (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ) + controlConfigFound = true; + const diagramId = dataflowConfig.selectedDiagramId; + const relationshipId = dataflowConfig.selectedRelationshipId; + + console.log(`๐ŸŽฏ ๋‹จ์ผ ์ œ์–ด๊ด€๋ฆฌ ์„ค์ • ๋ฐœ๊ฒฌ:`, { + componentId: layout.component_id, diagramId, - { - sourceData: [savedData], - dataSourceType: "formData", - buttonId: "save-button", - screenId: screenId, - userId: userId, - companyCode: companyCode, - formData: savedData, - } - ); + relationshipId, + triggerType, + }); - controlResult = { - success: executionResult.success, - message: executionResult.message, - executedActions: executionResult.nodes?.map((node) => ({ - nodeId: node.nodeId, - status: node.status, - duration: node.duration, - })), - errors: executionResult.nodes - ?.filter((node) => node.status === "failed") - .map((node) => node.error || "์‹คํ–‰ ์‹คํŒจ"), - }; - } else { - // ๊ด€๊ณ„ ๊ธฐ๋ฐ˜ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ - console.log( - `๐ŸŽฏ ๊ด€๊ณ„ ๊ธฐ๋ฐ˜ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ (relationshipId: ${relationshipId})` + await this.executeSingleFlowControl( + diagramId, + relationshipId, + savedData, + screenId, + tableName, + triggerType, + userId, + companyCode ); - controlResult = - await this.dataflowControlService.executeDataflowControl( - diagramId, - relationshipId, - triggerType, - savedData, - tableName, - userId - ); } - console.log(`๐ŸŽฏ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ ๊ฒฐ๊ณผ:`, controlResult); - - if (controlResult.success) { - console.log(`โœ… ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ ์„ฑ๊ณต: ${controlResult.message}`); - if ( - controlResult.executedActions && - controlResult.executedActions.length > 0 - ) { - console.log(`๐Ÿ“Š ์‹คํ–‰๋œ ์•ก์…˜๋“ค:`, controlResult.executedActions); - } - - // ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๊ฒฝ๊ณ  ๋กœ๊ทธ ์ถœ๋ ฅ (์„ฑ๊ณต์ด์ง€๋งŒ ์ผ๋ถ€ ์•ก์…˜ ์‹คํŒจ) - if (controlResult.errors && controlResult.errors.length > 0) { - console.warn( - `โš ๏ธ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ ์ค‘ ์ผ๋ถ€ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:`, - controlResult.errors - ); - // ์˜ค๋ฅ˜ ์ •๋ณด๋ฅผ ๋ณ„๋„๋กœ ์ €์žฅํ•˜์—ฌ ํ•„์š”์‹œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ฆผ ๊ฐ€๋Šฅ - // ํ˜„์žฌ๋Š” ๋กœ๊ทธ๋งŒ ์ถœ๋ ฅํ•˜๊ณ  ๋ฉ”์ธ ์ €์žฅ ํ”„๋กœ์„ธ์Šค๋Š” ๊ณ„์† ์ง„ํ–‰ - } - } else { - console.warn(`โš ๏ธ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ ์‹คํŒจ: ${controlResult.message}`); - // ์ œ์–ด๊ด€๋ฆฌ ์‹คํŒจ๋Š” ๋ฉ”์ธ ์ €์žฅ ํ”„๋กœ์„ธ์Šค์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ - } - - // ์ฒซ ๋ฒˆ์งธ ์„ค์ •๋œ ์ œ์–ด๊ด€๋ฆฌ๋งŒ ์‹คํ–‰ (์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ) + // ์ฒซ ๋ฒˆ์งธ ์„ค์ •๋œ ๋ฒ„ํŠผ์˜ ์ œ์–ด๊ด€๋ฆฌ๋งŒ ์‹คํ–‰ break; } } @@ -1741,6 +1708,218 @@ export class DynamicFormService { } } + /** + * ๋‹ค์ค‘ ์ œ์–ด ์ˆœ์ฐจ ์‹คํ–‰ + */ + private async executeMultipleFlowControls( + flowControls: Array<{ + id: string; + flowId: number; + flowName: string; + executionTiming: string; + order: number; + }>, + savedData: Record, + screenId: number, + tableName: string, + triggerType: "insert" | "update" | "delete", + userId: string, + companyCode: string + ): Promise { + console.log(`๐Ÿš€ ๋‹ค์ค‘ ์ œ์–ด ์ˆœ์ฐจ ์‹คํ–‰ ์‹œ์ž‘: ${flowControls.length}๊ฐœ`); + + const { NodeFlowExecutionService } = await import( + "./nodeFlowExecutionService" + ); + + const results: Array<{ + order: number; + flowId: number; + flowName: string; + success: boolean; + message: string; + duration: number; + }> = []; + + for (let i = 0; i < flowControls.length; i++) { + const control = flowControls[i]; + const startTime = Date.now(); + + console.log( + `\n๐Ÿ“ [${i + 1}/${flowControls.length}] ์ œ์–ด ์‹คํ–‰: ${control.flowName} (flowId: ${control.flowId})` + ); + + try { + // ์œ ํšจํ•˜์ง€ ์•Š์€ flowId ์Šคํ‚ต + if (!control.flowId || control.flowId <= 0) { + console.warn(`โš ๏ธ ์œ ํšจํ•˜์ง€ ์•Š์€ flowId, ์Šคํ‚ต: ${control.flowId}`); + results.push({ + order: control.order, + flowId: control.flowId, + flowName: control.flowName, + success: false, + message: "์œ ํšจํ•˜์ง€ ์•Š์€ flowId", + duration: 0, + }); + continue; + } + + const executionResult = await NodeFlowExecutionService.executeFlow( + control.flowId, + { + sourceData: [savedData], + dataSourceType: "formData", + buttonId: "save-button", + screenId: screenId, + userId: userId, + companyCode: companyCode, + formData: savedData, + } + ); + + const duration = Date.now() - startTime; + + results.push({ + order: control.order, + flowId: control.flowId, + flowName: control.flowName, + success: executionResult.success, + message: executionResult.message, + duration, + }); + + if (executionResult.success) { + console.log( + `โœ… [${i + 1}/${flowControls.length}] ์ œ์–ด ์„ฑ๊ณต: ${control.flowName} (${duration}ms)` + ); + } else { + console.error( + `โŒ [${i + 1}/${flowControls.length}] ์ œ์–ด ์‹คํŒจ: ${control.flowName} - ${executionResult.message}` + ); + // ์ด์ „ ์ œ์–ด ์‹คํŒจ ์‹œ ๋‹ค์Œ ์ œ์–ด ์‹คํ–‰ ์ค‘๋‹จ + console.warn(`โš ๏ธ ์ด์ „ ์ œ์–ด ์‹คํŒจ๋กœ ์ธํ•ด ๋‚˜๋จธ์ง€ ์ œ์–ด ์‹คํ–‰ ์ค‘๋‹จ`); + break; + } + } catch (error: any) { + const duration = Date.now() - startTime; + console.error( + `โŒ [${i + 1}/${flowControls.length}] ์ œ์–ด ์‹คํ–‰ ์˜ค๋ฅ˜: ${control.flowName}`, + error + ); + + results.push({ + order: control.order, + flowId: control.flowId, + flowName: control.flowName, + success: false, + message: error.message || "์‹คํ–‰ ์˜ค๋ฅ˜", + duration, + }); + + // ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ๋‹ค์Œ ์ œ์–ด ์‹คํ–‰ ์ค‘๋‹จ + console.warn(`โš ๏ธ ์ œ์–ด ์‹คํ–‰ ์˜ค๋ฅ˜๋กœ ์ธํ•ด ๋‚˜๋จธ์ง€ ์ œ์–ด ์‹คํ–‰ ์ค‘๋‹จ`); + break; + } + } + + // ์‹คํ–‰ ๊ฒฐ๊ณผ ์š”์•ฝ + const successCount = results.filter((r) => r.success).length; + const failCount = results.filter((r) => !r.success).length; + const totalDuration = results.reduce((sum, r) => sum + r.duration, 0); + + console.log(`\n๐Ÿ“Š ๋‹ค์ค‘ ์ œ์–ด ์‹คํ–‰ ์™„๋ฃŒ:`, { + total: flowControls.length, + executed: results.length, + success: successCount, + failed: failCount, + totalDuration: `${totalDuration}ms`, + }); + } + + /** + * ๋‹จ์ผ ์ œ์–ด ์‹คํ–‰ (๊ธฐ์กด ๋กœ์ง, ํ•˜์œ„ ํ˜ธํ™˜์„ฑ) + */ + private async executeSingleFlowControl( + diagramId: number, + relationshipId: string | null, + savedData: Record, + screenId: number, + tableName: string, + triggerType: "insert" | "update" | "delete", + userId: string, + companyCode: string + ): Promise { + let controlResult: any; + + if (!relationshipId) { + // ๋…ธ๋“œ ํ”Œ๋กœ์šฐ ์‹คํ–‰ + console.log(`๐Ÿš€ ๋…ธ๋“œ ํ”Œ๋กœ์šฐ ์‹คํ–‰ (flowId: ${diagramId})`); + const { NodeFlowExecutionService } = await import( + "./nodeFlowExecutionService" + ); + + const executionResult = await NodeFlowExecutionService.executeFlow( + diagramId, + { + sourceData: [savedData], + dataSourceType: "formData", + buttonId: "save-button", + screenId: screenId, + userId: userId, + companyCode: companyCode, + formData: savedData, + } + ); + + controlResult = { + success: executionResult.success, + message: executionResult.message, + executedActions: executionResult.nodes?.map((node) => ({ + nodeId: node.nodeId, + status: node.status, + duration: node.duration, + })), + errors: executionResult.nodes + ?.filter((node) => node.status === "failed") + .map((node) => node.error || "์‹คํ–‰ ์‹คํŒจ"), + }; + } else { + // ๊ด€๊ณ„ ๊ธฐ๋ฐ˜ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ + console.log( + `๐ŸŽฏ ๊ด€๊ณ„ ๊ธฐ๋ฐ˜ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ (relationshipId: ${relationshipId})` + ); + controlResult = await this.dataflowControlService.executeDataflowControl( + diagramId, + relationshipId, + triggerType, + savedData, + tableName, + userId + ); + } + + console.log(`๐ŸŽฏ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ ๊ฒฐ๊ณผ:`, controlResult); + + if (controlResult.success) { + console.log(`โœ… ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ ์„ฑ๊ณต: ${controlResult.message}`); + if ( + controlResult.executedActions && + controlResult.executedActions.length > 0 + ) { + console.log(`๐Ÿ“Š ์‹คํ–‰๋œ ์•ก์…˜๋“ค:`, controlResult.executedActions); + } + + if (controlResult.errors && controlResult.errors.length > 0) { + console.warn( + `โš ๏ธ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ ์ค‘ ์ผ๋ถ€ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:`, + controlResult.errors + ); + } + } else { + console.warn(`โš ๏ธ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ ์‹คํŒจ: ${controlResult.message}`); + } + } + /** * ํŠน์ • ํ…Œ์ด๋ธ”์˜ ํŠน์ • ํ•„๋“œ ๊ฐ’๋งŒ ์—…๋ฐ์ดํŠธ * (๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์˜ ๋ ˆ์ฝ”๋“œ ์—…๋ฐ์ดํŠธ ์ง€์›) diff --git a/docs/๋…ธ๋“œํ”Œ๋กœ์šฐ_๊ฐœ์„ ์‚ฌํ•ญ.md b/docs/๋…ธ๋“œํ”Œ๋กœ์šฐ_๊ฐœ์„ ์‚ฌํ•ญ.md index e80a1a61..985d730a 100644 --- a/docs/๋…ธ๋“œํ”Œ๋กœ์šฐ_๊ฐœ์„ ์‚ฌํ•ญ.md +++ b/docs/๋…ธ๋“œํ”Œ๋กœ์šฐ_๊ฐœ์„ ์‚ฌํ•ญ.md @@ -582,3 +582,4 @@ const result = await executeNodeFlow(flowId, { + diff --git a/docs/๋ฉ”์ผ๋ฐœ์†ก_๊ธฐ๋Šฅ_์‚ฌ์šฉ_๊ฐ€์ด๋“œ.md b/docs/๋ฉ”์ผ๋ฐœ์†ก_๊ธฐ๋Šฅ_์‚ฌ์šฉ_๊ฐ€์ด๋“œ.md index 2ef68524..285dc6ba 100644 --- a/docs/๋ฉ”์ผ๋ฐœ์†ก_๊ธฐ๋Šฅ_์‚ฌ์šฉ_๊ฐ€์ด๋“œ.md +++ b/docs/๋ฉ”์ผ๋ฐœ์†ก_๊ธฐ๋Šฅ_์‚ฌ์šฉ_๊ฐ€์ด๋“œ.md @@ -355,3 +355,4 @@ - [ ] ๋ถ€๋ชจ ํ™”๋ฉด์—์„œ ๋ชจ๋‹ฌ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์ „๋‹ฌ๋˜๋Š”๊ฐ€? - [ ] ๋ฐœ์†ก ๋ฒ„ํŠผ์˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์ •๋˜์–ด ์žˆ๋Š”๊ฐ€? + diff --git a/frontend/components/screen/EditModal.tsx b/frontend/components/screen/EditModal.tsx index e955fddc..85e502a0 100644 --- a/frontend/components/screen/EditModal.tsx +++ b/frontend/components/screen/EditModal.tsx @@ -62,7 +62,7 @@ export const EditModal: React.FC = ({ className }) => { // ํผ ๋ฐ์ดํ„ฐ ์ƒํƒœ (ํŽธ์ง‘ ๋ฐ์ดํ„ฐ๋กœ ์ดˆ๊ธฐํ™”๋จ) const [formData, setFormData] = useState>({}); const [originalData, setOriginalData] = useState>({}); - + // ๐Ÿ†• ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์ƒํƒœ (๊ฐ™์€ order_no์˜ ๋ชจ๋“  ํ’ˆ๋ชฉ) const [groupData, setGroupData] = useState[]>([]); const [originalGroupData, setOriginalGroupData] = useState[]>([]); @@ -118,7 +118,8 @@ export const EditModal: React.FC = ({ className }) => { // ์ „์—ญ ๋ชจ๋‹ฌ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ useEffect(() => { const handleOpenEditModal = (event: CustomEvent) => { - const { screenId, title, description, modalSize, editData, onSave, groupByColumns, tableName, isCreateMode } = event.detail; + const { screenId, title, description, modalSize, editData, onSave, groupByColumns, tableName, isCreateMode } = + event.detail; setModalState({ isOpen: true, @@ -136,8 +137,8 @@ export const EditModal: React.FC = ({ className }) => { setFormData(editData || {}); // ๐Ÿ†• isCreateMode๊ฐ€ true์ด๋ฉด originalData๋ฅผ ๋นˆ ๊ฐ์ฒด๋กœ ์„ค์ • (INSERT ๋ชจ๋“œ) // originalData๊ฐ€ ๋น„์–ด์žˆ์œผ๋ฉด INSERT, ์žˆ์œผ๋ฉด UPDATE๋กœ ์ฒ˜๋ฆฌ๋จ - setOriginalData(isCreateMode ? {} : (editData || {})); - + setOriginalData(isCreateMode ? {} : editData || {}); + if (isCreateMode) { console.log("[EditModal] ์ƒ์„ฑ ๋ชจ๋“œ๋กœ ์—ด๋ฆผ, ์ดˆ๊ธฐ๊ฐ’:", editData); } @@ -170,7 +171,7 @@ export const EditModal: React.FC = ({ className }) => { useEffect(() => { if (modalState.isOpen && modalState.screenId) { loadScreenData(modalState.screenId); - + // ๐Ÿ†• ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์กฐํšŒ (groupByColumns๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ) if (modalState.groupByColumns && modalState.groupByColumns.length > 0 && modalState.tableName) { loadGroupData(); @@ -308,7 +309,7 @@ export const EditModal: React.FC = ({ className }) => { // universal-form-modal ๋“ฑ์—์„œ ์ž์ฒด ์ €์žฅ ์™„๋ฃŒ ํ›„ ํ˜ธ์ถœ๋œ ๊ฒฝ์šฐ ์Šคํ‚ต if (saveData?._saveCompleted) { console.log("[EditModal] ์ž์ฒด ์ €์žฅ ์™„๋ฃŒ๋œ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ˜ธ์ถœ๋จ - ์ €์žฅ ์Šคํ‚ต"); - + // ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ onSave ์ฝœ๋ฐฑ ์‹คํ–‰ (ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ) if (modalState.onSave) { try { @@ -317,7 +318,7 @@ export const EditModal: React.FC = ({ className }) => { console.error("onSave ์ฝœ๋ฐฑ ์—๋Ÿฌ:", callbackError); } } - + handleClose(); return; } @@ -342,13 +343,13 @@ export const EditModal: React.FC = ({ className }) => { // ๐Ÿ†• ๋‚ ์งœ ํ•„๋“œ ์ •๊ทœํ™” ํ•จ์ˆ˜ (YYYY-MM-DD ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜) const normalizeDateField = (value: any): string | null => { if (!value) return null; - + // ISO 8601 ํ˜•์‹ (2025-11-26T00:00:00.000Z) ๋˜๋Š” Date ๊ฐ์ฒด if (value instanceof Date || typeof value === "string") { try { const date = new Date(value); if (isNaN(date.getTime())) return null; - + // YYYY-MM-DD ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); @@ -359,7 +360,7 @@ export const EditModal: React.FC = ({ className }) => { return null; } } - + return null; }; @@ -380,7 +381,7 @@ export const EditModal: React.FC = ({ className }) => { const insertData: Record = { ...currentData }; console.log("๐Ÿ“ฆ [์‹ ๊ทœ ํ’ˆ๋ชฉ] ๋ณต์‚ฌ ์งํ›„ insertData:", insertData); console.log("๐Ÿ“‹ [์‹ ๊ทœ ํ’ˆ๋ชฉ] insertData ํ‚ค ๋ชฉ๋ก:", Object.keys(insertData)); - + delete insertData.id; // id๋Š” ์ž๋™ ์ƒ์„ฑ๋˜๋ฏ€๋กœ ์ œ๊ฑฐ // ๐Ÿ†• ๋‚ ์งœ ํ•„๋“œ ์ •๊ทœํ™” (YYYY-MM-DD ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜) @@ -464,9 +465,7 @@ export const EditModal: React.FC = ({ className }) => { for (const currentData of groupData) { if (currentData.id) { // id ๊ธฐ๋ฐ˜ ๋งค์นญ (์ธ๋ฑ์Šค ๊ธฐ๋ฐ˜ X) - const originalItemData = originalGroupData.find( - (orig) => orig.id === currentData.id - ); + const originalItemData = originalGroupData.find((orig) => orig.id === currentData.id); if (!originalItemData) { console.warn(`์›๋ณธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค (id: ${currentData.id})`); @@ -476,13 +475,13 @@ export const EditModal: React.FC = ({ className }) => { // ๐Ÿ†• ๊ฐ’ ์ •๊ทœํ™” ํ•จ์ˆ˜ (ํƒ€์ž… ํ†ต์ผ) const normalizeValue = (val: any, fieldName?: string): any => { if (val === null || val === undefined || val === "") return null; - + // ๋‚ ์งœ ํ•„๋“œ์ธ ๊ฒฝ์šฐ YYYY-MM-DD ํ˜•์‹์œผ๋กœ ์ •๊ทœํ™” if (fieldName && dateFields.includes(fieldName)) { const normalizedDate = normalizeDateField(val); return normalizedDate; } - + if (typeof val === "string" && !isNaN(Number(val))) { // ์ˆซ์ž๋กœ ๋ณ€ํ™˜ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž์—ด์€ ์ˆซ์ž๋กœ return Number(val); @@ -539,9 +538,7 @@ export const EditModal: React.FC = ({ className }) => { // 3๏ธโƒฃ ์‚ญ์ œ๋œ ํ’ˆ๋ชฉ ์ œ๊ฑฐ (์›๋ณธ์—๋Š” ์žˆ์ง€๋งŒ ํ˜„์žฌ ๋ฐ์ดํ„ฐ์—๋Š” ์—†๋Š” ํ•ญ๋ชฉ) const currentIds = new Set(groupData.map((item) => item.id).filter(Boolean)); - const deletedItems = originalGroupData.filter( - (orig) => orig.id && !currentIds.has(orig.id) - ); + const deletedItems = originalGroupData.filter((orig) => orig.id && !currentIds.has(orig.id)); for (const deletedItem of deletedItems) { console.log("๐Ÿ—‘๏ธ ํ’ˆ๋ชฉ ์‚ญ์ œ:", deletedItem); @@ -549,7 +546,7 @@ export const EditModal: React.FC = ({ className }) => { try { const response = await dynamicFormApi.deleteFormDataFromTable( deletedItem.id, - screenData.screenInfo.tableName + screenData.screenInfo.tableName, ); if (response.success) { @@ -592,11 +589,11 @@ export const EditModal: React.FC = ({ className }) => { // originalData๊ฐ€ ๋น„์–ด์žˆ์œผ๋ฉด INSERT, ์žˆ์œผ๋ฉด UPDATE const isCreateMode = Object.keys(originalData).length === 0; - + if (isCreateMode) { // INSERT ๋ชจ๋“œ console.log("[EditModal] INSERT ๋ชจ๋“œ - ์ƒˆ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ:", formData); - + const response = await dynamicFormApi.saveFormData({ screenId: modalState.screenId!, tableName: screenData.screenInfo.tableName, @@ -701,10 +698,7 @@ export const EditModal: React.FC = ({ className }) => { return ( - +
{modalState.title || "๋ฐ์ดํ„ฐ ์ˆ˜์ •"} @@ -717,7 +711,7 @@ export const EditModal: React.FC = ({ className }) => {
-
+
{loading ? (
@@ -751,7 +745,6 @@ export const EditModal: React.FC = ({ className }) => { }, }; - const groupedDataProp = groupData.length > 0 ? groupData : undefined; // ๐Ÿ”‘ ์ฒจ๋ถ€ํŒŒ์ผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ–‰(๋ ˆ์ฝ”๋“œ) ๋‹จ์œ„๋กœ ํŒŒ์ผ์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก tableName ์ถ”๊ฐ€ @@ -760,7 +753,7 @@ export const EditModal: React.FC = ({ className }) => { tableName: screenData.screenInfo?.tableName, // ํ…Œ์ด๋ธ”๋ช… ์ถ”๊ฐ€ screenId: modalState.screenId, // ํ™”๋ฉด ID ์ถ”๊ฐ€ }; - + // ๐Ÿ” ๋””๋ฒ„๊น…: enrichedFormData ํ™•์ธ console.log("๐Ÿ”‘ [EditModal] enrichedFormData ์ƒ์„ฑ:", { "screenData.screenInfo": screenData.screenInfo, @@ -775,6 +768,7 @@ export const EditModal: React.FC = ({ className }) => { component={adjustedComponent} allComponents={screenData.components} formData={enrichedFormData} + originalData={originalData} // ๐Ÿ†• ์›๋ณธ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ (์ˆ˜์ • ๋ชจ๋“œ์—์„œ UniversalFormModal ์ดˆ๊ธฐํ™”์šฉ) onFormDataChange={(fieldName, value) => { // ๐Ÿ†• ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ์ฒ˜๋ฆฌ if (groupData.length > 0) { @@ -787,14 +781,14 @@ export const EditModal: React.FC = ({ className }) => { prev.map((item) => ({ ...item, [fieldName]: value, - })) + })), ); } } else { - setFormData((prev) => ({ - ...prev, - [fieldName]: value, - })); + setFormData((prev) => ({ + ...prev, + [fieldName]: value, + })); } }} screenInfo={{ diff --git a/frontend/components/screen/config-panels/ImprovedButtonControlConfigPanel.tsx b/frontend/components/screen/config-panels/ImprovedButtonControlConfigPanel.tsx index 18a51bba..197b9759 100644 --- a/frontend/components/screen/config-panels/ImprovedButtonControlConfigPanel.tsx +++ b/frontend/components/screen/config-panels/ImprovedButtonControlConfigPanel.tsx @@ -1,11 +1,14 @@ "use client"; -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Separator } from "@/components/ui/separator"; -import { Settings, Clock, Info, Workflow } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Settings, Clock, Info, Workflow, Plus, Trash2, GripVertical, ChevronUp, ChevronDown } from "lucide-react"; import { ComponentData } from "@/types/screen"; import { getNodeFlows, NodeFlow } from "@/lib/api/nodeFlows"; @@ -14,11 +17,22 @@ interface ImprovedButtonControlConfigPanelProps { onUpdateProperty: (path: string, value: any) => void; } +// ๋‹ค์ค‘ ์ œ์–ด ์„ค์ • ์ธํ„ฐํŽ˜์ด์Šค +interface FlowControlConfig { + id: string; + flowId: number; + flowName: string; + executionTiming: "before" | "after" | "replace"; + order: number; +} + /** - * ๐Ÿ”ฅ ๋‹จ์ˆœํ™”๋œ ๋ฒ„ํŠผ ์ œ์–ด ์„ค์ • ํŒจ๋„ + * ๐Ÿ”ฅ ๋‹ค์ค‘ ์ œ์–ด ์ง€์› ๋ฒ„ํŠผ ์„ค์ • ํŒจ๋„ * - * ๋…ธ๋“œ ํ”Œ๋กœ์šฐ ์‹คํ–‰๋งŒ ์ง€์›: - * - ํ”Œ๋กœ์šฐ ์„ ํƒ ๋ฐ ์‹คํ–‰ ํƒ€์ด๋ฐ ์„ค์ • + * ๊ธฐ๋Šฅ: + * - ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋…ธ๋“œ ํ”Œ๋กœ์šฐ ์„ ํƒ ๋ฐ ์ˆœ์„œ ์ง€์ • + * - ๊ฐ ํ”Œ๋กœ์šฐ๋ณ„ ์‹คํ–‰ ํƒ€์ด๋ฐ ์„ค์ • + * - ๋“œ๋ž˜๊ทธ์•ค๋“œ๋กญ ๋˜๋Š” ๋ฒ„ํŠผ์œผ๋กœ ์ˆœ์„œ ๋ณ€๊ฒฝ */ export const ImprovedButtonControlConfigPanel: React.FC = ({ component, @@ -27,6 +41,9 @@ export const ImprovedButtonControlConfigPanel: React.FC([]); const [loading, setLoading] = useState(false); @@ -58,24 +75,118 @@ export const ImprovedButtonControlConfigPanel: React.FC { - const selectedFlow = flows.find((f) => f.flowId.toString() === flowId); - if (selectedFlow) { - // ์ „์ฒด dataflowConfig ์—…๋ฐ์ดํŠธ (selectedDiagramId ํฌํ•จ) - onUpdateProperty("webTypeConfig.dataflowConfig", { - ...dataflowConfig, - selectedDiagramId: selectedFlow.flowId, // ๋ฐฑ์—”๋“œ์—์„œ ์‚ฌ์šฉ - selectedRelationshipId: null, // ๋…ธ๋“œ ํ”Œ๋กœ์šฐ๋Š” ๊ด€๊ณ„ ID ๋ถˆํ•„์š” - flowConfig: { - flowId: selectedFlow.flowId, - flowName: selectedFlow.flowName, - executionTiming: "before", // ๊ธฐ๋ณธ๊ฐ’ - contextData: {}, - }, - }); - } + const handleAddControl = useCallback(() => { + const newControl: FlowControlConfig = { + id: `control_${Date.now()}`, + flowId: 0, + flowName: "", + executionTiming: "after", + order: flowControls.length + 1, + }; + + const updatedControls = [...flowControls, newControl]; + updateFlowControls(updatedControls); + }, [flowControls]); + + /** + * ๐Ÿ”ฅ ์ œ์–ด ์‚ญ์ œ + */ + const handleRemoveControl = useCallback( + (controlId: string) => { + const updatedControls = flowControls + .filter((c) => c.id !== controlId) + .map((c, index) => ({ ...c, order: index + 1 })); + updateFlowControls(updatedControls); + }, + [flowControls], + ); + + /** + * ๐Ÿ”ฅ ์ œ์–ด ํ”Œ๋กœ์šฐ ์„ ํƒ + */ + const handleFlowSelect = useCallback( + (controlId: string, flowId: string) => { + const selectedFlow = flows.find((f) => f.flowId.toString() === flowId); + if (selectedFlow) { + const updatedControls = flowControls.map((c) => + c.id === controlId ? { ...c, flowId: selectedFlow.flowId, flowName: selectedFlow.flowName } : c, + ); + updateFlowControls(updatedControls); + } + }, + [flows, flowControls], + ); + + /** + * ๐Ÿ”ฅ ์‹คํ–‰ ํƒ€์ด๋ฐ ๋ณ€๊ฒฝ + */ + const handleTimingChange = useCallback( + (controlId: string, timing: "before" | "after" | "replace") => { + const updatedControls = flowControls.map((c) => (c.id === controlId ? { ...c, executionTiming: timing } : c)); + updateFlowControls(updatedControls); + }, + [flowControls], + ); + + /** + * ๐Ÿ”ฅ ์ˆœ์„œ ์œ„๋กœ ์ด๋™ + */ + const handleMoveUp = useCallback( + (controlId: string) => { + const index = flowControls.findIndex((c) => c.id === controlId); + if (index > 0) { + const updatedControls = [...flowControls]; + [updatedControls[index - 1], updatedControls[index]] = [updatedControls[index], updatedControls[index - 1]]; + // ์ˆœ์„œ ๋ฒˆํ˜ธ ์žฌ์ •๋ ฌ + updatedControls.forEach((c, i) => (c.order = i + 1)); + updateFlowControls(updatedControls); + } + }, + [flowControls], + ); + + /** + * ๐Ÿ”ฅ ์ˆœ์„œ ์•„๋ž˜๋กœ ์ด๋™ + */ + const handleMoveDown = useCallback( + (controlId: string) => { + const index = flowControls.findIndex((c) => c.id === controlId); + if (index < flowControls.length - 1) { + const updatedControls = [...flowControls]; + [updatedControls[index], updatedControls[index + 1]] = [updatedControls[index + 1], updatedControls[index]]; + // ์ˆœ์„œ ๋ฒˆํ˜ธ ์žฌ์ •๋ ฌ + updatedControls.forEach((c, i) => (c.order = i + 1)); + updateFlowControls(updatedControls); + } + }, + [flowControls], + ); + + /** + * ๐Ÿ”ฅ ์ œ์–ด ๋ชฉ๋ก ์—…๋ฐ์ดํŠธ (๋ฐฑ์—”๋“œ ํ˜ธํ™˜์„ฑ ์œ ์ง€) + */ + const updateFlowControls = (controls: FlowControlConfig[]) => { + // ์ฒซ ๋ฒˆ์งธ ์ œ์–ด๋ฅผ ๊ธฐ์กด ํ˜•์‹์œผ๋กœ๋„ ์ €์žฅ (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ) + const firstValidControl = controls.find((c) => c.flowId > 0); + + onUpdateProperty("webTypeConfig.dataflowConfig", { + ...dataflowConfig, + // ๊ธฐ์กด ํ˜•์‹ (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ) + selectedDiagramId: firstValidControl?.flowId || null, + selectedRelationshipId: null, + flowConfig: firstValidControl + ? { + flowId: firstValidControl.flowId, + flowName: firstValidControl.flowName, + executionTiming: firstValidControl.executionTiming, + contextData: {}, + } + : null, + // ์ƒˆ๋กœ์šด ๋‹ค์ค‘ ์ œ์–ด ํ˜•์‹ + flowControls: controls, + }); }; return ( @@ -98,32 +209,57 @@ export const ImprovedButtonControlConfigPanel: React.FC - + {/* ์ œ์–ด ๋ชฉ๋ก ํ—ค๋” */} +
+
+ + +
+ +
- {dataflowConfig.flowConfig && ( -
- - - onUpdateProperty("webTypeConfig.dataflowConfig.flowConfig.executionTiming", timing) - } - /> + {/* ์ œ์–ด ๋ชฉ๋ก */} + {flowControls.length === 0 ? ( +
+ +

๋“ฑ๋ก๋œ ์ œ์–ด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค

+ +
+ ) : ( +
+ {flowControls.map((control, index) => ( + handleFlowSelect(control.id, flowId)} + onTimingChange={(timing) => handleTimingChange(control.id, timing)} + onMoveUp={() => handleMoveUp(control.id)} + onMoveDown={() => handleMoveDown(control.id)} + onRemove={() => handleRemoveControl(control.id)} + /> + ))} +
+ )} -
-
- -
-

๋…ธ๋“œ ํ”Œ๋กœ์šฐ ์‹คํ–‰ ์ •๋ณด:

-

์„ ํƒํ•œ ํ”Œ๋กœ์šฐ์˜ ๋ชจ๋“  ๋…ธ๋“œ๊ฐ€ ์ˆœ์ฐจ์ /๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

-

โ€ข ๋…๋ฆฝ ํŠธ๋žœ์žญ์…˜: ๊ฐ ์•ก์…˜์€ ๋…๋ฆฝ์ ์œผ๋กœ ์ปค๋ฐ‹/๋กค๋ฐฑ

-

โ€ข ์—ฐ์‡„ ์ค‘๋‹จ: ๋ถ€๋ชจ ๋…ธ๋“œ ์‹คํŒจ ์‹œ ์ž์‹ ๋…ธ๋“œ ์Šคํ‚ต

-
+ {/* ์•ˆ๋‚ด ๋ฉ”์‹œ์ง€ */} + {flowControls.length > 0 && ( +
+
+ +
+

๋‹ค์ค‘ ์ œ์–ด ์‹คํ–‰ ์ •๋ณด:

+

โ€ข ์ œ์–ด๋Š” ์œ„์—์„œ ์•„๋ž˜ ์ˆœ์„œ๋Œ€๋กœ ์ˆœ์ฐจ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค

+

โ€ข ๊ฐ ์ œ์–ด๋Š” ๋…๋ฆฝ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค

+

โ€ข ์ด์ „ ์ œ์–ด ์‹คํŒจ ์‹œ ๋‹ค์Œ ์ œ์–ด๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค

@@ -135,90 +271,89 @@ export const ImprovedButtonControlConfigPanel: React.FC void; loading: boolean; -}> = ({ flows, selectedFlowId, onSelect, loading }) => { + isFirst: boolean; + isLast: boolean; + onFlowSelect: (flowId: string) => void; + onTimingChange: (timing: "before" | "after" | "replace") => void; + onMoveUp: () => void; + onMoveDown: () => void; + onRemove: () => void; +}> = ({ control, flows, loading, isFirst, isLast, onFlowSelect, onTimingChange, onMoveUp, onMoveDown, onRemove }) => { return ( -
-
- - -
+ +
+ {/* ์ˆœ์„œ ํ‘œ์‹œ ๋ฐ ์ด๋™ ๋ฒ„ํŠผ */} +
+ + {control.order} + +
+ + +
+
- 0 ? control.flowId.toString() : ""} onValueChange={onFlowSelect}> + + + + + {loading ? ( +
๋กœ๋”ฉ ์ค‘...
+ ) : flows.length === 0 ? ( +
ํ”Œ๋กœ์šฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค
+ ) : ( + flows.map((flow) => ( + + {flow.flowName} + + )) + )} +
+ + + {/* ์‹คํ–‰ ํƒ€์ด๋ฐ */} + -
- ); -}; + + After (์‚ฌํ›„ ์‹คํ–‰) + + + Replace (๋Œ€์ฒด ์‹คํ–‰) + + + +
-/** - * ๐Ÿ”ฅ ์‹คํ–‰ ํƒ€์ด๋ฐ ์„ ํƒ ์ปดํฌ๋„ŒํŠธ - */ -const ExecutionTimingSelector: React.FC<{ - value: string; - onChange: (timing: "before" | "after" | "replace") => void; -}> = ({ value, onChange }) => { - return ( -
-
- - + {/* ์‚ญ์ œ ๋ฒ„ํŠผ */} +
- - -
+ ); }; diff --git a/frontend/hooks/useAutoFill.ts b/frontend/hooks/useAutoFill.ts index c437f2c5..835a4886 100644 --- a/frontend/hooks/useAutoFill.ts +++ b/frontend/hooks/useAutoFill.ts @@ -192,3 +192,4 @@ export function applyAutoFillToFormData( return result; } + diff --git a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx index ae6a01d8..b453729d 100644 --- a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx +++ b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx @@ -47,7 +47,7 @@ export interface ButtonPrimaryComponentProps extends ComponentRendererProps { // ํ…Œ์ด๋ธ” ์„ ํƒ๋œ ํ–‰ ์ •๋ณด (๋‹ค์ค‘ ์„ ํƒ ์•ก์…˜์šฉ) selectedRows?: any[]; selectedRowsData?: any[]; - + // ํ…Œ์ด๋ธ” ์ •๋ ฌ ์ •๋ณด (์—‘์…€ ๋‹ค์šด๋กœ๋“œ์šฉ) sortBy?: string; sortOrder?: "asc" | "desc"; @@ -57,10 +57,10 @@ export interface ButtonPrimaryComponentProps extends ComponentRendererProps { // ํ”Œ๋กœ์šฐ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ์ •๋ณด (ํ”Œ๋กœ์šฐ ์œ„์ ฏ ์„ ํƒ ์•ก์…˜์šฉ) flowSelectedData?: any[]; flowSelectedStepId?: number | null; - + // ๐Ÿ†• ๊ฐ™์€ ํ™”๋ฉด์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ (TableList ์ž๋™ ๊ฐ์ง€์šฉ) allComponents?: any[]; - + // ๐Ÿ†• ๋ถ€๋ชจ์ฐฝ์—์„œ ์ „๋‹ฌ๋œ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ (๋ชจ๋‹ฌ์—์„œ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์šฉ) groupedData?: Record[]; } @@ -109,11 +109,11 @@ export const ButtonPrimaryComponent: React.FC = ({ const splitPanelContext = useSplitPanelContext(); // ๋ถ„ํ•  ํŒจ๋„ ์ปจํ…์ŠคํŠธ // ๐Ÿ†• ScreenContext์—์„œ splitPanelPosition ๊ฐ€์ ธ์˜ค๊ธฐ (์ค‘์ฒฉ ํ™”๋ฉด์—์„œ๋„ ์ž‘๋™) const splitPanelPosition = screenContext?.splitPanelPosition; - + // ๐Ÿ†• tableName์ด props๋กœ ์ „๋‹ฌ๋˜์ง€ ์•Š์œผ๋ฉด ScreenContext์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ const effectiveTableName = tableName || screenContext?.tableName; const effectiveScreenId = screenId || screenContext?.screenId; - + // ๐Ÿ†• props์—์„œ onSave ์ถ”์ถœ (๋ช…์‹œ์ ์œผ๋กœ ์„ ์–ธ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ...props์—์„œ ์ถ”์ถœ) const propsOnSave = (props as any).onSave as (() => Promise) | undefined; const finalOnSave = onSave || propsOnSave; @@ -169,10 +169,10 @@ export const ButtonPrimaryComponent: React.FC = ({ if (!shouldFetchStatus) return; let isMounted = true; - + const fetchStatus = async () => { if (!isMounted) return; - + try { const response = await apiClient.post(`/table-management/tables/${statusTableName}/data`, { page: 1, @@ -180,12 +180,12 @@ export const ButtonPrimaryComponent: React.FC = ({ search: { [statusKeyField]: userId }, autoFilter: true, }); - + if (!isMounted) return; - + const rows = response.data?.data?.data || response.data?.data?.rows || response.data?.rows || []; const firstRow = Array.isArray(rows) ? rows[0] : null; - + if (response.data?.success && firstRow) { const newStatus = firstRow[statusFieldName]; if (newStatus !== vehicleStatus) { @@ -206,10 +206,10 @@ export const ButtonPrimaryComponent: React.FC = ({ // ์ฆ‰์‹œ ์‹คํ–‰ setStatusLoading(true); fetchStatus(); - + // 2์ดˆ๋งˆ๋‹ค ๊ฐฑ์‹  const interval = setInterval(fetchStatus, 2000); - + return () => { isMounted = false; clearInterval(interval); @@ -219,22 +219,22 @@ export const ButtonPrimaryComponent: React.FC = ({ // ๋ฒ„ํŠผ ๋น„ํ™œ์„ฑํ™” ์กฐ๊ฑด ๊ณ„์‚ฐ const isOperationButtonDisabled = useMemo(() => { const actionConfig = component.componentConfig?.action; - + if (actionConfig?.type !== "operation_control") return false; // 1. ์ถœ๋ฐœ์ง€/๋„์ฐฉ์ง€ ํ•„์ˆ˜ ์ฒดํฌ if (actionConfig?.requireLocationFields) { const departureField = actionConfig.trackingDepartureField || "departure"; const destinationField = actionConfig.trackingArrivalField || "destination"; - + const departure = formData?.[departureField]; const destination = formData?.[destinationField]; - - // console.log("๐Ÿ” [ButtonPrimary] ์ถœ๋ฐœ์ง€/๋„์ฐฉ์ง€ ์ฒดํฌ:", { - // departureField, destinationField, departure, destination, - // buttonLabel: component.label + + // console.log("๐Ÿ” [ButtonPrimary] ์ถœ๋ฐœ์ง€/๋„์ฐฉ์ง€ ์ฒดํฌ:", { + // departureField, destinationField, departure, destination, + // buttonLabel: component.label // }); - + if (!departure || departure === "" || !destination || destination === "") { // console.log("๐Ÿšซ [ButtonPrimary] ์ถœ๋ฐœ์ง€/๋„์ฐฉ์ง€ ๋ฏธ์„ ํƒ โ†’ ๋น„ํ™œ์„ฑํ™”:", component.label); return true; @@ -246,20 +246,20 @@ export const ButtonPrimaryComponent: React.FC = ({ const statusField = actionConfig.statusCheckField || "status"; // API ์กฐํšŒ ๊ฒฐ๊ณผ๋ฅผ ์šฐ์„  ์‚ฌ์šฉ (์‹ค์‹œ๊ฐ„ DB ์ƒํƒœ ๋ฐ˜์˜) const currentStatus = vehicleStatus || formData?.[statusField]; - + const conditionType = actionConfig.statusConditionType || "enableOn"; const conditionValues = (actionConfig.statusConditionValues || "") .split(",") .map((v: string) => v.trim()) .filter((v: string) => v); - // console.log("๐Ÿ” [ButtonPrimary] ์ƒํƒœ ์กฐ๊ฑด ์ฒดํฌ:", { + // console.log("๐Ÿ” [ButtonPrimary] ์ƒํƒœ ์กฐ๊ฑด ์ฒดํฌ:", { // statusField, // formDataStatus: formData?.[statusField], // apiStatus: vehicleStatus, - // currentStatus, - // conditionType, - // conditionValues, + // currentStatus, + // conditionType, + // conditionValues, // buttonLabel: component.label, // }); @@ -274,7 +274,7 @@ export const ButtonPrimaryComponent: React.FC = ({ // console.log("๐Ÿšซ [ButtonPrimary] ์ƒํƒœ๊ฐ’ ์—†์Œ โ†’ ๋น„ํ™œ์„ฑํ™”:", component.label); return true; } - + if (conditionValues.length > 0) { if (conditionType === "enableOn") { // ์ด ์ƒํƒœ์ผ ๋•Œ๋งŒ ํ™œ์„ฑํ™” @@ -539,7 +539,7 @@ export const ButtonPrimaryComponent: React.FC = ({ */ const handleTransferDataAction = async (actionConfig: any) => { const dataTransferConfig = actionConfig.dataTransfer; - + if (!dataTransferConfig) { toast.error("๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค."); return; @@ -553,15 +553,15 @@ export const ButtonPrimaryComponent: React.FC = ({ try { // 1. ์†Œ์Šค ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ let sourceProvider = screenContext.getDataProvider(dataTransferConfig.sourceComponentId); - + // ๐Ÿ†• ์†Œ์Šค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์œผ๋ฉด, ํ˜„์žฌ ํ™”๋ฉด์—์„œ ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ ์ž๋™ ํƒ์ƒ‰ // (์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ์˜ ๋‹ค๋ฅธ ์„น์…˜์œผ๋กœ ์ „ํ™˜ํ–ˆ์„ ๋•Œ ์ด์ „ ์ปดํฌ๋„ŒํŠธ ID๊ฐ€ ๋‚จ์•„์žˆ๋Š” ๊ฒฝ์šฐ ๋Œ€์‘) if (!sourceProvider) { console.log(`โš ๏ธ [ButtonPrimary] ์ง€์ •๋œ ์†Œ์Šค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: ${dataTransferConfig.sourceComponentId}`); console.log(`๐Ÿ” [ButtonPrimary] ํ˜„์žฌ ํ™”๋ฉด์—์„œ DataProvider ์ž๋™ ํƒ์ƒ‰...`); - + const allProviders = screenContext.getAllDataProviders(); - + // ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ ์šฐ์„  ํƒ์ƒ‰ for (const [id, provider] of allProviders) { if (provider.componentType === "table-list") { @@ -570,16 +570,18 @@ export const ButtonPrimaryComponent: React.FC = ({ break; } } - + // ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ๊ฐ€ ์—†์œผ๋ฉด ์ฒซ ๋ฒˆ์งธ DataProvider ์‚ฌ์šฉ if (!sourceProvider && allProviders.size > 0) { const firstEntry = allProviders.entries().next().value; if (firstEntry) { sourceProvider = firstEntry[1]; - console.log(`โœ… [ButtonPrimary] ์ฒซ ๋ฒˆ์งธ DataProvider ์‚ฌ์šฉ: ${firstEntry[0]} (${sourceProvider.componentType})`); + console.log( + `โœ… [ButtonPrimary] ์ฒซ ๋ฒˆ์งธ DataProvider ์‚ฌ์šฉ: ${firstEntry[0]} (${sourceProvider.componentType})`, + ); } } - + if (!sourceProvider) { toast.error("๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); return; @@ -587,12 +589,12 @@ export const ButtonPrimaryComponent: React.FC = ({ } const rawSourceData = sourceProvider.getSelectedData(); - + // ๐Ÿ†• ๋ฐฐ์—ด์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ - const sourceData = Array.isArray(rawSourceData) ? rawSourceData : (rawSourceData ? [rawSourceData] : []); - + const sourceData = Array.isArray(rawSourceData) ? rawSourceData : rawSourceData ? [rawSourceData] : []; + console.log("๐Ÿ“ฆ ์†Œ์Šค ๋ฐ์ดํ„ฐ:", { rawSourceData, sourceData, isArray: Array.isArray(rawSourceData) }); - + if (!sourceData || sourceData.length === 0) { toast.warning("์„ ํƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."); return; @@ -600,31 +602,32 @@ export const ButtonPrimaryComponent: React.FC = ({ // 1.5. ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ ์†Œ์Šค ์ฒ˜๋ฆฌ (์˜ˆ: ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ์˜ ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐ’) let additionalData: Record = {}; - + // ๋ฐฉ๋ฒ• 1: additionalSources ์„ค์ •์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ if (dataTransferConfig.additionalSources && Array.isArray(dataTransferConfig.additionalSources)) { for (const additionalSource of dataTransferConfig.additionalSources) { const additionalProvider = screenContext.getDataProvider(additionalSource.componentId); - + if (additionalProvider) { const additionalValues = additionalProvider.getSelectedData(); - + if (additionalValues && additionalValues.length > 0) { // ์ฒซ ๋ฒˆ์งธ ๊ฐ’ ์‚ฌ์šฉ (์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ๋Š” ํ•ญ์ƒ 1๊ฐœ) const firstValue = additionalValues[0]; - + // fieldName์ด ์ง€์ •๋˜์–ด ์žˆ์œผ๋ฉด ๊ทธ ํ•„๋“œ๋งŒ ์ถ”์ถœ if (additionalSource.fieldName) { - additionalData[additionalSource.fieldName] = firstValue[additionalSource.fieldName] || firstValue.condition || firstValue; + additionalData[additionalSource.fieldName] = + firstValue[additionalSource.fieldName] || firstValue.condition || firstValue; } else { // fieldName์ด ์—†์œผ๋ฉด ์ „์ฒด ๊ฐ์ฒด ๋ณ‘ํ•ฉ additionalData = { ...additionalData, ...firstValue }; } - + console.log("๐Ÿ“ฆ ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ (additionalSources):", { sourceId: additionalSource.componentId, fieldName: additionalSource.fieldName, - value: additionalData[additionalSource.fieldName || 'all'], + value: additionalData[additionalSource.fieldName || "all"], }); } } @@ -639,7 +642,7 @@ export const ButtonPrimaryComponent: React.FC = ({ const conditionalValue = formData.__conditionalContainerValue; const conditionalLabel = formData.__conditionalContainerLabel; const controlField = formData.__conditionalContainerControlField; // ๐Ÿ†• ์ œ์–ด ํ•„๋“œ๋ช… ์ง์ ‘ ์‚ฌ์šฉ - + // ๐Ÿ†• controlField๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ์„ ํ•„๋“œ๋ช…์œผ๋กœ ์‚ฌ์šฉ (์ž๋™ ๋งคํ•‘!) if (controlField) { additionalData[controlField] = conditionalValue; @@ -651,7 +654,7 @@ export const ButtonPrimaryComponent: React.FC = ({ } else { // controlField๊ฐ€ ์—†์œผ๋ฉด ๊ธฐ์กด ๋ฐฉ์‹: formData์—์„œ ๊ฐ™์€ ๊ฐ’์„ ๊ฐ€์ง„ ํ‚ค ์ฐพ๊ธฐ for (const [key, value] of Object.entries(formData)) { - if (value === conditionalValue && !key.startsWith('__')) { + if (value === conditionalValue && !key.startsWith("__")) { additionalData[key] = conditionalValue; console.log("๐Ÿ“ฆ ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๊ฐ’ ์ž๋™ ํฌํ•จ:", { fieldName: key, @@ -661,12 +664,12 @@ export const ButtonPrimaryComponent: React.FC = ({ break; } } - + // ๋ชป ์ฐพ์•˜์œผ๋ฉด ๊ธฐ๋ณธ ํ•„๋“œ๋ช… ์‚ฌ์šฉ - if (!Object.keys(additionalData).some(k => !k.startsWith('__'))) { - additionalData['condition_type'] = conditionalValue; + if (!Object.keys(additionalData).some((k) => !k.startsWith("__"))) { + additionalData["condition_type"] = conditionalValue; console.log("๐Ÿ“ฆ ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๊ฐ’ (๊ธฐ๋ณธ ํ•„๋“œ๋ช…):", { - fieldName: 'condition_type', + fieldName: "condition_type", value: conditionalValue, }); } @@ -698,7 +701,7 @@ export const ButtonPrimaryComponent: React.FC = ({ // 4. ๋งคํ•‘ ๊ทœ์น™ ์ ์šฉ + ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ const mappedData = sourceData.map((row) => { const mappedRow = applyMappingRules(row, dataTransferConfig.mappingRules || []); - + // ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋“  ํ–‰์— ํฌํ•จ return { ...mappedRow, @@ -718,7 +721,7 @@ export const ButtonPrimaryComponent: React.FC = ({ if (dataTransferConfig.targetType === "component") { // ๊ฐ™์€ ํ™”๋ฉด์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ const targetReceiver = screenContext.getDataReceiver(dataTransferConfig.targetComponentId); - + if (!targetReceiver) { toast.error(`ํƒ€๊ฒŸ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: ${dataTransferConfig.targetComponentId}`); return; @@ -730,7 +733,7 @@ export const ButtonPrimaryComponent: React.FC = ({ mode: dataTransferConfig.mode || "append", mappingRules: dataTransferConfig.mappingRules || [], }); - + toast.success(`${sourceData.length}๊ฐœ ํ•ญ๋ชฉ์ด ์ „๋‹ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`); } else if (dataTransferConfig.targetType === "splitPanel") { // ๐Ÿ†• ๋ถ„ํ•  ํŒจ๋„์˜ ๋ฐ˜๋Œ€ํŽธ ํ™”๋ฉด์œผ๋กœ ์ „๋‹ฌ @@ -738,17 +741,18 @@ export const ButtonPrimaryComponent: React.FC = ({ toast.error("๋ถ„ํ•  ํŒจ๋„ ์ปจํ…์ŠคํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ๋ฒ„ํŠผ์ด ๋ถ„ํ•  ํŒจ๋„ ๋‚ด๋ถ€์— ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”."); return; } - + // ๐Ÿ†• useSplitPanelPosition ํ›…์œผ๋กœ ์œ„์น˜ ๊ฐ€์ ธ์˜ค๊ธฐ (์ค‘์ฒฉ๋œ ํ™”๋ฉด์—์„œ๋„ ์ž‘๋™) - // screenId๋กœ ์ฐพ๋Š” ๊ฒƒ์€ ์ง์ ‘ ์ž„๋ฒ ๋“œ๋œ ํ™”๋ฉด์—์„œ๋งŒ ์ž‘๋™ํ•˜๋ฏ€๋กœ, + // screenId๋กœ ์ฐพ๋Š” ๊ฒƒ์€ ์ง์ ‘ ์ž„๋ฒ ๋“œ๋œ ํ™”๋ฉด์—์„œ๋งŒ ์ž‘๋™ํ•˜๋ฏ€๋กœ, // SplitPanelPositionProvider๋กœ ์ „๋‹ฌ๋œ ์œ„์น˜๋ฅผ ์šฐ์„  ์‚ฌ์šฉ - const currentPosition = splitPanelPosition || (screenId ? splitPanelContext.getPositionByScreenId(screenId) : null); - + const currentPosition = + splitPanelPosition || (screenId ? splitPanelContext.getPositionByScreenId(screenId) : null); + if (!currentPosition) { toast.error("๋ถ„ํ•  ํŒจ๋„ ๋‚ด ์œ„์น˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. screenId: " + screenId); return; } - + console.log("๐Ÿ“ฆ ๋ถ„ํ•  ํŒจ๋„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ:", { currentPosition, splitPanelPositionFromHook: splitPanelPosition, @@ -756,14 +760,14 @@ export const ButtonPrimaryComponent: React.FC = ({ leftScreenId: splitPanelContext.leftScreenId, rightScreenId: splitPanelContext.rightScreenId, }); - + const result = await splitPanelContext.transferToOtherSide( currentPosition, mappedData, dataTransferConfig.targetComponentId, // ํŠน์ • ์ปดํฌ๋„ŒํŠธ ์ง€์ • (์„ ํƒ์‚ฌํ•ญ) - dataTransferConfig.mode || "append" + dataTransferConfig.mode || "append", ); - + if (result.success) { toast.success(result.message); } else { @@ -782,7 +786,6 @@ export const ButtonPrimaryComponent: React.FC = ({ if (dataTransferConfig.clearAfterTransfer) { sourceProvider.clearSelection(); } - } catch (error: any) { console.error("โŒ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์‹คํŒจ:", error); toast.error(error.message || "๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); @@ -816,16 +819,20 @@ export const ButtonPrimaryComponent: React.FC = ({ // 2. groupedData (๋ถ€๋ชจ์ฐฝ์—์„œ ๋ชจ๋‹ฌ๋กœ ์ „๋‹ฌ๋œ ๋ฐ์ดํ„ฐ) // 3. modalDataStore (๋ถ„ํ•  ํŒจ๋„ ๋“ฑ์—์„œ ์„ ํƒํ•œ ๋ฐ์ดํ„ฐ) let effectiveSelectedRowsData = selectedRowsData; - + // groupedData๊ฐ€ ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ (๋ชจ๋‹ฌ์—์„œ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ) - if ((!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) && groupedData && groupedData.length > 0) { + if ( + (!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) && + groupedData && + groupedData.length > 0 + ) { effectiveSelectedRowsData = groupedData; console.log("๐Ÿ”— [ButtonPrimaryComponent] groupedData์—์„œ ๋ถ€๋ชจ์ฐฝ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ด:", { count: groupedData.length, data: groupedData, }); } - + // modalDataStore์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ (๋ถ„ํ•  ํŒจ๋„ ๋“ฑ์—์„œ ์„ ํƒํ•œ ๋ฐ์ดํ„ฐ) if ((!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) && effectiveTableName) { try { @@ -833,11 +840,17 @@ export const ButtonPrimaryComponent: React.FC = ({ const dataRegistry = useModalDataStore.getState().dataRegistry; const modalData = dataRegistry[effectiveTableName]; if (modalData && modalData.length > 0) { - effectiveSelectedRowsData = modalData; + // modalDataStore๋Š” {id, originalData, additionalData} ํ˜•ํƒœ๋กœ ์ €์žฅ๋จ + // originalData๋ฅผ ์ถ”์ถœํ•˜์—ฌ ์‹ค์ œ ํ–‰ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ด + effectiveSelectedRowsData = modalData.map((item: any) => { + // originalData๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ์„ ์‚ฌ์šฉ, ์—†์œผ๋ฉด item ์ž์ฒด ์‚ฌ์šฉ (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ) + return item.originalData || item; + }); console.log("๐Ÿ”— [ButtonPrimaryComponent] modalDataStore์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ด:", { tableName: effectiveTableName, count: modalData.length, - data: modalData, + rawData: modalData, + extractedData: effectiveSelectedRowsData, }); } } catch (error) { @@ -847,7 +860,8 @@ export const ButtonPrimaryComponent: React.FC = ({ // ์‚ญ์ œ ์•ก์…˜์ธ๋ฐ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€ ํ‘œ์‹œํ•˜๊ณ  ์ค‘๋‹จ const hasDataToDelete = - (effectiveSelectedRowsData && effectiveSelectedRowsData.length > 0) || (flowSelectedData && flowSelectedData.length > 0); + (effectiveSelectedRowsData && effectiveSelectedRowsData.length > 0) || + (flowSelectedData && flowSelectedData.length > 0); if (processedConfig.action.type === "delete" && !hasDataToDelete) { toast.warning("์‚ญ์ œํ•  ํ•ญ๋ชฉ์„ ๋จผ์ € ์„ ํƒํ•ด์ฃผ์„ธ์š”."); @@ -1064,15 +1078,14 @@ export const ButtonPrimaryComponent: React.FC = ({ alignItems: "center", justifyContent: "center", // ๐Ÿ”ง ํฌ๊ธฐ์— ๋”ฐ๋ฅธ ํŒจ๋”ฉ ์กฐ์ • - padding: - componentConfig.size === "sm" ? "0 0.75rem" : componentConfig.size === "lg" ? "0 1.25rem" : "0 1rem", + padding: componentConfig.size === "sm" ? "0 0.75rem" : componentConfig.size === "lg" ? "0 1.25rem" : "0 1rem", margin: "0", lineHeight: "1.25", boxShadow: finalDisabled ? "none" : "0 1px 2px 0 rgba(0, 0, 0, 0.05)", // ๋””์ž์ธ ๋ชจ๋“œ์™€ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ชจ๋“œ ๋ชจ๋‘์—์„œ ์‚ฌ์šฉ์ž ์Šคํƒ€์ผ ์ ์šฉ (width/height ์ œ์™ธ) - ...(component.style ? Object.fromEntries( - Object.entries(component.style).filter(([key]) => key !== 'width' && key !== 'height') - ) : {}), + ...(component.style + ? Object.fromEntries(Object.entries(component.style).filter(([key]) => key !== "width" && key !== "height")) + : {}), }; const buttonContent = processedConfig.text !== undefined ? processedConfig.text : component.label || "๋ฒ„ํŠผ"; @@ -1094,7 +1107,7 @@ export const ButtonPrimaryComponent: React.FC = ({