# Quotation Polish + Share/Download — Design > **STATUS: DELIVERED (2026-07-13).** Kept as the design/task record; current state lives in STATUS.md. **Status:** approved in principle (founder, 2026-07-13). Sequenced to run **after** the live-preview build (wf live-preview) lands, so it builds on the finished `templates.ts` rendering + the live preview (every visual change is seen instantly) and never edits the template file concurrently with that build. Two threads: (A) make the document **look beautiful**, (B) **share & download** options. ## A. Beautiful document (templates.ts `documentHtml`) Restyle the one shared letterhead template — this automatically improves the PDF, the composer live preview, and the Document Template page (one renderer). A print-quality, professional Indian GST document, not a generic table. Direction (finalised with the frontend-design skill at build time): - **Typography:** a clear type hierarchy — company name prominent, a refined document title block (TAX INVOICE / QUOTATION), tabular-figure alignment for all money columns so rupee amounts line up to the paisa. Keep the bundled Segoe/Inter-class stack (renders identically on the office desktops and the Windows server — no web-font fetch in print). - **Layout:** a proper letterhead band (logo left via `template.logo`, company block + GSTIN right), a clean bill-to / ship-to and document-meta row, a well-spaced line table with subtle row rules (not heavy borders), a totals block visually anchored bottom-right with the grand total emphasised, amount-in-words, then terms / declaration / jurisdiction / signatory / footer from the `template.*` fields. - **Accent:** one restrained accent colour for rules/headers, sourced from a new optional `template.accent` setting (defaults to a professional slate/indigo) so it's brandable from the Document Template page later — no hard-coded brand colour. - **Print discipline:** A4, 14mm margins, avoid row-splitting across page breaks (`break-inside: avoid` on rows), repeat the table header on page 2+. All screen/print parity rules from the live-preview design still hold (one renderer, golden test guards). - **Fidelity:** the existing string-identity golden test continues to guarantee the beautified preview === the beautified PDF. ## B. Share & download (all three channels + download) On **DocumentView** (and optionally right after composing), an action group: 1. **Download PDF** — button hitting the existing `GET /api/documents/:id/pdf`, served with `Content-Disposition: attachment; filename="QT-26-27-0001.pdf"` (doc-no slugged, `/` → `-`). Trivial; no new endpoint. 2. **Shareable view link** (the load-bearing one — needs a secure public route): - New table `document_share (id, document_id, token, created_by, created_at, expires_at NULL, revoked INTEGER DEFAULT 0)` via `CREATE TABLE IF NOT EXISTS`. - `POST /api/documents/:id/share` (requireAuth) → mints a **cryptographically random** token (≥32 bytes, `randomBytes`), default `expires_at` = +30 days (configurable), returns the public URL. Audited. - **Public** `GET /share/:token` (NO auth, the ONLY unauthenticated read route) → validates token exists, not revoked, not expired; renders **only that one document's** PDF (reuse renderPdf) or an HTML view. Serves nothing else — no listing, no client record, no API. Unknown/expired/revoked → a plain "link expired" page. - `POST /api/documents/:id/share/:shareId/revoke` (requireAuth, audited) → sets `revoked=1`. A document's shares are listed on DocumentView so the owner can revoke. - **Security stance (documented, accepted):** anyone holding the link sees that one document's contents (client name + amounts) — the intended trade-off of any share link, mitigated by an unguessable token, optional expiry, and one-click revoke. Rate-limit `/share/:token` lookups to blunt token brute-forcing. No document data in the URL beyond the opaque token. 3. **WhatsApp (click-to-chat)** — a `wa.me/?text=` link opening WhatsApp with a ready message: greeting + doc type/no + total + **the shareable view link** (so the client taps through to the actual document — this is why the link and WhatsApp compose). Needs the client's phone (already on the client record); no WhatsApp Business API. If no share link exists yet, minting one is offered inline. 4. **Native share sheet** — `navigator.share({ title, text, url: shareLink })` where supported (mobile), sharing the view link to any app; where the File share is supported, share the PDF blob directly; desktop/unsupported → falls back to Download. ## Data / files (build time) - `apps/hq/src/db.ts` — `document_share` table (CREATE TABLE IF NOT EXISTS; no migrate). - `apps/hq/src/repos-shares.ts` — mint/validate/revoke/list shares. - `apps/hq/src/api.ts` — `POST /documents/:id/share`, `POST …/share/:shareId/revoke`; `GET /api/documents/:id/pdf` gains the attachment filename. - `apps/hq/src/server.ts` — public `GET /share/:token` mounted OUTSIDE the `/api` auth router, rate-limited; renders via renderPdf. - `apps/hq/src/templates.ts` — the beautification (thread A) + optional `template.accent`. - `apps/hq-web/src/pages/DocumentView.tsx` — the action group (Download / Link / WhatsApp / Share) + the shares list with revoke. - Tests (TDD, server): token mint is random + unguessable; `/share/:token` serves only the one doc and 404s on unknown/expired/revoked; revoke works; owner-gate/audit; download filename; beautify keeps the golden string-identity test green. ## Not this round Real WhatsApp Business API sending (click-to-chat only); SMS; share analytics (opened/viewed tracking) — a nice later addition on the same `document_share` row. ## Sequence 1. Live-preview build (in flight) completes and is verified. 2. Plan this doc (writing-plans) → ultracode build → verify. Beauty pass uses the frontend-design skill for the concrete visual system.