diff --git a/frontend/components/admin/dashboard/CanvasElement.tsx b/frontend/components/admin/dashboard/CanvasElement.tsx index e294a797..6b54daae 100644 --- a/frontend/components/admin/dashboard/CanvasElement.tsx +++ b/frontend/components/admin/dashboard/CanvasElement.tsx @@ -162,6 +162,11 @@ export function CanvasElement({ // 요소 선택 처리 const handleMouseDown = useCallback( (e: React.MouseEvent) => { + // 모달이나 다이얼로그가 열려있으면 드래그 무시 + if (document.querySelector('[role="dialog"]')) { + return; + } + // 닫기 버튼이나 리사이즈 핸들 클릭 시 무시 if ((e.target as HTMLElement).closest(".element-close, .resize-handle")) { return; @@ -192,6 +197,11 @@ export function CanvasElement({ // 리사이즈 핸들 마우스다운 const handleResizeMouseDown = useCallback( (e: React.MouseEvent, handle: string) => { + // 모달이나 다이얼로그가 열려있으면 리사이즈 무시 + if (document.querySelector('[role="dialog"]')) { + return; + } + e.stopPropagation(); setIsResizing(true); setResizeStart({ @@ -522,16 +532,15 @@ export function CanvasElement({ {element.customTitle || element.title}
{/* 설정 버튼 (기사관리 위젯만 자체 설정 UI 사용) */} - {onConfigure && - !(element.type === "widget" && element.subtype === "driver-management") && ( - - )} + {onConfigure && !(element.type === "widget" && element.subtype === "driver-management") && ( + + )} {/* 삭제 버튼 */} - - - + + +
@@ -315,13 +299,6 @@ ORDER BY Q4 DESC;`, )} - - {/* 키보드 단축키 안내 */} - -
- 단축키: Ctrl+Enter (쿼리 실행), Ctrl+/ (주석 토글) -
-
); } diff --git a/frontend/components/admin/dashboard/data-sources/DatabaseConfig.tsx b/frontend/components/admin/dashboard/data-sources/DatabaseConfig.tsx index 31e6ff7d..ff190184 100644 --- a/frontend/components/admin/dashboard/data-sources/DatabaseConfig.tsx +++ b/frontend/components/admin/dashboard/data-sources/DatabaseConfig.tsx @@ -150,7 +150,7 @@ export function DatabaseConfig({ dataSource, onChange }: DatabaseConfigProps) { - + {connections.map((conn) => (
diff --git a/frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx b/frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx index 2ba2e697..09c08dac 100644 --- a/frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx +++ b/frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx @@ -67,9 +67,9 @@ export default function YardManagement3DWidget({ }; // 새 레이아웃 생성 - const handleCreateLayout = async (name: string, description: string) => { + const handleCreateLayout = async (name: string) => { try { - const response = await yardLayoutApi.createLayout({ name, description }); + const response = await yardLayoutApi.createLayout({ name }); if (response.success) { await loadLayouts(); setIsCreateModalOpen(false); @@ -105,7 +105,7 @@ export default function YardManagement3DWidget({ // 편집 모드: 레이아웃 선택 UI if (isEditMode) { return ( -
+

야드 레이아웃 선택

diff --git a/frontend/components/admin/dashboard/widgets/yard-3d/MaterialEditPanel.tsx b/frontend/components/admin/dashboard/widgets/yard-3d/MaterialEditPanel.tsx index d2388711..a909a195 100644 --- a/frontend/components/admin/dashboard/widgets/yard-3d/MaterialEditPanel.tsx +++ b/frontend/components/admin/dashboard/widgets/yard-3d/MaterialEditPanel.tsx @@ -49,9 +49,6 @@ export default function MaterialEditPanel({ placement, onClose, onUpdate, onRemo useEffect(() => { if (placement) { setEditData({ - position_x: placement.position_x, - position_y: placement.position_y, - position_z: placement.position_z, size_x: placement.size_x, size_y: placement.size_y, size_z: placement.size_z, @@ -107,52 +104,6 @@ export default function MaterialEditPanel({ placement, onClose, onUpdate, onRemo
배치 정보 (편집 가능)
- {/* 3D 위치 */} -
- -
-
- - setEditData({ ...editData, position_x: parseFloat(e.target.value) || 0 })} - step="0.5" - className="h-8 text-xs" - /> -
-
- - setEditData({ ...editData, position_y: parseFloat(e.target.value) || 0 })} - step="0.5" - className="h-8 text-xs" - /> -
-
- - setEditData({ ...editData, position_z: parseFloat(e.target.value) || 0 })} - step="0.5" - className="h-8 text-xs" - /> -
-
-
- {/* 3D 크기 */}
@@ -167,7 +118,7 @@ export default function MaterialEditPanel({ placement, onClose, onUpdate, onRemo value={editData.size_x ?? placement.size_x} onChange={(e) => setEditData({ ...editData, size_x: parseFloat(e.target.value) || 1 })} min="1" - step="0.5" + step="1" className="h-8 text-xs" />
@@ -181,7 +132,7 @@ export default function MaterialEditPanel({ placement, onClose, onUpdate, onRemo value={editData.size_y ?? placement.size_y} onChange={(e) => setEditData({ ...editData, size_y: parseFloat(e.target.value) || 1 })} min="1" - step="0.5" + step="1" className="h-8 text-xs" />
@@ -195,7 +146,7 @@ export default function MaterialEditPanel({ placement, onClose, onUpdate, onRemo value={editData.size_z ?? placement.size_z} onChange={(e) => setEditData({ ...editData, size_z: parseFloat(e.target.value) || 1 })} min="1" - step="0.5" + step="1" className="h-8 text-xs" />
diff --git a/frontend/components/admin/dashboard/widgets/yard-3d/YardEditor.tsx b/frontend/components/admin/dashboard/widgets/yard-3d/YardEditor.tsx index 1d93f6a9..8dd82e5d 100644 --- a/frontend/components/admin/dashboard/widgets/yard-3d/YardEditor.tsx +++ b/frontend/components/admin/dashboard/widgets/yard-3d/YardEditor.tsx @@ -62,10 +62,15 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { const [placements, setPlacements] = useState([]); const [materials, setMaterials] = useState([]); const [selectedPlacement, setSelectedPlacement] = useState(null); - const [selectedMaterial, setSelectedMaterial] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [searchTerm, setSearchTerm] = useState(""); + const [editValues, setEditValues] = useState({ + size_x: 5, + size_y: 5, + size_z: 5, + color: "#3b82f6", + }); // 배치 목록 & 자재 목록 로드 useEffect(() => { @@ -78,10 +83,10 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { ]); if (placementsRes.success) { - setPlacements(placementsRes.data); + setPlacements(placementsRes.data as YardPlacement[]); } if (materialsRes.success) { - setMaterials(materialsRes.data); + setMaterials(materialsRes.data as TempMaterial[]); } } catch (error) { console.error("데이터 로드 실패:", error); @@ -93,6 +98,18 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { loadData(); }, [layout.id]); + // selectedPlacement 변경 시 editValues 업데이트 + useEffect(() => { + if (selectedPlacement) { + setEditValues({ + size_x: selectedPlacement.size_x, + size_y: selectedPlacement.size_y, + size_z: selectedPlacement.size_z, + color: selectedPlacement.color, + }); + } + }, [selectedPlacement]); + // 자재 클릭 → 배치 추가 const handleMaterialClick = async (material: TempMaterial) => { // 이미 배치되었는지 확인 @@ -102,8 +119,6 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { return; } - setSelectedMaterial(material); - // 기본 위치에 배치 const placementData = { external_material_id: `TEMP-${material.id}`, @@ -123,11 +138,10 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { try { const response = await yardLayoutApi.addMaterialPlacement(layout.id, placementData); if (response.success) { - setPlacements((prev) => [...prev, response.data]); - setSelectedPlacement(response.data); - setSelectedMaterial(null); + setPlacements((prev) => [...prev, response.data as YardPlacement]); + setSelectedPlacement(response.data as YardPlacement); } - } catch (error: any) { + } catch (error) { console.error("자재 배치 실패:", error); alert("자재 배치에 실패했습니다."); } @@ -166,7 +180,7 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { }; // 자재 배치 해제 - const handlePlacementRemove = async (id: number) => { + const handlePlacementRemove = async (id: number): Promise => { try { const response = await yardLayoutApi.removePlacement(id); if (response.success) { @@ -262,7 +276,7 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { setSelectedPlacement(placement as YardPlacement)} onPlacementDrag={handlePlacementDrag} /> )} @@ -304,60 +318,18 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) {
-
-
- - - handlePlacementUpdate(selectedPlacement.id, { - position_x: parseFloat(e.target.value), - }) - } - /> -
-
- - - handlePlacementUpdate(selectedPlacement.id, { - position_y: parseFloat(e.target.value), - }) - } - /> -
-
- - - handlePlacementUpdate(selectedPlacement.id, { - position_z: parseFloat(e.target.value), - }) - } - /> -
-
-
- handlePlacementUpdate(selectedPlacement.id, { - size_x: parseFloat(e.target.value), - }) - } + value={editValues.size_x} + onChange={(e) => { + const newValue = parseFloat(e.target.value) || 1; + setEditValues((prev) => ({ ...prev, size_x: newValue })); + handlePlacementUpdate(selectedPlacement.id, { size_x: newValue }); + }} />
@@ -365,12 +337,12 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { - handlePlacementUpdate(selectedPlacement.id, { - size_y: parseFloat(e.target.value), - }) - } + value={editValues.size_y} + onChange={(e) => { + const newValue = parseFloat(e.target.value) || 1; + setEditValues((prev) => ({ ...prev, size_y: newValue })); + handlePlacementUpdate(selectedPlacement.id, { size_y: newValue }); + }} />
@@ -378,12 +350,12 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { - handlePlacementUpdate(selectedPlacement.id, { - size_z: parseFloat(e.target.value), - }) - } + value={editValues.size_z} + onChange={(e) => { + const newValue = parseFloat(e.target.value) || 1; + setEditValues((prev) => ({ ...prev, size_z: newValue })); + handlePlacementUpdate(selectedPlacement.id, { size_z: newValue }); + }} />
@@ -392,8 +364,11 @@ export default function YardEditor({ layout, onBack }: YardEditorProps) { handlePlacementUpdate(selectedPlacement.id, { color: e.target.value })} + value={editValues.color} + onChange={(e) => { + setEditValues((prev) => ({ ...prev, color: e.target.value })); + handlePlacementUpdate(selectedPlacement.id, { color: e.target.value }); + }} />
diff --git a/frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx b/frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx index 14514f9f..e14472b7 100644 --- a/frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx +++ b/frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx @@ -12,18 +12,17 @@ import { } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; -import { Loader2 } from "lucide-react"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Loader2, AlertCircle } from "lucide-react"; interface YardLayoutCreateModalProps { isOpen: boolean; onClose: () => void; - onCreate: (name: string, description: string) => Promise; + onCreate: (name: string) => Promise; } export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: YardLayoutCreateModalProps) { const [name, setName] = useState(""); - const [description, setDescription] = useState(""); const [isCreating, setIsCreating] = useState(false); const [error, setError] = useState(""); @@ -38,9 +37,8 @@ export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: Yar setError(""); try { - await onCreate(name.trim(), description.trim()); + await onCreate(name.trim()); setName(""); - setDescription(""); } catch (error: any) { console.error("야드 생성 실패:", error); setError(error.message || "야드 생성에 실패했습니다"); @@ -53,7 +51,6 @@ export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: Yar const handleClose = () => { if (isCreating) return; setName(""); - setDescription(""); setError(""); onClose(); }; @@ -68,14 +65,13 @@ export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: Yar return ( - + e.stopPropagation()}> 새 야드 생성 - 야드의 이름과 설명을 입력하세요 + 야드 이름을 입력하세요
- {/* 야드 이름 */}
- {/* 설명 */} -
- -