diff --git a/public/src/components/map/DrawingHandler.tsx b/public/src/components/map/DrawingHandler.tsx index 76ab672..25f4e4b 100644 --- a/public/src/components/map/DrawingHandler.tsx +++ b/public/src/components/map/DrawingHandler.tsx @@ -21,7 +21,7 @@ export function DrawingHandler({ mapId, onItemCreated }: DrawingHandlerProps) { const [endDeviceId, setEndDeviceId] = useState(null); const isCableTool = ['fiber', 'cat6', 'cat6_poe'].includes(activeTool); - const isDeviceTool = ['switch', 'indoor_ap', 'outdoor_ap'].includes(activeTool); + const isDeviceTool = ['switch', 'indoor_ap', 'outdoor_ap', 'info'].includes(activeTool); const isWirelessTool = activeTool === 'wireless_mesh'; // Load all map items for snapping @@ -37,7 +37,7 @@ export function DrawingHandler({ mapId, onItemCreated }: DrawingHandlerProps) { loadItems(); }, [mapId]); - // Find nearby device for snapping + // Find nearby device for snapping (exclude info markers) const findNearbyDevice = (lat: number, lng: number, radiusMeters = 5): any | null => { const devices = allItems.filter(item => ['switch', 'indoor_ap', 'outdoor_ap'].includes(item.type) && @@ -109,17 +109,25 @@ export function DrawingHandler({ mapId, onItemCreated }: DrawingHandlerProps) { // Device placement - single click if (isDeviceTool) { try { + const properties: any = { + name: activeTool === 'info' + ? `Info at ${lat.toFixed(4)}, ${lng.toFixed(4)}` + : `${activeTool} at ${lat.toFixed(4)}, ${lng.toFixed(4)}`, + }; + + // Only add port_count and connections if it's not an info marker + if (activeTool !== 'info') { + properties.port_count = activeTool === 'switch' ? 5 : activeTool === 'outdoor_ap' ? 1 : 4; + properties.connections = []; + } + await mapItemService.createMapItem(mapId, { type: activeTool as any, geometry: { type: 'Point', coordinates: [lng, lat], }, - properties: { - name: `${activeTool} at ${lat.toFixed(4)}, ${lng.toFixed(4)}`, - port_count: activeTool === 'switch' ? 5 : activeTool === 'outdoor_ap' ? 1 : 4, - connections: [], - }, + properties, }); onItemCreated(); // Reload items for snapping diff --git a/public/src/components/map/MapItemsLayer.tsx b/public/src/components/map/MapItemsLayer.tsx index c966d46..c253647 100644 --- a/public/src/components/map/MapItemsLayer.tsx +++ b/public/src/components/map/MapItemsLayer.tsx @@ -86,6 +86,19 @@ const outdoorApIcon = new L.DivIcon({ iconAnchor: [20, 40], }); +const infoIcon = new L.DivIcon({ + html: `
+ + + + + +
`, + className: 'custom-info-marker', + iconSize: [20, 20], + iconAnchor: [10, 20], +}); + export function MapItemsLayer({ mapId, refreshTrigger, readOnly = false }: MapItemsLayerProps) { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); @@ -137,6 +150,8 @@ export function MapItemsLayer({ mapId, refreshTrigger, readOnly = false }: MapIt return indoorApIcon; case 'outdoor_ap': return outdoorApIcon; + case 'info': + return infoIcon; default: return undefined; } @@ -343,9 +358,9 @@ export function MapItemsLayer({ mapId, refreshTrigger, readOnly = false }: MapIt ); } - // Render devices + // Render devices and info markers if ( - ['switch', 'indoor_ap', 'outdoor_ap'].includes(item.type) && + ['switch', 'indoor_ap', 'outdoor_ap', 'info'].includes(item.type) && item.geometry.type === 'Point' ) { const [lng, lat] = item.geometry.coordinates; @@ -373,7 +388,9 @@ export function MapItemsLayer({ mapId, refreshTrigger, readOnly = false }: MapIt
{item.properties.name || item.type}
-
Type: {item.type}
+ {item.type !== 'info' && ( +
Type: {item.type}
+ )} {item.properties.port_count && (
Ports: {item.properties.connections?.length || 0} / {item.properties.port_count} diff --git a/public/src/components/map/Toolbar.tsx b/public/src/components/map/Toolbar.tsx index e794453..a4edd22 100644 --- a/public/src/components/map/Toolbar.tsx +++ b/public/src/components/map/Toolbar.tsx @@ -74,6 +74,13 @@ const DEVICE_TOOLS: ToolButton[] = [ }, ]; +const INFO_TOOL: ToolButton = { + id: 'info', + label: 'Info', + icon: 'info', + description: 'Information Marker', +}; + export function Toolbar({ mapId, readOnly = false }: ToolbarProps) { const { activeTool, setActiveTool } = useDrawingStore(); @@ -122,6 +129,17 @@ export function Toolbar({ mapId, readOnly = false }: ToolbarProps) { ); } + if (tool.icon === 'info') { + return ( + + + + + + + ); + } + return ( Devices {DEVICE_TOOLS.map(renderToolButton)} + +
+ + {/* Info section */} +

Annotations

+ {renderToolButton(INFO_TOOL)}
); } diff --git a/public/src/index.css b/public/src/index.css index 22bb816..1ab8506 100644 --- a/public/src/index.css +++ b/public/src/index.css @@ -125,6 +125,30 @@ color: #F59E0B; } +/* Info marker icon */ +.custom-info-marker { + background: transparent !important; + border: none !important; +} + +.info-marker-icon { + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + background: white; + border-radius: 50%; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); + border: 2px solid #6366F1; + color: #6366F1; +} + +.dark .info-marker-icon { + background: rgb(31, 41, 55); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.6); +} + /* Distance labels on map */ .distance-label { background-color: rgba(255, 255, 255, 0.95) !important; diff --git a/public/src/pages/Dashboard.tsx b/public/src/pages/Dashboard.tsx index 5a64d60..9b2555c 100644 --- a/public/src/pages/Dashboard.tsx +++ b/public/src/pages/Dashboard.tsx @@ -63,7 +63,7 @@ export function Dashboard() {
= { fiber: '#3B82F6', // Blue