# 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: 5–6 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)

5–6 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 |
