Commit Graph

192 Commits (a11d6504167d3a144127cac514c0936fc23a9a80)
 

Author SHA1 Message Date
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 e919a6b871 docs(plan): HQ console redesign — 14-task implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise dc3d651b08 feat(reminders): dated reminder_schedule table + resolveSchedule resolver (Phase 4)
reminder_schedule (id, rule_kind, effective_from/_to, day_offsets CSV,
subject, body) added to SCHEMA (CREATE TABLE IF NOT EXISTS, exec'd on every
open = the additive migration path). resolveSchedule(db, ruleKind, today)
picks the row active on the business date (from<=today, to NULL or >today,
latest from wins) with code-constant fallbacks (quote_followup 3/7/14 +
spec s7 subject/body; invoice_overdue 7/15/30). seedIfEmpty seeds one
open-ended dated row per kind, audited in the same transaction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 52420e072c docs(spec): HQ console redesign — warm ops-console system upgrade
Direction A v2 discipline (flat border-defined surfaces, black-ink primary,
accent-for-state, table/state patterns, Ctrl+K palette) wearing the H-04 warm
palette + teal accent. Client 360 with 12-month relationship-pulse ribbon.
UI-only; no backend changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise efdb34e710 feat(clients): account-owner plumbing — client.owner_id, audited owner route, role gate
Phase 3 of the quote-to-close funnel spec: client.owner_id added additively
(SCHEMA + PRAGMA-guarded migrate) so a bare lead is routable to an employee;
setClientOwner repo fn (validates active employee, audits set_owner in-txn);
PATCH /clients/:id/owner gated inline to owner/manager; shared ownerScope role
gate (staff forced to self, widening ignored) for later phases; ClientDetail
gains an owner/manager-only Account owner dropdown fed by GET /employees.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise ffba5c3d3e docs(spec): APEX 82963 UX reference — team mental-model patterns for the HQ polish pass
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise d4388c4f63 feat(employees): management surface — /employees API routes + owner-only Employees page
Phase 2 of the quote-to-close funnel slice. Six /employees routes in
apps/hq/src/api.ts (GET requireAuth for the picker; POST/PATCH/deactivate/
reactivate/password owner-only, acting as the signed-in user); Employees
page (table + add/edit/reset-password forms, email immutable on edit,
count shown, no truncation); owner-only nav item beside Document Template;
typed client fns in hq-web api.ts. 6 new route tests (192 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 51e6d30e86 chore: gitignore .superpowers brainstorm session files
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 1af7b87aab feat(hq): employee foundation — manager role, employee repo, session hardening (phase 1)
- rebuildTable helper: guarded, txn-wrapped CHECK-constraint rebuild (SQLite-only path)
- staff_user.role CHECK widened to owner|manager|staff (SCHEMA + migrate for old DBs)
- repos-employees.ts: list/get/create/update/setPassword/deactivate/reactivate,
  audited in-txn, password columns never selected, last-active-owner + self guards
- verifySession requires active=1; deactivation purges sessions in the same txn
- 9 tests (apps/hq/test/employees.test.ts); suite now 186 green

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 02306b1088 docs(spec): quote-to-close funnel design (10-phase roadmap)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise a925352e70 docs: rewrite HQ plans/specs to be self-contained after the split
The historical HQ-1/2/3a build plans and the live-preview spec referenced sibling
Store apps (apps/store-server, apps/backoffice) as copy-sources and cited Store-only
docs. Rewrote those to describe the patterns intrinsically and to name the Store
product/repo where the scope boundary is genuine, so each plan stands alone in the
HQ repo. Technical content and task lists unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 75e1795bfd docs: bring planning docs current with the HQ/Store split
Update the shared-heritage docs that still described the old combined monorepo:
D15 and the HQ console spec now note the 2026-07-16 split into two independent
repos, and the deploy runbook uses the correct repo name (sims-hq, was sims-next).
Historical decisions are preserved; only stale present-tense/naming is corrected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise c03fddc699 docs(spec): record split delivered + backup deleted
Marks the split spec DELIVERED with the outcome: both repos verified, the
pre-split combined repo deleted, and the kept-@sims/kept-env decisions recorded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 715b8c5f8c docs: correct HQ run instructions — launch server from repo root
The DB path resolves to ./data/hq.db against the process working directory, so
the server must run from the repo root; `cd apps/hq && npm start` would look in
apps/hq/data and boot an empty database. Found while red-teaming the split by
booting the server live against the real data/hq.db.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 115f2e6c41 docs: HQ project identity (README, STATUS, CLAUDE) + fix cross-repo doc links
Adds the front-page README, a first STATUS.md build-state doc (the console had
none), and a CLAUDE.md with the house rules that govern this repo. De-links four
references to Store-only planning docs (04-ROADMAP, 08-MARKET-ARCHITECTURES,
09-UX-FLOWS-AND-MENUS) that now live in the Store repo, and regenerates the
lockfile to drop the removed Store workspace entries.

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 85adbb553a docs(spec): design for splitting HQ and Store into independent projects
Two products share one repo: HQ (internal client book, proforma billing,
call tracking) and Store (the GST retail product shipped to ~300 clients).
Different DBs, different audiences, different cadences — 60 of 75 commits
touch HQ, 2 touch Store.

Design: two repos. HQ inherits the existing history; Store starts fresh so
its history carries zero HQ bytes. Shared packages fork into each side,
trimmed to verified usage — safe because GST rates are dated DB rows, not
code, so the forked arithmetic has no reason to diverge.

Project tracking and conversion reporting are deferred: they don't exist
yet, and mixing new features into a mechanical move hides failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 89d92df672 docs: bring README index current (build state, red-team review, HQ runbook)
- Add a "Build state" section pointing to STATUS.md / BUILDING.md as the
  entry point now that this is a working codebase, not just a plan.
- Add docs/18-RED-TEAM-REVIEW.md and DEPLOY-HQ.md to the index.
- Reorder the "start here" pointer to STATUS.md first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 week 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 4559958fa4 docs(hq): go-live runbook, env template and S3 backup script
Turnkey deployment for the AWS box: documented env vars, pm2/systemd run,
HTTPS reverse-proxy, nightly SQLite->S3 backup with rotation, and the Postgres
switch note. No secrets; unblocks go-live the moment the server/creds arrive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 5fe86f40ba chore(hq): demo-seed script + gitignore demo data/ruvector
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise cfa1cf81f6 feat(hq-web): DocumentView share & download action group
Add a Share & download group to DocumentView: Download PDF (hits
?download=1 via a bearer-authed blob + save anchor), Create/Copy a public
/share/<token> view link with the live-share list and one-click Revoke,
Share on WhatsApp (wa.me/<client digits> prefilled with greeting + doc no
+ total + the share link), and native share (navigator.share of the link,
falling back to Download where unsupported).

Client api.ts gains the Share type, shares on getDocumentFull, createShare/
revokeShare, and downloadDocumentPdf. Vite dev proxies /share to the hq
server so a minted link opens against the dev origin exactly as in prod
(the hq server serves both the SPA and the public /share route on one
origin). Server share/download endpoints and the golden renderer are
unchanged.

Browser-verified: minted a link, opened /share/<token> in a fresh
unauthenticated context (200 application/pdf, viewer renders the doc),
revoked and confirmed a 404 "Link unavailable" page with no data leak, and
confirmed the WhatsApp deep link is prefilled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 25b96e2514 feat(hq): attachment download filename on GET /documents/:id/pdf?download=1
Serve the document PDF as an attachment with a doc-no-derived filename
("QT/26-27-0001" -> "QT-26-27-0001.pdf", null docNo -> "draft.pdf") when
?download=1 is present; keep the default inline so the composer iframe
preview still works. renderPdf is now injectable into apiRouter so the
header logic is tested without launching Chrome (house DI pattern).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 63e377d649 feat(hq): public GET /share/:token document view route
Mount the one unauthenticated read route OUTSIDE the /api auth router.
A live token resolves via validateShare to exactly one document, rendered
inline (application/pdf) through the existing documentHtml -> renderPdf path;
unknown/expired/revoked tokens return a plain 404 "link expired or invalid"
HTML page carrying no document data. Adds a dependency-free per-IP fixed-window
rate limiter to blunt token brute-forcing. renderPdf is injected so the route
is TDD-tested without launching Chrome. Public reads write no audit; the token
is never logged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise a1759864bf feat(hq): shareable document link backend (mint/validate/revoke/list)
Add document_share table (CREATE TABLE IF NOT EXISTS; opaque token, optional
expiry, revoke flag) and repos-shares.ts:

- mintShare: 256-bit randomBytes hex token (64 hex, unique), default +30d expiry,
  audited — the token itself is never written to the audit trail or any log.
- validateShare: resolves a live token to its one document; null on
  unknown / revoked / expired (ISO-8601 UTC lexical compare).
- revokeShare: flips revoked=1, audited.
- listShares: a document's shares, scoped and newest-first.

Wire POST /api/documents/:id/share and
POST /api/documents/:id/share/:shareId/revoke (requireAuth, document-scoped
share guard) and surface shares on GET /api/documents/:id. TDD: 14 tests cover
token shape/uniqueness, validate rejections, revoke, and audit-without-token.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise d29025189c feat(hq): beautify document template — "Slate Ledger" visual system + template.accent
Rewrite documentHtml to a restrained, print-first GST layout: hairline table
rules, tabular money aligned to the paisa, a single accent driving every
structural color (letterhead rule, title, table-header rule + color-mix tint,
docmeta edge, amount-in-words edge, filled grand-total band).

Add optional template.accent setting (validated hex-or-fallback #334155
slate-indigo) read from the settings map and interpolated only after
validation, so a raw setting string can never be injected into the CSS.
print-color-adjust:exact so the accent tint and grand-total fill survive
puppeteer. DRAFT stays: accent chip in docmeta + optional watermark for
un-issued docs. lineRow/totalsRows untouched; @media screen paper + @page
14mm preserved; renderer stays one pure function so preview===pdf identity holds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 0d57c267ca feat(hq-web): reminder email preview in the manual queue
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 8212a9d898 docs: quotation polish + share/download design (beautify, tokenised share link, whatsapp, native share)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 0b340fd85f feat(hq-web): module quote-content live print preview
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise b65e03e1c4 feat(hq-web): Document Template page — editable letterhead data + live preview
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 08e067c7cc feat(hq-web): composer split view — server-truth totals strip, live paper, responsive
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise c2eb7acba0 feat(hq-web): LivePreview — debounced double-buffer iframe + preview fetchers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 52e8a03c81 feat(hq): GET /reminders/:id/preview — exact email via shared reminderContext
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 48731fdb2b feat(hq): GET/PUT /settings/template + logo upload (owner-gated, audited)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise c2f94df06d feat(hq): screen paper styles + documentHtmlSample + POST /previews/sample
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise a948a92350 feat(hq): render editable template.* letterhead fields with default fallback
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 1ac44ae7de test(hq): golden fidelity — preview HTML byte-identical to the PDF's HTML
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 268a7469d3 feat(hq): POST /documents/preview — permissive live preview, persists nothing
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 90df8b0e9f feat(hq): extract pure prepareDraft from createDraft (permissive preview path)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 7afd769a14 docs: live preview plan (13 tasks) + editable template-data scope in spec
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 152545555e docs: live document preview design (approved via ultracode panel)
Same-code preview endpoint (no drift), form+A4-paper split layout, server-truth
totals strip, permissive-preview/strict-save, + company-letterhead / quote-content /
reminder-email sibling previews. Store-product surfaces documented as handoff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise dbccb8f954 feat(hq-web): owner company profile editor and client 360 aws usage trend
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago
Thomas Joise 2acca18fc9 feat(hq-web): reports page — dues aging, module revenue, profitability, aws cost chart
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago
Thomas Joise 5495b42e30 feat(hq): schedule monthly aws cost pull and owner-gated manual pull route
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago
Thomas Joise ea8c2b23ad feat(hq): company profile settings — owner-gated GET/PUT, audited
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago
Thomas Joise b7085ce56e feat(hq): payment receipts — own RCT/ series, zero-GST acknowledgment PDF
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago
Thomas Joise 42cfa3d0b2 feat(hq): reports — dues aging, module revenue, client profitability with margin
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago
Thomas Joise 51732df5e5 feat(hq): aws cost explorer ingestion — sigv4 signer and monthly tag-grouped pull
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago
Thomas Joise 9d2eedc366 feat(hq): aws usage repo — upsert/list, cross-client cost ranking, routes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago
Thomas Joise 3a09feab6f feat(hq): hq-3a schema — aws_usage table (per client per month)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 weeks ago