Files
pn-new-crm/14-TECHNICAL-DECISIONS.md
Matt 67d7e6e3d5
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00

17 KiB
Raw Permalink Blame History

14 — Technical Decisions (Locked)

Status: All decisions locked as of 2026-03-12. This document is the single source of truth for every dependency, library, and tooling choice in the Port Nimara CRM V1 rebuild.


1. Framework & Runtime

Decision Choice Notes
Framework Next.js 15 (App Router) React 19, standalone output mode, next start behind nginx
Language TypeScript (strict mode) strict: true in tsconfig, no any escape hatches
Runtime Node.js 20 LTS Docker base image: node:20-alpine
Package manager pnpm Faster installs, strict dependency resolution

2. UI Layer

Decision Choice Notes
Styling Tailwind CSS 4 + Port Nimara design tokens Custom theme from brand guidelines. Full token system in 15-DESIGN-TOKENS.md
Typography Inter (UI), Georgia (formal docs), JetBrains Mono (data) Inter via Google Fonts. Brand fonts (Bill Corporate, Adobe Garamond) reserved for marketing materials — CRM uses approved in-house alternatives per brand guide p.14
Component library shadcn/ui Copy-paste components (not npm package). Built on Radix UI primitives + Tailwind. ~50 components. Run npx shadcn@latest add <component> to scaffold into components/ui/. Fully customizable — we own the source.
Icons Lucide React Tree-shakeable, consistent stroke style, bundled with shadcn/ui
Data tables TanStack Table via shadcn DataTable Server-side pagination, sorting, filtering. Column definitions per entity.
Forms React Hook Form + Zod via shadcn Form Zod schemas shared between client validation and API endpoint validation
Toasts / notifications Sonner via shadcn Toast Stacked toasts, auto-dismiss, action buttons
Command palette shadcn Command (⌘K) cmdk under the hood. Global search, quick navigation, actions.
Rich text editor TipTap Headless, ProseMirror-based, extension architecture. React integration via @tiptap/react. Used in 3 contexts (see Section 8). Pre-built shadcn integration available ("Minimal Tiptap").
Charts Recharts For dashboard widgets and report visualizations
Date handling date-fns Lightweight, tree-shakeable. No moment.js.

2.1 TipTap Configuration (3 contexts)

  1. Email composer — Full toolbar: bold, italic, underline, lists, links, images. Merge field insertion via custom extension (or repurposed @tiptap/extension-mention). HTML output for email body.
  2. Document template editor — Same as email composer plus: table support (@tiptap/extension-table), page break hints, merge field tokens rendered as styled inline chips.
  3. Notes fields — Lightweight config: bold, italic, lists, links only. No toolbar — slash commands or floating menu. Markdown-like shortcuts enabled.

2.2 shadcn/ui Components Expected in Use

Core set (installed during Layer 0): Button, Input, Label, Select, Textarea, Checkbox, RadioGroup, Switch, Dialog, Sheet, DropdownMenu, Command, Tabs, Table, Card, Badge, Avatar, Tooltip, Popover, Calendar, DatePicker, Form, Toast (Sonner), Skeleton, Separator, ScrollArea, AlertDialog, Accordion, Breadcrumb, NavigationMenu, Pagination, Progress, Slider

3. Data Layer

Decision Choice Notes
Database PostgreSQL 16 Docker container, named volume, same Compose stack
ORM Drizzle ORM Type-safe, SQL-like syntax, push-based migrations. Schema in db/schema/
Validation Zod Shared schemas: API validation + form validation + Drizzle type inference
Caching Redis 7 Session store, BullMQ backing store, rate limiting, Socket.io adapter

4. Authentication & Authorization

Decision Choice Notes
Auth library Better Auth Session-based auth with RBAC. No Keycloak dependency.
Session store Redis better-auth/plugins/redis for session persistence
Password hashing Argon2 (via Better Auth defaults)
RBAC 4 roles: super_admin, admin, manager, agent Permissions defined per-role in lib/permissions.ts

5. Real-time & Background Jobs

Decision Choice Notes
WebSocket Socket.io Real-time notifications, live updates, presence. Redis adapter for scaling.
Job queue BullMQ Redis-backed. Recurring jobs (calendar sync, email sync, backups), event-driven jobs (EOI generation, webhook processing). Dashboard at /admin/queues.
Job dashboard bull-board Embedded in admin panel for queue monitoring

6. File Storage & Documents

Decision Choice Notes
Object storage MinIO (existing self-hosted instance, older version) S3-compatible. Credentials in env vars. SSE-S3 encryption at rest. DB-backed file metadata in files table.
E-signatures Documenso (self-hosted) Webhooks primary (instant). BullMQ fallback poll every 6 hours (rare safety net).
PDF generation @pdfme Template-based PDF generation. Branded layouts with port logo/colors.
Receipt OCR OpenAI Vision API Via standalone PWA at /scan. Offline queueing with IndexedDB.

7. Email

Decision Choice Notes
SMTP relay Poste.io (self-hosted) Per-user encrypted SMTP credentials stored in DB
Sending Nodemailer Direct SMTP send via user's configured account
IMAP sync imapflow Background job syncs threads, metadata in PostgreSQL, raw emails in MinIO
System email templates MJML Compiled to HTML at build time. Templates for: password set/reset, EOI notifications, follow-up reminders, invoices

8. External APIs

Decision Choice Notes
Google Calendar googleapis OAuth2 flow, 3 sync triggers (30min poll, on-login, on-navigation if stale > 5min)
Currency rates Frankfurter API Free, no API key, ECB rates. Cached daily.
AI (receipt OCR) OpenAI Vision API Only AI dependency. Scoped to receipt scanning.

9. Testing

Decision Choice Notes
Unit / integration Vitest Modern Jest replacement. Same describe/it/expect API. Natively supports TypeScript/ESM. Built on Vite — significantly faster. Next.js officially recommends it.
E2E Playwright Real browser automation. V1 scope: 56 critical workflow tests only.
Component tests Skipped for V1 Vitest covers logic; Playwright covers critical flows. Component-level tests deferred.

9.1 Vitest Coverage Targets (V1)

  • All business rule functions in lib/business-rules/
  • All Zod validation schemas
  • API endpoint handlers (happy path + key error cases)
  • Utility functions (date formatting, currency conversion, permission checks)
  • Service layer functions (EOI generation, invoice calculation, email sending)

9.2 Playwright E2E Tests (V1)

56 critical workflows:

  1. Login → dashboard loads → navigate to clients
  2. Create client → link interest → generate EOI → verify PDF
  3. Create expense → upload receipt → verify in expense list
  4. Email compose → send → verify in thread
  5. Admin: create user → assign role → verify permissions
  6. Berth management: create berth → assign client → verify status transitions

10. DevOps & Infrastructure

Decision Choice Notes
Containerization Docker Compose Services: crm-app, postgres, redis, minio, documenso, nginx
Reverse proxy nginx TLS termination, rate limiting, static asset caching
CI/CD GitHub Actions Lint → type-check → Vitest → build → deploy
Backups pg_dump → MinIO Nightly at 02:00, encrypted, 30-day retention

11. Server-side State & API

Decision Choice Notes
API style REST Consistent pattern for CRM frontend, website berth map, future integrations
Server state TanStack Query Client-side caching, automatic revalidation, optimistic updates
Client state Zustand Minimal UI state (sidebar collapse, active port, theme)
API docs OpenAPI / Swagger Auto-generated from Zod schemas. Available at /api/docs in dev.

Decision Log

Date Decision Rationale
2026-02-xx Next.js 15 over Nuxt 3 / SvelteKit Largest AI training corpus, best Claude Code fluency
2026-02-xx REST over tRPC One API pattern for all consumers, no dual-paradigm
2026-02-xx PostgreSQL + Drizzle over NocoDB Relational integrity, type-safe ORM, proper migrations
2026-02-xx Better Auth over Keycloak Simpler, no external service, built-in RBAC
2026-02-xx Keep MinIO Working infrastructure, S3-compatible, no migration needed
2026-03-12 shadcn/ui confirmed Copy-paste ownership, Radix primitives, Claude Code fluent
2026-03-12 TipTap for rich text Headless ProseMirror, extension architecture, shadcn integration exists
2026-03-12 Vitest + Playwright Vitest for unit/integration (fast, TS-native), Playwright for 5-6 critical E2E
2026-03-12 Skip component tests V1 Logic covered by Vitest, flows covered by Playwright
2026-03-12 MinIO stays (older version) Self-hosted instance works, no migration overhead
2026-03-12 Port Nimara design tokens from brand guide Full token system in 15-DESIGN-TOKENS.md, replaces generic "Maritime tokens"
2026-03-12 Inter as CRM UI font Brand guide approves Arial for in-house; Inter is the modern web equivalent, shadcn default
2026-03-12 Georgia for formal generated docs Brand guide secondary font (Adobe Garamond) is commercial; Georgia is the approved in-house serif