React 19 types React.ReactElement props as unknown instead of any. Test helper functions that access element.props.children, .className, or .href need explicit props type parameters on React.ReactElement and isValidElement. Changes: - admin/PublicShell.test.ts: add WithChildren type, use React.ReactElement<WithChildren> - dashboard/PublicShell.test.ts: same pattern for childTypes and contentWrapper - dashboard/StatCard.test.ts: add ElementWithClass type for className access - dashboard/public-auth-pages.test.ts: add ElementWithHref type for href access No any, @ts-ignore, or type assertion bypass used.
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.tsapps/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 storefront 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/dashboardnpm run build --workspace @rentaldrivego/dashboardnpm 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:
basePathis/dashboard- browser API calls are proxied through
/dashboard/api/* - those calls are rewritten to the API origin configured by
API_INTERNAL_URLorAPI_URL - image loading allows Cloudinary plus
/storage/*assets from the API host DASHBOARD_ASSET_PREFIXcan 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 storefront/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 storefront/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
langanddir - 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:
- submits credentials to
POST /auth/employee/login - receives an HttpOnly
employee_sessioncookie from the API - stores only non-sensitive employee profile/preferences client-side
- 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 theemployee_sessioncookie - unauthenticated users are redirected either to the storefront root or to
/dashboard/sign-in, depending on host context - duplicate
/dashboard/dashboardpaths 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:
OWNERMANAGERAGENT
Shell and Navigation
The private shell is defined by:
src/app/(dashboard)/layout.tsxsrc/components/layout/Sidebar.tsxsrc/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
FormDatafor 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:
- mount
- call one or more
apiFetch(...)requests - store result in local component state
- show loading and empty states
- mutate through API actions
- 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.tsxsrc/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:
- basic company info via
PATCH /companies/me - brand/public profile via
PATCH /companies/me/brand - payment and storefront 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.tsxsrc/app/(dashboard)/fleet/[id]/page.tsxsrc/components/VehicleCalendar.tsx
Backed by:
GET /vehiclesPOST /vehiclesGET /vehicles/:idPATCH /vehicles/:idPATCH /vehicles/:id/statusPOST /vehicles/:id/photosDELETE /vehicles/:id/photos/:idxGET /vehicles/:id/maintenancePOST /vehicles/:id/maintenanceGET /vehicles/:id/calendarPOST /vehicles/:id/calendar/blocksDELETE /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.tsxsrc/app/(dashboard)/reservations/new/page.tsxsrc/app/(dashboard)/reservations/[id]/page.tsx
Backed by:
GET /reservationsPOST /reservationsGET /reservations/:idPATCH /reservations/:idPOST /reservations/:id/confirmPOST /reservations/:id/checkinPOST /reservations/:id/checkoutPOST /reservations/:id/closePOST /reservations/:id/extendPUT /reservations/:id/inspections/:typePATCH /reservations/:id/additional-drivers/:driverId/approvalGET|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.tsxReservationPhotoSection.tsxVehicleConditionSheet.tsx
5. Online reservations
Route:
src/app/(dashboard)/online-reservations/page.tsx
Backed by:
GET /reservations?source=MARKETPLACE&status=DRAFTPOST /reservations/:id/confirmPOST /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 /customersPATCH /customers/:idPOST /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 /offersGET /offers/:idPOST /offersPATCH /offers/:idDELETE /offers/:idGET /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.tscomponents/team/InviteModal.tsxcomponents/team/EditMemberModal.tsxcomponents/team/PermissionsMatrix.tsx
Backed by:
GET /teamGET /team/statsPOST /team/invitePATCH /team/:id/rolePOST /team/:id/deactivatePOST /team/:id/reactivateDELETE /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/meGET /subscriptions/invoicesGET /subscriptions/providersGET /subscriptions/plansGET /subscriptions/featuresPOST /subscriptions/checkoutPOST /subscriptions/cancelPOST /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 /reservationsGET /payments/companyPOST /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.tsxsrc/app/(dashboard)/contracts/[id]/page.tsx
Backed by:
GET /reservationsGET /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.tsxsrc/app/(dashboard)/complaints/page.tsx
Backed by:
GET /reviewsGET /reviews/statsPATCH /reviews/:id/replyGET /complaintsPOST /complaintsPATCH /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/companyGET /notifications/historyGET /notifications/company/preferencesPOST /notifications/company/:id/readPOST /notifications/company/read-allPATCH /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/brandPOST /companies/me/brand/logoPOST /companies/me/brand/heroPOST|DELETE /companies/me/brand/custom-domainGET|PATCH /companies/me/contract-settingsGET|POST|PATCH /companies/me/insurance-policiesGET|POST|PATCH /companies/me/pricing-rulesGET|PATCH /companies/me/accounting-settings
This is the broad configuration surface for the tenant.
It includes:
- public brand and storefront 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.tsxsrc/components/layout/PublicShell.tsxsrc/components/layout/PublicHeader.tsxsrc/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:
storefrontUrllinks staff back to the public storefront domainadminUrlsupports 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:
- Preserve the
/dashboardbase path andtoDashboardAppPathnormalization behavior. - Keep route protection aligned with
src/middleware.ts. - If a new page depends on employee role, gate it both in navigation and in-page behavior.
- Reuse
apiFetchfor auth and error handling consistency. - Keep language/theme strings inside the dashboard i18n system rather than scattering literals.
- Update this README when a new major page, workflow, or integration pattern is added.