ERP-node/frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx

199 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Plus, Check } from "lucide-react";
import YardLayoutList from "./yard-3d/YardLayoutList";
import YardLayoutCreateModal from "./yard-3d/YardLayoutCreateModal";
import YardEditor from "./yard-3d/YardEditor";
import Yard3DViewer from "./yard-3d/Yard3DViewer";
import { yardLayoutApi } from "@/lib/api/yardLayoutApi";
import type { YardManagementConfig } from "../types";
interface YardLayout {
id: number;
name: string;
description: string;
placement_count: number;
updated_at: string;
}
interface YardManagement3DWidgetProps {
isEditMode?: boolean;
config?: YardManagementConfig;
onConfigChange?: (config: YardManagementConfig) => void;
}
export default function YardManagement3DWidget({
isEditMode = false,
config,
onConfigChange,
}: YardManagement3DWidgetProps) {
const [layouts, setLayouts] = useState<YardLayout[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [editingLayout, setEditingLayout] = useState<YardLayout | null>(null);
// 레이아웃 목록 로드
const loadLayouts = async () => {
try {
setIsLoading(true);
const response = await yardLayoutApi.getAllLayouts();
if (response.success) {
setLayouts(response.data);
}
} catch (error) {
console.error("야드 레이아웃 목록 조회 실패:", error);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
if (isEditMode) {
loadLayouts();
}
}, [isEditMode]);
// 레이아웃 선택 (편집 모드에서만)
const handleSelectLayout = (layout: YardLayout) => {
if (onConfigChange) {
onConfigChange({
layoutId: layout.id,
layoutName: layout.name,
});
}
};
// 새 레이아웃 생성
const handleCreateLayout = async (name: string) => {
try {
const response = await yardLayoutApi.createLayout({ name });
if (response.success) {
await loadLayouts();
setIsCreateModalOpen(false);
setEditingLayout(response.data);
}
} catch (error) {
console.error("야드 레이아웃 생성 실패:", error);
throw error;
}
};
// 편집 완료
const handleEditComplete = () => {
if (editingLayout && onConfigChange) {
onConfigChange({
layoutId: editingLayout.id,
layoutName: editingLayout.name,
});
}
setEditingLayout(null);
loadLayouts();
};
// 편집 모드: 편집 중인 경우 YardEditor 표시
if (isEditMode && editingLayout) {
return (
<div className="h-full w-full">
<YardEditor layout={editingLayout} onBack={handleEditComplete} />
</div>
);
}
// 편집 모드: 레이아웃 선택 UI
if (isEditMode) {
return (
<div className="widget-interactive-area flex h-full w-full flex-col bg-white">
<div className="flex items-center justify-between border-b p-4">
<div>
<h3 className="text-sm font-semibold text-gray-700"> </h3>
<p className="mt-1 text-xs text-gray-500">
{config?.layoutName ? `선택됨: ${config.layoutName}` : "표시할 야드 레이아웃을 선택하세요"}
</p>
</div>
<Button onClick={() => setIsCreateModalOpen(true)} size="sm">
<Plus className="mr-1 h-4 w-4" />
</Button>
</div>
<div className="flex-1 overflow-auto p-4">
{isLoading ? (
<div className="flex h-full items-center justify-center">
<div className="text-sm text-gray-500"> ...</div>
</div>
) : layouts.length === 0 ? (
<div className="flex h-full items-center justify-center">
<div className="text-center">
<div className="mb-2 text-4xl">🏗</div>
<div className="text-sm text-gray-600"> </div>
<div className="mt-1 text-xs text-gray-400"> </div>
</div>
</div>
) : (
<div className="grid gap-3">
{layouts.map((layout) => (
<div
key={layout.id}
className={`rounded-lg border p-3 transition-all ${
config?.layoutId === layout.id ? "border-blue-500 bg-blue-50" : "border-gray-200 bg-white"
}`}
>
<div className="flex items-start justify-between gap-3">
<button onClick={() => handleSelectLayout(layout)} className="flex-1 text-left hover:opacity-80">
<div className="flex items-center gap-2">
<span className="font-medium text-gray-900">{layout.name}</span>
{config?.layoutId === layout.id && <Check className="h-4 w-4 text-blue-600" />}
</div>
{layout.description && <p className="mt-1 text-xs text-gray-500">{layout.description}</p>}
<div className="mt-2 text-xs text-gray-400"> : {layout.placement_count}</div>
</button>
<Button
variant="outline"
size="sm"
onClick={(e) => {
e.stopPropagation();
setEditingLayout(layout);
}}
>
</Button>
</div>
</div>
))}
</div>
)}
</div>
{/* 생성 모달 */}
<YardLayoutCreateModal
isOpen={isCreateModalOpen}
onClose={() => setIsCreateModalOpen(false)}
onCreate={handleCreateLayout}
/>
</div>
);
}
// 뷰 모드: 선택된 레이아웃의 3D 뷰어 표시
if (!config?.layoutId) {
return (
<div className="flex h-full w-full items-center justify-center bg-gray-50">
<div className="text-center">
<div className="mb-2 text-4xl">🏗</div>
<div className="text-sm font-medium text-gray-600"> </div>
<div className="mt-1 text-xs text-gray-400"> </div>
</div>
</div>
);
}
// 선택된 레이아웃의 3D 뷰어 표시
return (
<div className="h-full w-full">
<Yard3DViewer layoutId={config.layoutId} />
</div>
);
}