Skip to content

Latest commit

 

History

History
364 lines (286 loc) · 14.8 KB

File metadata and controls

364 lines (286 loc) · 14.8 KB

Technical Architecture — GoNow Logistics Planner

Version: 0.1.814 Last Updated: April 2026


1. System Overview

┌──────────────────────────────────────────────────────┐
│                    Browser (SPA)                      │
│  React 19 + TypeScript + Vite + Tailwind CSS          │
│                                                       │
│  ┌─────────────┐  ┌──────────────┐  ┌─────────────┐  │
│  │  Auth Layer  │  │  Data Layer  │  │  UI Layer   │  │
│  │ AuthContext  │  │ DataContext  │  │ Components  │  │
│  │ Firebase     │  │ Services     │  │ Hooks       │  │
│  │ Auth SDK     │  │ Firestore    │  │ Recharts    │  │
│  └─────────────┘  └──────────────┘  │ Leaflet     │  │
│                                      │ Framer      │  │
│                                      └─────────────┘  │
└──────────────────┬───────────────────────────────────┘
                   │ HTTPS
┌──────────────────▼───────────────────────────────────┐
│                  Firebase Platform                     │
│                                                       │
│  ┌────────────┐ ┌───────────┐ ┌──────────────────┐   │
│  │   Auth     │ │ Firestore │ │     Storage      │   │
│  │ Email/Pass │ │ RBAC Rules│ │  Storage Rules   │   │
│  │ Phone Auth │ │ Indexes   │ │  Hive-partition  │   │
│  └────────────┘ └───────────┘ └──────────────────┘   │
│                                                       │
│  ┌─────────────────────────────────────────────────┐  │
│  │           Cloud Functions (Node 22)              │  │
│  │  reseed | getStorageStats | migrateSearchKeys    │  │
│  └─────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────┘
                   │
┌──────────────────▼───────────────────────────────────┐
│              External APIs                            │
│  Google Maps Directions API | Google Maps JS API      │
│  OpenStreetMap tiles (Leaflet)                        │
└──────────────────────────────────────────────────────┘

2. Tech Stack

Layer Technology Version
UI Framework React 19.2.1
Language TypeScript ~5.8.2
Build Tool Vite 6.2.0
Styling Tailwind CSS 3.4.17
Animation Framer Motion 12.23.26
Icons Lucide React 0.554.0
Charts Recharts 3.5.0
Maps (display) Leaflet + react-leaflet 1.9.4 / 5.0.0
Maps (routing) Google Maps JS API + Directions via loader
i18n i18next + react-i18next 25.6.3 / 16.3.5
PDF export jsPDF + html2canvas 4.0.0 / 1.4.1
File export jszip + file-saver 3.10.1 / 2.0.5
Date utilities date-fns 4.1.0
Firebase Client SDK firebase 12.6.0
AI (available) @google/genai + Vercel AI SDK 1.30.0 / 5.0.114
Unit testing Vitest + Testing Library 4.0.15 / 16.3.0
E2E testing Cypress 15.8.1
Cloud Functions runtime Firebase Functions v2 Node 22
Image processing (Functions) Jimp 1.6.0

3. Frontend Architecture

3.1 Entry Point

index.html
  └── index.tsx          React root, i18n init, AuthProvider wrapper
        └── App.tsx       Main router — tab state, global listeners, role gates
              ├── AuthContext.tsx    Firebase Auth state
              ├── DataContext.tsx    Shared Firestore data (users, routes, etc.)
              ├── Sidebar.tsx        Navigation with role-based visibility
              ├── AppHeader.tsx      Top bar, user avatar, language toggle
              └── [Module Components]

3.2 State Management

No external state management library. State is distributed across:

Layer Mechanism Scope
Auth state AuthContext (React Context) App-wide
Shared data DataContext (React Context) App-wide
App logic useAppState (custom hook) App.tsx
Route editor Local component state RouteEditor
Camera useCameraManager (custom hook) CameraCapture
Optimization useRouteOptimization (custom hook) RouteEditor

3.3 Component Structure

components/
  App/
    AppHeader.tsx         Top navigation bar
    Sidebar.tsx           Left navigation with role gates
  UI/
    LoadingSpinner.tsx    Reusable spinner
    RefreshButton.tsx     Reusable refresh trigger
  RouteEditor/
    RouteEditorForm.tsx   Route fields and destination list
    RouteEditorHeader.tsx Save/cancel/optimize controls
    RouteMapContainer.tsx Leaflet map with polyline
    DestinationItem.tsx   Individual stop with photo capture
  reports/
    VisitStatusReport.tsx
    CustomerHistoryReport.tsx
    EmployeePerformanceReport.tsx
    CustomerDeliveryVolumeReport.tsx
    FleetStatusReport.tsx
  app-status/
    AppHealth.tsx
    LoginHistory.tsx
    ReportIssues.tsx
  [Top-level modules]
    LogisticsDashboard.tsx
    CalendarView.tsx
    RouteEditor.tsx         (orchestrator)
    WarehouseView.tsx
    Reporting.tsx           (orchestrator)
    SystemSettings.tsx
    UserManagement.tsx
    CustomerManagement.tsx
    FleetManagement.tsx
    ProductManagement.tsx
    AppStatus.tsx           (orchestrator)

3.4 Routing

No React Router. Tab-based navigation managed in App.tsx via activeTab state. The Sidebar emits tab change events; App.tsx renders the corresponding component. This is appropriate for an authenticated SPA with no deep-link requirements.


4. Backend Architecture

4.1 Firebase Services

Service Usage
Authentication Email/password login. Secondary app instance for creating users without ending admin session.
Firestore Primary datastore. All collections have server-side RBAC rules. Real-time listeners for live dashboard updates.
Cloud Storage Photo proof-of-delivery uploads. Hive-style folder partitioning.
Cloud Functions Admin-only or Manager-only operations too privileged/expensive for the client.
Hosting SPA hosting from dist/ with CSP headers and SPA rewrite rule.

4.2 Service Layer (/services/)

Each entity has its own service file. Services are plain objects or exported functions — no class instances except where secondary Firebase apps are needed.

File Responsibility
firebase.ts SDK init; exports auth, db, storage, functions
RouteService.ts Route CRUD, atomic ID generation, paginated queries, date-range queries
customerService.ts Customer CRUD, aggregation count, paginated + prefix search
userService.ts User CRUD, aggregation count, paginated + prefix search
productService.ts Product CRUD, aggregation count, paginated + prefix search
vehicleService.ts Vehicle + maintenance CRUD with optional date filters
audit.ts Login event logging; multi-provider IP detection with 3s timeout
adminService.ts Secondary Firebase app for user creation without session interruption
BackupService.ts Full Firestore → JSON backup/restore via Storage (450-doc batch chunks)
GoogleMapsService.ts Directions API integration; max 25 waypoints; returns order + polyline + distance
clientSeedingService.ts Seeding constants and city/industry config shared with Cloud Functions
imageUtils.ts Client-side image compression and Base64 conversion

4.3 Cloud Functions (/functions/src/)

All functions are HTTPS Callable (v2). Auth is verified server-side in every function.

Function Auth Required Description
reseed ADMINISTRATOR Generate full demo dataset (users, customers, products, routes, maintenance) for a given city/industry/scale
getStorageStats MANAGER or ADMINISTRATOR Enumerate Firebase Storage buckets and Firestore collection sizes
migrateSearchKeys ADMINISTRATOR Backfill lowercase searchKey field on users and customers for case-insensitive search

5. Security Model

5.1 Enforcement Layers

Request
  │
  ├─► Frontend role gates (Sidebar visibility, button guards)
  │     └─ Defense-in-depth only. Can be bypassed. Not authoritative.
  │
  └─► Firebase Security Rules (Firestore + Storage)
        └─ Authoritative. Cannot be bypassed from client.
              │
              └─► Cloud Functions: verify auth + role in Firestore
                    └─ Authoritative for server-side operations.

5.2 Firestore Rule Patterns

// Helper functions used throughout rules
function isAuthenticated()   request.auth != null
function getUserRole()       firestore.get(users/{uid}).data.role
function isApproved()        role in [MANAGER, EMPLOYEE, VIEWER, ADMINISTRATOR]
function isManager()         role in [MANAGER, ADMINISTRATOR]

// Privilege escalation prevention
// Users cannot update their own role field
allow update: if !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role'])

5.3 Notable Rules

  • Login logs: Allow unauthenticated creation (to capture failed login attempts)
  • App status docs: Schema-validated — max 20 fields, type constraints
  • config/settings: Only Managers can write warehouse/camera config
  • Employees: Can update routes but cannot change assignedTo or dueDate

5.4 Storage Rule Pattern

proofs/**   → create: any authenticated user (field ops upload their own proofs)
             read/update/delete: MANAGER or ADMINISTRATOR only
backups/**  → read/write: MANAGER or ADMINISTRATOR only
default     → deny all

6. Data Patterns

6.1 Pagination

All list views use cursor-based pagination (not offset). Pattern:

const { items, lastVisible } = await CustomerService.getPaginated(pageSize, cursor);
// Next page:
const { items: nextPage } = await CustomerService.getPaginated(pageSize, lastVisible);

Advantage: stable under concurrent writes, no skipped/duplicated items.

6.2 Case-Insensitive Search

Firestore lacks full-text search. GoNow uses a searchKey field (stored as name.toLowerCase()) with range queries:

query.where('searchKey', '>=', term)
     .where('searchKey', '<=', term + '\uf8ff')

Applied to: users, customers, products.

6.3 Route ID Generation

IDs are human-readable and daily-scoped. Generated via Firestore transaction:

Format: yyyyMMdd.sequence
Example: 20260415.3  (3rd route on April 15, 2026)

Transaction on config/counters:
  if lastDate == today → increment currentCount
  else → reset currentCount to 1, set lastDate = today

6.4 Aggregation Counts

Document counts use Firebase Aggregation API (getCountFromServer) — a single read regardless of collection size. Used in CustomerManagement, UserManagement, and ProductManagement for pagination metadata.

6.5 Multi-Instance Auth (User Creation)

Creating a user via Firebase Admin SDK from the client would sign out the current session. GoNow initializes a secondary Firebase app instance (adminService.ts) for user creation, keeping the manager's active session intact.


7. Build & Deploy Pipeline

7.1 Local Development

npm run dev          # Vite dev server (port 3000, HMR)

Requires .env file with Firebase credentials and Google Maps API key.

7.2 Deploy Script (scripts/deploy.ts)

Automated pipeline — run with npm run deploy:

  1. Increment version in package.json and i18n files
  2. Build Cloud Functions: TypeScript → functions/lib/
  3. Build frontend: Vite production build → dist/
  4. Create annotated git tag v{version}
  5. Deploy to Firebase: Firestore rules + Storage rules + Hosting + Cloud Functions

7.3 Bundle Optimization

Vite splits vendor bundles to optimize caching:

Chunk Contents
vendor-react React, ReactDOM
vendor-firebase Firebase SDK modules
vendor-ui Lucide, Framer Motion, Recharts
vendor-maps Leaflet, react-leaflet, Google Maps loader
vendor-pdf jsPDF, html2canvas, jszip, file-saver
vendor-utils date-fns, i18next

8. Testing

Type Tool Location
Unit / Component Vitest + Testing Library tests/
E2E Cypress cypress/e2e/
Coverage @vitest/coverage-v8 Run npx vitest run --coverage
npm test              # Unit tests (Vitest)
npm run test:e2e      # E2E tests (Cypress headless)
npm run test:e2e:headed  # Cypress with browser visible

Key Test Suites

File Coverage Area
Auth.test.tsx Login flows, phone auth, profile completion
RouteService.test.ts Route ID generation, daily counters
CustomerManagement.test.tsx Customer CRUD flows
CameraCapture.test.tsx Camera permissions, image capture
imageUtils.test.ts Image compression, Base64 conversion
AppIntegration.test.tsx App-level rendering

9. Environment Variables

Variable Required Description
VITE_FIREBASE_API_KEY Yes Firebase web API key
VITE_FIREBASE_AUTH_DOMAIN Yes Firebase auth domain
VITE_FIREBASE_PROJECT_ID Yes Firebase project ID
VITE_FIREBASE_STORAGE_BUCKET Yes Firebase storage bucket
VITE_FIREBASE_MESSAGING_SENDER_ID Yes Firebase messaging sender ID
VITE_FIREBASE_APP_ID Yes Firebase app ID
VITE_GOOGLE_MAPS_API_KEY No Enables Google Directions API for route optimization; falls back to local 2-Opt heuristic if absent
SEED_USER_PASSWORD Dev only Password for seeded demo users
TEST_USER_PASSWORD Dev only Password for Cypress test user

See .env.example for the full template.