diff --git a/frontend/app/(main)/screens/[screenId]/page.tsx b/frontend/app/(main)/screens/[screenId]/page.tsx index f33a93bf..dac590d6 100644 --- a/frontend/app/(main)/screens/[screenId]/page.tsx +++ b/frontend/app/(main)/screens/[screenId]/page.tsx @@ -17,6 +17,7 @@ import { findAllButtonGroups } from "@/lib/utils/flowButtonGroupUtils"; import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer"; import { ScreenPreviewProvider } from "@/contexts/ScreenPreviewContext"; import { useAuth } from "@/hooks/useAuth"; // ๐Ÿ†• ์‚ฌ์šฉ์ž ์ •๋ณด +import { useResponsive } from "@/lib/hooks/useResponsive"; // ๐Ÿ†• ๋ฐ˜์‘ํ˜• ๊ฐ์ง€ export default function ScreenViewPage() { const params = useParams(); @@ -25,6 +26,9 @@ export default function ScreenViewPage() { // ๐Ÿ†• ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด const { user, userName, companyCode } = useAuth(); + + // ๐Ÿ†• ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ ๊ฐ์ง€ + const { isMobile } = useResponsive(); const [screen, setScreen] = useState(null); const [layout, setLayout] = useState(null); @@ -59,6 +63,7 @@ export default function ScreenViewPage() { const containerRef = React.useRef(null); const [scale, setScale] = useState(1); + const [containerWidth, setContainerWidth] = useState(0); useEffect(() => { const initComponents = async () => { @@ -142,22 +147,33 @@ export default function ScreenViewPage() { } }, [screenId]); - // ์บ”๋ฒ„์Šค ๋น„์œจ ์กฐ์ • (์‚ฌ์šฉ์ž ํ™”๋ฉด์— ๋งž๊ฒŒ ์ž๋™ ์Šค์ผ€์ผ) + // ์บ”๋ฒ„์Šค ๋น„์œจ ์กฐ์ • (์‚ฌ์šฉ์ž ํ™”๋ฉด์— ๋งž๊ฒŒ ์ž๋™ ์Šค์ผ€์ผ) - ๋ชจ๋ฐ”์ผ์—์„œ๋Š” ๋น„ํ™œ์„ฑํ™” useEffect(() => { + // ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์—์„œ๋Š” ์Šค์ผ€์ผ ์กฐ์ • ๋น„ํ™œ์„ฑํ™” (๋ฐ˜์‘ํ˜•๋งŒ ์ž‘๋™) + if (isMobile) { + setScale(1); + return; + } + const updateScale = () => { if (containerRef.current && layout) { const designWidth = layout?.screenResolution?.width || 1200; const designHeight = layout?.screenResolution?.height || 800; + // containerRef๋Š” ์ด๋ฏธ ํŒจ๋”ฉ์ด ์ ์šฉ๋œ ์˜์—ญ ๋‚ด๋ถ€์ด๋ฏ€๋กœ offsetWidth๋Š” ํŒจ๋”ฉ์„ ์ œ์™ธํ•œ ํฌ๊ธฐ์ž…๋‹ˆ๋‹ค const containerWidth = containerRef.current.offsetWidth; const containerHeight = containerRef.current.offsetHeight; - // ๊ฐ€๋กœ/์„ธ๋กœ ๋น„์œจ ์ค‘ ์ž‘์€ ๊ฒƒ์„ ์„ ํƒ (ํ™”๋ฉด์— ๋งž๊ฒŒ) + // ๊ฐ€๋กœ/์„ธ๋กœ ๋น„์œจ ์ค‘ ์ž‘์€ ๊ฒƒ์„ ์„ ํƒํ•˜์—ฌ ํ™”๋ฉด์— ๋งž๊ฒŒ ์Šค์ผ€์ผ ์กฐ์ • + // ํ•˜์ง€๋งŒ ํ™”๋ฉด์ด ์ปจํ…Œ์ด๋„ˆ ์ „์ฒด ๋„ˆ๋น„๋ฅผ ์ฐจ์ง€ํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€๋กœ๋ฅผ ์šฐ์„ ์‹œ const scaleX = containerWidth / designWidth; const scaleY = containerHeight / designHeight; + // ๊ฐ€๋กœ๋ฅผ ์šฐ์„ ์œผ๋กœ ํ•˜๋˜, ์„ธ๋กœ๊ฐ€ ๋„˜์น˜์ง€ ์•Š๋„๋ก ์ œํ•œ const newScale = Math.min(scaleX, scaleY); setScale(newScale); + // ์ปจํ…Œ์ด๋„ˆ ๋„ˆ๋น„ ์—…๋ฐ์ดํŠธ + setContainerWidth(containerWidth); } }; @@ -169,7 +185,7 @@ export default function ScreenViewPage() { clearTimeout(timer); window.removeEventListener("resize", updateScale); }; - }, [layout]); + }, [layout, isMobile]); if (loading) { return ( @@ -205,18 +221,16 @@ export default function ScreenViewPage() { return ( -
+
{/* ์ ˆ๋Œ€ ์œ„์น˜ ๊ธฐ๋ฐ˜ ๋ Œ๋”๋ง */} {layout && layout.components.length > 0 ? (
0 ? `${containerWidth / scale}px` : "100%", + minWidth: containerWidth > 0 ? `${containerWidth / scale}px` : "100%", }} > {/* ์ตœ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค ๋ Œ๋”๋ง */} diff --git a/frontend/app/globals.css b/frontend/app/globals.css index f5e35eca..7818c287 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -342,4 +342,18 @@ select { /* ํ•„์š”์‹œ ํŠน์ • ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ ์Šคํƒ€์ผ ์˜ค๋ฒ„๋ผ์ด๋“œ๋ฅผ ์—ฌ๊ธฐ์— ์ถ”๊ฐ€ */ /* ์˜ˆ: Calendar, Table ๋“ฑ์˜ ๋ฏธ์„ธ ์กฐ์ • */ +/* ๋ชจ๋ฐ”์ผ์—์„œ ํ…Œ์ด๋ธ” ๋ ˆ์ด์•„์›ƒ ๊ณ ์ • (ํ™”๋ฉด ๋ฐ–์œผ๋กœ ๋„˜์–ด๊ฐ€์ง€ ์•Š๋„๋ก) */ +@media (max-width: 639px) { + .table-mobile-fixed { + table-layout: fixed; + } +} + +/* ๋ฐ์Šคํฌํ†ฑ์—์„œ ํ…Œ์ด๋ธ” ๋ ˆ์ด์•„์›ƒ ์ž๋™ (๊ธฐ๋ณธ๊ฐ’์ด์ง€๋งŒ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •) */ +@media (min-width: 640px) { + .table-mobile-fixed { + table-layout: auto; + } +} + /* ===== End of Global Styles ===== */ diff --git a/frontend/components/dashboard/DashboardViewer.tsx b/frontend/components/dashboard/DashboardViewer.tsx index 5828bea9..2b21b5f4 100644 --- a/frontend/components/dashboard/DashboardViewer.tsx +++ b/frontend/components/dashboard/DashboardViewer.tsx @@ -542,7 +542,7 @@ export function DashboardViewer({ {/* ๋ฐ์Šคํฌํ†ฑ: ๋””์ž์ด๋„ˆ์—์„œ ์„ค์ •ํ•œ ์œ„์น˜ ๊ทธ๋Œ€๋กœ ๋ Œ๋”๋ง (ํ™”๋ฉด์— ๋งž์ถฐ ๋น„์œจ ์œ ์ง€) */}
-
+
{/* ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ */}
@@ -700,12 +700,13 @@ function ViewerElement({ element, data, isLoading, onRefresh, isMobile, canvasWi // ๋ฐ์Šคํฌํ†ฑ: ๋””์ž์ด๋„ˆ์—์„œ ์„ค์ •ํ•œ ์œ„์น˜ ๊ทธ๋Œ€๋กœ absolute positioning // ๋‹จ, ๋„ˆ๋น„๋Š” ํ™”๋ฉด ํฌ๊ธฐ์— ๋”ฐ๋ผ ๋น„์œจ๋กœ ์กฐ์ • const widthPercentage = (element.size.width / canvasWidth) * 100; + const leftPercentage = (element.position.x / canvasWidth) * 100; return (
{/* ๊ฐ€์šด๋ฐ ์ปจํ…์ธ  ์˜์—ญ - ์Šคํฌ๋กค ๊ฐ€๋Šฅ */} -
{children}
+
+
{children}
+
{/* ํ”„๋กœํ•„ ์ˆ˜์ • ๋ชจ๋‹ฌ */} diff --git a/frontend/components/screen/RealtimePreview.tsx b/frontend/components/screen/RealtimePreview.tsx index 7cf02aa0..12c300de 100644 --- a/frontend/components/screen/RealtimePreview.tsx +++ b/frontend/components/screen/RealtimePreview.tsx @@ -389,14 +389,27 @@ export const RealtimePreviewDynamic: React.FC = ({ finalHeight = actualHeight; } + // ๐Ÿ” ๋””๋ฒ„๊น…: position.x ๊ฐ’ ํ™•์ธ + const positionX = position?.x || 0; + console.log("๐Ÿ” RealtimePreview componentStyle ์„ค์ •:", { + componentId: id, + positionX, + sizeWidth: size?.width, + styleWidth: style?.width, + willUse100Percent: positionX === 0, + }); + const componentStyle = { position: "absolute" as const, - left: position?.x || 0, + ...style, // ๋จผ์ € ์ ์šฉํ•˜๊ณ  + left: positionX, top: position?.y || 0, - width: size?.width || 200, + // ๐Ÿ†• left๊ฐ€ 0์ด๋ฉด ๋ถ€๋ชจ ๋„ˆ๋น„๋ฅผ 100% ์ฑ„์šฐ๋„๋ก ์ˆ˜์ • (์šฐ์ธก ์—ฌ๋ฐฑ ์ œ๊ฑฐ) + width: positionX === 0 ? "100%" : (size?.width || 200), height: finalHeight, zIndex: position?.z || 1, - ...style, + // right ์†์„ฑ ๊ฐ•์ œ ์ œ๊ฑฐ + right: undefined, }; // ์„ ํƒ๋œ ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ diff --git a/frontend/components/screen/RealtimePreviewDynamic.tsx b/frontend/components/screen/RealtimePreviewDynamic.tsx index df14084b..f4e0fec9 100644 --- a/frontend/components/screen/RealtimePreviewDynamic.tsx +++ b/frontend/components/screen/RealtimePreviewDynamic.tsx @@ -238,11 +238,15 @@ export const RealtimePreviewDynamic: React.FC = ({ const baseStyle = { left: `${position.x}px`, top: `${position.y}px`, - width: getWidth(), + // ๐Ÿ†• left๊ฐ€ 0์ด๋ฉด ๋ถ€๋ชจ ๋„ˆ๋น„๋ฅผ 100% ์ฑ„์šฐ๋„๋ก ์ˆ˜์ • (์šฐ์ธก ์—ฌ๋ฐฑ ์ œ๊ฑฐ) + width: position.x === 0 ? "100%" : getWidth(), height: getHeight(), // ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ ๊ณ ์ • ๋†’์ด๋กœ ๋ณ€๊ฒฝ zIndex: component.type === "layout" ? 1 : position.z || 2, // ๋ ˆ์ด์•„์›ƒ์€ z-index 1, ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋Š” 2 ์ด์ƒ ...componentStyle, - // style.width์™€ style.height๋Š” ์ด๋ฏธ getWidth/getHeight์—์„œ ์ฒ˜๋ฆฌํ–ˆ์œผ๋ฏ€๋กœ ์ค‘๋ณต ์ ์šฉ๋จ + // style.width๊ฐ€ ์žˆ์–ด๋„ position.x === 0์ด๋ฉด 100%๋กœ ๊ฐ•์ œ + ...(position.x === 0 && { width: "100%" }), + // right ์†์„ฑ ๊ฐ•์ œ ์ œ๊ฑฐ + right: undefined, }; const handleClick = (e: React.MouseEvent) => { diff --git a/frontend/lib/registry/components/table-list/SingleTableWithSticky.tsx b/frontend/lib/registry/components/table-list/SingleTableWithSticky.tsx index cbfe678c..d429fbf4 100644 --- a/frontend/lib/registry/components/table-list/SingleTableWithSticky.tsx +++ b/frontend/lib/registry/components/table-list/SingleTableWithSticky.tsx @@ -91,8 +91,8 @@ export const SingleTableWithSticky: React.FC = ({ key={column.columnName} className={cn( column.columnName === "__checkbox__" - ? "h-12 border-0 px-6 py-3 text-center align-middle" - : "h-12 cursor-pointer border-0 px-6 py-3 text-left align-middle font-semibold whitespace-nowrap text-foreground transition-all duration-200 select-none hover:text-foreground", + ? "h-10 border-0 px-3 py-2 text-center align-middle sm:h-12 sm:px-6 sm:py-3" + : "h-10 cursor-pointer border-0 px-3 py-2 text-left align-middle font-semibold whitespace-nowrap text-xs text-foreground transition-all duration-200 select-none hover:text-foreground sm:h-12 sm:px-6 sm:py-3 sm:text-sm", `text-${column.align}`, column.sortable && "hover:bg-primary/10", // ๊ณ ์ • ์ปฌ๋Ÿผ ์Šคํƒ€์ผ @@ -133,11 +133,11 @@ export const SingleTableWithSticky: React.FC = ({ {columnLabels[column.columnName] || column.displayName || column.columnName} {column.sortable && sortColumn === column.columnName && ( - + {sortDirection === "asc" ? ( - + ) : ( - + )} )} @@ -177,7 +177,7 @@ export const SingleTableWithSticky: React.FC = ({ handleRowClick(row)} @@ -201,7 +201,7 @@ export const SingleTableWithSticky: React.FC = ({ = ({ return (
{/* ์ค‘์•™ ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ปจํŠธ๋กค */}
- + {currentPage} / {totalPages || 1} @@ -819,19 +806,21 @@ export const TableListComponent: React.FC = ({ size="sm" onClick={() => handlePageChange(currentPage + 1)} disabled={currentPage >= totalPages || loading} + className="h-8 w-8 p-0 sm:h-9 sm:w-auto sm:px-3" > - + - + ์ „์ฒด {totalItems.toLocaleString()}๊ฐœ
@@ -842,9 +831,9 @@ export const TableListComponent: React.FC = ({ size="sm" onClick={handleRefresh} disabled={loading} - style={{ position: "absolute", right: "24px" }} + className="absolute right-2 h-8 w-8 p-0 sm:right-6 sm:h-9 sm:w-auto sm:px-3" > - +
); @@ -884,14 +873,14 @@ export const TableListComponent: React.FC = ({ return (
{tableConfig.showHeader && ( -
-

{tableConfig.title || tableLabel}

+
+

{tableConfig.title || tableLabel}

)} {tableConfig.filter?.enabled && ( -
-
+
+
= ({ variant="outline" size="sm" onClick={() => setIsFilterSettingOpen(true)} - className="flex-shrink-0 mt-1" + className="flex-shrink-0 w-full sm:w-auto sm:mt-1" > ํ•„ํ„ฐ ์„ค์ • @@ -946,16 +935,16 @@ export const TableListComponent: React.FC = ({
{/* ํ—ค๋” */} {tableConfig.showHeader && ( -
-

{tableConfig.title || tableLabel}

+
+

{tableConfig.title || tableLabel}

)} {/* ํ•„ํ„ฐ */} {tableConfig.filter?.enabled && ( -
-
-
+
+
+
= ({ variant="outline" size="sm" onClick={() => setIsFilterSettingOpen(true)} - style={{ flexShrink: 0, marginTop: "4px" }} + className="flex-shrink-0 w-full sm:w-auto sm:mt-1" > ํ•„ํ„ฐ ์„ค์ • @@ -978,33 +967,34 @@ export const TableListComponent: React.FC = ({ )} {/* ํ…Œ์ด๋ธ” ์ปจํ…Œ์ด๋„ˆ */} -
+
{/* ์Šคํฌ๋กค ์˜์—ญ */}
{/* ํ…Œ์ด๋ธ” */} {/* ํ—ค๋” (sticky) */} - + {visibleColumns.map((column) => (
column.sortable && handleSort(column.columnName)} > @@ -1063,7 +1053,7 @@ export const TableListComponent: React.FC = ({ onDragStart={(e) => handleRowDragStart(e, row, index)} onDragEnd={handleRowDragEnd} className={cn( - "h-16 border-b transition-colors bg-background hover:bg-muted/50 cursor-pointer" + "h-14 border-b transition-colors bg-background hover:bg-muted/50 cursor-pointer sm:h-16" )} onClick={() => handleRowClick(row)} > @@ -1075,10 +1065,11 @@ export const TableListComponent: React.FC = ({ {column.columnName === "__checkbox__"