You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
Go to file
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>
23 hours ago
apps fix(ui): restore sticky table headers, dashboard loading flash, ticket-type over-fetch 23 hours ago
deploy feat(deploy): hardcode DB connection + owner email (no .env), update docs 4 days ago
docs merge: Client Detail redesign (D38) — composed onboarding pipeline, status line, payments context, mobile fixes 1 day ago
packages fix(ui): restore sticky table headers, dashboard loading flash, ticket-type over-fetch 23 hours ago
scripts feat(d33): database backup script (pg_dump + rotation) 2 days ago
.dockerignore chore: add .dockerignore (completes the tracked Docker setup) 4 days ago
.gitignore feat(d33): database backup script (pg_dump + rotation) 2 days ago
CLAUDE.md docs: bring README/STATUS/CLAUDE/DECISIONS current (D34-D37) 2 days ago
Dockerfile chore(deploy): track the container + nginx deploy setup 4 days ago
README.md docs: bring README/STATUS/CLAUDE/DECISIONS current (D34-D37) 2 days ago
STATUS.md fix(ui): restore sticky table headers, dashboard loading flash, ticket-type over-fetch 23 hours ago
docker-compose.yml feat(deploy): hardcode DB connection + owner email (no .env), update docs 4 days ago
package-lock.json feat: theme palettes + Geist font + dashboard cockpit styles 3 days ago
package.json feat(d33): database backup script (pg_dump + rotation) 2 days ago
tsconfig.base.json Split: make this the standalone HQ project (remove Store) 5 days ago
tsconfig.json chore: bring the full store-product codebase under version control 2 weeks ago
vitest.config.ts Split: make this the standalone HQ project (remove Store) 5 days ago

README.md

SiMS HQ — Internal Ops Console

The console for running our own software business. It manages our ~300 Classic clients end to end — and it replaces the internal Oracle APEX app. This repo is internal only; it is not shipped to clients.

One place for the client book, what each client has bought, the money they owe, and every document and conversation we've had with them.

What it does

  • Client book — the registry of ~300 clients: contacts, GSTIN, status, notes.
  • Modules + price book — the sellable software modules with a dated price book (a price change is a new row, not a code release) and per-client subscriptions.
  • Documents — quotations (QT), proforma invoices (PI), invoices (INV) and credit notes (CN), built on editable letterhead templates, rendered to PDF (headless Chromium), with shareable public links (one token-guarded, rate-limited, revocable read route).
  • Interactions — call / visit / meeting tracking against each client.
  • AMC contracts and AWS cost recovery — per-client cost attribution pulled monthly.
  • Payments + allocations, recurring plans, and reminders sent via Gmail on the company mailbox, with bounce handling and a manual queue when the token dies.
  • Quote-to-close funnel — employee accounts (owner / manager / staff), a cross-client pipeline chase-list (stages derived, oldest-overdue on top), and escalating quote follow-up that nudges the owning employee and emails the client a share link on a dated schedule (reminder_schedule), stopping automatically on accept / lose / convert.
  • Reports, a Dashboard, an append-only audit log (every mutation), an APEX importer for the initial 300-client load, and a scheduler (runs on boot + every 6h: reminders, bounce polling, AWS cost pull).
  • A redesigned ops UI — light/dark theme with accent + neutral palettes, grouped sidebar with a live reminder badge, a Ctrl-K command palette (clients, documents, tickets, pages, actions), and a Client 360 with tabbed sections and a 12-month relationship-pulse ribbon; phone-usable drawer layout.
  • Support tickets (aging/SLA/assignee workbench, bill-from-ticket), onboarding milestones, and per-module operational fields each module declares (config, not code) — surfaced per client on Client 360, across all clients in the Module data directory, and as a Portals quick-list of every stored login. For the SMS module those fields include a live credit balance pulled from the provider gateway (manual refresh + a daily pass), with low balances flagged for a one-click top-up quote.
  • Renewals command center, an owner MIS cockpit, an audit-log viewer, and database backups (npm run backup → compressed, restorable pg_dump). Credentials are stored in the clear (no HQ_SECRET_KEY), so the DB and its backups are the security boundary.
  • Employees with per-user logins (imported from the master; an "invited" flag until they change their default password), and a login history (every sign-in with IP + device).
  • Client classification — each client is a PACS or Store, and carries a society category matching the Kerala co-op directory (Service Bank / Vanitha / Housing / Employees / …); both are filterable on the Clients screen.

Build state (start here)

Working codebase, not a plan. npm install + typecheck are clean and 416 tests pass. Current state and run-vs-pending detail live in STATUS.md; the go-live runbook (server, HTTPS, backups, Gmail/AWS/import wiring, the Postgres switch) is in docs/DEPLOY-HQ.md.

Architecture

  • Backend — apps/hq/src: an Express JSON API over better-sqlite3, behind portable repositories (repos-*.ts, one per domain: clients, modules, documents, payments, amc, aws, interactions, recurring, reminders, shares, reports, dashboard, email). SQLite is the dev/test engine; Postgres is the locked production engine (see DEPLOY-HQ.md). DB at data/hq.db; server on :5182 (also serves the built web UI and the public /share/:token route).
  • Frontend — apps/hq-web: React + Vite. Pages: Home, Dashboard, Pipeline, Reminders, Tickets, Onboarding, Clients, ClientDetail, Documents, NewDocument, DocumentView, Renewals, SMS credits, Modules, Module data, Portals, Reports, MIS cockpit (owner), Employees (owner), Profile, APEX Import (owner), Audit log (owner), Login history (owner), Settings.
  • Shared packages (trimmed forks): @sims/domain (ids, money, business-day, doc-series, gstin, documents), @sims/auth (pin/scrypt), @sims/billing-engine (compute, tax — our invoices are GST documents, computed to the paisa), @sims/ui (design system).

Planning docs

Doc What it covers
docs/14-SPEC-HQ-CONSOLE.md The console spec: shape, data model, features, delivery sequence (D15)
docs/superpowers/specs/2026-07-17-quote-to-close-funnel-design.md The delivered D16 funnel slice — employees, pipeline, escalating reminders, convert/supersede, module roster
docs/superpowers/specs/2026-07-17-hq-console-redesign-design.md The delivered warm ops-console UI redesign
docs/11-ADMIN-SUPPORT-CONSOLE.md The vendor-side HQ / support console this app implements
docs/07-DB-AND-CONFIG.md Everything-in-DB design (messages, plans, settings) and the local DB engine analysis
docs/06-DECISIONS.md Founder decisions, including D15 (build HQ early) and the Postgres call
docs/16-PROJECT-RULES.md The hard rules a PR must satisfy to merge
docs/DEPLOY-HQ.md Go-live runbook for the console

Run

npm install                     # from the repo root (workspaces: packages/*, apps/*)

# Backend — HQ server. Build the bundle, then run it FROM THE REPO ROOT: the DB
# path resolves to ./data/hq.db against the current working directory, so the
# server must be launched from the repo root (not from apps/hq, which would look
# in apps/hq/data and boot an empty database).
npm run build --workspace @sims/hq
node apps/hq/dist/server.cjs     # serves :5182 (API, built web UI, /share/:token)
# First boot on an empty data/ creates data/hq.db and prints a one-time owner
# password — capture it. Log in as admin@sims.com, then change it.

# Frontend — hq-web dev server (Vite on :5183, proxies /api and /share to :5182)
cd apps/hq-web && npm run dev

Nothing in apps/hq/.env is required to boot; each var unlocks a capability (Gmail sending, AWS cost pull, security peppers) — see apps/hq/.env.example and DEPLOY-HQ.md. Repo-wide checks from the root: npm run typecheck and npm test.