feat: redesign dashboard with sidebar layout and table components

- Add new Table UI component with rich features (sorting, empty states, interactive rows)
- Create DashboardLayout component with sidebar navigation
- Redesign Agents page with table layout showing provider, tools, and actions
- Redesign Collections page with table layout showing visibility and tool counts
- Add surface-secondary color token for proper dark mode support
- Add home and user icons to icon library
- Fix missing foreground-quaternary with muted color

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ajax Davis 2026-01-07 18:40:52 +10:00
parent 0730ae6f42
commit a137afd65d
9 changed files with 801 additions and 196 deletions

View file

@ -156,6 +156,10 @@
"./ToolHealthBanner/ToolHealthBanner": {
"types": "./dist/ToolHealthBanner/ToolHealthBanner.d.ts",
"default": "./dist/ToolHealthBanner/ToolHealthBanner.js"
},
"./Table/Table": {
"types": "./dist/Table/Table.d.ts",
"default": "./dist/Table/Table.js"
}
},
"files": ["dist"],

View file

@ -120,6 +120,14 @@ export const icons = {
viewBox: '0 0 24 24',
path: 'M2.01 21L23 12 2.01 3 2 10l15 2-15 2z',
},
home: {
viewBox: '0 0 24 24',
path: 'M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z',
},
user: {
viewBox: '0 0 24 24',
path: 'M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z',
},
} as const;
export type IconName = keyof typeof icons;

View file

@ -0,0 +1,219 @@
import { cn } from '@tpmjs/utils/cn';
import { forwardRef } from 'react';
// ============================================================================
// Table Root
// ============================================================================
export interface TableProps extends React.HTMLAttributes<HTMLTableElement> {
/** Visual style variant */
variant?: 'default' | 'bordered';
}
const Table = forwardRef<HTMLTableElement, TableProps>(
({ className, variant = 'default', ...props }, ref) => (
<div className="w-full overflow-auto">
<table
ref={ref}
className={cn(
'w-full caption-bottom text-sm',
variant === 'bordered' && 'border border-border rounded-lg overflow-hidden',
className
)}
{...props}
/>
</div>
)
);
Table.displayName = 'Table';
// ============================================================================
// Table Header
// ============================================================================
const TableHeader = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn('bg-surface-secondary [&_tr]:border-b', className)} {...props} />
));
TableHeader.displayName = 'TableHeader';
// ============================================================================
// Table Body
// ============================================================================
const TableBody = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} />
));
TableBody.displayName = 'TableBody';
// ============================================================================
// Table Footer
// ============================================================================
const TableFooter = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn('border-t bg-surface-secondary font-medium', className)}
{...props}
/>
));
TableFooter.displayName = 'TableFooter';
// ============================================================================
// Table Row
// ============================================================================
export interface TableRowProps extends React.HTMLAttributes<HTMLTableRowElement> {
/** Whether the row is selected */
selected?: boolean;
/** Whether the row is clickable/interactive */
interactive?: boolean;
}
const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(
({ className, selected, interactive, ...props }, ref) => (
<tr
ref={ref}
className={cn(
'border-b border-border transition-colors',
interactive && 'cursor-pointer hover:bg-surface',
selected && 'bg-surface',
className
)}
{...props}
/>
)
);
TableRow.displayName = 'TableRow';
// ============================================================================
// Table Head Cell
// ============================================================================
export interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
/** Whether the column is sortable */
sortable?: boolean;
/** Current sort direction */
sortDirection?: 'asc' | 'desc' | null;
}
const TableHead = forwardRef<HTMLTableCellElement, TableHeadProps>(
({ className, sortable, sortDirection, children, ...props }, ref) => (
<th
ref={ref}
className={cn(
'h-10 px-4 text-left align-middle font-medium text-foreground-secondary',
'[&:has([role=checkbox])]:pr-0',
sortable && 'cursor-pointer select-none hover:text-foreground',
className
)}
{...props}
>
{sortable ? (
<div className="flex items-center gap-1">
{children}
<span className="text-foreground-tertiary">
{sortDirection === 'asc' && '↑'}
{sortDirection === 'desc' && '↓'}
{!sortDirection && '↕'}
</span>
</div>
) : (
children
)}
</th>
)
);
TableHead.displayName = 'TableHead';
// ============================================================================
// Table Cell
// ============================================================================
const TableCell = forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(
({ className, ...props }, ref) => (
<td
ref={ref}
className={cn(
'px-4 py-3 align-middle text-foreground',
'[&:has([role=checkbox])]:pr-0',
className
)}
{...props}
/>
)
);
TableCell.displayName = 'TableCell';
// ============================================================================
// Table Caption
// ============================================================================
const TableCaption = forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn('mt-4 text-sm text-foreground-secondary', className)}
{...props}
/>
));
TableCaption.displayName = 'TableCaption';
// ============================================================================
// Table Empty State
// ============================================================================
export interface TableEmptyProps extends React.HTMLAttributes<HTMLTableRowElement> {
/** Number of columns to span */
colSpan: number;
/** Icon to display */
icon?: React.ReactNode;
/** Title text */
title: string;
/** Description text */
description?: string;
/** Action button/element */
action?: React.ReactNode;
}
const TableEmpty = forwardRef<HTMLTableRowElement, TableEmptyProps>(
({ className, colSpan, icon, title, description, action, ...props }, ref) => (
<tr ref={ref} className={className} {...props}>
<td colSpan={colSpan} className="py-16 text-center">
{icon && <div className="flex justify-center mb-4">{icon}</div>}
<h3 className="text-lg font-medium text-foreground mb-1">{title}</h3>
{description && (
<p className="text-foreground-secondary mb-4 max-w-md mx-auto">{description}</p>
)}
{action && <div className="flex justify-center">{action}</div>}
</td>
</tr>
)
);
TableEmpty.displayName = 'TableEmpty';
// ============================================================================
// Exports
// ============================================================================
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableRow,
TableHead,
TableCell,
TableCaption,
TableEmpty,
};