import { useState, useEffect, useRef } from 'react'; import { mapItemService } from '../../services/mapItemService'; interface ItemContextMenuProps { item: any; position: { x: number; y: number }; onClose: () => void; onUpdate: () => void; } export function ItemContextMenu({ item, position, onClose, onUpdate }: ItemContextMenuProps) { const [showRenameDialog, setShowRenameDialog] = useState(false); const [showNotesDialog, setShowNotesDialog] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [showPortConfigDialog, setShowPortConfigDialog] = useState(false); const [newName, setNewName] = useState(item.properties.name || ''); const [notes, setNotes] = useState(item.properties.notes || ''); const [imageData, setImageData] = useState(item.properties.image || null); const [portCount, setPortCount] = useState(item.properties.port_count || 5); const [deleteConnectedCables, setDeleteConnectedCables] = useState(false); const menuRef = useRef(null); const fileInputRef = useRef(null); const isSwitch = item.type === 'switch'; const isDevice = ['switch', 'indoor_ap', 'outdoor_ap'].includes(item.type); const hasConnections = item.properties.connections && item.properties.connections.length > 0; useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(e.target as Node)) { onClose(); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, [onClose]); const handleDelete = async () => { try { // If device with connections and user wants to delete connected cables if (isDevice && deleteConnectedCables && hasConnections) { // First delete all connected cables const { mapItemService: itemService } = await import('../../services/mapItemService'); const allItems = await itemService.getMapItems(item.map_id); // Find all cables connected to this device const connectedCableIds = item.properties.connections.map((conn: any) => conn.cable_id); const cablesToDelete = allItems.filter((i: any) => i.type === 'cable' && connectedCableIds.includes(i.id) ); // Delete each cable for (const cable of cablesToDelete) { await itemService.deleteMapItem(item.map_id, cable.id); } } // Delete the device/item itself await mapItemService.deleteMapItem(item.map_id, item.id); onUpdate(); onClose(); } catch (error) { console.error('Failed to delete item:', error); alert('Failed to delete item'); } }; const handleRename = async () => { try { await mapItemService.updateMapItem(item.map_id, item.id, { properties: { ...item.properties, name: newName, }, }); onUpdate(); setShowRenameDialog(false); onClose(); } catch (error) { console.error('Failed to rename item:', error); alert('Failed to rename item'); } }; const handleImageUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; // Check file size (max 2MB) if (file.size > 2 * 1024 * 1024) { alert('Image too large. Please use an image smaller than 2MB.'); return; } // Check file type if (!file.type.startsWith('image/')) { alert('Please select an image file.'); return; } const reader = new FileReader(); reader.onload = () => { setImageData(reader.result as string); }; reader.readAsDataURL(file); }; const handleRemoveImage = () => { setImageData(null); if (fileInputRef.current) { fileInputRef.current.value = ''; } }; const handleSaveNotes = async () => { try { await mapItemService.updateMapItem(item.map_id, item.id, { properties: { ...item.properties, notes: notes, image: imageData, }, }); onUpdate(); setShowNotesDialog(false); onClose(); } catch (error) { console.error('Failed to save notes:', error); alert('Failed to save notes'); } }; const handleSavePortConfig = async () => { try { await mapItemService.updateMapItem(item.map_id, item.id, { properties: { ...item.properties, port_count: portCount, }, }); onUpdate(); setShowPortConfigDialog(false); onClose(); } catch (error) { console.error('Failed to save port configuration:', error); alert('Failed to save port configuration'); } }; if (showPortConfigDialog) { return (

Configure Ports

setPortCount(parseInt(e.target.value) || 1)} className="w-full px-3 py-2 border border-gray-300 rounded mb-3" autoFocus onKeyDown={(e) => { if (e.key === 'Enter') handleSavePortConfig(); if (e.key === 'Escape') { setShowPortConfigDialog(false); onClose(); } }} />
Currently used: {item.properties.connections?.length || 0} ports
); } if (showDeleteDialog) { return (

Delete Item

Are you sure you want to delete {item.properties.name || item.type}? {item.type === 'cable' && item.properties.start_device_id && ( This will also remove the connection from the connected devices. )}

{/* Checkbox for deleting connected cables */} {isDevice && hasConnections && ( )}
); } if (showRenameDialog) { return (

Rename Item

setNewName(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded mb-3" placeholder="Enter new name" autoFocus onKeyDown={(e) => { if (e.key === 'Enter') handleRename(); if (e.key === 'Escape') { setShowRenameDialog(false); onClose(); } }} />
); } if (showNotesDialog) { return (

Edit Notes