# EDR Platform

Monorepo for the **Ethio-Djibouti Railway** digital platform. Hosts two product lines — **Freight Management** and **Passenger Management** — each with a NestJS API plus React portal and back-office web apps, sharing TypeScript types, NestJS utilities, and a React component library.

---

## Tech Stack

### Backend

| Layer            | Tech                                            |
| ---------------- | ----------------------------------------------- |
| Runtime          | Node.js ≥ 20                                    |
| Framework        | NestJS 11 (modular architecture)                |
| Language         | TypeScript 5 (strict mode, project-wide)        |
| ORM              | TypeORM 0.3 (UUID PKs, soft deletes, `snake_case` columns) |
| Database         | PostgreSQL 16 (one DB per domain)               |
| Validation       | class-validator + class-transformer             |
| API docs         | Swagger via `@nestjs/swagger`                   |
| Messaging        | `@nestjs/microservices` (inter-service ready)   |
| Testing          | Jest + Supertest                                |

### Frontend

| Layer            | Tech                                            |
| ---------------- | ----------------------------------------------- |
| Framework        | React 18 + Vite 5                               |
| Language         | TypeScript 5 (strict)                           |
| Routing          | React Router v6                                 |
| Styling          | Tailwind CSS v4 (`@tailwindcss/vite`) + `tailwind-merge` + `class-variance-authority` |
| UI primitives    | Radix UI (meta `radix-ui` package, shadcn-style components) |
| Icons            | lucide-react                                    |
| State / Data     | Zustand (client state) · TanStack Query (server state) |
| HTTP             | Axios                                           |
| Auth UI          | `@tria-plc/iamui-common` (external IAM)         |

### Shared Packages

| Package                | Purpose                                                              |
| ---------------------- | -------------------------------------------------------------------- |
| `@edr/types`           | Shared TypeScript interfaces and enums                               |
| `@edr/api-common`      | NestJS decorators, filters, interceptors, pipes, `BaseEntity`, `BaseRepository` |
| `@edr/ui-common`       | Shared React components (`DashboardLayout`, `Sidebar`, `Button`, `Modal`, etc.) and theme tokens |
| `@edr/eslint-config`   | Shared ESLint configs (base / nestjs / react)                        |
| `@edr/tsconfig`        | Shared TypeScript configs                                            |
| `@edr/prettier-config` | Shared Prettier configuration                                        |

### Tooling

- **pnpm 9** — workspace package manager (sole supported PM)
- **Turborepo 2** — task orchestrator with caching
- **Husky + lint-staged + commitlint** — pre-commit ESLint/Prettier and conventional-commit enforcement
- **Docker Compose** — local Postgres instances + production stack (see `infrastructure/`)
- **Nginx** — reverse proxy / static asset server in production

### Planned Integrations

- **MinIO** — S3-compatible object storage for documents (object keys already shaped under `edr-freight/{linkedType}/{ref}/{filename}`)

---

## Architecture

### High-level layout

```
┌──────────────────────────────────────────────────────────────────┐
│                        EDR Platform (monorepo)                   │
├──────────────────────┬───────────────────────────────────────────┤
│  Freight domain      │  Passenger domain                         │
│  ┌────────────────┐  │  ┌────────────────┐                      │
│  │ freight-portal │  │  │ passenger-     │                      │
│  │  (React)       │  │  │  portal (React)│                      │
│  ├────────────────┤  │  ├────────────────┤                      │
│  │ freight-       │  │  │ passenger-     │                      │
│  │  backoffice    │  │  │  backoffice    │                      │
│  └───────┬────────┘  │  └───────┬────────┘                      │
│          │           │          │                                │
│          ▼           │          ▼                                │
│  ┌────────────────┐  │  ┌────────────────┐                      │
│  │ freight-api    │  │  │ passenger-api  │                      │
│  │  (NestJS)      │  │  │  (NestJS)      │                      │
│  └───────┬────────┘  │  └───────┬────────┘                      │
│          │           │          │                                │
│          ▼           │          ▼                                │
│  ┌────────────────┐  │  ┌────────────────┐                      │
│  │ postgres-      │  │  │ postgres-      │                      │
│  │  freight       │  │  │  passenger     │                      │
│  └────────────────┘  │  └────────────────┘                      │
└──────────────────────┴───────────────────────────────────────────┘
                  Shared (workspace) packages
       @edr/types · @edr/api-common · @edr/ui-common · @edr/{eslint,tsconfig,prettier}-config
```

### Domain isolation

- **One database per domain.** `postgres-freight` (port 5433, db `edr_freight`) and `postgres-passenger` (port 5434, db `edr_passenger`). No cross-database joins. Cross-domain data flows only through API calls or message queues.
- **Each domain owns its data model.** Freight bookings/consignments/shipments/trains/invoices/documents live only in the freight DB; passenger journeys/tickets live only in the passenger DB.

### NestJS module pattern (per feature)

```
modules/<feature>/
  entities/<feature>.entity.ts          // extends BaseEntity (UUID, timestamps, soft delete)
  dto/<verb>-<feature>.dto.ts           // class-validator DTOs
  <feature>.module.ts                   // wires controller + service + repository
  <feature>.controller.ts               // HTTP layer only — no business logic
  <feature>.service.ts                  // business logic
  <feature>.repository.ts               // extends BaseRepository<Entity>; services inject this, NEVER `Repository<T>` directly
```

Conventions enforced across the codebase:

- All entities have UUID primary keys (`@PrimaryGeneratedColumn('uuid')`).
- All entities inherit `createdAt` / `updatedAt` / `deletedAt` from `BaseEntity` (soft delete).
- DB columns use `snake_case` via `@Column({ name: '...' })`; TS properties stay `camelCase`.
- **No `synchronize: true`** in production — schema changes go through TypeORM migrations.
- ESLint + Prettier run on pre-commit via Husky + lint-staged.
- Conventional-commits enforced via commitlint.

### Frontend application structure

```
apps/edr-freight-web/portal/src/
  App.tsx                   // routes + sidebar definition
  main.tsx                  // React Router + QueryClient providers
  components/
    Breadcrumbs.tsx         // shared local UI
    ui/                     // shadcn-style primitives (Button, Input, Dialog, Label, Textarea)
  pages/
    customers/              // CustomersPage + CustomerDetailPage + NewCustomerPage (dialog)
    bookings/               // ...                + multimodal Transport Legs editor
    consignments/
    tracking/               // Shipment grid + table view toggle
    trains/                 // Fleet roster
    billing/                // Invoices
    documents/              // MinIO-shaped document library
  hooks/                    // TanStack Query hooks (per feature)
  services/                 // Axios clients (per feature)
  store/                    // Zustand stores
  lib/                      // utilities (`cn` helper, formatters)
```

Each feature folder typically contains: `*Page.tsx` (list), `*DetailPage.tsx`, `New*Page.tsx` (create/edit dialog with `mode: "create" | "edit"`), `Delete*Dialog.tsx`, and a `*.mock.ts` seed file used by the current mock UI.

### Shared layout (`@edr/ui-common`)

`DashboardLayout` provides the sidebar + header shell shared across all freight and passenger web apps:

- **Sidebar** — brand-tinted, icon-led navigation. Main brand color: `#10B981` (`rgb(16, 185, 129)` — emerald-500). Icon containers use the filled style: brand-color background with a white icon.
- **Header** — language picker, notifications, user dropdown (click-driven, click-outside / Escape close); host apps opt into a light/dark theme toggle via `enableThemeToggle` (Tailwind class-based, persisted in `localStorage`).

### Auth integration (planned)

Authentication is provided by an external `@edr/iamui-common` / `@tria-plc/iamapi-common` package. **Do not** implement login/JWT/password logic in this repo. Use placeholder TODO comments next to controllers and `@CurrentUser` decorators (in `@edr/api-common`) until the integration ships.

---

## Apps & Ports

| App                            | Package name                | Purpose                                  | Port |
| ------------------------------ | --------------------------- | ---------------------------------------- | ---- |
| `edr-freight-api`              | `@edr/freight-api`          | NestJS API for freight                   | 3001 |
| `edr-freight-web/portal`       | `@edr/freight-portal`       | React frontend for freight customers     | 5173 |
| `edr-freight-web/backoffice`   | `@edr/freight-backoffice`   | React frontend for freight employees     | 5183 |
| `edr-passenger-api`            | `@edr/passenger-api`        | NestJS API for passengers                | 3002 |
| `edr-passenger-web/portal`     | `@edr/passenger-portal`     | React frontend for passenger customers   | 5174 |
| `edr-passenger-web/backoffice` | `@edr/passenger-backoffice` | React frontend for passenger employees   | 5184 |

`edr-freight-web` and `edr-passenger-web` are grouping folders, not workspace packages. Each holds independent `portal/` and `backoffice/` workspace packages declared in `pnpm-workspace.yaml`.

---

## Getting Started

### Prerequisites

- Node.js ≥ 20
- pnpm 9 (`corepack enable && corepack prepare pnpm@9.12.0 --activate`)
- Docker (for local Postgres)

### Install

```bash
pnpm install
```

### Start local databases

```bash
docker compose -f infrastructure/docker/docker-compose.db.dev.yml up -d
```

### Run apps

```bash
pnpm dev               # every app
pnpm dev:freight       # freight API + portal + backoffice
pnpm dev:passenger     # passenger API + portal + backoffice
```

### Common scripts

| Script              | Description                          |
| ------------------- | ------------------------------------ |
| `pnpm install`      | Install workspace dependencies       |
| `pnpm dev`          | Run every app in watch mode          |
| `pnpm build`        | Build every package and app          |
| `pnpm test`         | Run all tests                        |
| `pnpm lint`         | Lint everything                      |
| `pnpm type-check`   | Type-check every package             |
| `pnpm format`       | Format with Prettier                 |

---

## Repository Layout

```
.
├── apps/
│   ├── edr-freight-api/              NestJS — freight backend
│   ├── edr-freight-web/
│   │   ├── portal/                   React — freight customer portal
│   │   └── backoffice/               React — freight back-office
│   ├── edr-passenger-api/            NestJS — passenger backend
│   └── edr-passenger-web/
│       ├── portal/                   React — passenger customer portal
│       └── backoffice/               React — passenger back-office
├── packages/
│   ├── api-common/                   Shared NestJS utilities + BaseEntity/Repository
│   ├── types/                        Shared TS types/enums
│   ├── ui-common/                    Shared React components + theme
│   └── config/
│       ├── eslint/                   @edr/eslint-config
│       ├── tsconfig/                 @edr/tsconfig
│       └── prettier/                 @edr/prettier-config
├── infrastructure/
│   ├── docker/                       docker-compose files (db.dev / dev / prod)
│   └── nginx/                        Production nginx config
├── CLAUDE.md                         Developer guide for AI-assisted work
├── turbo.json                        Turborepo task pipeline
├── pnpm-workspace.yaml               Workspace manifest
└── README.md
```

---

## Standards (recap)

- **TypeScript strict mode** is enabled in every package.
- **pnpm only** — never run `npm install` or `yarn`.
- **Conventional commits** — enforced via commitlint on every commit.
- **NestJS 4-layer pattern** — `module → controller → service → repository`.
- **Repository injection** — services inject the custom `*Repository` class, not `Repository<T>`.
- **Controllers are thin** — no business logic; delegate to services.
- **Migrations only** — never enable TypeORM `synchronize` in production.
- **One DB per domain** — no cross-database joins.

See [`CLAUDE.md`](./CLAUDE.md) for the deeper developer guide used during AI-assisted contributions.
