Commit Graph

3 Commits

Author SHA1 Message Date
7bf587de90 feat(documenso-phase-4): recipient configurator + field placement UI
Phase 4 lands the visual half of the Documenso build — the upload-
for-signing dialog the Contract + Reservation tabs hand off to. Four
files of new code; the existing tab placeholders point at it.

Files added:
- lib/services/document-field-detector.ts — Phase 4c auto-detect
  scanner. Uses pdfjs-dist to extract per-page text + positions, then
  matches anchor patterns (Signature, Date, Initials, Email, Name,
  underscore-runs) and produces percent-coordinate DetectedField
  rows. Recipient label inference walks ±100pt of each match for
  Buyer/Seller/Client/Witness/Notary keywords. Returns [] when the
  PDF is image-only; UI falls back to manual placement without an
  error. 6 unit tests pin the matching + coordinate math.

- app/api/v1/documents/auto-detect-fields/route.ts — multipart POST
  endpoint that delegates to detectFields(). Permission-gated by
  documents.send_for_signing.

- app/api/v1/documents/signing-defaults/route.ts — GET endpoint that
  surfaces just the per-port developer + approver display name/email
  + sendMode flag. No secrets exposed; lets the dialog prefill the
  recipient configurator without an admin-scoped settings read.

- components/documents/upload-for-signing-dialog.tsx — the Phase 4
  UI. Three-step state machine inside a single Dialog:
    1. select-file:  drop/click PDF picker + title input
    2. configure-recipients: client + developer + approver prefilled,
       rep can add/remove/reorder + change role (SIGNER/APPROVER/CC)
    3. place-fields: react-pdf renders the source PDF; auto-detect
       runs in the background on file load and seeds the overlay;
       rep places, drags, resizes, deletes, reassigns fields via the
       palette + side panel. Native DOM drag (no dnd-kit dependency
       added — the coordinate math stays obvious).
  Send fires POST /api/v1/interests/[id]/upload-for-signing (Phase 3
  service); success toast reflects port sendMode (auto fires the
  invite immediately, manual leaves it for the rep).

Files modified:
- components/interests/interest-contract-tab.tsx + reservation-tab.tsx:
  swap the ComingSoonDialog placeholder for the real
  UploadForSigningDialog with the matching documentType prop. The
  placeholder ComingSoonDialog helper is deleted from both.

- scripts/tsc-staged.mjs: pull src/types/**/*.d.ts into the temp
  staged-only tsconfig so side-effect CSS imports (e.g.
  react-pdf/dist/Page/AnnotationLayer.css) resolve via the existing
  declare-module shim. Without this fix the staged compile reports
  TS2882 even though the full tsc --noEmit pass passes.

Design choices noted in code comments:
- Native drag over dnd-kit: the field overlay's percent-based
  coordinate math is short enough that adding a drag library adds
  complexity without saving lines.
- Auto-detect on file-load (not on demand): runs immediately so the
  rep doesn't have to click a second button — empty result drops
  back to manual placement silently.
- Per-recipient color swatches indexed by signingOrder.
- Recipient seed via useMemo + user-event handler instead of
  useEffect → setRecipients (Wave 3 set-state-in-effect avoidance).

Server-side, Phase 3 plumbing handles the rest: tenant guard, magic-
byte verify, Documenso round-trip with per-port v1/v2 routing,
recipient signingToken capture for Phase 2 webhook cascade, auto-
send when port.sendMode === 'auto'.

Tests: 1334 → 1340  (6 new for the detector); tsc clean.

Deferred polish (Phase 6):
- Per-field metadata side panel for DROPDOWN/RADIO option lists
- Pinch-zoom + zoom-out controls on the field-placement canvas
- Recipient drag-reorder via dnd-kit
- Required toggle per field

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:03:27 +02:00
544b129b00 feat(audit-wave-1): real db:migrate runner with CONCURRENTLY support
Closes Wave 1.1 (CRITICAL): the production-grade migration runner the
audit flagged as missing.

Why drizzle-kit migrate alone wasn't enough:
- Wraps every migration in a single transaction. Postgres forbids
  CREATE INDEX CONCURRENTLY inside a transaction (25001), so the
  6 composite indexes in 0052_audit_critical_fixes.sql never landed
  in prod.
- db:push silently diverges from migration-tracked truth on DDL the
  kit can't infer from the schema (CHECK constraints, partial unique
  indexes, the berth-pdf circular FK).

scripts/db-migrate.ts:
- Reads journal-ordered migrations from src/lib/db/migrations.
- Tracks applied state in drizzle.__drizzle_migrations (same schema
  Drizzle's own tools use).
- Splits each migration on `--> statement-breakpoint`.
- Classifies each statement: CREATE/REINDEX/DROP INDEX CONCURRENTLY
  → outside transaction; everything else → batched in one tx per
  migration. Transactional batch runs first, CONCURRENTLY second.

Three modes:
- `pnpm db:migrate`           — apply pending migrations
- `pnpm db:migrate:status`    — diff applied vs disk
- `pnpm db:migrate:baseline`  — mark all as applied without running
                                them. Use ONCE per env when schema
                                was bootstrapped via db:push.

Also fixes scripts/tsc-staged.mjs: temp tsconfig now lives in
`node_modules/.cache/tsc-staged/` (was /tmp) AND explicitly lists
`types: [node, react, react-dom]` so @types/* auto-resolution works
when `include: []` short-circuits TS's default discovery.

For the existing prod cutover:
After `db:migrate:baseline`, manually verify 0052's composite
indexes exist:
  SELECT indexname FROM pg_indexes
   WHERE indexname IN ('idx_files_port_client', 'idx_files_port_company',
                       'idx_files_port_yacht', 'idx_docs_port_client',
                       'idx_docs_port_company', 'idx_docs_port_yacht');
If missing, paste 0052's CREATE INDEX CONCURRENTLY statements into
a `psql` session directly (each runs OUTSIDE a transaction).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 00:04:52 +02:00
8baf239759 feat(deps): pre-commit type-check on staged TS files
Pre-commit now runs `tsc` against the staged ts/tsx files (and their
dep graph) in ~3s, catching type errors before they hit CI. Used to
skip type-check entirely on pre-commit because full-project tsc is
~22s — too slow for the commit hook.

Drops a 30-LOC shim in `scripts/tsc-staged.mjs` instead of the
`tsc-files` package: that lib's binary-resolution path
(`typescript/../.bin/tsc`) doesn't exist under pnpm's virtual-store
layout, so spawnSync returns `status: null` and the check silently
no-ops. Filed upstream-style: the package hasn't shipped in 3 years.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:00:43 +02:00