Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into feature/screen-management
This commit is contained in:
@@ -2,13 +2,13 @@
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
} from "@/components/ui/dialog";
|
||||
ResizableDialog,
|
||||
ResizableDialogContent,
|
||||
ResizableDialogHeader,
|
||||
ResizableDialogTitle,
|
||||
ResizableDialogDescription,
|
||||
ResizableDialogFooter,
|
||||
} from "@/components/ui/resizable-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
@@ -101,17 +101,17 @@ export default function CopyScreenModal({ isOpen, onClose, sourceScreen, onCopyS
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||
<DialogContent className="sm:max-w-[500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<ResizableDialog open={isOpen} onOpenChange={handleClose}>
|
||||
<ResizableDialogContent className="sm:max-w-[500px]">
|
||||
<ResizableDialogHeader>
|
||||
<ResizableDialogTitle className="flex items-center gap-2">
|
||||
<Copy className="h-5 w-5" />
|
||||
화면 복사
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
</ResizableDialogTitle>
|
||||
<ResizableDialogDescription>
|
||||
{sourceScreen?.screenName} 화면을 복사합니다. 화면 구성도 함께 복사됩니다.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
</ResizableDialogDescription>
|
||||
</ResizableDialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* 원본 화면 정보 */}
|
||||
@@ -185,8 +185,8 @@ export default function CopyScreenModal({ isOpen, onClose, sourceScreen, onCopyS
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</ResizableDialogFooter>
|
||||
</ResizableDialogContent>
|
||||
</ResizableDialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,7 @@ import React, { useState, useEffect, useRef } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
} from "@/components/ui/dialog";
|
||||
} from "@/components/ui/resizable-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
@@ -550,7 +546,7 @@ export const MenuAssignmentModal: React.FC<MenuAssignmentModalProps> = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<DialogFooter className="flex gap-2">
|
||||
<ResizableDialogFooter className="flex gap-2">
|
||||
<Button variant="outline" onClick={handleAssignLater} disabled={assigning}>
|
||||
<X className="mr-2 h-4 w-4" />
|
||||
나중에 할당
|
||||
@@ -628,7 +624,7 @@ export const MenuAssignmentModal: React.FC<MenuAssignmentModalProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="flex gap-2">
|
||||
<ResizableDialogFooter className="flex gap-2">
|
||||
<Button variant="outline" onClick={() => setShowReplaceDialog(false)} disabled={assigning}>
|
||||
취소
|
||||
</Button>
|
||||
|
||||
@@ -54,7 +54,7 @@ interface RealtimePreviewProps {
|
||||
// 폼 데이터 관련 props
|
||||
formData?: Record<string, any>;
|
||||
onFormDataChange?: (fieldName: string, value: any) => void;
|
||||
|
||||
|
||||
// 테이블 정렬 정보
|
||||
sortBy?: string;
|
||||
sortOrder?: "asc" | "desc";
|
||||
@@ -229,10 +229,10 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||
}
|
||||
|
||||
// 2순위: x=0인 컴포넌트는 전체 너비 사용 (버튼 제외)
|
||||
const isButtonComponent =
|
||||
const isButtonComponent =
|
||||
(component.type === "widget" && (component as WidgetComponent).widgetType === "button") ||
|
||||
(component.type === "component" && (component as any).componentType?.includes("button"));
|
||||
|
||||
|
||||
if (position.x === 0 && !isButtonComponent) {
|
||||
console.log("⚠️ [getWidth] 100% 사용 (x=0):", {
|
||||
componentId: id,
|
||||
@@ -269,9 +269,9 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||
return `${actualHeight}px`;
|
||||
}
|
||||
|
||||
// 1순위: style.height가 있으면 우선 사용
|
||||
// 1순위: style.height가 있으면 우선 사용 (문자열 그대로 또는 숫자+px)
|
||||
if (componentStyle?.height) {
|
||||
return componentStyle.height;
|
||||
return typeof componentStyle.height === "number" ? `${componentStyle.height}px` : componentStyle.height;
|
||||
}
|
||||
|
||||
// 2순위: size.height (픽셀)
|
||||
@@ -283,6 +283,20 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||
return `${size?.height || 10}px`;
|
||||
};
|
||||
|
||||
// layout 타입 컴포넌트인지 확인
|
||||
const isLayoutComponent = component.type === "layout" || (component.componentConfig as any)?.type?.includes("layout");
|
||||
|
||||
// layout 컴포넌트는 component 객체에 style.height 추가
|
||||
const enhancedComponent = isLayoutComponent
|
||||
? {
|
||||
...component,
|
||||
style: {
|
||||
...component.style,
|
||||
height: getHeight(),
|
||||
},
|
||||
}
|
||||
: component;
|
||||
|
||||
const baseStyle = {
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
@@ -296,14 +310,14 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||
// 🔍 DOM 렌더링 후 실제 크기 측정
|
||||
const innerDivRef = React.useRef<HTMLDivElement>(null);
|
||||
const outerDivRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (outerDivRef.current && innerDivRef.current) {
|
||||
const outerRect = outerDivRef.current.getBoundingClientRect();
|
||||
const innerRect = innerDivRef.current.getBoundingClientRect();
|
||||
const computedOuter = window.getComputedStyle(outerDivRef.current);
|
||||
const computedInner = window.getComputedStyle(innerDivRef.current);
|
||||
|
||||
|
||||
console.log("📐 [DOM 실제 크기 상세]:", {
|
||||
componentId: id,
|
||||
label: component.label,
|
||||
@@ -325,7 +339,7 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||
},
|
||||
"4. 너비 비교": {
|
||||
"외부 / 내부": `${outerRect.width}px / ${innerRect.width}px`,
|
||||
"비율": `${((innerRect.width / outerRect.width) * 100).toFixed(2)}%`,
|
||||
비율: `${((innerRect.width / outerRect.width) * 100).toFixed(2)}%`,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -376,7 +390,7 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||
style={{ width: "100%", maxWidth: "100%" }}
|
||||
>
|
||||
<DynamicComponentRenderer
|
||||
component={component}
|
||||
component={enhancedComponent}
|
||||
isSelected={isSelected}
|
||||
isDesignMode={isDesignMode}
|
||||
isInteractive={!isDesignMode} // 편집 모드가 아닐 때만 인터랙티브
|
||||
|
||||
@@ -25,16 +25,13 @@ import {
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter,
|
||||
ResizableDialog,
|
||||
ResizableDialogContent,
|
||||
ResizableDialogHeader
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
ResizableDialog,
|
||||
ResizableDialogContent,
|
||||
ResizableDialogHeader,
|
||||
ResizableDialogTitle,
|
||||
ResizableDialogFooter,
|
||||
} from "@/components/ui/resizable-dialog";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { MoreHorizontal, Edit, Trash2, Copy, Eye, Plus, Search, Palette, RotateCcw, Trash } from "lucide-react";
|
||||
import { ScreenDefinition } from "@/types/screen";
|
||||
@@ -459,7 +456,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
}`}
|
||||
onClick={() => onDesignScreen(screen)}
|
||||
>
|
||||
<TableCell className="h-16 px-6 py-3 cursor-pointer">
|
||||
<TableCell className="h-16 cursor-pointer px-6 py-3">
|
||||
<div>
|
||||
<div className="font-medium">{screen.screenName}</div>
|
||||
{screen.description && (
|
||||
@@ -699,7 +696,10 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{deletedScreens.map((screen) => (
|
||||
<TableRow key={screen.screenId} className="bg-background hover:bg-muted/50 border-b transition-colors">
|
||||
<TableRow
|
||||
key={screen.screenId}
|
||||
className="bg-background hover:bg-muted/50 border-b transition-colors"
|
||||
>
|
||||
<TableCell className="h-16 px-6 py-3">
|
||||
<Checkbox
|
||||
checked={selectedScreenIds.includes(screen.screenId)}
|
||||
@@ -1065,11 +1065,11 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
</AlertDialog>
|
||||
|
||||
{/* 화면 편집 다이얼로그 */}
|
||||
<Dialog open={editDialogOpen} onOpenChange={setEditDialogOpen}>
|
||||
<DialogContent className="sm:max-w-[500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>화면 정보 편집</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ResizableDialog open={editDialogOpen} onOpenChange={setEditDialogOpen}>
|
||||
<ResizableDialogContent className="sm:max-w-[500px]">
|
||||
<ResizableDialogHeader>
|
||||
<ResizableDialogTitle>화면 정보 편집</ResizableDialogTitle>
|
||||
</ResizableDialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="edit-screenName">화면명 *</Label>
|
||||
@@ -1106,23 +1106,23 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<ResizableDialogFooter>
|
||||
<Button variant="outline" onClick={() => setEditDialogOpen(false)}>
|
||||
취소
|
||||
</Button>
|
||||
<Button onClick={handleEditSave} disabled={!editFormData.screenName.trim()}>
|
||||
저장
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</ResizableDialogFooter>
|
||||
</ResizableDialogContent>
|
||||
</ResizableDialog>
|
||||
|
||||
{/* 화면 미리보기 다이얼로그 */}
|
||||
<Dialog open={previewDialogOpen} onOpenChange={setPreviewDialogOpen}>
|
||||
<DialogContent className="h-[95vh] max-w-[95vw]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>화면 미리보기 - {screenToPreview?.screenName}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ResizableDialog open={previewDialogOpen} onOpenChange={setPreviewDialogOpen}>
|
||||
<ResizableDialogContent className="h-[95vh] max-w-[95vw]">
|
||||
<ResizableDialogHeader>
|
||||
<ResizableDialogTitle>화면 미리보기 - {screenToPreview?.screenName}</ResizableDialogTitle>
|
||||
</ResizableDialogHeader>
|
||||
<div className="flex flex-1 items-center justify-center overflow-hidden bg-gradient-to-br from-gray-50 to-slate-100 p-6">
|
||||
{isLoadingPreview ? (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
@@ -1268,11 +1268,12 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
height: component.style?.height || `${component.size.height}px`,
|
||||
zIndex: component.position.z || 1,
|
||||
};
|
||||
|
||||
|
||||
// 버튼 타입일 때 디버깅 (widget 타입 또는 component 타입 모두 체크)
|
||||
if (
|
||||
(component.type === "widget" && (component as any).widgetType === "button") ||
|
||||
(component.type === "component" && (component as any).componentType?.includes("button"))
|
||||
(component.type === "component" &&
|
||||
(component as any).componentType?.includes("button"))
|
||||
) {
|
||||
console.log("🔘 ScreenList 버튼 외부 div 스타일:", {
|
||||
id: component.id,
|
||||
@@ -1283,7 +1284,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
appliedStyle: style,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return style;
|
||||
})()}
|
||||
>
|
||||
@@ -1360,7 +1361,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<ResizableDialogFooter>
|
||||
<Button variant="outline" onClick={() => setPreviewDialogOpen(false)}>
|
||||
닫기
|
||||
</Button>
|
||||
@@ -1368,9 +1369,9 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
<Palette className="mr-2 h-4 w-4" />
|
||||
편집 모드로 전환
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</ResizableDialogFooter>
|
||||
</ResizableDialogContent>
|
||||
</ResizableDialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user