mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
- Define shared payment types and interfaces in payment.ts - Create PaymentModal component for handling payment interactions - Implement payment API client for initiating and confirming payments - Add payment history management with localStorage - Create PaymentsPage for displaying payment status and history - Introduce utility functions for money conversion and formatting - Set up hooks for managing payment flow and state - Update SeamanBookApplicationPage to utilize new payment modal - Add tests for payment API functions and history management
132 lines
4.7 KiB
Markdown
132 lines
4.7 KiB
Markdown
# EMA Platform — Nx Monorepo Frontend
|
|
|
|
A fully scaffolded Nx monorepo housing two Vite + React 19 SPAs (Backoffice and Portal) with shared libraries for API integration, UI components, and theming.
|
|
|
|
---
|
|
|
|
## Tech Stack
|
|
|
|
| Tool | Version |
|
|
|------|---------|
|
|
| React | 19 |
|
|
| Nx | 22 |
|
|
| Vite | 7 |
|
|
| TypeScript | 5.9 |
|
|
| Redux Toolkit | 2.11 |
|
|
| Mantine | 8.3 |
|
|
| React Router | 7 |
|
|
| TanStack Query | 5 |
|
|
| React Hook Form | 7 |
|
|
| Zod | 4 |
|
|
| Tailwind CSS | 3.4 |
|
|
| Vitest | 4 |
|
|
|
|
---
|
|
|
|
## Monorepo Structure
|
|
|
|
```
|
|
emaui/
|
|
├── apps/
|
|
│ ├── backoffice/ # Admin/operator SPA — port 4201
|
|
│ └── portal/ # End-user SPA — port 4200
|
|
└── libs/
|
|
├── api/ # RTK Query baseApi, session resolution, generic query/mutation hooks
|
|
├── ui/ # Shared Mantine components (ConfirmModal, ApiErrorAlert, notify)
|
|
└── shared/ # Mantine theme (emaTheme), design tokens
|
|
```
|
|
|
|
### libs/api
|
|
- `base-api/` — RTK Query `createApi` instance with `prepareHeaders` that injects the Bearer token from Redux state or localStorage.
|
|
- `session/` — `resolveTokenFromStorage()` reads from `localStorage` keys or `auth-token` cookie. `resolveSessionContext()` merges Redux state token with storage fallback.
|
|
- `query-and-mutation/` — Generic `useApiQuery` / `useApiMutation` wrappers for one-off API calls without defining a dedicated endpoint file.
|
|
|
|
### libs/ui
|
|
- `ConfirmModal` — Reusable Mantine modal for destructive-action confirmation.
|
|
- `ApiErrorAlert` — Extracts a human-readable message from RTK Query error shapes or Error objects.
|
|
- `notify` — Thin wrapper around `@mantine/notifications` with `.success`, `.error`, `.info`, `.warning` helpers.
|
|
|
|
### libs/shared
|
|
- `ema-theme` — Mantine v8 `createTheme()` with `emaPrimary` (blue) and `emaSecondary` (warm) color tuples, Inter font, and custom shadow scale.
|
|
|
|
---
|
|
|
|
## Auth Flow
|
|
|
|
1. User submits the login form (LoginForm / LoginPage).
|
|
2. The form calls the `login` RTK Query mutation (backoffice) or a plain `fetch` (portal).
|
|
3. On success, `loginSuccess` action is dispatched → Redux `auth` slice stores `token` and `user`; `authStorage.setToken()` persists the token to `localStorage`.
|
|
4. `baseApi`'s `prepareHeaders` reads the token via `resolveSessionContext(getState())` and attaches `Authorization: Bearer <token>` to every RTK Query request.
|
|
5. `ProtectedRoute` checks `localStorage` for the token key on every navigation — if absent, redirects to `/login`.
|
|
6. `logout` action clears Redux state and calls `authStorage.clear()` to remove all localStorage keys.
|
|
|
|
---
|
|
|
|
## Local Setup
|
|
|
|
```bash
|
|
# 1. Install dependencies
|
|
npm install
|
|
|
|
# 2. Copy environment config
|
|
cp .env.example .env
|
|
# Edit VITE_BASE_API_URL to point at your running backend
|
|
|
|
# 3. Start the backoffice (port 4201)
|
|
npm run backoffice
|
|
|
|
# 4. Start the portal (port 4200)
|
|
npm run portal
|
|
|
|
# 5. Or start both in parallel
|
|
npm run dev:all
|
|
```
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Required | Default | Description |
|
|
|---|---|---|---|
|
|
| `VITE_BASE_API_URL` | Yes | `http://localhost:3000` | Base URL for all API requests |
|
|
| `VITE_ENABLE_DEVELOPER_TOOLS` | No | `true` | Toggle Redux DevTools |
|
|
| `VITE_PAYMENT_API_URL` | Yes (portal) | — | Base URL for the payment service (portal `payment` feature). Can be a same-origin path if a backend proxy is later placed in front of it |
|
|
| `VITE_PAYMENT_SERVICE_TOKEN` | Yes (portal) | — | Sent as `x-service-token` on every payment request. Note: bundled `VITE_*` values are public in the built app, not secret |
|
|
| `PORTAL_PORT` | No | `4200` | Docker host port for portal |
|
|
| `BACKOFFICE_PORT` | No | `4201` | Docker host port for backoffice |
|
|
|
|
---
|
|
|
|
## Docker
|
|
|
|
```bash
|
|
# Build and run both apps via Docker Compose
|
|
cp .env.example .env
|
|
docker compose up --build
|
|
```
|
|
|
|
The `Dockerfile` uses multi-stage builds with named targets (`portal` / `backoffice`). Each stage produces an nginx image serving the built SPA.
|
|
|
|
---
|
|
|
|
## Adding a New Feature
|
|
|
|
1. Create the feature folder under the relevant app:
|
|
```
|
|
apps/backoffice/src/app/features/<feature-name>/
|
|
├── types/ # TypeScript interfaces
|
|
├── api/ # RTK Query injectEndpoints
|
|
├── store/ # Redux slice (if local state needed)
|
|
├── hooks/ # Custom hooks wrapping store/api
|
|
├── components/ # Presentational React components
|
|
└── pages/ # Route-level components
|
|
```
|
|
|
|
2. Wire up the API endpoint in `<feature-name>/api/<feature>-api.ts` using `baseApi.injectEndpoints(...)`.
|
|
|
|
3. Add a route in `apps/<app>/src/app/router/index.tsx` (backoffice) or `apps/<app>/src/app/router.tsx` (portal).
|
|
|
|
4. Add a sidebar entry in `AppSidebar.tsx` (backoffice only) for the new route.
|
|
|
|
5. If the feature needs shared UI, add components to `libs/ui/` and export from `libs/ui/src/index.ts`.
|