diff --git a/README.md b/README.md index 7459b28..49a331d 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,10 @@ Datacenter Modeler is a React-based web application for designing datacenter rac - Asset tag - Power watts - Notes +- Edit patch panel-specific properties: + - Copper or fiber optic medium + - Number of ports + - Port identification - Save diagrams as JSON - Load previously saved JSON diagrams - Browser autosave with `localStorage` @@ -128,7 +132,7 @@ Diagram data is stored as JSON with this top-level shape: } ``` -Each rack/cabinet contains its own metadata and an array of installed equipment. Equipment records include type, name, size, U position, manufacturer/model details, asset information, power, notes, and display color. +Each rack/cabinet contains its own metadata and an array of installed equipment. Equipment records include type, name, size, U position, manufacturer/model details, asset information, power, notes, and display color. Patch panel records also include medium, port count, and port identification metadata. ## Project Structure @@ -156,4 +160,3 @@ Each rack/cabinet contains its own metadata and an array of installed equipment. - JSON export is the portable save format for moving diagrams between browsers or computers. - PDF and image exports are generated from the rendered workspace. - The current implementation is client-side only; no backend server or database is required. - diff --git a/index.html b/index.html index d6beb01..969a2bc 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,7 @@ + Datacenter Modeler diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..5128772 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/App.tsx b/src/App.tsx index 06cb937..c66d59b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -462,7 +462,11 @@ export default function App() { [...rack.equipment] .sort((a, b) => b.uStart - a.uStart) .forEach((item) => { - const line = `U${item.uStart}-${item.uStart + item.sizeU - 1}: ${item.name} | ${item.manufacturer || 'n/a'} ${item.model || ''} | ${item.assetTag || 'no asset tag'}`; + const patchPanelDetails = + item.type === 'patch-panel' + ? ` | ${item.patchPanelMedium === 'fiberoptic' ? 'Fiber optic' : 'Copper'} | ${item.patchPanelPorts} ports` + : ''; + const line = `U${item.uStart}-${item.uStart + item.sizeU - 1}: ${item.name} | ${item.manufacturer || 'n/a'} ${item.model || ''} | ${item.assetTag || 'no asset tag'}${patchPanelDetails}`; pdf.text(line.slice(0, 115), margin + 12, y); y += 14; }); @@ -949,6 +953,54 @@ function PropertiesPanel({ onChange={(event) => onEquipmentChange(selectedEquipmentRack.id, selectedEquipment.id, { assetTag: event.target.value })} fullWidth /> + {selectedEquipment.type === 'patch-panel' && ( + <> + + + Patch Panel + + + Medium + + + + onEquipmentChange(selectedEquipmentRack.id, selectedEquipment.id, { + patchPanelPorts: clampNumber(event.target.value, 0, 10000, selectedEquipment.patchPanelPorts), + }) + } + fullWidth + /> + + onEquipmentChange(selectedEquipmentRack.id, selectedEquipment.id, { + patchPanelPortIdentification: event.target.value, + }) + } + multiline + minRows={3} + placeholder="A01-A24, VLAN labels, fiber strands, or room/circuit references" + fullWidth + /> + + )} { serialNumber: item.serialNumber || '', assetTag: item.assetTag || '', powerWatts: clampNumber(item.powerWatts, 0, 100000, 0), + patchPanelMedium: + item.patchPanelMedium === 'fiberoptic' || item.patchPanelMedium === 'copper' + ? item.patchPanelMedium + : libraryItem.type === 'patch-panel' + ? 'copper' + : '', + patchPanelPorts: clampNumber(item.patchPanelPorts, 0, 10000, libraryItem.type === 'patch-panel' ? 24 : 0), + patchPanelPortIdentification: item.patchPanelPortIdentification || '', notes: item.notes || '', color: item.color || libraryItem.color, };