From be2c5ce7e290e43fce2c5197674b244fd28bfd6a Mon Sep 17 00:00:00 2001 From: Pouzor Date: Fri, 14 Aug 2026 02:36:38 +0200 Subject: [PATCH] feat(inventory): group the device sheet by what a reader is after MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The services sat in the curation column, beside the properties, while the addresses that reach them were two columns away. They move under Identity: what answers on a host belongs with how to reach it, and the curation column now carries the properties alone. Every section heading takes an icon — identity, services, monitoring, activity, notes, properties, make & model — so a column is scannable without reading each title. `Section` requires it, so a new section cannot be added without one. The properties editor keeps its own plain heading: it comes from the shared `PropertyList`, which the rack cable panel also renders. The three view columns carry a data-testid, which is what lets the layout be asserted rather than eyeballed. ha-relevant: maybe --- .../modals/InventoryDeviceModal.tsx | 159 ++++++++++-------- .../InventoryDeviceModalLayout.test.tsx | 24 +++ 2 files changed, 117 insertions(+), 66 deletions(-) diff --git a/frontend/src/components/modals/InventoryDeviceModal.tsx b/frontend/src/components/modals/InventoryDeviceModal.tsx index ba1bdac..1d37f59 100644 --- a/frontend/src/components/modals/InventoryDeviceModal.tsx +++ b/frontend/src/components/modals/InventoryDeviceModal.tsx @@ -15,7 +15,22 @@ * reads in one screen instead of a scrolling column of key-value pairs. */ import { useEffect, useState } from 'react' -import { Check, Copy, Layers, Pencil, Plus, X } from 'lucide-react' +import { + Check, + Copy, + Factory, + Fingerprint, + HeartPulse, + History, + Layers, + Network, + Pencil, + Plus, + StickyNote, + Tags, + X, + type LucideIcon, +} from 'lucide-react' import { toast } from 'sonner' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' @@ -91,20 +106,25 @@ function categoryColor(category: string | null | undefined) { return CATEGORY_COLORS[category.toLowerCase()] ?? '#8b949e' } -/** A section card: heading bar, optional action, body. */ +/** A section card: heading bar with its icon, optional action, body. */ function Section({ title, + icon: Icon, action, children, }: { title: string + icon: LucideIcon action?: React.ReactNode children: React.ReactNode }) { return (
- {title} + + + {title} + {action}
{children}
@@ -398,7 +418,7 @@ export function InventoryDeviceModal({ device, onClose, onApprove, onHide, onIgn {editing ? (
-
+
set('label', e.target.value)} placeholder="Display name" className={INPUT} /> @@ -440,7 +460,7 @@ export function InventoryDeviceModal({ device, onClose, onApprove, onHide, onIgn
-
+
set('vendor', e.target.value)} className={INPUT} /> @@ -459,7 +479,7 @@ export function InventoryDeviceModal({ device, onClose, onApprove, onHide, onIgn
-
+