# Dashboard App

This document explains the purpose, structure, runtime behavior, and main feature flows of the dashboard application in `apps/dashboard`.

Source of truth:

- `apps/dashboard/src/app/*`
- `apps/dashboard/src/components/*`
- `apps/dashboard/src/lib/*`
- `apps/dashboard/src/hooks/*`
- `apps/dashboard/src/middleware.ts`
- `apps/dashboard/next.config.js`

## Purpose

The dashboard is the internal company workspace for RentalDriveGo. It is the private B2B interface used by rental-company staff to run day-to-day operations.

The app covers:

- employee sign-in
- onboarding after company creation
- fleet management
- reservations and rental lifecycle operations
- online reservation intake
- customer CRM
- contracts and billing
- subscription management
- reviews, complaints, notifications, and settings

It is distinct from:

- the Carplace app, which is public and cross-company
- the company site app, which is public and company-branded
- the admin app, which is platform-internal rather than tenant-scoped

## Tech and Runtime

- framework: Next.js 14 App Router
- ui: React 18 + Tailwind CSS
- charts: Recharts
- realtime notifications: `socket.io-client`
- shared types: `@rentaldrivego/types`
- default dev port: `3001`
- base path: `/dashboard`

Defined in `package.json`:

- `npm run dev --workspace @rentaldrivego/dashboard`
- `npm run build --workspace @rentaldrivego/dashboard`
- `npm run start --workspace @rentaldrivego/dashboard`

## URL and Deployment Model

The app is designed to run under `/dashboard`, not at the domain root.

Key behavior from `next.config.js`:

- `basePath` is `/dashboard`
- browser API calls are proxied through `/dashboard/api/*`
- those calls are rewritten to the API origin configured by `API_INTERNAL_URL` or `API_URL`
- image loading allows Cloudinary plus `/storage/*` assets from the API host
- `DASHBOARD_ASSET_PREFIX` can force absolute JS/CSS asset URLs when the dashboard is served behind another frontend proxy

This lets the dashboard run:

- directly in dev on port `3001`
- behind a reverse proxy
- embedded under a shared Carplace/admin hostname while still loading its own chunks correctly

## Top-Level Structure

Main folders:

```text
apps/dashboard/
  src/app/
    (dashboard)/        private employee workspace
    (public)/           public shell pages
    onboarding/         post-signup setup
    sign-in/            employee sign-in
    sign-up/            company signup
    forgot-password/
    reset-password/
  src/components/
    layout/             sidebar, topbar, public shell
    reservations/       inspections, photo flows, condition sheet
    team/               invite/edit modals and permissions matrix
    ui/                 shared view components
  src/hooks/
    useTeam.ts          team management data/actions
  src/lib/
    api.ts              cookie-aware API fetch wrapper
    preferences.ts      language/theme persistence scoped per employee
    dashboardPaths.ts   normalizes basePath-aware routes
    urls.ts             resolves Carplace/admin app links
  src/middleware.ts     auth gate and redirect logic
```

## Rendering Strategy

Most dashboard screens are client components.

That is a deliberate fit for the app because the dashboard relies heavily on:

- HttpOnly session-cookie authentication
- cookie and embedded-host redirects
- optimistic UI updates
- direct REST fetches after mount
- websocket notification updates
- large interactive forms and modal-heavy workflows

The root `src/app/layout.tsx` is still server-rendered enough to:

- read the shared language cookie
- set `lang` and `dir`
- inject a theme bootstrapping script before hydration

## Auth and Session Model

The dashboard uses API-issued employee sessions stored in an HttpOnly cookie.

### Login flow

`src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx`:

1. submits credentials to `POST /auth/employee/login`
2. receives an HttpOnly `employee_session` cookie from the API
3. stores only non-sensitive employee profile/preferences client-side
4. redirects to the requested dashboard path

The sign-in page also supports:

- language and theme query params
- embedded usage through `postMessage`
- redirect handoff to the admin app after the API sets the admin HttpOnly session cookie

### Protected-route middleware

`src/middleware.ts` protects the app.

Important behavior:

- public dashboard routes are limited to sign-in and forgot-password flows
- all other `/dashboard/*` routes require the `employee_session` cookie
- unauthenticated users are redirected either to the Carplace root or to `/dashboard/sign-in`, depending on host context
- duplicate `/dashboard/dashboard` paths are normalized back to `/dashboard`

This means route protection is enforced before React renders the protected pages.

### Role gating

The dashboard does not depend only on route middleware. It also performs UI-level role gating.

Examples:

- the sidebar filters navigation items by employee role
- subscription access is restricted to `OWNER`
- manager/owner-only actions are hidden or disabled in feature pages

Role rank used in the shell:

- `OWNER`
- `MANAGER`
- `AGENT`

## Shell and Navigation

The private shell is defined by:

- `src/app/(dashboard)/layout.tsx`
- `src/components/layout/Sidebar.tsx`
- `src/components/layout/TopBar.tsx`

### Sidebar

Responsibilities:

- tenant branding fetch from `/companies/me/brand`
- current employee profile fetch from `/auth/employee/me`
- role-based navigation filtering
- language and theme controls
- logout handling
- communication with a parent frame when embedded

Main sections exposed in navigation:

- dashboard home
- fleet
- reservations
- online reservations
- customers
- offers
- team
- reports
- subscription
- billing
- contracts
- reviews
- complaints
- notifications
- settings

### Top bar

Responsibilities:

- current page title resolution based on normalized dashboard path
- unread notification count
- compact inbox dropdown
- websocket subscription to realtime notification events

Notification reads are synchronized through the API and local UI events.

## Shared State: Language and Theme

The dashboard uses `DashboardI18nProvider` in `src/components/I18nProvider.tsx`.

It handles:

- current language: `en`, `fr`, `ar`
- current theme: `light`, `dark`
- full in-app copy dictionaries
- persistence to cookies and `localStorage`
- per-employee scoped preferences using non-sensitive cached profile metadata

`src/lib/preferences.ts` is the key utility here. It stores both:

- shared values like `rentaldrivego-language`
- employee-scoped variants so one staff member does not override another on the same device

Arabic also flips layout direction through the root layout by setting `dir="rtl"`.

## Data Fetching Conventions

The dashboard centralizes API access in `src/lib/api.ts`.

### `apiFetch`

Browser fetch behavior:

- never reads authentication tokens from `localStorage`
- sends authenticated requests with the HttpOnly session cookie
- defaults JSON requests to `Content-Type: application/json`
- preserves `FormData` for file uploads
- includes credentials for cookie-aware flows
- unwraps `{ data }` responses automatically

Browser base URL:

- `NEXT_PUBLIC_API_URL`
- fallback `/dashboard/api/v1`

### `apiFetchServer`

Server-side helper for routes/components that explicitly receive a trusted server-side token and need `cache: 'no-store'`. Browser code should use HttpOnly cookies instead.

### Pattern used across pages

Most pages follow the same client-side pattern:

1. mount
2. call one or more `apiFetch(...)` requests
3. store result in local component state
4. show loading and empty states
5. mutate through API actions
6. refetch or patch local state

There is intentionally very little abstraction beyond this. The code favors explicit page-level orchestration over a large client data framework.

## Feature Areas

### 1. Company signup and onboarding

Relevant routes:

- `src/app/sign-up/[[...sign-up]]/page.tsx`
- `src/app/onboarding/page.tsx`

The signup screen creates the company through:

- `POST /auth/company/signup`

The onboarding flow then completes the initial tenant setup in three steps:

1. basic company info via `PATCH /companies/me`
2. brand/public profile via `PATCH /companies/me/brand`
3. payment and Carplace settings via `PATCH /companies/me/brand`

This makes onboarding a dashboard-owned continuation of the API signup flow.

### 2. Dashboard home

Route:

- `src/app/(dashboard)/page.tsx`

Backed by:

- `GET /analytics/dashboard`

Primary contents:

- KPI cards
- source breakdown chart
- subscription-status banner
- recent reservations list

Revenue visibility is reduced for lower-privilege roles.

### 3. Fleet

Routes:

- `src/app/(dashboard)/fleet/page.tsx`
- `src/app/(dashboard)/fleet/[id]/page.tsx`
- `src/components/VehicleCalendar.tsx`

Backed by:

- `GET /vehicles`
- `POST /vehicles`
- `GET /vehicles/:id`
- `PATCH /vehicles/:id`
- `PATCH /vehicles/:id/status`
- `POST /vehicles/:id/photos`
- `DELETE /vehicles/:id/photos/:idx`
- `GET /vehicles/:id/maintenance`
- `POST /vehicles/:id/maintenance`
- `GET /vehicles/:id/calendar`
- `POST /vehicles/:id/calendar/blocks`
- `DELETE /vehicles/:id/calendar/blocks/:blockId`

Key responsibilities:

- create and edit vehicles
- upload vehicle images
- publish/unpublish vehicles
- change operational status
- record maintenance
- visualize reservations and manual blocks on a calendar
- configure pickup and dropoff location rules

### 4. Reservations

Routes:

- `src/app/(dashboard)/reservations/page.tsx`
- `src/app/(dashboard)/reservations/new/page.tsx`
- `src/app/(dashboard)/reservations/[id]/page.tsx`

Backed by:

- `GET /reservations`
- `POST /reservations`
- `GET /reservations/:id`
- `PATCH /reservations/:id`
- `POST /reservations/:id/confirm`
- `POST /reservations/:id/checkin`
- `POST /reservations/:id/checkout`
- `POST /reservations/:id/close`
- `POST /reservations/:id/extend`
- `PUT /reservations/:id/inspections/:type`
- `PATCH /reservations/:id/additional-drivers/:driverId/approval`
- `GET|POST|DELETE /reservations/:id/photos*`

This is the heaviest operational area of the dashboard.

Major workflows:

- create draft bookings
- choose existing or new customers
- upload and manage customer license images
- confirm/check-in/check-out/close rentals
- edit booking vs return data based on reservation workflow state
- complete check-in/check-out inspections
- upload pickup and dropoff photos
- extend rentals
- approve additional drivers

Supporting reservation UI components:

- `DamageInspectionCard.tsx`
- `ReservationPhotoSection.tsx`
- `VehicleConditionSheet.tsx`

### 5. Online reservations

Route:

- `src/app/(dashboard)/online-reservations/page.tsx`

Backed by:

- `GET /reservations?source=CARPLACE&status=DRAFT`
- `POST /reservations/:id/confirm`
- `POST /reservations/:id/cancel`

This page is the triage surface for new public booking requests before staff converts them into active operational reservations.

### 6. Customers

Route:

- `src/app/(dashboard)/customers/page.tsx`

Backed mainly by:

- `GET /customers`

The reservations-create flow also uses:

- `POST /customers`
- `PATCH /customers/:id`
- `POST /customers/:id/license-image`

The customer surface acts as a company-local CRM rather than a global renter account manager.

### 7. Offers

Route:

- `src/app/(dashboard)/offers/page.tsx`

Backed by:

- `GET /offers`
- `GET /offers/:id`
- `POST /offers`
- `PATCH /offers/:id`
- `DELETE /offers/:id`
- `GET /vehicles`

Purpose:

- create and edit discounts
- target specific vehicles
- manage promo details and active/public state

### 8. Team

Route:

- `src/app/(dashboard)/team/page.tsx`

Supporting hook and components:

- `src/hooks/useTeam.ts`
- `components/team/InviteModal.tsx`
- `components/team/EditMemberModal.tsx`
- `components/team/PermissionsMatrix.tsx`

Backed by:

- `GET /team`
- `GET /team/stats`
- `POST /team/invite`
- `PATCH /team/:id/role`
- `POST /team/:id/deactivate`
- `POST /team/:id/reactivate`
- `DELETE /team/:id`

This area centralizes employee lifecycle management.

### 9. Reports

Route:

- `src/app/(dashboard)/reports/page.tsx`

Backed by:

- `GET /analytics/report?period=...`

Purpose:

- revenue and operational analytics
- summarized reporting views

### 10. Subscription

Route:

- `src/app/(dashboard)/subscription/page.tsx`

Backed by:

- `GET /subscriptions/me`
- `GET /subscriptions/invoices`
- `GET /subscriptions/providers`
- `GET /subscriptions/plans`
- `GET /subscriptions/features`
- `POST /subscriptions/checkout`
- `POST /subscriptions/cancel`
- `POST /subscriptions/resume`

This is owner-only and manages the SaaS relationship between the company and RentalDriveGo.

Key functions:

- current plan visibility
- plan comparison
- provider availability checks
- checkout redirection
- invoice history
- cancel/resume lifecycle

### 11. Billing

Route:

- `src/app/(dashboard)/billing/page.tsx`

Backed by:

- `GET /reservations`
- `GET /payments/company`
- `POST /payments/reservations/:id/manual`

This page handles customer-side rental billing, not SaaS subscription billing.

It focuses on:

- reservation invoice state
- collected vs outstanding balances
- payment history
- manual payment recording
- quick jumps to reservation and contract pages

### 12. Contracts

Routes:

- `src/app/(dashboard)/contracts/page.tsx`
- `src/app/(dashboard)/contracts/[id]/page.tsx`

Backed by:

- `GET /reservations`
- `GET /reservations/:id/contract`

Purpose:

- list reservations with contract/invoice state
- generate and reopen rental-contract views
- present printable contract payloads for staff operations

### 13. Reviews and complaints

Routes:

- `src/app/(dashboard)/reviews/page.tsx`
- `src/app/(dashboard)/complaints/page.tsx`

Backed by:

- `GET /reviews`
- `GET /reviews/stats`
- `PATCH /reviews/:id/reply`
- `GET /complaints`
- `POST /complaints`
- `PATCH /complaints/:id`

These pages give staff post-rental quality and dispute management tools.

### 14. Notifications

Route:

- `src/app/(dashboard)/notifications/page.tsx`

Backed by:

- `GET /notifications/company`
- `GET /notifications/history`
- `GET /notifications/company/preferences`
- `POST /notifications/company/:id/read`
- `POST /notifications/company/read-all`
- `PATCH /notifications/company/preferences`

This page complements the top-bar notification dropdown by adding:

- full inbox view
- delivery history audit
- per-event, per-channel preference management

### 15. Settings

Route:

- `src/app/(dashboard)/settings/page.tsx`

Backed by:

- `GET|PATCH /companies/me/brand`
- `POST /companies/me/brand/logo`
- `POST /companies/me/brand/hero`
- `POST|DELETE /companies/me/brand/custom-domain`
- `GET|PATCH /companies/me/contract-settings`
- `GET|POST|PATCH /companies/me/insurance-policies`
- `GET|POST|PATCH /companies/me/pricing-rules`
- `GET|PATCH /companies/me/accounting-settings`

This is the broad configuration surface for the tenant.

It includes:

- public brand and Carplace profile
- payment account identifiers
- custom domain setup
- rental policies
- insurance products
- pricing rules
- accounting defaults

## Public Shell Pages

Public-facing dashboard pages live outside the private shell.

Notable routes:

- `/dashboard/sign-in`
- `/dashboard/sign-up`
- `/dashboard/forgot-password`
- `/dashboard/reset-password`
- `/dashboard/onboarding`

Layout:

- `src/app/(public)/layout.tsx`
- `src/components/layout/PublicShell.tsx`
- `src/components/layout/PublicHeader.tsx`
- `src/components/layout/PublicFooter.tsx`

These pages share the branding system and language/theme controls but do not render the private sidebar/topbar shell.

## Embedded and Cross-App Behavior

The dashboard supports embedded scenarios.

Examples in the code:

- sign-in posts messages to the parent frame
- logout posts an employee-logout event
- current embedded path is reported upward

It also coordinates with sibling apps:

- `CarplaceUrl` links staff back to the public Carplace domain
- `adminUrl` supports cookie-based handoff into the admin interface
- host-aware URL rewriting allows the same app to function under external domains, internal Docker hostnames, and proxied environments

## API Dependency Summary

The dashboard is a thin client over the company API. Its main backend dependencies are:

- auth: employee login and profile
- companies: brand, contract settings, pricing rules, accounting, domains
- vehicles: fleet CRUD, photos, maintenance, availability calendar
- reservations: booking lifecycle and inspections
- customers: CRM and license documents
- offers: promotions
- team: employee management
- analytics: dashboard and reports
- notifications: inbox, history, preferences
- subscriptions: SaaS plan management
- payments: rental payment recording
- reviews and complaints: post-rental follow-up

The frontend does not try to reproduce business rules locally. It delegates workflow authority to the API and reflects the resulting state.

## Design Characteristics

The current dashboard code favors:

- explicit page-local state over a global data layer
- direct REST integration over generated clients
- strong tenant scoping through backend auth rather than frontend assumptions
- rich operational forms over minimal CRUD scaffolds
- multilingual support directly in the app shell

Tradeoffs of this approach:

- easy to reason about per page
- low indirection when debugging API interactions
- more repeated fetch/state patterns than a centralized query layer would have
- large single-file page components in the heaviest screens

## Recommended Maintenance Rules

When changing the dashboard, keep these rules in mind:

1. Preserve the `/dashboard` base path and `toDashboardAppPath` normalization behavior.
2. Keep route protection aligned with `src/middleware.ts`.
3. If a new page depends on employee role, gate it both in navigation and in-page behavior.
4. Reuse `apiFetch` for auth and error handling consistency.
5. Keep language/theme strings inside the dashboard i18n system rather than scattering literals.
6. Update this README when a new major page, workflow, or integration pattern is added.
