"use client"; import { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { ArrowLeft, Save, Loader2, Plus, Settings, Trash2 } from "lucide-react"; import { yardLayoutApi } from "@/lib/api/yardLayoutApi"; import dynamic from "next/dynamic"; import { YardLayout, YardPlacement } from "./types"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; const Yard3DCanvas = dynamic(() => import("./Yard3DCanvas"), { ssr: false, loading: () => (
), }); // 나중에 구현할 데이터 바인딩 패널 const YardElementConfigPanel = dynamic(() => import("./YardElementConfigPanel"), { ssr: false, loading: () =>
로딩 중...
, }); interface YardEditorProps { layout: YardLayout; onBack: () => void; } export default function YardEditor({ layout, onBack }: YardEditorProps) { const [placements, setPlacements] = useState([]); const [selectedPlacement, setSelectedPlacement] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [showConfigPanel, setShowConfigPanel] = useState(false); const [error, setError] = useState(null); // 배치 목록 로드 useEffect(() => { const loadPlacements = async () => { try { setIsLoading(true); const response = await yardLayoutApi.getPlacementsByLayoutId(layout.id); if (response.success) { setPlacements(response.data as YardPlacement[]); } } catch (error) { console.error("배치 목록 로드 실패:", error); setError("배치 목록을 불러올 수 없습니다."); } finally { setIsLoading(false); } }; loadPlacements(); }, [layout.id]); // 빈 요소 추가 const handleAddElement = async () => { try { const newPlacementData = { position_x: 0, position_y: 0, position_z: 0, size_x: 5, size_y: 5, size_z: 5, color: "#9ca3af", // 회색 (미설정 상태) }; console.log("요소 추가 요청:", { layoutId: layout.id, data: newPlacementData }); const response = await yardLayoutApi.addMaterialPlacement(layout.id, newPlacementData); console.log("요소 추가 응답:", response); if (response.success) { const newPlacement = response.data as YardPlacement; setPlacements((prev) => [...prev, newPlacement]); setSelectedPlacement(newPlacement); setShowConfigPanel(true); // 자동으로 설정 패널 표시 } else { console.error("요소 추가 실패 (응답):", response); setError(response.message || "요소 추가에 실패했습니다."); } } catch (error) { console.error("요소 추가 실패 (예외):", error); setError(`요소 추가에 실패했습니다: ${error instanceof Error ? error.message : String(error)}`); } }; // 요소 선택 (3D 캔버스 또는 목록에서) const handleSelectPlacement = (placement: YardPlacement) => { setSelectedPlacement(placement); setShowConfigPanel(false); // 선택 시에는 설정 패널 닫기 }; // 설정 버튼 클릭 const handleConfigClick = (placement: YardPlacement) => { setSelectedPlacement(placement); setShowConfigPanel(true); }; // 요소 삭제 const handleDeletePlacement = async (placementId: number) => { if (!confirm("이 요소를 삭제하시겠습니까?")) { return; } try { const response = await yardLayoutApi.removePlacement(placementId); if (response.success) { setPlacements((prev) => prev.filter((p) => p.id !== placementId)); if (selectedPlacement?.id === placementId) { setSelectedPlacement(null); setShowConfigPanel(false); } } } catch (error) { console.error("요소 삭제 실패:", error); setError("요소 삭제에 실패했습니다."); } }; // 자재 드래그 (3D 캔버스에서) const handlePlacementDrag = (id: number, position: { x: number; y: number; z: number }) => { const updatedPosition = { position_x: Math.round(position.x * 2) / 2, position_y: position.y, position_z: Math.round(position.z * 2) / 2, }; setPlacements((prev) => prev.map((p) => (p.id === id ? { ...p, ...updatedPosition } : p))); if (selectedPlacement?.id === id) { setSelectedPlacement((prev) => (prev ? { ...prev, ...updatedPosition } : null)); } }; // 저장 const handleSave = async () => { setIsSaving(true); try { const response = await yardLayoutApi.batchUpdatePlacements( layout.id, placements.map((p) => ({ id: p.id, position_x: p.position_x, position_y: p.position_y, position_z: p.position_z, size_x: p.size_x, size_y: p.size_y, size_z: p.size_z, color: p.color, })), ); if (response.success) { alert("저장되었습니다"); } } catch (error) { console.error("저장 실패:", error); alert("저장에 실패했습니다"); } finally { setIsSaving(false); } }; // 설정 패널에서 저장 const handleSaveConfig = async (updatedData: Partial) => { if (!selectedPlacement) return; try { const response = await yardLayoutApi.updatePlacement(selectedPlacement.id, updatedData); if (response.success) { const updated = response.data as YardPlacement; // 현재 위치 정보를 유지하면서 업데이트 setPlacements((prev) => prev.map((p) => { if (p.id === updated.id) { // 현재 프론트엔드 상태의 위치를 유지 return { ...updated, position_x: p.position_x, position_y: p.position_y, position_z: p.position_z, }; } return p; }), ); // 선택된 요소도 동일하게 업데이트 setSelectedPlacement({ ...updated, position_x: selectedPlacement.position_x, position_y: selectedPlacement.position_y, position_z: selectedPlacement.position_z, }); setShowConfigPanel(false); } } catch (error) { console.error("설정 저장 실패:", error); setError("설정 저장에 실패했습니다."); } }; // 요소가 설정되었는지 확인 const isConfigured = (placement: YardPlacement): boolean => { return !!(placement.material_name && placement.quantity && placement.unit); }; return (
{/* 상단 툴바 */}

{layout.name}

{layout.description &&

{layout.description}

}
{/* 에러 메시지 */} {error && ( {error} )} {/* 메인 컨텐츠 영역 */}
{/* 좌측: 3D 캔버스 */}
{isLoading ? (
) : ( handleSelectPlacement(placement as YardPlacement)} onPlacementDrag={handlePlacementDrag} /> )}
{/* 우측: 요소 목록 또는 설정 패널 */}
{showConfigPanel && selectedPlacement ? ( // 설정 패널 setShowConfigPanel(false)} /> ) : ( // 요소 목록

요소 목록

총 {placements.length}개

{placements.length === 0 ? (
요소가 없습니다.
{'위의 "요소 추가" 버튼을 클릭하세요.'}
) : (
{placements.map((placement) => { const configured = isConfigured(placement); const isSelected = selectedPlacement?.id === placement.id; return (
handleSelectPlacement(placement)} >
{configured ? ( <>
{placement.material_name}
수량: {placement.quantity} {placement.unit}
) : ( <>
요소 #{placement.id}
데이터 바인딩 설정 필요
)}
); })}
)}
)}
); }