diff --git a/README.md b/README.md index 302dab618..fe91ea611 100644 --- a/README.md +++ b/README.md @@ -1 +1,259 @@ -# edr-platform +# 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// + entities/.entity.ts // extends BaseEntity (UUID, timestamps, soft delete) + dto/-.dto.ts // class-validator DTOs + .module.ts // wires controller + service + repository + .controller.ts // HTTP layer only — no business logic + .service.ts // business logic + .repository.ts // extends BaseRepository; services inject this, NEVER `Repository` 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; the brand color (`#33578D`) marks the active item. +- **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`. +- **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.