실제 화면과 동일하게 보여지도록 수정

This commit is contained in:
kjs 2025-09-03 11:55:38 +09:00
parent f2bdf5356a
commit 55a7e1dc89
1 changed files with 97 additions and 181 deletions

View File

@ -2,10 +2,8 @@
import { useEffect, useState } from "react";
import { useParams } from "next/navigation";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { ArrowLeft, Save, Loader2 } from "lucide-react";
import { Loader2 } from "lucide-react";
import { screenApi } from "@/lib/api/screen";
import { ScreenDefinition, LayoutData } from "@/types/screen";
import { InteractiveScreenViewer } from "@/components/screen/InteractiveScreenViewer";
@ -21,7 +19,6 @@ export default function ScreenViewPage() {
const [layout, setLayout] = useState<LayoutData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [saving, setSaving] = useState(false);
const [formData, setFormData] = useState<Record<string, any>>({});
useEffect(() => {
@ -59,30 +56,9 @@ export default function ScreenViewPage() {
}
}, [screenId]);
// 폼 데이터 저장 함수
const handleSaveData = async () => {
if (!screen) return;
try {
setSaving(true);
console.log("저장할 데이터:", formData);
console.log("화면 정보:", screen);
// 여기에 실제 데이터 저장 API 호출을 추가할 수 있습니다
// await saveFormData(screen.tableName, formData);
toast.success("데이터가 성공적으로 저장되었습니다.");
} catch (error) {
console.error("데이터 저장 실패:", error);
toast.error("데이터 저장에 실패했습니다.");
} finally {
setSaving(false);
}
};
if (loading) {
return (
<div className="flex h-full items-center justify-center">
<div className="flex h-screen w-screen items-center justify-center bg-white">
<div className="text-center">
<Loader2 className="mx-auto h-8 w-8 animate-spin text-blue-600" />
<p className="mt-2 text-gray-600"> ...</p>
@ -93,7 +69,7 @@ export default function ScreenViewPage() {
if (error || !screen) {
return (
<div className="flex h-full items-center justify-center">
<div className="flex h-screen w-screen items-center justify-center bg-white">
<div className="text-center">
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-100">
<span className="text-2xl"></span>
@ -101,7 +77,6 @@ export default function ScreenViewPage() {
<h2 className="mb-2 text-xl font-semibold text-gray-900"> </h2>
<p className="mb-4 text-gray-600">{error || "요청하신 화면이 존재하지 않습니다."}</p>
<Button onClick={() => router.back()} variant="outline">
<ArrowLeft className="mr-2 h-4 w-4" />
</Button>
</div>
@ -110,167 +85,108 @@ export default function ScreenViewPage() {
}
return (
<div className="flex h-full w-full flex-col">
{/* 헤더 */}
<div className="flex items-center justify-between border-b bg-white p-4 shadow-sm">
<div className="flex items-center space-x-4">
<Button variant="outline" size="sm" onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
</Button>
<div>
<h1 className="text-xl font-semibold text-gray-900">{screen.screenName}</h1>
<div className="mt-1 flex items-center space-x-2">
<Badge variant="outline" className="font-mono text-xs">
{screen.screenCode}
</Badge>
<Badge variant="secondary" className="text-xs">
{screen.tableName}
</Badge>
<Badge variant={screen.isActive === "Y" ? "default" : "secondary"} className="text-xs">
{screen.isActive === "Y" ? "활성" : "비활성"}
</Badge>
</div>
</div>
</div>
<div className="flex items-center space-x-2">
<span className="text-sm text-gray-500">: {screen.createdDate.toLocaleDateString()}</span>
</div>
</div>
<div className="h-screen w-screen bg-white">
{layout && layout.components.length > 0 ? (
// 캔버스 컴포넌트들만 표시 - 전체 화면 사용
<div className="relative h-full w-full">
{layout.components
.filter((comp) => !comp.parentId) // 최상위 컴포넌트만 렌더링 (그룹 포함)
.map((component) => {
// 그룹 컴포넌트인 경우 특별 처리
if (component.type === "group") {
const groupChildren = layout.components.filter((child) => child.parentId === component.id);
{/* 메인 컨텐츠 영역 */}
<div className="flex-1 overflow-hidden">
{layout && layout.components.length > 0 ? (
<div className="h-full p-6">
<Card className="h-full">
<CardHeader>
<CardTitle className="flex items-center justify-between">
<span>{screen.screenName}</span>
<Button
size="sm"
className="bg-blue-600 hover:bg-blue-700"
onClick={handleSaveData}
disabled={saving}
return (
<div
key={component.id}
style={{
position: "absolute",
left: `${component.position.x}px`,
top: `${component.position.y}px`,
width: `${component.size.width}px`,
height: `${component.size.height}px`,
zIndex: component.position.z || 1,
backgroundColor: (component as any).backgroundColor || "rgba(59, 130, 246, 0.1)",
border: (component as any).border || "2px dashed #3b82f6",
borderRadius: (component as any).borderRadius || "8px",
padding: "16px",
}}
>
{saving ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
...
</>
) : (
<>
<Save className="mr-2 h-4 w-4" />
</>
{/* 그룹 제목 */}
{(component as any).title && (
<div className="mb-2 text-sm font-medium text-blue-700">{(component as any).title}</div>
)}
</Button>
</CardTitle>
{screen.description && <p className="text-sm text-gray-600">{screen.description}</p>}
</CardHeader>
<CardContent className="h-[calc(100%-5rem)] overflow-auto">
{/* 실제 화면 렌더링 영역 */}
<div className="relative h-full w-full bg-white">
{layout.components
.filter((comp) => !comp.parentId) // 최상위 컴포넌트만 렌더링 (그룹 포함)
.map((component) => {
// 그룹 컴포넌트인 경우 특별 처리
if (component.type === "group") {
const groupChildren = layout.components.filter((child) => child.parentId === component.id);
return (
<div
key={component.id}
style={{
position: "absolute",
left: `${component.position.x}px`,
top: `${component.position.y}px`,
width: `${component.size.width}px`,
height: `${component.size.height}px`,
zIndex: component.position.z || 1,
backgroundColor: (component as any).backgroundColor || "rgba(59, 130, 246, 0.1)",
border: (component as any).border || "2px dashed #3b82f6",
borderRadius: (component as any).borderRadius || "8px",
padding: "16px",
}}
>
{/* 그룹 제목 */}
{(component as any).title && (
<div className="mb-2 text-sm font-medium text-blue-700">{(component as any).title}</div>
)}
{/* 그룹 내 자식 컴포넌트들 렌더링 */}
{groupChildren.map((child) => (
<div
key={child.id}
style={{
position: "absolute",
left: `${child.position.x}px`,
top: `${child.position.y}px`,
width: `${child.size.width}px`,
height: `${child.size.height}px`,
zIndex: child.position.z || 1,
}}
>
<InteractiveScreenViewer
component={child}
allComponents={layout.components}
formData={formData}
onFormDataChange={(fieldName, value) => {
setFormData((prev) => ({
...prev,
[fieldName]: value,
}));
}}
/>
</div>
))}
</div>
);
}
// 일반 컴포넌트 렌더링
return (
<div
key={component.id}
style={{
position: "absolute",
left: `${component.position.x}px`,
top: `${component.position.y}px`,
width: `${component.size.width}px`,
height: `${component.size.height}px`,
zIndex: component.position.z || 1,
{/* 그룹 내 자식 컴포넌트들 렌더링 */}
{groupChildren.map((child) => (
<div
key={child.id}
style={{
position: "absolute",
left: `${child.position.x}px`,
top: `${child.position.y}px`,
width: `${child.size.width}px`,
height: `${child.size.height}px`,
zIndex: child.position.z || 1,
}}
>
<InteractiveScreenViewer
component={child}
allComponents={layout.components}
formData={formData}
onFormDataChange={(fieldName, value) => {
setFormData((prev) => ({
...prev,
[fieldName]: value,
}));
}}
>
<InteractiveScreenViewer
component={component}
allComponents={layout.components}
formData={formData}
onFormDataChange={(fieldName, value) => {
setFormData((prev) => ({
...prev,
[fieldName]: value,
}));
}}
/>
</div>
);
})}
/>
</div>
))}
</div>
);
}
// 일반 컴포넌트 렌더링
return (
<div
key={component.id}
style={{
position: "absolute",
left: `${component.position.x}px`,
top: `${component.position.y}px`,
width: `${component.size.width}px`,
height: `${component.size.height}px`,
zIndex: component.position.z || 1,
}}
>
<InteractiveScreenViewer
component={component}
allComponents={layout.components}
formData={formData}
onFormDataChange={(fieldName, value) => {
setFormData((prev) => ({
...prev,
[fieldName]: value,
}));
}}
/>
</div>
</CardContent>
</Card>
</div>
) : (
<div className="flex h-full items-center justify-center">
<div className="text-center">
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gray-100">
<span className="text-2xl">📄</span>
</div>
<h2 className="mb-2 text-xl font-semibold text-gray-900"> </h2>
<p className="text-gray-600"> .</p>
);
})}
</div>
) : (
// 빈 화면일 때도 깔끔하게 표시
<div className="flex h-full items-center justify-center bg-gray-50">
<div className="text-center">
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-white shadow-sm">
<span className="text-2xl">📄</span>
</div>
<h2 className="mb-2 text-xl font-semibold text-gray-900"> </h2>
<p className="text-gray-600"> .</p>
</div>
)}
</div>
</div>
)}
</div>
);
}