SPA -> MPA

The goals of this project

1. Fast, responsive application


Load only the data needed for the page the user is viewing.

2. Modern navigation


This plan modernizes Vessel Vanguard into a fast, modular, enterprise-class application by replacing the SPA “dashboard with tabs” design with proper pages:

/vessels/:id/tasks

/vessels/:id/equipment

/vessels/:id/documents

etc.

3. Fleet-wide visibility


Add new “All” pages:

  • All Tasks
  • All Documents
  • All Checklists
  • All Logs

These allow fleet managers to see everything across vessels without clicking into each one.

4. Better user experience

Clean URLs, predictable navigation, and the ability to open modules in new tabs.

What this achieves

By moving away from the “load everything upfront” SPA model to lazy, page-specific data loading, the app will:

Expected Performance Improvements

Area
Before
After
Improvement

Current Technical Architecture (diagnosing the problem)

The current routing/UI behavior is an SPA with inline expansions

Vessel Vanguard renders nearly everything inside a single “dashboard” route:

  • src/App.tsx wires a single layout with nested routes under /vessel/:vesselid → VesselDashboard, with sub routes for maintenance, equipment,  documents, checklists, logs, etc. That’s why the header/URL stays put while the center swaps content.
  • Task rows open via an inline ‘expando’ (MaintenanceList → MaintenanceListRow → ExpandoRow), not a dedicated page.

Data loading pattern:

Upon sign-in:

  1. configureDataStore triggers (middleware in src/app/AmplifyMiddleware.tsx)
  2. It immediately calls configureObserveQueries
  3. This registers dozens of Amplify DataStore observeQuery subscriptions:
  • tasks (safety + maintenance)
  • procedure steps
  • equipment
  • components
  • logs
  • checklists
  • documents
  • media
  • contacts

…all at once.

The task subscription pulls all maintenance/safety tasks for every vessel the user can access (filtered only by vesselId and a ~2‑year date window in syncExpressionsForNormalUser), then dispatches setTasks. Procedure steps, equipment, runtimes, components, etc. are also loaded globally for those vessels.

MaintenanceView/MaintenanceList just reads from Redux selectors; expanding a task uses already-synced data. There’s no “load details on demand” per task or per tab—the bulk of the data comes down up front during that initial sync.

4. The dashboard waits for this entire sync to finish.

Implications and speed options

  • Even a simple user login forces a huge dataset sync. The UI feels slow because the SPA waits for these global queries.
  • Simply linking tasks/equipment/documents to standalone pages won’t cut network load unless we also change the data strategy. To speed things up, narrow the sync/observe scope: subscribe only to the current vessel/module, trim the task date/status window further, and lazily start module subscriptions when their route is opened (plus code-split those routes). Without that, you’re always paying the cost of syncing everything on landing.

New Architecture (Modern, Fast, Scalable)

So the initial project is to decouple the modules into real pages and reducing initial data load. Here’s a concrete routing/UX plan to move modules out of the nested dashboard and into per-vessel pages.

New UX

  • The “Vessels” landing shows all vessels vertically, each with the existing banner (hero image + quick stats + actions).
  • Each module opens a dedicated page per vessel (full page load in the outlet), e.g. “Tasks” opens /vessels/:vesselId/tasks showing MaintenanceView for that vessel; same pattern for equipment, documents, checklists, logs.
  • The banner on a module page shows the vessel info and module-specific actions; content below is only that module.

Routing changes

  1. Vessel list / fleet overview: Define top-level vessel module routes (no nesting under dashboard):
    • /dashboard → new VesselList page with stacked banners and “Tasks / Equipment / Documents / Checklists / Logs” entry points.

2. Dedicated module pages per vessel: Define top-level vessel module routes with each module becoming a standalone page with its own route (no nesting under dashboard):

    • /vessels/:vesselId/tasks → MaintenanceView (safety toggle as today, using query tab=… if needed).
    • /vessels/:vesselId/equipment → existing EquipmentRouter root component.
    • /vessels/:vesselId/documents → existing DocumentRouter root component.
    • /vessels/:vesselId/checklists → ChecklistRouter.
    • /vessels/:vesselId/logs → LogRouter.

3. Remove the nested module routes under /vessel/:vesselid. Keep /vessel/:vesselid only for a dashboard summary view, but modules no longer live under it.

5. Update navigation/links:

    • Top nav entries point to /vessels for the list, and module links point to /vessels/:id/.
    • Banner buttons on VesselList use those URLs.

Router wiring (App.tsx):

  • Keep Layout and RequireAuth.
  • Add a route group under /vessels:
    • index → VesselList
    • :vesselId/tasks → MaintenanceView
    • :vesselId/equipment/* → EquipmentRouter(“equipment”, false) adjusted to accept :vesselId at root.
    • :vesselId/documents/* → DocumentRouter(“documents”, false) adjusted similarly.
    • :vesselId/checklists/* → ChecklistRouter(“checklists”, false)
    • :vesselId/logs/* → LogRouter()
  • Remove {MaintenanceRouter(“maintenance”, false)} etc. from the /vessel/:vesselid children.

Component tweaks:

  • Ensure each module reads vesselId from useParams (already true for MaintenanceView); adjust routers that previously assumed a nested path to accept the top-level :vesselId.
  • Add a shared VesselBanner component to reuse the hero/summary across VesselList cards and module pages.

DataStore consideration to be addressed separately 

New fleet-wide “All” tables

  • Add a horizontal topbar strip below the logo and above the vessel content with buttons/links: All Tasks, All Documents, All Checklists, All Logs (and any others you want).
  • Clicking a button routes to a full-page table for that entity, not scoped to a single vessel.
  • Include a vessel column so users can see origin and click back to a vessel-specific page.
  • Add top-level routes (parallel to existing vessel routes):
Topbar buttons simply navigate to these paths.

Data sourcing approach: lazy & modular

  • Only load vessel list + minimal user profile on login.
  • When user opens a module, load data for that vessel + that module only.
  • Optionally prefetch data for commonly used modules in the background.
  • Code-split modules so the JS bundle loads only what is needed.

Tasks: DataStore already syncs all tasks for the user’s accessible vessels into state.maintenance.tasks. Current selectors filter by vessel. Add a new selector to return all tasks, with optional filters (status, search, date range).

Documents: Use state.documents.files/folders (and/or state.media.media if documents are stored there). Add a selector that returns all document rows, enriched with vessel/organization metadata.

Checklists: Use state.checklists.tasks or equivalent; add a selector that returns all checklist tasks without vessel filtering.

Logs: Use state.logs.logs (and entries) with a selector that returns all logs, unfiltered by vessel.

Expected Improvement

Implementation Plan

Phase 1 — Routing & UX foundations (1–2 weeks)

  • Implement new top-level routing
  • Build /vessels list page
  • Extract banner into shared VesselBanner component
  • Move each module to its own page

Outcome: Fleet managers can see everything across vessels.

Phase 2 — “All” fleet tables (1 week)

  • Build:

/all/tasks

/all/documents

/all/checklists

/all/logs

  • Add topbar with global menu
  • Add filters + linking back to vessels

Outcome: New navigation structure working.

Phase 3 — DataStore Optimization (2 weeks)

  • Reduce initial sync to:
    • vessels
    • user profile
    • basic permissions
  • Lazy load module data on demand
  • Optional: Code splitting

Outcome: Login performance improves dramatically.

Phase 4 — Decommission Dashboard SPA (1 week)

  • Remove outdated nested SPA routes
  • Optional: Remove inline expandos
  • Reduce bundle size

Outcome: Cleaner, smaller, more maintainable codebase.

Risks & Mitigation

Risk
Mitigation