From d64ca5a8c0f62345637e17dc8ef686eec1e6810f Mon Sep 17 00:00:00 2001 From: kjs Date: Tue, 4 Nov 2025 11:41:20 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(main)/screens/[screenId]/page.tsx | 282 ++++++++++++------ .../screen/InteractiveScreenViewer.tsx | 15 +- .../screen/InteractiveScreenViewerDynamic.tsx | 6 +- .../screen/RealtimePreviewDynamic.tsx | 29 +- frontend/components/screen/ScreenList.tsx | 35 ++- .../screen/widgets/types/ButtonWidget.tsx | 6 +- .../lib/registry/DynamicWebTypeRenderer.tsx | 16 +- 7 files changed, 256 insertions(+), 133 deletions(-) diff --git a/frontend/app/(main)/screens/[screenId]/page.tsx b/frontend/app/(main)/screens/[screenId]/page.tsx index dac590d6..a0b12c7f 100644 --- a/frontend/app/(main)/screens/[screenId]/page.tsx +++ b/frontend/app/(main)/screens/[screenId]/page.tsx @@ -26,7 +26,7 @@ export default function ScreenViewPage() { // ๐Ÿ†• ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด const { user, userName, companyCode } = useAuth(); - + // ๐Ÿ†• ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ ๊ฐ์ง€ const { isMobile } = useResponsive(); @@ -189,10 +189,10 @@ export default function ScreenViewPage() { if (loading) { return ( -
-
- -

ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...

+
+
+ +

ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...

); @@ -200,13 +200,13 @@ export default function ScreenViewPage() { if (error || !screen) { return ( -
-
-
+
+
+
โš ๏ธ
-

ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค

-

{error || "์š”์ฒญํ•˜์‹  ํ™”๋ฉด์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."}

+

ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค

+

{error || "์š”์ฒญํ•˜์‹  ํ™”๋ฉด์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."}

@@ -225,7 +225,7 @@ export default function ScreenViewPage() { {/* ์ ˆ๋Œ€ ์œ„์น˜ ๊ธฐ๋ฐ˜ ๋ Œ๋”๋ง */} {layout && layout.components.length > 0 ? (
!component.parentId); + // ๋ฒ„ํŠผ์€ scale์— ๋งž์ถฐ ์œ„์น˜๋งŒ ์กฐ์ •ํ•˜๋ฉด ๋จ (scale = 1.0์ด๋ฉด ๊ทธ๋Œ€๋กœ, scale < 1.0์ด๋ฉด ์™ผ์ชฝ์œผ๋กœ) + // ํ•˜์ง€๋งŒ x=0 ์ปดํฌ๋„ŒํŠธ๋Š” width: 100%๋กœ ํ™•์žฅ๋˜๋ฏ€๋กœ, ๊ทธ๋งŒํผ ๋ฒ„ํŠผ์„ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ์ด๋™ + const leftmostComponent = topLevelComponents.find((c) => c.position.x === 0); + let widthOffset = 0; + + if (leftmostComponent && containerWidth > 0) { + const originalWidth = leftmostComponent.size?.width || screenWidth; + const actualWidth = containerWidth / scale; + widthOffset = Math.max(0, actualWidth - originalWidth); + + console.log("๐Ÿ“Š widthOffset ๊ณ„์‚ฐ:", { + containerWidth, + scale, + screenWidth, + originalWidth, + actualWidth, + widthOffset, + leftmostType: leftmostComponent.type, + }); + } + const buttonGroups: Record = {}; const processedButtonIds = new Set(); + // ๐Ÿ” ์ „์ฒด ๋ฒ„ํŠผ ๋ชฉ๋ก ํ™•์ธ + const allButtons = topLevelComponents.filter((component) => { + const isButton = + (component.type === "component" && + ["button-primary", "button-secondary"].includes((component as any).componentType)) || + (component.type === "widget" && (component as any).widgetType === "button"); + return isButton; + }); + + console.log( + "๐Ÿ” ๋ฉ”๋‰ด์—์„œ ๋ฐœ๊ฒฌ๋œ ์ „์ฒด ๋ฒ„ํŠผ:", + allButtons.map((b) => ({ + id: b.id, + label: b.label, + positionX: b.position.x, + positionY: b.position.y, + })), + ); topLevelComponents.forEach((component) => { const isButton = - component.type === "button" || (component.type === "component" && - ["button-primary", "button-secondary"].includes((component as any).componentType)); + ["button-primary", "button-secondary"].includes((component as any).componentType)) || + (component.type === "widget" && (component as any).widgetType === "button"); if (isButton) { const flowConfig = (component as any).webTypeConfig?.flowVisibilityConfig as | FlowVisibilityConfig | undefined; - if (flowConfig?.enabled && flowConfig.layoutBehavior === "auto-compact" && flowConfig.groupId) { + // ๐Ÿ”ง ์ž„์‹œ: ๋ฒ„ํŠผ ๊ทธ๋ฃน ๊ธฐ๋Šฅ ์™„์ „ ๋น„ํ™œ์„ฑํ™” + // TODO: ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ๊ทธ๋ฃน์„ ์›ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ํ™œ์„ฑํ™”ํ•˜๋„๋ก UI ๊ฐœ์„  ํ•„์š” + const DISABLE_BUTTON_GROUPS = true; + + if ( + !DISABLE_BUTTON_GROUPS && + flowConfig?.enabled && + flowConfig.layoutBehavior === "auto-compact" && + flowConfig.groupId + ) { if (!buttonGroups[flowConfig.groupId]) { buttonGroups[flowConfig.groupId] = []; } buttonGroups[flowConfig.groupId].push(component); processedButtonIds.add(component.id); } + // else: ๋ชจ๋“  ๋ฒ„ํŠผ์„ ๊ฐœ๋ณ„ ๋ Œ๋”๋ง } }); @@ -267,92 +316,121 @@ export default function ScreenViewPage() { return ( <> {/* ์ผ๋ฐ˜ ์ปดํฌ๋„ŒํŠธ๋“ค */} - {regularComponents.map((component) => ( - {}} - screenId={screenId} - tableName={screen?.tableName} - userId={user?.userId} - userName={userName} - companyCode={companyCode} - selectedRowsData={selectedRowsData} - onSelectedRowsChange={(_, selectedData) => { - console.log("๐Ÿ” ํ™”๋ฉด์—์„œ ์„ ํƒ๋œ ํ–‰ ๋ฐ์ดํ„ฐ:", selectedData); - setSelectedRowsData(selectedData); - }} - flowSelectedData={flowSelectedData} - flowSelectedStepId={flowSelectedStepId} - onFlowSelectedDataChange={(selectedData: any[], stepId: number | null) => { - setFlowSelectedData(selectedData); - setFlowSelectedStepId(stepId); - }} - refreshKey={tableRefreshKey} - onRefresh={() => { - setTableRefreshKey((prev) => prev + 1); - setSelectedRowsData([]); // ์„ ํƒ ํ•ด์ œ - }} - flowRefreshKey={flowRefreshKey} - onFlowRefresh={() => { - setFlowRefreshKey((prev) => prev + 1); - setFlowSelectedData([]); // ์„ ํƒ ํ•ด์ œ - setFlowSelectedStepId(null); - }} - formData={formData} - onFormDataChange={(fieldName, value) => { - setFormData((prev) => ({ ...prev, [fieldName]: value })); - }} - > - {/* ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค */} - {(component.type === "group" || component.type === "container" || component.type === "area") && - layout.components - .filter((child) => child.parentId === component.id) - .map((child) => { - // ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ์œ„์น˜๋ฅผ ๋ถ€๋ชจ ๊ธฐ์ค€ ์ƒ๋Œ€ ์ขŒํ‘œ๋กœ ์กฐ์ • - const relativeChildComponent = { - ...child, - position: { - x: child.position.x - component.position.x, - y: child.position.y - component.position.y, - z: child.position.z || 1, - }, - }; + {regularComponents.map((component) => { + // ๋ฒ„ํŠผ์ธ ๊ฒฝ์šฐ ์œ„์น˜ ์กฐ์ • (ํ…Œ์ด๋ธ”์ด ๋Š˜์–ด๋‚œ ๋งŒํผ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ์ด๋™) + const isButton = + (component.type === "component" && + ["button-primary", "button-secondary"].includes((component as any).componentType)) || + (component.type === "widget" && (component as any).widgetType === "button"); - return ( - {}} - screenId={screenId} - tableName={screen?.tableName} - userId={user?.userId} - userName={userName} - companyCode={companyCode} - selectedRowsData={selectedRowsData} - onSelectedRowsChange={(_, selectedData) => { - console.log("๐Ÿ” ํ™”๋ฉด์—์„œ ์„ ํƒ๋œ ํ–‰ ๋ฐ์ดํ„ฐ (์ž์‹):", selectedData); - setSelectedRowsData(selectedData); - }} - refreshKey={tableRefreshKey} - onRefresh={() => { - console.log("๐Ÿ”„ ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ ์š”์ฒญ๋จ (์ž์‹)"); - setTableRefreshKey((prev) => prev + 1); - setSelectedRowsData([]); // ์„ ํƒ ํ•ด์ œ - }} - formData={formData} - onFormDataChange={(fieldName, value) => { - setFormData((prev) => ({ ...prev, [fieldName]: value })); - }} - /> - ); - })} - - ))} + const adjustedComponent = + isButton && widthOffset > 0 + ? { + ...component, + position: { + ...component.position, + x: component.position.x + widthOffset, + }, + } + : component; + + // ๋ฒ„ํŠผ์ผ ๊ฒฝ์šฐ ๋กœ๊ทธ ์ถœ๋ ฅ + if (isButton) { + console.log("๐Ÿ”˜ ๋ฒ„ํŠผ ์œ„์น˜ ์กฐ์ •:", { + label: component.label, + originalX: component.position.x, + adjustedX: component.position.x + widthOffset, + widthOffset, + }); + } + + return ( + {}} + screenId={screenId} + tableName={screen?.tableName} + userId={user?.userId} + userName={userName} + companyCode={companyCode} + selectedRowsData={selectedRowsData} + onSelectedRowsChange={(_, selectedData) => { + console.log("๐Ÿ” ํ™”๋ฉด์—์„œ ์„ ํƒ๋œ ํ–‰ ๋ฐ์ดํ„ฐ:", selectedData); + setSelectedRowsData(selectedData); + }} + flowSelectedData={flowSelectedData} + flowSelectedStepId={flowSelectedStepId} + onFlowSelectedDataChange={(selectedData: any[], stepId: number | null) => { + setFlowSelectedData(selectedData); + setFlowSelectedStepId(stepId); + }} + refreshKey={tableRefreshKey} + onRefresh={() => { + setTableRefreshKey((prev) => prev + 1); + setSelectedRowsData([]); // ์„ ํƒ ํ•ด์ œ + }} + flowRefreshKey={flowRefreshKey} + onFlowRefresh={() => { + setFlowRefreshKey((prev) => prev + 1); + setFlowSelectedData([]); // ์„ ํƒ ํ•ด์ œ + setFlowSelectedStepId(null); + }} + formData={formData} + onFormDataChange={(fieldName, value) => { + setFormData((prev) => ({ ...prev, [fieldName]: value })); + }} + > + {/* ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค */} + {(component.type === "group" || component.type === "container" || component.type === "area") && + layout.components + .filter((child) => child.parentId === component.id) + .map((child) => { + // ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ์œ„์น˜๋ฅผ ๋ถ€๋ชจ ๊ธฐ์ค€ ์ƒ๋Œ€ ์ขŒํ‘œ๋กœ ์กฐ์ • + const relativeChildComponent = { + ...child, + position: { + x: child.position.x - component.position.x, + y: child.position.y - component.position.y, + z: child.position.z || 1, + }, + }; + + return ( + {}} + screenId={screenId} + tableName={screen?.tableName} + userId={user?.userId} + userName={userName} + companyCode={companyCode} + selectedRowsData={selectedRowsData} + onSelectedRowsChange={(_, selectedData) => { + console.log("๐Ÿ” ํ™”๋ฉด์—์„œ ์„ ํƒ๋œ ํ–‰ ๋ฐ์ดํ„ฐ (์ž์‹):", selectedData); + setSelectedRowsData(selectedData); + }} + refreshKey={tableRefreshKey} + onRefresh={() => { + console.log("๐Ÿ”„ ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ ์š”์ฒญ๋จ (์ž์‹)"); + setTableRefreshKey((prev) => prev + 1); + setSelectedRowsData([]); // ์„ ํƒ ํ•ด์ œ + }} + formData={formData} + onFormDataChange={(fieldName, value) => { + setFormData((prev) => ({ ...prev, [fieldName]: value })); + }} + /> + ); + })} + + ); + })} {/* ๐Ÿ†• ํ”Œ๋กœ์šฐ ๋ฒ„ํŠผ ๊ทธ๋ฃน๋“ค */} {Object.entries(buttonGroups).map(([groupId, buttons]) => { @@ -372,6 +450,12 @@ export default function ScreenViewPage() { { x: buttons[0].position.x, y: buttons[0].position.y, z: buttons[0].position.z || 2 }, ); + // ๋ฒ„ํŠผ ๊ทธ๋ฃน ์œ„์น˜์—๋„ widthOffset ์ ์šฉ (ํ…Œ์ด๋ธ”์ด ๋Š˜์–ด๋‚œ ๋งŒํผ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ์ด๋™) + const adjustedGroupPosition = { + ...groupPosition, + x: groupPosition.x + widthOffset, + }; + // ๊ทธ๋ฃน์˜ ํฌ๊ธฐ ๊ณ„์‚ฐ: ๋ฒ„ํŠผ๋“ค์˜ ์‹ค์ œ ํฌ๊ธฐ + ๊ฐ„๊ฒฉ์„ ๊ธฐ์ค€์œผ๋กœ ๊ณ„์‚ฐ const direction = groupConfig.groupDirection || "horizontal"; const gap = groupConfig.groupGap ?? 8; diff --git a/frontend/components/screen/InteractiveScreenViewer.tsx b/frontend/components/screen/InteractiveScreenViewer.tsx index 8a114aa4..45b263d6 100644 --- a/frontend/components/screen/InteractiveScreenViewer.tsx +++ b/frontend/components/screen/InteractiveScreenViewer.tsx @@ -1633,24 +1633,19 @@ export const InteractiveScreenViewer: React.FC = ( } }; - return ( + return applyStyles(