Skip to content

Web Client

Web Client is build on vite and react.

  • Frontend: React Router v7 SPA (Vite) - Port 4202
  • Backend: Fastify + Drizzle ORM + PostgreSQL - Port 4200
  • API-First Design: Same API serves web, future desktop, and mobile clients
  • /login - User authentication
  • /register - User registration
  • /demo/* - Demo routes mirroring real app with sample data
    • /demo/events/:id/*
    • /demo/contacts/:id/*
    • /demo/lessons/:id/*

All routes require authentication. Auth check in layout loader redirects to /login if not authenticated.

Each sub-app has:

  • Sidebar with list/search
  • Detail view with nested routes for different forms/sections

Events App (/events/:id/)

  • /events/:id/detail - Event details form
  • /events/:id/songs - Songs management
  • /events/:id/setlist - Setlist builder
  • Additional sub-routes as needed

Contacts App (/contacts/:id/)

  • /contacts/:id/detail - Contact information
  • Additional sub-routes as needed

Lessons App (/lessons/:id/)

  • /lessons/:id/detail - Lesson details
  • Additional sub-routes as needed

Users App (/users/)

  • /users/profile - User profile management (email, password, settings)
  1. Site Header - Logo, login/user menu (always visible)
  2. Workspace Selector - Top-level groupings (Organize, Events, Projects)
  3. Sub-App Header - Specific apps within each grouping (Contacts, Lessons, Events)

Stored in users table:

{
"last_group": "events",
"last_routes": {
"events": "/contacts/123/detail",
"projects": "/tasks/456",
"organize": "/lessons/789"
}
}
  • Clicking workspace group navigates to last-visited sub-app in that group
  • Cross-device sync ensures consistent experience
  • Browser history handles navigation within current session
  • URLs are shareable and bookmarkable
  • Display and edit use same page/component
  • Individual forms per section (not one big form)
  • Each form submits via PATCH to update specific fields
  • Leverages React Router <Form> and clientAction
  • Parent route (/events/:id/) loads main entity data once
  • Stays mounted while child routes swap via <Outlet />
  • Sidebar and shared state persist across sub-route navigation
  • Child routes inherit parent loader data
/events/:id → EventApp (sidebar + data loader)
/events/:id/detail → DetailForm (swaps in <Outlet />)
/events/:id/songs → SongsForm (swaps in <Outlet />)
  • _protected/route.tsx loader checks authentication
  • All nested routes automatically protected
  • Single point of auth logic
  • Mirror real route structure under /demo/*
  • Use hardcoded sample data (no API calls)
  • Same components, isDemo prop to toggle data source
  • Allows discovery before registration
  • User logs in and accesses only their own data
  • No multi-tenancy or user management features
  • Simplified auth and data access patterns