Modified Patch Panel component
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>Datacenter Modeler</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
8
public/favicon.svg
Normal file
8
public/favicon.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" rx="12" fill="#0f1720"/>
|
||||
<rect x="15" y="10" width="34" height="44" rx="4" fill="#1e293b" stroke="#7dd3fc" stroke-width="3"/>
|
||||
<path d="M21 18h22M21 26h22M21 34h22M21 42h22" stroke="#f59e0b" stroke-width="3" stroke-linecap="round"/>
|
||||
<circle cx="25" cy="50" r="2" fill="#22c55e"/>
|
||||
<circle cx="32" cy="50" r="2" fill="#7dd3fc"/>
|
||||
<circle cx="39" cy="50" r="2" fill="#ef4444"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 482 B |
54
src/App.tsx
54
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' && (
|
||||
<>
|
||||
<Divider />
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 800 }}>
|
||||
Patch Panel
|
||||
</Typography>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>Medium</InputLabel>
|
||||
<Select
|
||||
value={selectedEquipment.patchPanelMedium || 'copper'}
|
||||
label="Medium"
|
||||
onChange={(event: SelectChangeEvent) =>
|
||||
onEquipmentChange(selectedEquipmentRack.id, selectedEquipment.id, {
|
||||
patchPanelMedium: event.target.value as Equipment['patchPanelMedium'],
|
||||
})
|
||||
}
|
||||
>
|
||||
<MenuItem value="copper">Copper</MenuItem>
|
||||
<MenuItem value="fiberoptic">Fiber optic</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<TextField
|
||||
label="Number of ports"
|
||||
type="number"
|
||||
value={selectedEquipment.patchPanelPorts}
|
||||
inputProps={{ min: 0, max: 10000 }}
|
||||
onChange={(event) =>
|
||||
onEquipmentChange(selectedEquipmentRack.id, selectedEquipment.id, {
|
||||
patchPanelPorts: clampNumber(event.target.value, 0, 10000, selectedEquipment.patchPanelPorts),
|
||||
})
|
||||
}
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
label="Port identification"
|
||||
value={selectedEquipment.patchPanelPortIdentification}
|
||||
onChange={(event) =>
|
||||
onEquipmentChange(selectedEquipmentRack.id, selectedEquipment.id, {
|
||||
patchPanelPortIdentification: event.target.value,
|
||||
})
|
||||
}
|
||||
multiline
|
||||
minRows={3}
|
||||
placeholder="A01-A24, VLAN labels, fiber strands, or room/circuit references"
|
||||
fullWidth
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<TextField
|
||||
label="Power watts"
|
||||
type="number"
|
||||
|
||||
@@ -17,6 +17,8 @@ export type ComponentType =
|
||||
|
||||
export type LibraryCategory = 'container' | 'equipment';
|
||||
|
||||
export type PatchPanelMedium = '' | 'copper' | 'fiberoptic';
|
||||
|
||||
export interface LibraryItem {
|
||||
type: ComponentType;
|
||||
category: LibraryCategory;
|
||||
@@ -38,6 +40,9 @@ export interface Equipment {
|
||||
serialNumber: string;
|
||||
assetTag: string;
|
||||
powerWatts: number;
|
||||
patchPanelMedium: PatchPanelMedium;
|
||||
patchPanelPorts: number;
|
||||
patchPanelPortIdentification: string;
|
||||
notes: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ export const createEquipment = (type: string, rack: RackContainer, preferredU: n
|
||||
serialNumber: '',
|
||||
assetTag: '',
|
||||
powerWatts: 0,
|
||||
patchPanelMedium: libraryItem.type === 'patch-panel' ? 'copper' : '',
|
||||
patchPanelPorts: libraryItem.type === 'patch-panel' ? 24 : 0,
|
||||
patchPanelPortIdentification: '',
|
||||
notes: '',
|
||||
color: libraryItem.color,
|
||||
};
|
||||
@@ -150,6 +153,14 @@ export const normalizeDiagram = (value: unknown): DiagramData => {
|
||||
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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user