118 lines
4.9 KiB
Markdown
118 lines
4.9 KiB
Markdown
# KROW Workforce — redesign demo
|
||
|
||
A complete redesign prototype of the KROW Workforce product with Owliver, the
|
||
workforce-intelligence panel, built in.
|
||
|
||
Everything is local. There is no backend, API, database, authentication or
|
||
outbound network call — all figures come from a single typed mock-data module.
|
||
|
||
## Running it
|
||
|
||
```bash
|
||
npm install
|
||
npm run dev # http://localhost:5173
|
||
npm run build # production build
|
||
npm run typecheck # tsc --noEmit
|
||
npm run lint # oxlint
|
||
```
|
||
|
||
## Stack
|
||
|
||
Vite · React 19 · TypeScript · Tailwind CSS v4 · shadcn/ui-style components on
|
||
Radix · Lucide · Recharts · TanStack Table v9 · React Router.
|
||
|
||
## Layout
|
||
|
||
One CSS grid (`.app-shell` in `src/index.css`):
|
||
|
||
```
|
||
┌──────────────────────────────────────────────┐
|
||
│ sidebar │ GLOBAL HEADER (64px) │
|
||
│ (full ├────────────────────┬─────────────┤
|
||
│ height) │ MAIN CONTENT │ OWLIVER │
|
||
└───────────┴────────────────────┴─────────────┘
|
||
```
|
||
|
||
The header spans main + Owliver only; the sidebar owns the full viewport
|
||
height and all navigation. Owliver sits in its own rail on row 2, so it can
|
||
never overlap or be clipped by the header.
|
||
|
||
**Breakpoints** — desktop ≥1280: all three columns. Tablet 768–1279: icon rail
|
||
plus main, Owliver becomes an overlay. Mobile <768: sidebar becomes a drawer
|
||
(with the complete navigation, nothing removed), Owliver a bottom sheet.
|
||
|
||
## Structure
|
||
|
||
```
|
||
src/
|
||
config/navigation.ts information architecture (mirrors the vendor workspace)
|
||
data/ types, mockData, selectors, store
|
||
owliver/ block types, response engine, block renderers
|
||
components/shell/ AppShell, Sidebar, TopHeader
|
||
components/owliver/ panel + composer
|
||
components/shared/ MetricCard, DataTable, Page, Toolbar, StatusBadge
|
||
components/ui/ Radix-based primitives
|
||
pages/ the fourteen product pages
|
||
```
|
||
|
||
`src/data/mockData.ts` is the single source of truth and `selectors.ts` derives
|
||
every computed figure, so a number on a KPI card and the same number quoted by
|
||
Owliver cannot disagree.
|
||
|
||
## Home
|
||
|
||
Home is an AI workspace rather than a dashboard. The hierarchy is greeting →
|
||
central Owliver card → quick actions → compact metrics → Today.
|
||
|
||
Performance content (utilisation, trends, attendance, performance health)
|
||
deliberately does **not** appear on Home — it lives on the Performance page,
|
||
which remains complete.
|
||
|
||
## Owliver
|
||
|
||
**One Owliver per page, never two.** Home shows the central card and no right
|
||
rail; every other page shows the right rail and no central card. On Home the
|
||
grid drops to two columns, so main reflows into the space rather than leaving an
|
||
empty rail behind — and the rail stays hidden on Home even if it was left open
|
||
elsewhere.
|
||
|
||
That means the central card owns the whole interaction on Home: it holds the
|
||
conversation, the composer and the suggestions, and an answer appears inside
|
||
that thread. Replies are typed block lists rendered as real components — text,
|
||
metric cards, alerts, tables, charts, a staff-allocation flow diagram,
|
||
recommendations, expandable details and working action buttons.
|
||
|
||
Home's thread starts empty (an invitation to ask). Every other page opens with a
|
||
greeting, its own brief and one seeded exchange, so the You / Owliver
|
||
distinction is legible straight away.
|
||
|
||
State lives in `OwliverProvider`, above the panel, which gives two properties
|
||
the panel could not provide on its own:
|
||
|
||
- **History survives closing.** Collapsing the rail, or the tablet/mobile sheet
|
||
unmounting, no longer discards the conversation.
|
||
- **Each page keeps its own thread.** Navigating switches context instead of
|
||
leaving Home's answers on the Payroll page; returning to a page restores what
|
||
was asked there.
|
||
|
||
Responses are deterministic and delayed 500–900 ms behind a typing indicator.
|
||
**Resolve Understaffed Shifts** mutates the shared data, so the greeting, the
|
||
metrics and the sidebar badges all update behind the panel.
|
||
|
||
## Shell controls
|
||
|
||
The sidebar collapses to a 72px icon rail (labels become tooltips; active state
|
||
and badges are preserved) and Owliver closes so main reclaims its column. Both
|
||
are driven by `data-sidebar` / `data-owliver` attributes on the grid container,
|
||
so the layout reflows rather than overlaying.
|
||
|
||
## Notes for future work
|
||
|
||
Two bugs worth remembering, both fixed here:
|
||
|
||
- Passing a fresh `state` / `initialState` object to TanStack Table v9's
|
||
`useTable` on every render makes its store notify → re-render → notify.
|
||
Both are memoised in `DataTable.tsx`.
|
||
- Loading the whole app in one chunk blocked first paint long enough to look
|
||
like a hang on heavier routes. Pages are lazy-loaded in `App.tsx`.
|