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

159 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |