Files
carmanagement/apps/dashboard
root cd67db99ed
Build & Deploy / Build & Push Docker Image (push) Successful in 48s
Test / API Unit Tests (push) Successful in 9m50s
Test / Marketplace Unit Tests (push) Successful in 9m37s
Test / Admin Unit Tests (push) Successful in 9m33s
Test / Dashboard Unit Tests (push) Successful in 9m37s
Test / API Integration Tests (push) Successful in 9m49s
Build & Deploy / Deploy to VPS (push) Successful in 2s
fix signin
2026-06-25 01:29:36 -04:00
..
2026-05-15 15:44:31 +00:00
2026-06-25 01:29:36 -04:00
2026-06-24 02:00:31 -04:00
2026-04-30 14:59:57 -04:00
2026-06-10 00:40:19 -04:00
2026-06-24 03:04:01 -04:00
2026-06-24 02:00:31 -04:00

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 marketplace 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 marketplace/admin hostname while still loading its own chunks correctly

Top-Level Structure

Main folders:

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 marketplace/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 marketplace 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 marketplace 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=MARKETPLACE&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 marketplace 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:

  • marketplaceUrl links staff back to the public marketplace 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

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.