diff --git a/CLAUDE.md b/CLAUDE.md index a91ec37..2d4a989 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ dashboard, an append-only audit log, an APEX importer, and a scheduler. | Part | Where | Notes | |---|---|---| | Backend | `apps/hq/src` | Express over `better-sqlite3`, DB at `data/hq.db` (WAL) | -| Frontend | `apps/hq-web/src` | React + Vite; pages Dashboard / Pipeline / Clients / ClientDetail / Modules / Reports / NewDocument / DocumentView / Employees (owner-only) / DocumentTemplate (owner-only) | +| Frontend | `apps/hq-web/src` | React + Vite; pages Dashboard (My Day) / Pipeline / Reminders / Clients / ClientDetail / Documents / NewDocument / DocumentView / Modules / Reports / Employees (owner-only) / Profile / APEX Import (owner-only) / Settings | | Repos | `apps/hq/src/repos-*.ts` | one file per domain; plain functions over a `DB` handle | | Schema | `apps/hq/src/db.ts` + `apps/hq/schema.sql` | `CREATE TABLE IF NOT EXISTS` + additive `migrate()` | | Shared packages | `packages/*` | trimmed forks — see below | @@ -43,7 +43,7 @@ distilled to what applies to HQ. A change that breaks one does not land. | Append-only audit | Every mutation calls `writeAudit(db, userId, action, entity, id, before, after)` in the **same transaction** as the change. The audit log is append-only — never update or delete rows. Previews and the public share route write nothing. | `audit.ts` | | Client id is the future tenant id | `client.id` is a client-generated UUIDv7 that becomes `tenant_id` when the cloud tier stands up — same rows, promoted once. Never re-key clients. | `repos-clients.ts` | | No silent caps | Every list that can grow paginates or warns; never silently truncate. | reports, directory queries | -| Strict TS, green before landing | Strict TypeScript everywhere; `npm run typecheck` and `npm test` both clean before anything lands. Money math is locked by tests — a rounding change without a test change does not land. | `tsc`, vitest (293 tests) | +| Strict TS, green before landing | Strict TypeScript everywhere; `npm run typecheck` and `npm test` both clean before anything lands. Money math is locked by tests — a rounding change without a test change does not land. | `tsc`, vitest (344 tests) | ## Running it @@ -63,7 +63,7 @@ cd apps/hq-web && npm run dev - Env: `HQ_DATA_DIR` (DB location), `SIMS_PIN_PEPPER` (set once, never change), `HQ_SECRET_KEY` (before Gmail), `GOOGLE_*` (Gmail), `AWS_*` (Cost Explorer), `HQ_PORT` (default 5182). - Health check: `GET /api/health` → `{ ok: true }`. -- Verified state: `npm install` + typecheck clean, 293 tests pass. Exercise real flows end +- Verified state: `npm install` + typecheck clean, 344 tests pass. Exercise real flows end to end before claiming done. ## Docs (kept in `docs/`) diff --git a/README.md b/README.md index daec8f2..5511030 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ document and conversation we've had with them. ## Build state (start here) -Working codebase, not a plan. `npm install` + typecheck are clean and **293 tests pass**. +Working codebase, not a plan. `npm install` + typecheck are clean and **344 tests pass**. Current state and run-vs-pending detail live in **[STATUS.md](STATUS.md)**; the go-live runbook (server, HTTPS, backups, Gmail/AWS/import wiring, the Postgres switch) is in **[docs/DEPLOY-HQ.md](docs/DEPLOY-HQ.md)**. diff --git a/STATUS.md b/STATUS.md index 55023f5..a78336a 100644 --- a/STATUS.md +++ b/STATUS.md @@ -21,9 +21,9 @@ reminders driven by the dated `reminder_schedule` table. | --- | --- | | `npm install` | clean | | `npm run typecheck` (root + workspaces) | clean, no errors | -| `npm test` (`vitest run`) | **293 tests pass across 57 files** | +| `npm test` (`vitest run`) | **344 tests pass across 66 files** | -The 293 figure is the live `vitest run` count measured 2026-07-17, after the +The 344 figure is the live `vitest run` count measured 2026-07-17, after the quote-to-close funnel slice and its review fixes. Test files live under `apps/hq/test/` (49 files, including `employees`, `employees-routes`, `client-owner`, `pipeline`, `quote-followup`, and `reminder-schedule`), @@ -269,9 +269,17 @@ rasterizes for the PDF. - **AWS cost pull is env-gated** — the scheduled and manual pulls run only when `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` are configured; otherwise usage is owner-entered by hand. -- **APEX import is CLI-only** — no web UI for staging/commit. -- **Reminders live on the Dashboard** — there is no standalone reminders page, - and there is no separate documents-list page (documents are reached from the - client ledger and the dashboard). - **A few tax/identity defaults are provisional** — the seeded company state code (`32`, Kerala) and several SAC codes carry "founder / CA to confirm" notes. + +_Closed by the D18 go-live cluster (2026-07-17): APEX import now has an owner-only +web UI (`/import`: stage → per-row problems → verify → confirm-gated commit); +standalone Reminders (`/reminders`) and Documents (`/documents`, honestly paginated) +pages exist; invoices carry **due dates** (terms default + override, stamped at +issue — the overdue chase says "was due on X, N days overdue"); self-service +profile + own-password change; sidebar collapses; the dashboard has a My Day mode +(staff always scoped to their book); clients carry support-access data (AnyDesk, +OS, district/sector filters, an encrypted DB password with audited reveal); the +module roster is fully editable both ways (one audited write path with Client 360); +"Convert & send" is one click from the register and the document view; and a +multi-module quotation prints one titled section per module._ diff --git a/docs/06-DECISIONS.md b/docs/06-DECISIONS.md index e45a80e..90b3940 100644 --- a/docs/06-DECISIONS.md +++ b/docs/06-DECISIONS.md @@ -230,3 +230,30 @@ locked with it: - **UI-only slice.** No backend/schema changes; `@sims/ui` stays generic (no router or icon deps — `lucide-react` lives in `apps/hq-web` only). Known deferred: server-side "outstanding ₹" figure for the Client 360 KPI row (client-side paise math is banned). + +## D18 — Go-live cluster: due dates, ops pages, shell & profile, client support data ✅ DELIVERED (2026-07-17) +Founder-approved slice (spec `superpowers/specs/2026-07-17-golive-cluster-design.md`), built +in ten phases the same day. The calls that matter going forward: +- **Invoice due dates = terms default + per-document override.** `billing.payment_terms_days` + (seeded 15) is the operational default; the *dated* fact is the `due_date` stamped on each + invoice at issue (covers manual issue, convert-and-send and recurring generation in one + place). Overdue reminders anchor `COALESCE(due_date, doc_date)`; the chase mail says + "was due on X and is now N day(s) overdue"; legacy invoices keep the old anchor. +- **Client support data (APEX parity):** `anydesk`/`os`/`district`/`sector` on the client; + district+sector filter the book. The DB password is AES-256-GCM at rest (`HQ_SECRET_KEY`, + loud refusal without it), NEVER in payloads (only `hasDbPassword`), write and reveal are + owner/manager-gated with one audit row per reveal. +- **Roster single-write-path:** the module→clients roster edits (kind/edition/renewal/ + status, assign, unassign) reuse `updateClientModule`/`assignModule` — the same audited + functions Client 360 uses, so the two views cannot disagree. `ClientModulePatch` gained + kind/edition with allowedKinds validation. +- **Quotation sectioning:** QUOTATION prints one titled section per module (name + + what's-included + price; print page-break per module); INVOICE/PI/CN/RECEIPT keep the + billing grid. Rendered in the ONE `documentHtml`, preserving preview↔PDF fidelity. +- **Self-service profile:** `/me` routes are strictly self-scoped (role/email untouchable); + changing your own password requires the current one and kills every OTHER session. +- **My Day:** staff dashboards are always scoped to their own book server-side; + owner/manager toggle Mine/Everyone (`?mine=1`). Sidebar collapses to an icon rail. +- **APEX import UI:** owner-only `/import` wraps the existing stage/verify/commit — + commit stays locked while any staged row carries a problem. +Suite at 344 tests / 66 files at delivery. diff --git a/docs/superpowers/specs/2026-07-17-golive-cluster-design.md b/docs/superpowers/specs/2026-07-17-golive-cluster-design.md index 3f84041..e95ada2 100644 --- a/docs/superpowers/specs/2026-07-17-golive-cluster-design.md +++ b/docs/superpowers/specs/2026-07-17-golive-cluster-design.md @@ -1,6 +1,6 @@ # HQ Console — Go-Live Cluster Spec (Due Dates, Import UI, Reminders/Documents Pages, Shell & Profile, Client Support Data) -> **STATUS: DRAFT — awaiting founder review.** Successor slice to the delivered D16 funnel +> **STATUS: DELIVERED 2026-07-17.** Successor slice to the delivered D16 funnel > (`2026-07-17-quote-to-close-funnel-design.md`). The Postgres switch (D15) is deliberately > NOT in this slice — it is the dedicated task queued immediately after it, before the > real-data import commit.