docs: add EntityList, docs updates, and inventory to spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -84,6 +84,74 @@ interface SplitPaneProps {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2b. EntityList — New Composite
|
||||||
|
|
||||||
|
**Problem:** The left-side list panels in UsersTab, GroupsTab, and RolesTab all build the same frame: a search input + "Add" button header, a scrollable list of items (avatar + text + badges), and selection highlighting. Each tab re-implements this frame with ~50 lines of identical structure.
|
||||||
|
|
||||||
|
**Solution:** New composite `EntityList`.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
interface EntityListProps<T> {
|
||||||
|
items: T[]
|
||||||
|
renderItem: (item: T, isSelected: boolean) => ReactNode
|
||||||
|
getItemId: (item: T) => string
|
||||||
|
selectedId?: string
|
||||||
|
onSelect?: (id: string) => void
|
||||||
|
searchPlaceholder?: string // Default: "Search..."
|
||||||
|
onSearch?: (query: string) => void
|
||||||
|
addLabel?: string // e.g. "+ Add user" — omit to hide button
|
||||||
|
onAdd?: () => void
|
||||||
|
emptyMessage?: string // Default: "No items found"
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Layout:**
|
||||||
|
- Header row: `Input` (search, with icon) on the left, `Button variant="secondary" size="sm"` (add) on the right. Header hidden when both `onSearch` and `onAdd` are omitted.
|
||||||
|
- Scrollable list below header, `var(--bg-surface)` background
|
||||||
|
- Each item: clickable row with `var(--bg-hover)` on hover, `var(--amber-bg)` + left amber border when selected
|
||||||
|
- Items rendered via `renderItem` — the component provides the clickable row wrapper, the caller provides the content
|
||||||
|
- `role="listbox"` on the list, `role="option"` on each item for accessibility
|
||||||
|
- Empty state: centered `emptyMessage` text when `items` is empty
|
||||||
|
|
||||||
|
**Typical item content (provided by caller via `renderItem`):**
|
||||||
|
- Avatar + name + subtitle + badge tags — but this is not prescribed by EntityList. The component is agnostic about item content.
|
||||||
|
|
||||||
|
**Combined usage with SplitPane:**
|
||||||
|
```tsx
|
||||||
|
<SplitPane
|
||||||
|
list={
|
||||||
|
<EntityList
|
||||||
|
items={filteredUsers}
|
||||||
|
renderItem={(user, isSelected) => (
|
||||||
|
<>
|
||||||
|
<Avatar name={user.name} size="sm" />
|
||||||
|
<div>
|
||||||
|
<div>{user.name}</div>
|
||||||
|
<div>{user.email}</div>
|
||||||
|
<div>{user.roles.map(r => <Badge key={r} label={r} />)}</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
getItemId={(u) => u.id}
|
||||||
|
selectedId={selectedId}
|
||||||
|
onSelect={setSelectedId}
|
||||||
|
searchPlaceholder="Search users..."
|
||||||
|
onSearch={setSearchQuery}
|
||||||
|
addLabel="+ Add user"
|
||||||
|
onAdd={() => setAddDialogOpen(true)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
detail={selectedUser ? <UserDetail user={selectedUser} /> : null}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
**File location:** `src/design-system/composites/EntityList/`
|
||||||
|
|
||||||
|
**Pages to refactor:** UsersTab.tsx, GroupsTab.tsx, RolesTab.tsx — replace custom list rendering with `<EntityList>`. Combined with SplitPane, each tab reduces from ~200 lines to ~50 lines of domain-specific render logic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 3. Refactor AgentHealth Instance Table to DataTable
|
## 3. Refactor AgentHealth Instance Table to DataTable
|
||||||
|
|
||||||
**Problem:** AgentHealth builds instance tables using raw HTML `<table>` elements instead of the existing `DataTable` composite.
|
**Problem:** AgentHealth builds instance tables using raw HTML `<table>` elements instead of the existing `DataTable` composite.
|
||||||
@@ -189,7 +257,7 @@ interface CardProps {
|
|||||||
1. **KpiStrip** — highest impact, 3 pages, ~320 lines eliminated
|
1. **KpiStrip** — highest impact, 3 pages, ~320 lines eliminated
|
||||||
2. **StatusText** — smallest scope, quick win, unblocks cleaner page code
|
2. **StatusText** — smallest scope, quick win, unblocks cleaner page code
|
||||||
3. **Card title** — small change to existing component, unblocks Routes cleanup
|
3. **Card title** — small change to existing component, unblocks Routes cleanup
|
||||||
4. **SplitPane** — 3 admin tabs, clean pattern
|
4. **SplitPane + EntityList** — 3 admin tabs, clean pattern. Build together since EntityList is the natural content for SplitPane's list slot.
|
||||||
5. **LogViewer** — 1 page but important for real app
|
5. **LogViewer** — 1 page but important for real app
|
||||||
6. **AgentHealth DataTable refactor** — pure page cleanup, no DS changes
|
6. **AgentHealth DataTable refactor** — pure page cleanup, no DS changes
|
||||||
|
|
||||||
@@ -201,4 +269,27 @@ All new components tested with Vitest + React Testing Library, co-located test f
|
|||||||
|
|
||||||
New components added to respective barrel exports:
|
New components added to respective barrel exports:
|
||||||
- `src/design-system/primitives/index.ts` — StatusText
|
- `src/design-system/primitives/index.ts` — StatusText
|
||||||
- `src/design-system/composites/index.ts` — KpiStrip, SplitPane, LogViewer
|
- `src/design-system/composites/index.ts` — KpiStrip, SplitPane, EntityList, LogViewer
|
||||||
|
|
||||||
|
## Documentation Updates
|
||||||
|
|
||||||
|
### COMPONENT_GUIDE.md
|
||||||
|
|
||||||
|
Add entries for each new component to the appropriate decision trees:
|
||||||
|
|
||||||
|
- **Data Display section:** Add KpiStrip — "Use KpiStrip for a row of summary metrics at the top of a page (exchanges, error rate, latency, etc.)"
|
||||||
|
- **Data Display section:** Add LogViewer — "Use LogViewer for scrollable log output with timestamped, severity-colored entries"
|
||||||
|
- **Layout section:** Add SplitPane — "Use SplitPane for master/detail layouts: selectable list on the left, detail view on the right"
|
||||||
|
- **Data Display section:** Add EntityList — "Use EntityList for searchable, selectable lists of entities (users, groups, roles, etc.). Combine with SplitPane for CRUD management screens."
|
||||||
|
- **Text & Labels section:** Add StatusText — "Use StatusText for inline colored status values (success rates, SLA status, trend indicators). Use StatusDot for colored dot indicators."
|
||||||
|
- **Card section:** Document new `title` prop — "Pass `title` to Card for a titled content container (e.g., chart cards). Title renders as an uppercase header with separator."
|
||||||
|
|
||||||
|
### Inventory Page
|
||||||
|
|
||||||
|
Add demos for each new component to `src/pages/Inventory/sections/`:
|
||||||
|
|
||||||
|
- **CompositesSection.tsx:** Add KpiStrip, SplitPane, EntityList, LogViewer demos with realistic sample data
|
||||||
|
- **PrimitivesSection.tsx:** Add StatusText demo showing all variants
|
||||||
|
- **Card demo:** Update existing Card demo to show the `title` prop variant
|
||||||
|
|
||||||
|
Each demo follows the existing DemoCard pattern with `id` anchors, and nav entries are added to `Inventory.tsx`.
|
||||||
|
|||||||
Reference in New Issue
Block a user