# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

**Xpeditis** is a B2B SaaS maritime freight booking platform. Freight forwarders search and compare real-time shipping rates, book containers, and manage shipments. Monorepo with NestJS 10 backend (Hexagonal Architecture) and Next.js 14 frontend.

## Development Commands

All commands run from repo root unless noted otherwise.

```bash
# Infrastructure (PostgreSQL 15 + Redis 7 + MinIO)
docker-compose up -d

# Install all dependencies
npm run install:all

# Environment setup (required on first run)
cp apps/backend/.env.example apps/backend/.env
cp apps/frontend/.env.example apps/frontend/.env

# Database migrations (from apps/backend/)
cd apps/backend && npm run migration:run

# Development servers
npm run backend:dev    # http://localhost:4000, Swagger: /api/docs
npm run frontend:dev   # http://localhost:3000
```

### Testing

```bash
# Backend (from apps/backend/)
npm test                                           # Unit tests (Jest)
npm test -- booking.entity.spec.ts                 # Single file
npm test -- --testNamePattern="should create"      # Filter by test name
npm run test:cov                                   # With coverage
npm run test:integration              # Integration tests (needs DB/Redis, 30s timeout)
npm run test:e2e                      # E2E tests

# Frontend (from apps/frontend/)
npm test
npm run test:e2e                      # Playwright (chromium, firefox, webkit + mobile)

# From root
npm run backend:test
npm run frontend:test
```

Backend test config is in `apps/backend/package.json` (Jest). Integration test config: `apps/backend/jest-integration.json` (covers infrastructure layer, setup in `test/setup-integration.ts`). Frontend E2E config: `apps/frontend/playwright.config.ts`.

### Linting, Formatting & Type Checking

```bash
npm run backend:lint       # ESLint backend
npm run frontend:lint      # ESLint frontend
npm run format             # Prettier (all files)
npm run format:check       # Check formatting
# From apps/frontend/
npm run type-check         # TypeScript checking (frontend only)
```

### Database Migrations

```bash
cd apps/backend
npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/MigrationName
npm run migration:run
npm run migration:revert
```

### Build

```bash
npm run backend:build    # NestJS build with tsc-alias for path resolution
npm run frontend:build   # Next.js production build (standalone output)
npm run clean            # Remove all node_modules, dist, .next directories
```

## Local Infrastructure

Docker-compose defaults (no `.env` changes needed for local dev):
- **PostgreSQL**: `xpeditis:xpeditis_dev_password@localhost:5432/xpeditis_dev`
- **Redis**: password `xpeditis_redis_password`, port 6379
- **MinIO** (S3-compatible storage): `minioadmin:minioadmin`, API port 9000, console port 9001

Frontend env var: `NEXT_PUBLIC_API_URL` (defaults to `http://localhost:4000`) — configured in `next.config.js`.

## Architecture

### Hexagonal Architecture (Backend)

```
apps/backend/src/
├── domain/           # CORE - Pure TypeScript, NO framework imports
│   ├── entities/     # Booking, RateQuote, Carrier, Port, Container, Notification, Webhook,
│   │                 # AuditLog, User, Organization, Subscription, License, CsvBooking,
│   │                 # CsvRate, InvitationToken
│   ├── value-objects/ # Money, Email, BookingNumber, BookingStatus, PortCode, ContainerType,
│   │                  # Volume, DateRange, Surcharge
│   ├── services/     # Pure domain services (csv-rate-price-calculator)
│   ├── ports/
│   │   ├── in/       # Use case interfaces with execute() method
│   │   └── out/      # Repository/SPI interfaces (token constants like BOOKING_REPOSITORY = 'BookingRepository')
│   └── exceptions/   # Domain-specific exceptions
├── application/      # Controllers, DTOs (class-validator), Guards, Decorators, Mappers
│   ├── [feature]/    # Feature modules: auth/, bookings/, csv-bookings, rates/, ports/,
│   │                 # organizations/, users/, dashboard/, audit/, notifications/, webhooks/,
│   │                 # gdpr/, admin/, subscriptions/
│   ├── controllers/  # REST controllers (also nested under feature folders)
│   ├── services/     # Application services: audit, notification, webhook,
│   │                 # booking-automation, export, fuzzy-search, brute-force-protection
│   ├── gateways/     # WebSocket gateways (notifications.gateway.ts via Socket.IO)
│   ├── guards/       # JwtAuthGuard, RolesGuard, CustomThrottlerGuard
│   ├── decorators/   # @Public(), @Roles(), @CurrentUser()
│   ├── dto/          # Request/response DTOs with class-validator
│   ├── mappers/      # Domain ↔ DTO mappers
│   └── interceptors/ # PerformanceMonitoringInterceptor
└── infrastructure/   # TypeORM entities/repos/mappers, Redis cache, carrier APIs,
                      # MinIO/S3, email (MJML+Nodemailer), Stripe, Sentry,
                      # Pappers (French SIRET registry), PDF generation
```

**Critical dependency rules**:
- Domain layer: zero imports from NestJS, TypeORM, Redis, or any framework
- Dependencies flow inward only: Infrastructure → Application → Domain
- Path aliases: `@domain/*`, `@application/*`, `@infrastructure/*` (defined in `apps/backend/tsconfig.json`)
- Domain tests run without NestJS TestingModule
- Backend has strict TypeScript: `strict: true`, `strictNullChecks: true` (but `strictPropertyInitialization: false`)
- Env vars validated at startup via Joi schema in `app.module.ts` — required vars include DATABASE_*, REDIS_*, JWT_SECRET, SMTP_*

### NestJS Modules (app.module.ts)

Global guards: JwtAuthGuard (all routes protected by default), CustomThrottlerGuard.

Feature modules: Auth, Rates, Ports, Bookings, CsvBookings, Organizations, Users, Dashboard, Audit, Notifications, Webhooks, GDPR, Admin, Subscriptions.

Infrastructure modules: CacheModule, CarrierModule, SecurityModule, CsvRateModule, StripeModule, PdfModule, StorageModule, EmailModule.

Swagger plugin enabled in `nest-cli.json` — DTOs auto-documented. Logging via `nestjs-pino` (pino-pretty in dev).

### Frontend (Next.js 14 App Router)

```
apps/frontend/
├── app/              # App Router pages (root-level)
│   ├── dashboard/    # Protected routes (bookings, admin, settings, wiki, search)
│   ├── carrier/      # Carrier portal (magic link auth — accept/reject/documents)
│   ├── booking/      # Booking confirmation/rejection flows
│   └── [auth pages]  # login, register, forgot-password, verify-email
└── src/
    ├── app/          # Additional app pages (e.g. rates/csv-search)
    ├── components/   # React components (ui/, layout/, bookings/, admin/, rate-search/, organization/)
    ├── hooks/        # useBookings, useNotifications, useCsvRateSearch, useCompanies, useFilterOptions
    ├── lib/
    │   ├── api/      # Fetch-based API client with auto token refresh (client.ts + per-module files)
    │   ├── context/  # Auth context, cookie context
    │   ├── providers/ # QueryProvider (TanStack Query / React Query)
    │   └── fonts.ts  # Manrope (headings) + Montserrat (body)
    ├── types/        # TypeScript type definitions
    ├── utils/        # Export utilities (Excel, PDF)
    └── legacy-pages/ # Archived page components (BookingsManagement, CarrierManagement, CarrierMonitoring)
```

Path aliases: `@/*` → `./src/*`, `@/components/*`, `@/lib/*`, `@/app/*` → `./app/*`, `@/types/*`, `@/hooks/*`, `@/utils/*`

**Note**: Frontend tsconfig has `strict: false`, `noImplicitAny: false`, `strictNullChecks: false` (unlike backend which is strict). Uses TanStack Query (React Query) for server state — wrap new data fetching in hooks, not bare `fetch` calls.

### Brand Design

Colors: Navy `#10183A` (primary), Turquoise `#34CCCD` (accent), Green `#067224` (success), Gray `#F2F2F2`.
Fonts: Manrope (headings), Montserrat (body).
Landing page is in French.

## Key Patterns

### Entity Pattern (Domain)
Private constructor + static `create()` factory. Immutable — mutation methods return new instances. Some entities also have `fromPersistence()` for reconstitution and `toObject()` for serialization.
```typescript
export class Booking {
  private readonly props: BookingProps;
  static create(props: Omit<BookingProps, 'bookingNumber' | 'status'>): Booking { ... }
  updateStatus(newStatus: BookingStatus): Booking { // Returns new instance
    return new Booking({ ...this.props, status: newStatus });
  }
}
```

### Value Object Pattern
Immutable, self-validating via static `create()`. E.g. `Money` supports USD, EUR, GBP, CNY, JPY with arithmetic and formatting methods.

### Repository Pattern
- Interface in `domain/ports/out/` with token constant (e.g. `BOOKING_REPOSITORY = 'BookingRepository'`)
- Implementation in `infrastructure/persistence/typeorm/repositories/`
- ORM entities: `infrastructure/persistence/typeorm/entities/*.orm-entity.ts`
- Separate mapper classes (`infrastructure/persistence/typeorm/mappers/`) with static `toOrm()`, `toDomain()`, `toDomainMany()` methods

### Frontend API Client
Custom Fetch wrapper in `src/lib/api/client.ts` — exports `get()`, `post()`, `patch()`, `del()`, `upload()`, `download()`. Auto-refreshes JWT on 401. Tokens stored in localStorage **and synced to cookies** (`accessToken` cookie) so Next.js middleware can read them server-side. Per-module files (auth.ts, bookings.ts, rates.ts, etc.) import from client.

### Route Protection (Middleware)
`apps/frontend/middleware.ts` checks the `accessToken` cookie to protect routes. Public paths are defined in two lists:
- `exactPublicPaths`: exact matches (e.g. `/`)
- `prefixPublicPaths`: prefix matches including sub-paths (e.g. `/login`, `/carrier`, `/about`, etc.)

All other routes redirect to `/login?redirect=<pathname>` when the cookie is absent.

### Application Decorators
- `@Public()` — skip JWT auth
- `@Roles()` — role-based access control
- `@CurrentUser()` — inject authenticated user

### API Key Authentication
A second auth mechanism alongside JWT. `ApiKey` domain entity (`domain/entities/api-key.entity.ts`) — keys are hashed with Argon2. `ApiKeyGuard` in `application/guards/` checks the `x-api-key` header. Routes can accept either JWT or API key; see `admin.controller.ts` for examples.

### WebSocket (Real-time Notifications)
Socket.IO gateway at `application/gateways/notifications.gateway.ts`. Clients connect to `/` namespace with a JWT bearer token in the handshake auth. Server emits `notification` events. The frontend `useNotifications` hook handles subscriptions.

### Carrier Connectors
Five carrier connectors (Maersk, MSC, CMA CGM, Hapag-Lloyd, ONE) extending `base-carrier.connector.ts`, each with request/response mappers. Circuit breaker via `opossum` (5s timeout).

### Caching
Redis with 15-min TTL for rate quotes. Key format: `rate:{origin}:{destination}:{containerType}`.

## Business Rules

- Booking number format: `WCM-YYYY-XXXXXX`
- Booking status flow: draft → confirmed → shipped → delivered
- Rate quotes expire after 15 minutes
- Multi-currency: USD, EUR, GBP, CNY, JPY
- RBAC Roles: ADMIN, MANAGER, USER, VIEWER, CARRIER
- JWT: access token 15min, refresh token 7d
- Password hashing: Argon2
- OAuth providers: Google, Microsoft (configured via passport strategies)
- Organizations can be validated via Pappers API (French SIRET/company registry) at `infrastructure/external/pappers-siret.adapter.ts`

### Carrier Portal Workflow
1. Admin creates CSV booking → assigns carrier
2. Email with magic link sent (1-hour expiry)
3. Carrier auto-login → accept/reject booking
4. Activity logged in `carrier_activities` table (via `CarrierProfile` + `CarrierActivity` ORM entities)

## Common Pitfalls

- Never import NestJS/TypeORM in domain layer
- Never use `any` type in backend (strict mode enabled)
- Never modify applied migrations — create new ones
- Always validate DTOs with `class-validator` decorators
- Always create separate mappers for Domain ↔ ORM conversions
- ORM entity files must match pattern `*.orm-entity.{ts,js}` (auto-discovered by data-source)
- Migration files must be in `infrastructure/persistence/typeorm/migrations/`
- Database synchronize is hard-coded to `false` — always use migrations

## Adding a New Feature

1. **Domain Entity** → `domain/entities/*.entity.ts` (pure TS, unit tests)
2. **Value Objects** → `domain/value-objects/*.vo.ts` (immutable)
3. **In Port (Use Case)** → `domain/ports/in/*.use-case.ts` (interface with `execute()`)
4. **Out Port (Repository)** → `domain/ports/out/*.repository.ts` (with token constant)
5. **ORM Entity** → `infrastructure/persistence/typeorm/entities/*.orm-entity.ts`
6. **Migration** → `npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/MigrationName`
7. **Repository Impl** → `infrastructure/persistence/typeorm/repositories/`
8. **Mapper** → `infrastructure/persistence/typeorm/mappers/` (static toOrm/toDomain/toDomainMany)
9. **DTOs** → `application/dto/` (with class-validator decorators)
10. **Controller** → `application/controllers/` (with Swagger decorators)
11. **Module** → Register repository + use-case providers, import in `app.module.ts`

## Documentation

- API Docs: http://localhost:4000/api/docs (Swagger, when running)
- Setup guide: `docs/installation/START-HERE.md`
- Carrier Portal API: `apps/backend/docs/CARRIER_PORTAL_API.md`
- Full docs index: `docs/README.md`
- Development roadmap: `TODO.md`
- Infrastructure configs (CI/CD, Docker): `infra/`
