ERP-node/frontend/components/admin/dashboard/widgets/yard-3d/Yard3DViewer.tsx

156 lines
4.8 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.

This file contains Unicode characters that might be confused with other characters. 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 Yard3DCanvas from "./Yard3DCanvas";
import { yardLayoutApi } from "@/lib/api/yardLayoutApi";
import { Loader2 } from "lucide-react";
interface YardPlacement {
id: number;
yard_layout_id: number;
external_material_id: string;
material_code: string;
material_name: string;
quantity: number;
unit: string;
position_x: number;
position_y: number;
position_z: number;
size_x: number;
size_y: number;
size_z: number;
color: string;
status?: string;
memo?: string;
}
interface Yard3DViewerProps {
layoutId: number;
}
export default function Yard3DViewer({ layoutId }: Yard3DViewerProps) {
const [placements, setPlacements] = useState<YardPlacement[]>([]);
const [selectedPlacement, setSelectedPlacement] = useState<YardPlacement | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// 배치 데이터 로드
useEffect(() => {
const loadPlacements = async () => {
try {
setIsLoading(true);
setError(null);
const response = await yardLayoutApi.getPlacementsByLayoutId(layoutId);
if (response.success) {
setPlacements(response.data);
} else {
setError("배치 데이터를 불러올 수 없습니다.");
}
} catch (err) {
console.error("배치 데이터 로드 실패:", err);
setError("배치 데이터를 불러오는 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
loadPlacements();
}, [layoutId]);
if (isLoading) {
return (
<div className="flex h-full w-full items-center justify-center bg-gray-50">
<div className="text-center">
<Loader2 className="mx-auto h-8 w-8 animate-spin text-blue-600" />
<div className="mt-2 text-sm text-gray-600">3D ...</div>
</div>
</div>
);
}
if (error) {
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">{error}</div>
</div>
</div>
);
}
if (placements.length === 0) {
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>
</div>
);
}
return (
<div className="flex h-full w-full">
{/* 3D 캔버스 */}
<div className="flex-1">
<Yard3DCanvas
placements={placements}
selectedPlacementId={selectedPlacement?.id || null}
onPlacementClick={setSelectedPlacement}
/>
</div>
{/* 선택된 자재 정보 패널 (우측) */}
{selectedPlacement && (
<div className="w-80 border-l bg-white p-4">
<div className="mb-4">
<h3 className="text-sm font-semibold text-gray-700"> </h3>
</div>
<div className="space-y-3">
<div>
<label className="text-xs text-gray-500"> </label>
<div className="mt-1 text-sm font-medium">{selectedPlacement.material_code}</div>
</div>
<div>
<label className="text-xs text-gray-500"></label>
<div className="mt-1 text-sm font-medium">{selectedPlacement.material_name}</div>
</div>
<div>
<label className="text-xs text-gray-500"></label>
<div className="mt-1 text-sm">
{selectedPlacement.quantity} {selectedPlacement.unit}
</div>
</div>
<div>
<label className="text-xs text-gray-500"> (X, Y, Z)</label>
<div className="mt-1 text-sm">
({selectedPlacement.position_x.toFixed(1)}, {selectedPlacement.position_y.toFixed(1)},{" "}
{selectedPlacement.position_z.toFixed(1)})
</div>
</div>
<div>
<label className="text-xs text-gray-500"> (W × H × D)</label>
<div className="mt-1 text-sm">
{selectedPlacement.size_x} × {selectedPlacement.size_y} × {selectedPlacement.size_z}
</div>
</div>
{selectedPlacement.memo && (
<div>
<label className="text-xs text-gray-500"></label>
<div className="mt-1 text-sm text-gray-700">{selectedPlacement.memo}</div>
</div>
)}
</div>
</div>
)}
</div>
);
}