Skip to main content

Technology

Application structure

Domain types for REST responses live in lib/domain-types.ts (canonical) with transactional order types in lib/types.ts.

Bootstrap

main.tsx wraps the app in three providers:
  • SessionProvider (better-auth/react) provides useSession() for auth state
  • QueryClientProvider provides useQuery() / useMutation() for all API data
  • Toaster (sonner) is the toast mount point

Routing

Routes are defined in App.tsx and every page component is lazy-loaded:

Route guards

Route-level error boundaries isolate admin and manager segments so one failing screen does not whiteout the whole portal.

Route groups

State management

MUZE does not use Redux, Zustand, or any global client-side store. All state falls into five kinds:
  1. Server state: TanStack React Query (useQuery / useMutation) for all list, detail, and mutation data
  2. Auth state: better-auth/react via useSession() and the useAuth() hook
  3. Form state: React Hook Form with Zod schemas
  4. URL state: React Router’s useSearchParams() and useParams()
  5. Local UI state: useState() inside components (modals, toggles, filters)
The active client for admins is app-wide context (ClientProvider + useActiveClient()), and every client-sensitive query embeds it in its query key so a client switch refetches cleanly. See React Query.

API communication

All API calls go through the fetch wrapper in src/lib/api.ts. The wrapper:
  • Prepends VITE_API_BASE_URL
  • Includes credentials: 'include' for session cookies
  • Parses error bodies into a typed ApiError with status, message, and optional details
  • Redirects to /login?expired=true on 401 responses, with a session-expired toast

Component patterns

  • UI primitives live in src/components/ui/: shadcn/ui components copied into the repo, not installed as a package
  • Feature components are co-located under components/admin/, components/manager/, and components/orders/ next to the pages that use them
  • Shared building blocks live in src/components/common/: ConfirmDialog, SearchInput, DataTableShell, TableSkeleton, TablePagination, EmptyState, ErrorState, SelectionBar
  • Class merging uses cn() from src/lib/utils.ts (clsx + tailwind-merge)
  • Status rendering goes through the central registry in src/lib/status.ts (StatusBadge, StatusIndicator)

Build and bundle

Vite produces the production build in frontend/dist/, deployed to Firebase Hosting. All pages are code-split through lazy loading, keeping the initial bundle small.

SEO and metadata

The portal is an authenticated client application, so the indexable surface is limited to the public pages: /login, /forgot-password, and /reset-password. MUZE still keeps those pages discoverable and consistent:
  • Per-route document titles. src/hooks/use-document-title.ts sets a unique <title> (and optional meta description and canonical link) per route. Public pages pass a description and canonical; authenticated pages set a title only.
  • Static metadata. index.html carries the base title, meta description, Open Graph and Twitter card tags, a canonical link, and Organization/WebSite JSON-LD structured data. The canonical domain is injected at build time from VITE_APP_URL (%VITE_APP_URL%).
  • Canonical origin. src/lib/seo.ts reads VITE_APP_URL and exposes APP_URL / absoluteUrl() so pages build absolute links from one source instead of hardcoding the domain.
  • robots.txt and sitemap.xml. Vite copies public/ verbatim without env substitution, so these are generated at build time by frontend/scripts/generate-seo-files.cjs into dist/, driven by VITE_APP_URL. robots.txt is permissive and references the sitemap; the sitemap lists only the public pages.
External URLs live in one place for tracking and updating: src/lib/constants.ts (currently USER_GUIDE_URL, pointing at the User Guide Mintlify site). The navbar exposes a Documents button next to the brand on larger screens, with the same item folded into the user menu on small screens, so the documentation is one click away on every viewport.