feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
# Pulse — Design System
This file documents how the UI is put together today: the tokens, the
component vocabulary, the navigation IA, and the patterns each page is
expected to follow. It also calls out the rough edges that the
`nav-design-improvements` branch exists to address — treat the "Open
improvements" section as the working backlog for visual/UX cleanup.
System overview lives in `ARCHITECTURE.md` . Per-feature notes live in `docs/` .
## 1. Design principles
- **Information density over decoration.** Pulse is an internal operations
console. Tables, dashboards, and admin tools win over hero spacing.
- **One source of truth per pattern.** If shadcn/ui has a primitive, use it —
don't re-implement (`<table>` raw, custom skeletons, ad-hoc dropdowns).
- **Tokens, not hex.** Colors come from CSS variables in `app/globals.css` ,
not Tailwind's full color palette. The same goes for radius and spacing.
- **Roles shape the nav, not the layout.** Hide items the user can't access;
don't restructure the page shell per role.
- **Read-first, write-confirmed.** Destructive or external-effect actions
(RMM execute, IT Glue write) live behind explicit dialogs with confirmation,
not inline buttons.
## 2. Tokens
App tokens are defined in `app/globals.css` (shadcn defaults). The Wulf
brand layer in `app/styles/brand.css` is imported last and repoints
`--primary` / `--accent` / `--ring` / `--chart-2` to the standards-guide
blue (`#0075AD` ) and provides Wulf gray ramps (`--wulf-gray-100/300/700` ).
Tailwind 4 reads them via `@theme inline` ; never hard-code colors or
radii in components.
### Colors (OKLch)
| Token | Light | Dark | Use |
|---|---|---|---|
| `--background` / `--foreground` | white / near-black | near-black / near-white | Page surface and primary text |
| `--card` / `--card-foreground` | white | `oklch(0.205 0 0)` | Card surfaces |
| `--primary` / `--primary-foreground` | logo blue `oklch(0.55 0.16 220)` | brighter blue `oklch(0.62 0.17 220)` | Primary actions, active nav, focus ring |
| `--secondary` / `--muted` | `oklch(0.97 0 0)` | `oklch(0.269 0 0)` | Subtle surfaces, table headers |
| `--muted-foreground` | `oklch(0.556 0 0)` | `oklch(0.708 0 0)` | Secondary text, descriptions |
| `--accent` | same as primary | same as primary | Hover/selection accents |
| `--destructive` | `oklch(0.577 0.245 27.325)` | `oklch(0.704 0.191 22.216)` | Errors, destructive buttons |
| `--border` / `--input` | `oklch(0.922 0 0)` | `oklch(1 0 0 / 10%)` | Borders, input outlines |
| `--ring` | logo blue | brighter blue | Focus ring |
| `--chart-1` … `--chart-5` | distinct hues | distinct hues | Recharts series |
| `--sidebar*` | mirrors page tokens | mirrors page tokens | Reserved for a future sidebar nav |
Status hues outside this set (success greens, warning ambers, info blues)
should still go through Tailwind's named palette at `-500` or `-600` and use
the muted background pair `bg-{hue}-500/15 text-{hue}-600` for badges so the
contrast stays acceptable in both modes.
### Radius
`--radius: 0.625rem` . Derived: `radius-sm` (`-4px` ), `radius-md` (`-2px` ),
`radius-lg` (=), `radius-xl` (`+4px` ). Use `rounded-md` / `rounded-lg` —
don't introduce custom radii.
### Type
Brand mandate (per `docs/StandardsGuide (1).pdf` , 2013): Helvetica /
Arial, Bold for headers and Light for the tagline. **As of 2026-05 the
app uses IBM Plex Sans instead** — same spirit (engineered sans, Light
weight available), reliably hosted, and pairs with Plex Mono for
numerics. Helvetica / Arial remain in the fallback chain.
- Sans: **IBM Plex Sans** via `next/font/google` , weights 300/400/500/600/700.
Wired via `--font-plex-sans` in `app/layout.tsx` .
- Mono: **IBM Plex Mono** via `next/font/google` , weights 400/500/600.
Used for numerics, IDs, timestamps — not for body text.
Type scale: stick to Tailwind defaults. Reserve `text-2xl font-bold
tracking-tight` for page titles (matches ` PageHeader`); use ` text-lg
font-semibold` for card titles and ` text-sm text-muted-foreground` for
descriptions. Numerics go through the `.num` / `.num-lg` / `.num-xl`
utilities defined in `app/styles/brand.css` .
### Dark mode
CSS-class strategy (`.dark` on `<html>` ). Toggled by `ThemeToggle` in the
top bar; persisted via `next-themes` . Component-level dark variants should be
unnecessary if you stick to tokens.
## 3. Layout
### Page shell
```
┌─ < AppNavigation / > (sticky h-16, backdrop blur, z-50) ─┐
├─ < PageHeader / > (optional, bordered, container-aligned)
└─ < main > (container mx-auto px-6 py-6)
├─ Cards / sections, separated by space-y-6
└─ …
```
`PageHeader` lives at the bottom of `components/navigation/app-navigation.tsx` .
It owns the title (`h1.text-2xl.font-bold.tracking-tight` ), description
(`text-muted-foreground` ), optional breadcrumbs (`/` -separated), and an
actions slot.
### Container & spacing rules
| Rule | Value |
|---|---|
| Horizontal container | `container mx-auto px-6` |
| Vertical page padding | `py-6` (default), `py-8` only on auth/landing |
| Section gap | `space-y-6` for stacked sections, `gap-6` for grid layouts |
| Card body padding | shadcn default — don't override unless wrapping a table (`p-0` ) |
| Item-level gap | `gap-2` for inline rows, `gap-4` for form rows |
These are the targets. Today's pages don't all conform — see "Open
improvements" below.
## 4. Navigation IA
The top bar (`components/navigation/app-navigation.tsx` ) is the only nav. It
is sticky, full-width, and structured left-to-right as:
1. **Brand** — logo + "Pulse" wordmark (`hidden sm:block` for the wordmark)
2. **Primary menu** (`<NavigationMenu>` )
3. **Right-side controls** — `<ThemeToggle />`
### Items today
| Label | Route | Visibility |
|---|---|---|
| Dashboard | `/` | All |
| Configuration Items | `/configuration-items` | All |
| Backup Status ▾ | submenu | All |
| → Backup Status | `/backup-status` | |
| → RPO Comparison | `/veeam-comparison` | |
| → Ticket Analysis | `/veeam-analysis` | |
| Engagement ▾ | submenu | super-admin |
| → Overview | `/engagement` | |
| → Employee Profile | `/engagement/profile` | |
| Analyzer ▾ | submenu | All |
| → Browse Tickets | `/analyzer/tickets` | |
| → Aggregate Reports | `/analyzer/reports` | |
| → Needs Review | `/analyzer/queue` | |
| → IT Glue — Applications | `/analyzer/itglue/applications` | |
| → IT Glue — Configurations | `/analyzer/itglue/configurations` | |
| Admin | `/admin` | super-admin |
Role gating is hardcoded against `session.user.role` . Hidden items are not
rendered (no greyed-out variants).
`/admin` is the entry tile for everything admin-only — sync schedules, rules,
mappings, RMM Overshell, IT Glue write log, device-link conflicts. Sub-pages
don't appear in the top nav; they're reached from the admin landing grid.
### Mobile
`AppNavigation` returns `null` for any path under `/mobile` — kiosk and field
flows have their own shell. The desktop nav doesn't currently collapse to a
hamburger; under `sm` the brand wordmark hides and the menu items wrap.
Mobile-friendly behavior on small viewports is on the improvements list.
## 5. Components
### shadcn/ui primitives (in `components/ui/`)
| Available | Used heavily | Rare / underused |
|---|---|---|
| `accordion` | | yes (collapsible filters could use it) |
| `alert` , `alert-dialog` | yes (errors, confirmations) | |
| `badge` | yes (status, counts) | |
| `button` | yes | |
| `calendar` , `popover` | | engagement profile |
| `card` , `card-header/title/content` | yes (page sections) | |
| `checkbox` , `switch` | yes | |
| `collapsible` | yes (CI page) | |
| `dialog` , `alert-dialog` | yes (modals) | |
| `dropdown-menu` | yes (actions) | |
| `form` (react-hook-form bridge) | admin/auth only | |
| `input` , `label` , `textarea` | yes | |
| `multi-select` | analyzer filters | |
| `navigation-menu` | top bar only | |
| `progress` | | yes (could replace ad-hoc bars) |
| `scroll-area` | | yes |
| `select` | yes | |
| `separator` | yes | |
| `skeleton` | yes (loading states) | |
| `slider` | rare | |
| `sonner` (toast) | yes — `richColors` , top-right | |
| `table` | **inconsistent** — many pages use raw `<table>` | |
| `tabs` | yes (Engagement, CI, DetailModal) | |
### Feature components
| Path | Purpose |
|---|---|
| `components/navigation/app-navigation.tsx` | Top nav + `PageHeader` |
| `components/admin/DataTable.tsx` | Custom paginated/sortable table (not @tanstack ) |
| `components/admin/DetailModal.tsx` | Ticket detail shell, tabs (status/priority maps now in `lib/status-registry.ts` ) |
| `components/branding/wulf-mark.tsx` | `<WulfMark />` — W glyph or full wordmark |
| `components/branding/tagline-footer.tsx` | "Don't be afraid to cry · Wulf Consulting" footer line |
| `components/dashboard/kpi-card.tsx` | KpiCard with delta indicator and tonal left border |
| `components/navigation/page-header.tsx` | `<PageHeader />` — title, breadcrumbs, actions; supports `accent` and `watermark` |
| `components/navigation/status-indicator.tsx` | Top-bar StatusLight that links to `/status` |
| `components/ui/empty-state.tsx` | Shared zero-data placeholder |
| `components/ui/status-badge.tsx` | Small rounded pill driven by `lib/status-registry.ts` |
| `components/ui/status-light.tsx` | 8 px square indicator (5 states, 3 sizes) |
| `components/admin/SyncScheduler.tsx` | Schedule editor on `/admin` |
| `components/analyzer/analyze-button.tsx` | Trigger analysis from a ticket |
| `components/analyzer/share-modal.tsx` | Email share dialog |
| `components/analyzer/provider-toggle.tsx` | Anthropic/OpenRouter switch |
| `components/analyzer/related-tickets-panel.tsx` | Bundle members on analysis page |
| `components/analyzer/itglue-suggestions-panel.tsx` | Inline IT Glue cross-refs |
| `components/rmm/rmm-dispatch-dialog.tsx` | Pick + execute RMM script |
| `components/rmm/rmm-script-picker.tsx` | Script registry browser |
| `components/rmm/rmm-execution-stream.tsx` | Live tail of an execution |
| `components/configuration-items/config-item-modal.tsx` | CI detail shell |
Anything not in those two directories — and not a one-off page-local
subcomponent — should probably move there.
## 6. Icons
`lucide-react` , imported per-icon. Conventions:
- Inline with text: `h-4 w-4` (16 px), with `mr-2` if leading.
- Standalone tile/card icons: `h-6 w-6` .
- Admin landing tile icons: `h-8 w-8` plus a muted color (`text-muted-foreground` ).
- Color: inherit from text; status icons (`text-orange-600` , `text-green-600` )
only when they carry semantic meaning, not for decoration.
Mixed `size-4` vs `h-4 w-4` exists today. Prefer `h-4 w-4` to match shadcn.
## 7. Tables, modals, forms
### Tables
There are two patterns in the codebase. Pick the leftmost that fits:
1. ** `components/admin/DataTable.tsx` ** — paginated, sortable, searchable.
Backed by `@tanstack/react-table` v8 in manual mode. Use this for any
list ≥ 25 rows or where users need to filter / sort. Pass
`getRowCanExpand` + `renderSubRow` for drill-down rows.
2. **shadcn `Table`** primitive in `components/ui/table.tsx` — for short
static lists wrapped in a `<Card>` with `<CardContent className="p-0">` .
Raw `<table>` markup is no longer in use anywhere under `app/` . If you find
yourself reaching for it, lift the layout into one of the two primitives.
### Modals & dialogs
- **`Dialog` ** — most cases (forms, confirmations, RMM dispatch).
- **`AlertDialog` ** — destructive confirmations only.
- **`DetailModal` ** — tabbed read-only deep-dive on a ticket. Hardcoded
status/priority color map; if you need that map elsewhere, lift it out
rather than copy.
- A "panel" (e.g., `related-tickets-panel` ) is a card laid out like a side
panel — it is not a modal and shouldn't trap focus.
### Forms
- **react-hook-form + Zod resolver** with the shadcn `Form` primitive — used
on auth and admin forms (invites, role edits). Use this for any new form
with ≥ 3 fields or any field that needs validation.
- **Controlled inputs** (`useState` + `onChange` ) — acceptable for one-off
filter bars. Don't introduce a third pattern.
Error display today is inconsistent (toast vs inline). New forms should
display field errors inline via `<FormMessage>` and use toasts only for
submit-time outcomes.
## 8. Feedback
- **Toasts** — `sonner` configured top-right with `richColors` (`app/layout.tsx` ).
Use `toast.success` / `toast.error` / `toast.info` . Don't render error
banners inside the page when a toast fits.
- **Loading** — `<Skeleton>` for content placeholders, `<Loader2 />` from
lucide with `animate-spin` for inline button spinners. Don't roll a custom
spinner div.
- **Empty states** — today: a centered `text-muted-foreground` line. Aim:
a small icon (lucide), a one-line headline, an optional CTA. There is no
shared `EmptyState` component yet — adding one is on the improvements list.
## 9. Charts
`recharts` , themed via the `--chart-1` … `--chart-5` CSS variables (Tailwind
exposes them as `text-chart-1` etc., so colors flip with light/dark
automatically). Common margins `{ top: 5, right: 8, bottom: 24, left: 0 }` .
Examples: `app/dashboard/page.tsx` , `app/engagement/page.tsx` .
## 10. Open improvements (this branch)
The `nav-design-improvements` branch tracks visual/UX cleanup. The list
below is the working backlog; expand as we go.
### Navigation
- [x] ~~Top-nav "Admin" item is a flat link~~ — now a dropdown with the
seven most-used admin pages (Sync, Workflow, RMM Overshell, IT Glue
Writes, Device Conflicts, Users & roles).
- [x] ~~No user menu / avatar / sign-out in the top bar~~ — `<UserMenu />`
now sits next to `ThemeToggle` . Shows initials, name/email, role
badge, links to `/settings` + `/settings/security` , and sign-out.
- [x] ~~No mobile-collapsed (hamburger) version~~ — `<MobileNav />` in a
Sheet. Reuses the desktop nav config so IA stays in sync. Desktop nav
hides under `md` .
- [x] ~~Submenu width is fixed~~ — now content-driven via `min-w-[320px]
max-w-[440px]`, single column with denser rows, so 7-item Admin and
5-item Analyzer menus fit without forced two-column awkwardness.
- [x] ~~Active-route highlighting on submenu items~~ — every top-bar item
(flat or trigger) marks active state with a 2 px Wulf-blue underline
that echoes the PageHeader rule. Active children inside dropdowns get
a tinted `bg-primary/10` background.
### Page shell
- [x] ~~Adopt `PageHeader` everywhere~~ — `/dashboard` , `/status` , all
`/admin/*` sub-pages, `/addigy-devices` , `/analyzer/ticket/[…]` ,
2026-05-03 09:42:04 -04:00
`/analyzer/analysis/[…]` , `/settings` , `/settings/security` ,
`/sentinelone/coverage` , `/sentinelone/mappings` now use it. Kiosk
uses its own shell; the only stragglers are very small/internal
pages.
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
- [ ] Standardize container + padding (`container mx-auto px-6 py-6` ). Pages
using `px-4` , `py-8` , `max-w-2xl` etc. should justify the deviation.
- [ ] Standardize section spacing (`space-y-6` between cards; pick `gap-6`
for grids).
- [x] ~~Add breadcrumbs to ticket / analysis detail routes~~ . Still open
for `/configuration-items/[id]` and `/analyzer/itglue/{applications,
configurations}/[id]`.
### Components
- [x] ~~Build a shared `<EmptyState />`~~ — `components/ui/empty-state.tsx` .
- [x] ~~Build a shared `<StatusBadge />` that consumes the priority/status maps
from `DetailModal` ~~ — registry in `lib/status-registry.ts` ,
badge in `components/ui/status-badge.tsx` , DetailModal migrated.
- [x] ~~Extract `PageHeader` and add `accent` + `watermark` props~~ —
`components/navigation/page-header.tsx` . Adopted on `/dashboard`
and `/status` .
- [x] ~~Replace raw `<table>` markup on admin and Addigy pages with shadcn
`Table` ~~ — Addigy, /status, all admin/sync/* pages, backup-status,
engagement/profile, veeam-comparison migrated. Only `/veeam-analysis`
remains (DataTable territory; filter + expand + paginate).
- [x] ~~Replace raw `<input type="checkbox">` on `/addigy-devices`~~ —
done; also caught one in `/admin/sync/mimecast` .
- [x] ~~/veeam-analysis raw `<table>`~~ — migrated to shadcn `Table`
primitive. The page keeps its own pagination + category filter + row
expansion (DataTable doesn't support row expansion yet, so a deeper
DataTable migration is folded into the long-term decision below).
- [x] ~~Decide on `DataTable` long-term~~ — **migrated to
`@tanstack/react-table` v8** in manual mode. The external API stays
stable (existing data-browser pages keep working without changes);
internally TanStack drives sort + expansion. New optional props for
consumers: `getRowCanExpand` and `renderSubRow` enable
expandable-row patterns (useful for `/veeam-analysis` -style
drill-downs).
2026-05-03 10:20:27 -04:00
### Status indicator popover (2026-05-03)
- [x] Top-bar StatusIndicator now opens a Popover instead of routing
directly to `/status` . The popover groups inline issues by tone
(failing integrations, expired tokens, expiring tokens) so a quick
glance answers "what's broken." A "View full status" link at the
bottom routes to `/status` .
### CSV export on DataTable (2026-05-03)
- [x] Optional `exportable` + `exportFilename` props add an "Export CSV"
button next to the search bar. Defaults to current-page export;
provide `onExportAll` for server-side full-result downloads.
Enabled on `/admin/data-browser/companies` and
`/admin/data-browser/tickets` .
2026-05-03 10:11:08 -04:00
### Command palette (2026-05-03)
- [x] **Cmd+K / Ctrl+K** opens a global launcher
(`components/navigation/command-palette.tsx` ). Three sections:
Navigation (every primary route, role-gated), Recent activity
(last 5 audits + 5 observations from `/api/dashboard/overview` ),
Companies (filtered client-side from `/api/companies` ). The `/`
key also opens it when no input is focused. Top-bar shows a small
"Search · ⌘K" button (md+) for discoverability.
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
### Status & dashboard split (2026-05-03)
- [x] Move integration health + sync health off `/dashboard` onto a
dedicated `/status` route. Top-bar `<StatusIndicator />` links there.
- [x] Rebuild `/dashboard` around KPI cards (today snapshot + needs
attention) and recent activity. Drop the integration / sync
cards.
- [x] ~~Add queue × priority heatmap to `/dashboard`~~ — `QueueHeatmap`
backed by `/api/dashboard/trends` .
- [x] ~~Add 30-day volume + mean resolution charts~~ — `VolumeTrend` and
`ResolutionTrend` components, recharts, brand-blue series.
- [x] ~~Add active engineers panel~~ — `ActiveEngineers` (today's hours
logged, ticket touch count).
- [x] ~~Worker pulse section on `/status`~~ — analyzer / RMM / sync
scheduler heartbeats via `/api/status/workers` and `WorkerPulse` .
2026-05-03 10:03:54 -04:00
- [x] ~~Worker activity sparklines~~ — `ActivitySparkline` shows 24
hourly buckets per worker (success bottom-up in primary, failure
top-down in destructive). Backed by zero-filled hour series
generated in the `/api/status/workers` query.
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
### Tokens & theming
2026-05-03 09:42:04 -04:00
- [-] Hard-coded Tailwind palette colors are extensive (~770 references)
but most are **semantic** (status reds, info blues, warning ambers
via the `bg-{hue}-500/15 text-{hue}-700` recipe documented above).
Surveyed and deprioritized — case-by-case cleanup as new work
touches a page.
feat(admin): DB-backed integration toggles + sticky cols + dark contrast
Builds on the env-var INTEGRATIONS_DISABLED shipped with the nav-design
overhaul. Adds a DB-backed admin UI so operators can flip integrations
without editing .env and restarting the container, plus the remaining
visual cleanup items from the design backlog.
Integration toggles
- Migration 081 — integration_settings table (key PK, disabled flag,
reason, disabled_by audit, disabled_at). Seeded with all 13 known
integrations as enabled.
- GET / PATCH /api/admin/integrations — gated by requirePermission
(admin, access). PATCH clears the in-process integration-health
cache so toggles take effect within seconds.
- /admin/integrations admin page with a Switch per integration, optional
reason input, audit-info subtitle (disabled by, when, why), live
status light from /api/dashboard/integration-health.
- integration-health service merges env-var disable list with DB rows;
degrades gracefully if migration unapplied / DB unreachable.
- Wired into the Admin nav dropdown (eight items now).
- CLAUDE.md describes both env + DB sources.
Sticky first column on tables
- Table primitive accepts stickyFirstColumn?: boolean. When true, TH
and TD :first-child stay pinned during horizontal scroll, with
background inheritance preserving hover and selected row tints.
- DataTable exposes the prop too — on by default for paginated tables.
- /addigy-devices opts in.
Dark-mode contrast
- --border lifted from 10% to 14% in .dark; --input from 15% to 18%;
--sidebar-border to 14%.
- StatusLight outline ring lifted from /10 to /15 (light) and /20 (dark).
- DetailModal empty-cell em-dash lifted from /40 to /70 so missing
values are legible on dark surfaces.
DESIGN.md
- Closed sticky-first-column, dark-mode contrast, and palette-audit
items (palette deprioritized — most uses are semantic).
- Skeleton helpers documented as preferred for new code; existing
ad-hoc patterns left in place.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:55:22 -04:00
- [x] ~~Verify dark-mode contrast on status badges and chart legends~~ —
bumped dark `--border` from 10% to 14%, `--input` from 15% to 18%,
`--sidebar-border` to 14%. `<StatusLight>` outline lifted to
`ring-foreground/15 dark:ring-foreground/20` . DetailModal empty-cell
em-dash lifted from `/40` to `/70` so missing-value placeholders are
legible on dark surfaces.
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
### Loading & empty
- [x] ~~Standardize Skeleton heights~~ — helpers in
`components/ui/skeleton-helpers.tsx` : `SkeletonRow` , `SkeletonRows` ,
`SkeletonCard` , `SkeletonChart` , `SkeletonHeader` , `SkeletonTable` .
feat(admin): DB-backed integration toggles + sticky cols + dark contrast
Builds on the env-var INTEGRATIONS_DISABLED shipped with the nav-design
overhaul. Adds a DB-backed admin UI so operators can flip integrations
without editing .env and restarting the container, plus the remaining
visual cleanup items from the design backlog.
Integration toggles
- Migration 081 — integration_settings table (key PK, disabled flag,
reason, disabled_by audit, disabled_at). Seeded with all 13 known
integrations as enabled.
- GET / PATCH /api/admin/integrations — gated by requirePermission
(admin, access). PATCH clears the in-process integration-health
cache so toggles take effect within seconds.
- /admin/integrations admin page with a Switch per integration, optional
reason input, audit-info subtitle (disabled by, when, why), live
status light from /api/dashboard/integration-health.
- integration-health service merges env-var disable list with DB rows;
degrades gracefully if migration unapplied / DB unreachable.
- Wired into the Admin nav dropdown (eight items now).
- CLAUDE.md describes both env + DB sources.
Sticky first column on tables
- Table primitive accepts stickyFirstColumn?: boolean. When true, TH
and TD :first-child stay pinned during horizontal scroll, with
background inheritance preserving hover and selected row tints.
- DataTable exposes the prop too — on by default for paginated tables.
- /addigy-devices opts in.
Dark-mode contrast
- --border lifted from 10% to 14% in .dark; --input from 15% to 18%;
--sidebar-border to 14%.
- StatusLight outline ring lifted from /10 to /15 (light) and /20 (dark).
- DetailModal empty-cell em-dash lifted from /40 to /70 so missing
values are legible on dark surfaces.
DESIGN.md
- Closed sticky-first-column, dark-mode contrast, and palette-audit
items (palette deprioritized — most uses are semantic).
- Skeleton helpers documented as preferred for new code; existing
ad-hoc patterns left in place.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:55:22 -04:00
Adopted on `/dashboard` and `/status` .
- [-] Helpers are **preferred for new code** . Existing ad-hoc
`<Skeleton h-NN>` patterns aren't broken (they render the same
shape just with arbitrary heights); leave them in place and
migrate opportunistically when touching the surrounding code.
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
### Mobile
- [x] ~~CI filter bar overflows on small viewports~~ — company selector
now wraps and shrinks; the stat pill flows below.
- [x] ~~Analyzer multi-select dropdowns clip on narrow widths~~ —
Popover gets `max-w-[calc(100vw-1rem)]` and `collisionPadding={8}` .
feat(admin): DB-backed integration toggles + sticky cols + dark contrast
Builds on the env-var INTEGRATIONS_DISABLED shipped with the nav-design
overhaul. Adds a DB-backed admin UI so operators can flip integrations
without editing .env and restarting the container, plus the remaining
visual cleanup items from the design backlog.
Integration toggles
- Migration 081 — integration_settings table (key PK, disabled flag,
reason, disabled_by audit, disabled_at). Seeded with all 13 known
integrations as enabled.
- GET / PATCH /api/admin/integrations — gated by requirePermission
(admin, access). PATCH clears the in-process integration-health
cache so toggles take effect within seconds.
- /admin/integrations admin page with a Switch per integration, optional
reason input, audit-info subtitle (disabled by, when, why), live
status light from /api/dashboard/integration-health.
- integration-health service merges env-var disable list with DB rows;
degrades gracefully if migration unapplied / DB unreachable.
- Wired into the Admin nav dropdown (eight items now).
- CLAUDE.md describes both env + DB sources.
Sticky first column on tables
- Table primitive accepts stickyFirstColumn?: boolean. When true, TH
and TD :first-child stay pinned during horizontal scroll, with
background inheritance preserving hover and selected row tints.
- DataTable exposes the prop too — on by default for paginated tables.
- /addigy-devices opts in.
Dark-mode contrast
- --border lifted from 10% to 14% in .dark; --input from 15% to 18%;
--sidebar-border to 14%.
- StatusLight outline ring lifted from /10 to /15 (light) and /20 (dark).
- DetailModal empty-cell em-dash lifted from /40 to /70 so missing
values are legible on dark surfaces.
DESIGN.md
- Closed sticky-first-column, dark-mode contrast, and palette-audit
items (palette deprioritized — most uses are semantic).
- Skeleton helpers documented as preferred for new code; existing
ad-hoc patterns left in place.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:55:22 -04:00
- [x] ~~Tables horizontally scroll without a sticky first column~~ —
`Table` primitive accepts `stickyFirstColumn` (also exposed on
`DataTable` and on by default for paginated tables). Hover and
selected row backgrounds carry through.
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
## 11. When in doubt
- Use a token, not a hex value.
- Use the shadcn primitive, not a custom one.
- Match the surrounding page's spacing scale rather than introducing a new
one.
- If the same thing exists in two shapes (e.g., raw vs DataTable), pick the
shape this doc documents and migrate the other.