Commit Graph

26 Commits (551c9e3d30adbf0957bfc1927affb46877f6fe38)

Author SHA1 Message Date
Thomas Joise 4de152a7f4 fix(ui): restore sticky table headers, dashboard loading flash, ticket-type over-fetch
- packages/ui/src/tokens.css: bound .wf-table-wrap to a viewport-derived max-height
  and matching overflow-y so it's a real scroll container again. overflow-x: auto
  alone left overflow-y at its computed "auto" (per the CSS overflow spec, a
  visible/non-visible axis pairing promotes visible to auto) with no height bound,
  so its scrollTop was permanently 0 and table.wf th's `position: sticky` never had
  anything to stick against — the header just scrolled away with the page on every
  table screen. Chose the bounded-wrapper fix (over deleting the dead sticky rule)
  since it keeps the header pinned as advertised; short tables render unchanged,
  long ones scroll inside the box exactly like the existing .dash-list pattern.
  Also moved the .wf-table-wrap rule out of apps/hq-web/src/app.css into tokens.css
  next to the other table.wf rules, since it's part of the @sims/ui component.
- apps/hq-web/src/pages/Dashboard.tsx: the "Onboarding stalled" card treated
  "still loading" the same as "genuinely empty", flashing "Nothing stalled."
  before the fetch resolved. Card now takes a loading prop and shows the usual
  skeleton placeholder while pending; the positive empty copy and error state
  are unchanged.
- apps/hq-web/src/pages/ClientDetail.tsx: getTicketTypes was fetched unconditionally
  on every Client 360 load for the tickets tab's Type column. Gated it behind the
  tickets tab being active (moved the tab computation earlier so it's available to
  gate the loader); NewTicketDialog's own getTicketTypes call for the new-ticket
  pre-fill is untouched.
- STATUS.md: prose under the verification table still said "The 416 figure..." while
  the table itself says 528 — updated the prose to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 day ago
Thomas Joise 0bcbf8d410 fix(responsive): stop desktop tables and mobile rows from silently clipping content
Red-team pass on the redesigned Client 360 / Tickets / Dashboard / Modules screens found
real overlap/clipping bugs, all fixed:

- Client header still broke at 768px (tablet): the button cluster only stacked below the
  name under 640px, so at 768 the name got squeezed to ~38px and force-wrapped mid-word.
  Bumped the stacking breakpoint to 860px.
- Per-module Documents rows and the Payments Outstanding-panel rows were plain nowrap flex
  rows with no scroll; at 375px the amount/status badge silently clipped past the card
  edge with no way to reach them. Both now wrap.
- Biggest one: every DataTable silently lost its rightmost columns at desktop width
  (>=901px) — table.wf's display:block/table toggle assumed overflow-x:auto still worked
  once display flipped to table, but Chromium computes overflow-x as visible on a
  display:table box, so wide tables (Tickets' 11 columns) overflowed the viewport with no
  scrollbar. Fixed at the root: DataTable now wraps its <table> in a .wf-table-wrap scroll
  container instead of relying on the table element to scroll itself.

Verified against screenshots at 375/768/1440, light+dark, before and after; typecheck and
the full test suite (498 passed) stay clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 day ago
Thomas Joise 371cdb1c69 fix(ui): drop redundant table-scroll CSS, rely on existing app.css mechanism
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 days ago
Thomas Joise 0050f73a70 fix(ui): stack record-header actions and scroll wide tables on mobile
RecordHeader's action-button cluster crowded/overlapped the client name on
narrow phones, and DataTable (Tickets, etc.) ran wider than the viewport,
clipping columns with no way to reach them.

Add a <=640px media query: .wf-rec wraps so .wf-rec-actions drops to its
own full-width row below the avatar/name/meta; desktop layout is untouched
outside the query. Wrap DataTable's <table> in a new .wf-table-wrap div
that scrolls horizontally on overflow at that breakpoint (nowrap cells so
the table can exceed 100% instead of being squeezed illegibly), keeping
the page body itself from scrolling sideways.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 days ago
Thomas Joise 5679a94d3c feat(ui): click-to-sort on every DataTable (asc/desc/off), index-safe
Column headers are now sortable across every table in the app (one shared component).
Sort compares cell text — numeric columns by their leading number, else natural text.
Sorting preserves the original row index so onRowClick/rowTone keep pointing at the
right record; action columns (empty label) aren't sortable. asc → desc → off cycle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 days ago
Thomas Joise 24f52fad0a feat: theme palettes + Geist font + dashboard cockpit styles
Add a `data-palette` theme axis (neutral ground) alongside mode + accent:
Warm (default), Slate, Graphite, Zinc — selectable in Settings > Appearance,
persisted per browser. Backward-compatible: `warm` needs no attribute so the
existing look is unchanged; accent (7 hues) and light/dark stay orthogonal.

Swap the UI font Inter -> Geist Variable + Geist Mono Variable (bundled offline).

Add the `.dash-*` cockpit styles finishing the redesigned Dashboard markup
already in tree (flat Today hero, KPI row, reminder queue, bento sections;
low-colour, red reserved for real failures).

packages/ui theme.ts/tokens.css/ThemeSwitcher.tsx are shared — restyles every
page. Typecheck clean; 410 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3 days ago
Thomas Joise bbe125892d fix(d26): accent picker off top bar + mobile UX pass + responsive billing
- The 7-swatch accent selector is removed from the top bar (it now lives only
  in Settings > Appearance); the bar keeps a single compact light/dark ModeToggle.
  This declutters the header and fixes most of the mobile congestion.
- Mobile pass (<=640px): search takes the bar, Ctrl-K hint + Gmail pill hidden,
  avatar-only user chip; pages get real padding + bottom room; toolbars/chips
  wrap with air; inputs full-width & 42px tall; stats 2-up; tables padded.
- Billing composer: client search + line-item cards go full-width and stack on
  phones (composer-search / composer-line classes) so no container is undersized.
typecheck + web build clean, web tests 4/4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 9b920e50f9 feat(d25): bright doc type/status colours + sectioned client details + username UI
- Badge extended with green/blue/violet/gold/red tones (light+dark). Document
  TYPE badges: Quotation=gold, Proforma=violet, Invoice=teal, Credit note=red,
  Receipt=green (Documents, DocumentView, Client 360). STATUS brightened:
  paid/accepted=green, sent=blue, part-paid=amber, draft=grey, lost/cancelled=red.
- Client 360: new sectioned Details card (identity, full address, contacts
  labeled Office/Secretary/WhatsApp with mailto:/tel: links).
- Employees create form gains a required Username (login id) field + table
  column; Login field relabeled 'Username'.
typecheck + web build clean, web tests 4/4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 35a26de442 fix(review): frontend quality pass — a11y, UX safety, error handling (medium/low)
Red-team frontend clusters (a11y + UX + code quality), three coordinated passes:

Accessibility:
- DataTable clickable rows keyboard-operable (role=button, tabindex, Enter/Space).
- <main> landmark + skip link; icon-only buttons get aria-labels; inputs relying
  on placeholder alone get aria-labels; AWS-ranking link is a real <button>;
  Login fields wrapped in a <form> so Enter submits from either field; toasts
  role=alert for errors (urgency not by color alone).

UX safety:
- 'Notify all' and reminder 'Send' now behind ConfirmDialogs naming the count/
  client; document action bar disables while an action is in flight (no
  double-submit dupes); SendDialog surfaces why a send is blocked instead of
  failing silently; client search + ticket search debounced (~300ms); inline
  Client-360 edit failures surface via toast, not an off-screen banner; LivePreview
  gets a real Retry; destructive client-status transitions confirm first.

Code quality:
- ErrorBoundary wraps the app (a render throw no longer white-screens).
- useData clears stale data on dep change (no record flashing under a new id).
- Shared ErrorBoundary/SecretField/Pager components extracted; Pager adopted in
  Documents; write-call api types tightened off Record<string,unknown>.

typecheck clean (root + both workspaces), web build clean, web tests 4/4, full
suite 388 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise f812555528 fix(sec/d24): money-integrity guards — CN cap, atomic doc-no, qty, TDS, markStatus
Red-team money findings:
- createCreditNote caps the CN to the invoice's remaining creditable value
  (invoice payable − already-credited by non-cancelled CNs), read+insert in
  one txn. Blocks a CN exceeding its invoice / unlimited CNs flipping it 'paid'.
- nextDocNo allocates via one atomic UPDATE ... RETURNING (row lock) instead of
  read-then-bump — no duplicate/burned numbers under concurrent issue (Postgres).
- markStatus rejects accepted/lost on non-QUOTATION docs — an issued invoice can
  no longer be hidden from overdue chasing via a quote-only transition.
- computeBill bounds qty magnitude (keeps fractional for weighed Store goods);
  HQ buildLines additionally requires whole-unit integer qty (SMS/module seats).
- recordPayment rejects TDS greater than the cash received — no fabricated-TDS
  'settlement' with no money.
money-guards test locks all four. Suite money paths green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 3c2e51ae6c feat(d22): P1 — onboarding milestone tracker (backend) + monotonic uuidv7
project_milestone: each client_module (a project) carries a checklist
seeded from the dated project.milestone_template setting (advance paid /
setup / installed / go-live / balance / AMC — owner-editable). Ticking
stamps the date; audited. ensureProjectMilestones is idempotent and
back-fills imported projects. Cross-project board (per-milestone pending
counts) + drill-down (who's pending go-live). Routes: GET/POST
/client-modules/:id/milestones, GET /projects/board, /projects/pending/:key.
assignModule seeds the checklist for new projects.

Also fixes a latent flaky-ordering bug: uuidv7() now bumps monotonically
within a process, so ORDER BY id is genuine creation order (the guarantee
pagination + the sync cursor already assume) — sub-millisecond ties no
longer fall back to the random tail. Suite 377 green, stable across runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 60698fc08f fix(iwa): final-review fixes — dialog layering/scrim/scroll-lock, a11y labels+disabled+alerts, settings guards+errors, edit-mode locks, strict offsets, titles
27 adversarially-confirmed findings from the multi-agent whole-branch review (2 deferred).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 days ago
Thomas Joise 4e2b71cf9f fix(ui): toast auto-dismiss id derived inside the state updater (React 18 batching)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 03402ba0e1 feat(ui): Toaster with pure store (TDD) + FormGrid; provider mounted at root
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 0b46c03530 fix(ui): ConfirmDialog error handling + Dialog focus hardening
- ConfirmDialog: a rejected onConfirm no longer closes the dialog as if it
  succeeded; the rejection is handled (no unhandledrejection), busy clears,
  and the error message is shown inline (role=alert)
- ConfirmDialog: Cancel / scrim / Escape / X are gated while busy, so the
  dialog cannot be dismissed (and onClose cannot double-fire) mid-flight
- Dialog: Escape + Tab trap moved to a document-level keydown listener so
  they still work when focus escapes the card (e.g. lands on <body>)
- Dialog: focus restore now runs via effect cleanup, covering the
  {show && <Dialog/>} unmount-while-open pattern
- Dialog: initial focus skips disabled controls

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 2fbb4c6442 feat(ui): Dialog + ConfirmDialog (Pinterest chrome), pill buttons; Modal removed
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise e9357bd8e6 fix(redesign): final-review fixes — palette races+a11y, avatar dark contrast, micro-label floor, drawer focus, pulse legend/labels/targets, warn contrast
26 adversarially-confirmed findings from the multi-agent whole-branch review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 00ce1d657e feat(hq-web): dashboard — skeletons, toned stats, tinted queue, 2-col grid
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 3045b889ff feat(ui): command palette with pure AND-term matcher
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 6dbddd9822 feat(ui): RecordHeader; PageHeader breadcrumb; DataTable rowTone + mono columns
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise c299aea18c feat(ui): Skeleton, ErrorState, FilterChips, Tabs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise b767a0c191 feat(ui): deterministic avatar initials + hue helper
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise fccc189b2c feat(ui): warm token system — teal default accent, ink primary, flat surfaces
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 7e1c93cee0 Split: make this the standalone HQ project (remove Store)
This repo keeps its 60-commit HQ history and becomes independent of the Store
product. Removed the Store apps (pos, store-server, backoffice), the Store-only
packages (config, scanning, search-core, printing — printing was already a dead
dependency in hq-web), and the Store planning docs.

Shared packages are forked and trimmed to exactly what HQ uses: domain keeps
ids/money/business-day/doc-series/gstin/documents; auth keeps pin; billing-engine
keeps compute/tax. Store-only tests for the removed modules were dropped, and
auth.test.ts was trimmed to its PIN blocks. Package renamed sims-hq; root
tsconfig/vitest paths narrowed to the four kept packages.

Verified independently: npm install + typecheck clean, 177 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 3d3487c85f wip(store-product): returns, GST returns, offline queue, security hardening + docs
Snapshot of the store-product workstream's in-progress work (apps/pos,
apps/backoffice, apps/store-server, packages): returns + credit notes, GST
returns exports, offline bill queue, rate-limiting, session policy, print
guard, batch tracking, and the SEC-C6/SEC-A5 auth fixes; plus docs/18 red-team
review. Committed to preserve work for the repo push. HQ console already
committed separately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 406839659d chore: bring the full store-product codebase under version control
POS, back office, store-server, all packages, workspace configs, README/
BUILDING, and the WIP HQ-2 plan — everything except gitignored data
(client import staging, .env, local DBs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago