+ {type === "container" && (
+
+
+
+
+
{label}
+
{tableName}
+
+
+
+ )}
+
+ {type === "widget" && (
+
+ {/* 위젯 헤더 */}
+
+ {getWidgetIcon(widgetType)}
+
+
+
+
+
+ {/* 위젯 본체 */}
+
{renderWidget(component)}
+
+ {/* 위젯 정보 */}
+
+ {columnName} ({widgetType})
+
+
+ )}
+
+ );
+};
+
+export default RealtimePreview;
diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx
index 82ac8100..b8f37e9f 100644
--- a/frontend/components/screen/ScreenDesigner.tsx
+++ b/frontend/components/screen/ScreenDesigner.tsx
@@ -25,9 +25,12 @@ import {
Table,
Settings,
ChevronDown,
- ChevronRight,
+ Code,
+ Building,
+ File,
List,
AlignLeft,
+ ChevronRight,
} from "lucide-react";
import {
ScreenDefinition,
@@ -53,6 +56,7 @@ import StyleEditor from "./StyleEditor";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
+import { RealtimePreview } from "./RealtimePreview";
interface ScreenDesignerProps {
selectedScreen: ScreenDefinition | null;
@@ -226,6 +230,30 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
dataType: "BOOLEAN",
isNullable: "NO",
},
+ {
+ tableName: "user_info",
+ columnName: "profile_code",
+ columnLabel: "프로필 코드",
+ webType: "code",
+ dataType: "TEXT",
+ isNullable: "YES",
+ },
+ {
+ tableName: "user_info",
+ columnName: "department",
+ columnLabel: "부서",
+ webType: "entity",
+ dataType: "VARCHAR",
+ isNullable: "YES",
+ },
+ {
+ tableName: "user_info",
+ columnName: "profile_image",
+ columnLabel: "프로필 이미지",
+ webType: "file",
+ dataType: "VARCHAR",
+ isNullable: "YES",
+ },
],
},
{
@@ -353,29 +381,44 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 웹타입에 따른 위젯 타입 매핑
const getWidgetTypeFromWebType = useCallback((webType: string): string => {
+ console.log("getWidgetTypeFromWebType - input webType:", webType);
switch (webType) {
case "text":
- case "email":
- case "tel":
return "text";
+ case "email":
+ return "email";
+ case "tel":
+ return "tel";
case "number":
- case "decimal":
return "number";
+ case "decimal":
+ return "decimal";
case "date":
- case "datetime":
return "date";
+ case "datetime":
+ return "datetime";
case "select":
- case "dropdown":
return "select";
+ case "dropdown":
+ return "dropdown";
case "textarea":
- case "text_area":
return "textarea";
+ case "text_area":
+ return "text_area";
case "checkbox":
- case "boolean":
return "checkbox";
+ case "boolean":
+ return "boolean";
case "radio":
return "radio";
+ case "code":
+ return "code";
+ case "entity":
+ return "entity";
+ case "file":
+ return "file";
default:
+ console.log("getWidgetTypeFromWebType - default case, returning text for:", webType);
return "text";
}
}, []);
@@ -682,31 +725,41 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
key={column.columnName}
className="flex cursor-pointer items-center space-x-2 p-2 pl-6 hover:bg-gray-100"
draggable
- onDragStart={(e) =>
+ onDragStart={(e) => {
+ console.log("Drag start - column:", column.columnName, "webType:", column.webType);
+ const widgetType = getWidgetTypeFromWebType(column.webType || "text");
+ console.log("Drag start - widgetType:", widgetType);
startDrag(
{
type: "widget",
tableName: table.tableName,
columnName: column.columnName,
- widgetType: getWidgetTypeFromWebType(column.webType || "text"),
+ widgetType: widgetType as WebType,
label: column.columnLabel || column.columnName,
size: { width: 6, height: 60 },
},
e,
- )
- }
+ );
+ }}
>
{column.webType === "text" &&
}
+ {column.webType === "email" &&
}
+ {column.webType === "tel" &&
}
{column.webType === "number" &&
}
+ {column.webType === "decimal" &&
}
{column.webType === "date" &&
}
+ {column.webType === "datetime" &&
}
{column.webType === "select" &&
}
+ {column.webType === "dropdown" &&
}
{column.webType === "textarea" &&
}
+ {column.webType === "text_area" &&
}
{column.webType === "checkbox" &&
}
+ {column.webType === "boolean" &&
}
{column.webType === "radio" &&
}
- {!["text", "number", "date", "select", "textarea", "checkbox", "radio"].includes(
- column.webType,
- ) &&
}
+ {column.webType === "code" &&
}
+ {column.webType === "entity" &&
}
+ {column.webType === "file" &&
}
{column.columnLabel || column.columnName}
@@ -777,56 +830,16 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
- {/* 컴포넌트들 */}
+ {/* 컴포넌트들 - 실시간 미리보기 */}
{layout.components.map((component) => (
- handleComponentClick(component)}
- draggable
onDragStart={(e) => startComponentDrag(component, e)}
onDragEnd={endDrag}
- >
-
- {component.type === "container" && (
-
-
-
-
{component.label}
-
{component.tableName}
-
-
- )}
- {component.type === "widget" && (
-
- {component.widgetType === "text" &&
}
- {component.widgetType === "number" &&
}
- {component.widgetType === "date" &&
}
- {component.widgetType === "select" &&
}
- {component.widgetType === "textarea" &&
}
- {component.widgetType === "checkbox" &&
}
- {component.widgetType === "radio" &&
}
- {!["text", "number", "date", "select", "textarea", "checkbox", "radio"].includes(
- component.widgetType || "text",
- ) &&
}
-
-
{component.label}
-
{component.columnName}
-
-
- )}
-
-
+ />
))}
)}
diff --git a/frontend/components/ui/calendar.tsx b/frontend/components/ui/calendar.tsx
new file mode 100644
index 00000000..ea740d9b
--- /dev/null
+++ b/frontend/components/ui/calendar.tsx
@@ -0,0 +1,56 @@
+"use client";
+
+import * as React from "react";
+import { ChevronLeft, ChevronRight } from "lucide-react";
+import { DayPicker } from "react-day-picker";
+
+import { cn } from "@/lib/utils";
+import { buttonVariants } from "@/components/ui/button";
+
+export type CalendarProps = React.ComponentProps