diff --git a/frontend/src/devices/DeviceDetail.jsx b/frontend/src/devices/DeviceDetail.jsx index e019fd6..6e4c94c 100644 --- a/frontend/src/devices/DeviceDetail.jsx +++ b/frontend/src/devices/DeviceDetail.jsx @@ -65,9 +65,15 @@ function Subsection({ title, children, isFirst = false }) { ); } -/** A single row of fields that wraps if it doesn't fit */ -function FieldRow({ children }) { - return
{children}
; +/** A grid row of fields — Nth items align across rows within a subsection */ +function FieldRow({ children, columns }) { + const childArray = Array.isArray(children) ? children.filter(Boolean) : [children]; + const count = columns || childArray.length; + return ( +
+ {children} +
+ ); } // --- Log level styles --- @@ -452,9 +458,9 @@ export default function DeviceDetail() { const deviceInfoSection = (
-
- {/* Status */} -
+
+ {/* Row 1, Col 1: Status */} +
- {/* Serial + Hardware Variant */} -
-
-
Serial Number
-
{device.device_id}
-
HW: VesperCore
-
+ {/* Row 1, Col 2: Hardware Variant */} +
+
Hardware Variant
+
VesperCore
- {/* Admin Note */} -
-
-
Admin Note
-
-
-
+ {/* Row 1-2, Col 3: Admin Notes (spans 2 rows) */} +
+
Admin Notes
+
-
- {/* Document ID */} -
-
-
Document ID
-
{device.id}
-
+ {/* Row 2, Col 1: Serial Number */} +
+
Serial Number
+
{device.device_id}
+
+ + {/* Row 2, Col 2: Document ID */} +
+
Document ID
+
{device.id}
@@ -534,10 +539,12 @@ export default function DeviceDetail() { ); const locationSection = ( - +
{coords ? ( -
-
+
+ {/* Left column: title + fields */} +
+

Location

{device.device_location} @@ -558,8 +565,9 @@ export default function DeviceDetail() { {locationName && {locationName}}
-
-
+ {/* Right column: map with equal padding */} +
+
) : ( -
- {device.device_location} - {device.device_location_coordinates || "-"} -
+
+

Location

+
+ {device.device_location} + {device.device_location_coordinates || "-"} +
+
)} - +
); const deviceSettingsSection = ( @@ -592,27 +603,29 @@ export default function DeviceDetail() { - + {clock.ringAlerts || "-"} {clock.ringIntervals} - + {clock.hourAlertsBell} {clock.halfhourAlertsBell} {clock.quarterAlertsBell} - + {formatTimestamp(clock.daySilenceFrom)} - {formatTimestamp(clock.daySilenceTo)} +
- + {formatTimestamp(clock.nightSilenceFrom)} - {formatTimestamp(clock.nightSilenceTo)} +
@@ -635,27 +648,27 @@ export default function DeviceDetail() { {/* Right Column */}
- + {net.hostname} - + {clock.ringIntervals} - + {clock.clockOutputs?.[0] ?? "-"} {clock.clockOutputs?.[1] ?? "-"} - + {clock.clockTimings?.[0] != null ? msToSeconds(clock.clockTimings[0]) : "-"} {clock.clockTimings?.[1] != null ? msToSeconds(clock.clockTimings[1]) : "-"} - + {attr.totalBells ?? "-"} @@ -844,26 +857,23 @@ export default function DeviceDetail() { const renderDoubleColumn = () => (
{/* Row 1: Device Info + Subscription — equal height */} -
+
{deviceInfoSection} {subscriptionSection}
{/* Row 2: Device Settings — full width */} {deviceSettingsSection} - {/* Row 3: Location+Misc (left) vs Warranty (right) */} -
-
-
{locationSection}
- {miscSection} -
-
- {warrantySection} -
+ {/* Row 3: Location + Warranty */} +
+ {locationSection} + {warrantySection}
- {/* Row 4: Notes vs App Users */} -
-
{notesSection}
-
{appUsersSection}
+ {/* Row 4: Misc full width */} + {miscSection} + {/* Row 5: Notes + App Users — equal width */} +
+ {notesSection} + {appUsersSection}
{/* Latest Logs */} {logsSection} @@ -872,24 +882,22 @@ export default function DeviceDetail() { const renderTripleColumn = () => (
- {/* Row 1: DevInfo+Subscription (cols 1-2 equal height) + Location (col 3) */} -
-
- {deviceInfoSection} - {subscriptionSection} -
-
{locationSection}
+ {/* Row 1: DevInfo + Subscription + Location — all equal height */} +
+ {deviceInfoSection} + {subscriptionSection} + {locationSection}
{/* Row 2: Device Settings (cols 1-2) + Warranty (col 3) */} -
-
{deviceSettingsSection}
-
{warrantySection}
+
+ {deviceSettingsSection} + {warrantySection}
- {/* Row 3: Misc (col1) + Notes (col2) + App Users (col3) */} -
-
{miscSection}
-
{notesSection}
-
{appUsersSection}
+ {/* Row 3: Misc + Notes + App Users — equal width */} +
+ {miscSection} + {notesSection} + {appUsersSection}
{/* Latest Logs */} {logsSection} diff --git a/frontend/src/index.css b/frontend/src/index.css index b55830a..463a884 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -146,69 +146,10 @@ input[type="range"]::-moz-range-thumb { } /* Device detail column layout */ -.device-columns { - display: flex; - gap: 1.5rem; - align-items: flex-start; -} .device-column { display: flex; flex-direction: column; gap: 1.5rem; - flex: 1; - min-width: 0; -} -.device-full-row { - width: 100%; - margin-bottom: 1.5rem; -} -.device-equal-row { - display: flex; - gap: 1.5rem; - align-items: stretch; -} -.device-equal-row > * { - flex: 1; - min-width: 0; -} -.device-flex-fill { - display: flex; - flex-direction: column; -} -.device-flex-fill > .flex-grow { - flex: 1; -} -/* Device info horizontal subsections */ -.device-info-row { - display: flex; - flex-wrap: wrap; - gap: 0; -} -.device-info-row > .device-info-item { - padding: 0 1.5rem; - border-left: 1px solid var(--border-secondary); - display: flex; - align-items: center; - gap: 0.75rem; -} -.device-info-row > .device-info-item:first-child { - padding-left: 0; - border-left: none; -} -/* Location 2-column internal layout */ -.location-split { - display: flex; - gap: 1.5rem; - align-items: stretch; -} -.location-split > .location-fields { - flex: 0 0 auto; - min-width: 200px; -} -.location-split > .location-map { - flex: 1; - display: flex; - align-items: center; } /* File input */