Skip to content

Commit f0009ec

Browse files
authored
Merge pull request #16 from plagosus/half-unit-modules
Half Unit Modules Support
2 parents 945fbe1 + 0527614 commit f0009ec

12 files changed

Lines changed: 459 additions & 209 deletions

File tree

src/App.tsx

Lines changed: 308 additions & 161 deletions
Large diffs are not rendered by default.

src/components/ModuleFace.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,18 @@ export const ModuleFace = ({
3636
>
3737
{!hasImage && (
3838
<div className="flex flex-col items-center opacity-80 pointer-events-none w-full px-4">
39-
<span className="font-mono text-xs uppercase tracking-widest text-white/50 mb-1">
40-
{module.name.substring(0, 32)}
41-
</span>
39+
{/* Label - Hide if showName is explicitly false */}
40+
{module.showName !== false && (
41+
<span className="font-mono text-xs uppercase tracking-widest text-white/50 mb-1">
42+
{module.name.substring(0, 32)}
43+
</span>
44+
)}
4245

4346
{/* Visual Features based on Type */}
4447
<div className="flex gap-2 w-full justify-center">
4548
{/* Networking: Ports */}
4649
{module.type === 'network' && (
47-
<NetworkFace module={module} isPowered={isPowered} />
50+
<NetworkFace module={module} isPowered={isPowered} rackWidth={rackWidth} />
4851
)}
4952

5053
{/* Server: Indicators or Drive Bays */}

src/components/module-faces/AccessoryFace.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const AccessoryFace = ({
2525
}
2626

2727
if (id === 'patch-panel' || name.includes('patch panel')) {
28-
return <PatchPanelFace rackWidth={rackWidth} isPowered={isPowered} />;
28+
return <PatchPanelFace rackWidth={rackWidth} isPowered={isPowered} uSize={module.uSize} />;
2929
}
3030

3131
if (id === 'shelf' || name.includes('shelf')) {

src/components/module-faces/NetworkFace.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
1-
import type { RackModule } from '../../types';
1+
import type { RackModule, RackWidth } from '../../types';
2+
import { Switch48Port } from './network/Switch48Port';
23
import { Switch24Port } from './network/Switch24Port';
34
import { Switch16Port } from './network/Switch16Port';
45
import { Switch8Port } from './network/Switch8Port';
56
import { Switch5Port } from './network/Switch5Port';
67

7-
export const NetworkFace = ({ module, isPowered }: { module: RackModule; isPowered?: boolean }) => {
8+
export const NetworkFace = ({
9+
module,
10+
isPowered,
11+
rackWidth,
12+
}: {
13+
module: RackModule;
14+
isPowered?: boolean;
15+
rackWidth?: RackWidth;
16+
}) => {
817
// Determine which networking device to show
918
const name = module.name.toLowerCase();
1019
const id = module.id.toLowerCase();
1120

21+
if (id === 'switch-48' || name.includes('48-port')) {
22+
return <Switch48Port isPowered={isPowered} rackWidth={rackWidth} />;
23+
}
1224
if (id === 'switch-24' || name.includes('24-port')) {
1325
return <Switch24Port isPowered={isPowered} />;
1426
}

src/components/module-faces/accessory/PatchPanelFace.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { type RackWidth } from '../../../types';
44
export const PatchPanelFace = ({
55
rackWidth,
66
isPowered,
7+
uSize = 1,
78
}: {
89
rackWidth?: RackWidth;
910
isPowered?: boolean;
11+
uSize?: number;
1012
}) => {
1113
const is10Inch = rackWidth === '10inch';
1214
const portCount = is10Inch ? 12 : 24;
@@ -16,8 +18,10 @@ export const PatchPanelFace = ({
1618
<div className="flex w-full justify-between gap-[2px]">
1719
{Array.from({ length: portCount }).map((_, i) => (
1820
<div key={i} className="flex flex-col items-center gap-2 w-full">
19-
{/* White Label */}
20-
<div className="w-[75%] h-1.5 bg-white/70 rounded-[1px] shadow-sm"></div>
21+
{/* White Label - Hide on 0.5U */}
22+
{uSize >= 1 && (
23+
<div className="w-[75%] h-1.5 bg-white/70 rounded-[1px] shadow-sm"></div>
24+
)}
2125
{/* Keystone */}
2226
<div className="w-[85%] aspect-square bg-black rounded-[2px] flex items-center justify-center">
2327
<Port isPowered={isPowered} />

src/components/module-faces/accessory/RPIMountSlot.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
export const RPIMountSlot = ({ vertical = false }: { vertical?: boolean }) => {
22
return (
33
<div
4-
className={`${
5-
vertical ? 'w-[70px] h-[130px] flex-col py-3' : 'w-[150px] h-[45px] flex-row px-3'
6-
} border-[1px] border-white/20 rounded-md bg-transparent flex items-center justify-between`}
4+
className={`${vertical ? 'w-[70px] h-[150px] flex-col py-3' : 'w-[150px] h-[70px] flex-row px-3'
5+
} border-[1px] border-white/20 rounded-md bg-transparent flex items-center justify-between`}
76
>
87
<div className="w-2 h-2 rounded-full border border-white/20 bg-transparent" />
98
<div className="w-2 h-2 rounded-full border border-white/20 bg-transparent" />

src/components/module-faces/network/Port.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect, useState } from 'react';
22

33
export const Port = ({
4-
className = 'w-4 h-4',
4+
className = 'w-5 h-5',
55
isPowered = true,
66
}: {
77
className?: string;
@@ -69,26 +69,24 @@ export const Port = ({
6969
<div className="flex flex-col items-center gap-[0px]">
7070
{/* Port */}
7171
<div
72-
className={`bg-black/80 rounded-[1px] border border-gray-600 shadow-inner ${className}`}
72+
className={`bg-black/80 rounded-[1px] border border-gray-600 shadow-inner flex items-start pt-[1px] justify-center ${className}`}
7373
>
7474
{/* LEDs */}
7575
<div className="flex justify-between w-full px-[1px]">
7676
{/* Green LED: On when online or active */}
7777
<div
78-
className={`w-0.75 h-0.75 rounded-[0.5px] transition-colors duration-200 ${
79-
isPowered && status !== 'off'
80-
? 'bg-green-500/50 shadow-[0_0_2px_rgba(34,197,94,0.6)]'
81-
: 'bg-green-900/60'
82-
}`}
78+
className={`w-0.5 h-0.5 rounded-[0.5px] transition-colors duration-200 ${isPowered && status !== 'off'
79+
? 'bg-green-500/50 shadow-[0_0_2px_rgba(34,197,94,0.6)]'
80+
: 'bg-green-900/60'
81+
}`}
8382
></div>
8483

8584
{/* Amber LED: Blinking when active, Off otherwise */}
8685
<div
87-
className={`w-0.75 h-0.75 rounded-[0.5px] transition-colors duration-50 ${
88-
isPowered && status === 'active' && isAmberOn
89-
? 'bg-amber-500/40 shadow-[0_0_2px_rgba(245,158,11,0.6)]'
90-
: 'bg-amber-900/60'
91-
}`}
86+
className={`w-0.5 h-0.5 rounded-[0.5px] transition-colors duration-50 ${isPowered && status === 'active' && isAmberOn
87+
? 'bg-amber-500/40 shadow-[0_0_2px_rgba(245,158,11,0.6)]'
88+
: 'bg-amber-900/60'
89+
}`}
9290
></div>
9391
</div>
9492
</div>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import type { RackWidth } from '../../../types';
2+
import { Port } from './Port';
3+
4+
export const Switch48Port = ({
5+
isPowered,
6+
rackWidth = '19inch',
7+
}: {
8+
isPowered?: boolean;
9+
rackWidth?: RackWidth;
10+
}) => {
11+
const is10Inch = rackWidth === '10inch';
12+
13+
if (is10Inch) {
14+
// 10-inch Rack Layout (Currently same as 19-inch, but separated for future customization)
15+
return (
16+
<div className="flex flex-col gap-1 w-full max-w-[90%] items-end pr-4 mt-3">
17+
{/* 2 Rows of 12 Ports (3 groups of 4) */}
18+
<div className="flex gap-3 justify-end w-full">
19+
{[0, 1, 2].map((groupIndex) => (
20+
<div key={`top-group-${groupIndex}`} className="flex gap-1">
21+
{Array.from({ length: 4 }).map((_, i) => (
22+
<div key={`top-${groupIndex}-${i}`}>
23+
<Port isPowered={isPowered} />
24+
</div>
25+
))}
26+
</div>
27+
))}
28+
</div>
29+
<div className="flex gap-3 justify-end w-full">
30+
{[0, 1, 2].map((groupIndex) => (
31+
<div key={`bottom-group-${groupIndex}`} className="flex gap-1">
32+
{Array.from({ length: 4 }).map((_, i) => (
33+
<div key={`bottom-${groupIndex}-${i}`}>
34+
<Port isPowered={isPowered} />
35+
</div>
36+
))}
37+
</div>
38+
))}
39+
</div>
40+
</div>
41+
);
42+
}
43+
44+
// 19-inch Rack Layout (Standard)
45+
return (
46+
<div className="flex flex-col gap-1 w-full max-w-[95%] items-end pr-2 mt-3">
47+
{/* 2 Rows of 24 Ports (4 groups of 6) */}
48+
<div className="flex gap-5 justify-end w-full">
49+
{[0, 1, 2].map((groupIndex) => (
50+
<div key={`top-group-${groupIndex}`} className="flex gap-[2px]">
51+
{Array.from({ length: 8 }).map((_, i) => (
52+
<div key={`top-${groupIndex}-${i}`}>
53+
<Port isPowered={isPowered} />
54+
</div>
55+
))}
56+
</div>
57+
))}
58+
</div>
59+
<div className="flex gap-5 justify-end w-full">
60+
{[0, 1, 2].map((groupIndex) => (
61+
<div key={`bottom-group-${groupIndex}`} className="flex gap-[2px]">
62+
{Array.from({ length: 8 }).map((_, i) => (
63+
<div key={`bottom-${groupIndex}-${i}`}>
64+
<Port isPowered={isPowered} />
65+
</div>
66+
))}
67+
</div>
68+
))}
69+
</div>
70+
</div>
71+
);
72+
};

src/components/module-faces/power/PDUFace.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const PDUFace = ({
3434
</div>
3535

3636
{/* Schuko Outlets */}
37-
<div className="flex items-center gap-8 overflow-hidden">
37+
<div className="flex items-center gap-10 overflow-hidden">
3838
{Array.from({ length: outletCount }).map((_, i) => (
3939
<div
4040
key={i}

src/components/module-faces/storage/DriveBay35.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,24 @@ export const DriveBay35 = ({
1212
const { mode, isAmberOn } = useDriveLedMode(forceOff || !isPowered);
1313
return (
1414
<div
15-
className={`h-12 bg-gray-900 border border-gray-700 rounded-sm shadow-inner flex items-center p-0 gap-0 overflow-hidden ${className}`}
15+
className={`h-13 bg-gray-900 border border-gray-700 rounded-sm shadow-inner flex items-center p-0 gap-0 overflow-hidden ${className}`}
1616
>
1717
{/* LEDs (left side, vertical) */}
1818
<div className={`flex flex-col gap-1.5 pl-4`}>
1919
{/* Green LED (Power/Status) */}
2020
<div
21-
className={`w-1 h-1 rounded-full transition-all duration-300 ${
22-
mode !== 'off'
23-
? 'bg-green-500/80 shadow-[0_0_4px_rgba(34,197,94,0.8)]'
24-
: 'bg-green-900/30'
25-
}`}
21+
className={`w-1 h-1 rounded-full transition-all duration-300 ${mode !== 'off'
22+
? 'bg-green-500/80 shadow-[0_0_4px_rgba(34,197,94,0.8)]'
23+
: 'bg-green-900/30'
24+
}`}
2625
></div>
2726

2827
{/* Amber LED (Activity) */}
2928
<div
30-
className={`w-1 h-1 rounded-full transition-all duration-75 ${
31-
isAmberOn
32-
? 'bg-amber-500/80 shadow-[0_0_4px_rgba(245,158,11,0.8)]'
33-
: 'bg-amber-900/30'
34-
}`}
29+
className={`w-1 h-1 rounded-full transition-all duration-75 ${isAmberOn
30+
? 'bg-amber-500/80 shadow-[0_0_4px_rgba(245,158,11,0.8)]'
31+
: 'bg-amber-900/30'
32+
}`}
3533
></div>
3634
</div>
3735
</div>

0 commit comments

Comments
 (0)