> ## Documentation Index
> Fetch the complete documentation index at: https://system.muzemus.online/llms.txt
> Use this file to discover all available pages before exploring further.

# Frontend architecture

> React SPA structure, routing, state management, and component patterns

## Technology

| Concern           | Library                    |
| ----------------- | -------------------------- |
| UI framework      | React 19                   |
| Build tool        | Vite 8                     |
| Routing           | React Router v8            |
| Server state      | TanStack React Query v5    |
| Form state        | React Hook Form + Zod      |
| Styling           | Tailwind CSS v4            |
| Component library | shadcn/ui (Radix UI + CVA) |
| Auth              | better-auth/react          |
| Animations        | Motion (Framer Motion)     |
| Charts            | Recharts                   |
| Toasts            | sonner                     |
| Icons             | lucide-react               |

## Application structure

```
frontend/src/
├── main.tsx              # Entry point: providers + router mount
├── App.tsx               # Route tree, lazy loading, route guards, error boundaries
├── index.css             # Design tokens, Tailwind, motion vocabulary
├── api                   # (via lib/) fetch wrapper
├── lib/                  # api.ts, auth-client.ts, domain-types.ts, status.ts, money.ts
├── hooks/                # use-auth, use-active-client, use-permissions, use-order-cart, ...
├── components/
│   ├── ui/               # shadcn/ui primitives (Button, Dialog, Table, ...)
│   ├── common/           # ConfirmDialog, SearchInput, DataTableShell, ErrorState, ...
│   ├── layout/           # App shell: navbar, sidebar
│   ├── admin/            # Admin feature components (dialogs, tabs)
│   ├── manager/          # Manager feature components
│   └── orders/           # Shared ordering components
└── pages/
    ├── login.tsx, dashboard.tsx, notifications.tsx, profile.tsx, ...
    ├── admin/            # Muze Admin pages (orders, employees, stores, catalog, ...)
    └── manager/          # Store Manager pages (store, employees, orders, approvals)
```

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:

```tsx theme={null}
<SessionProvider>
  <QueryClientProvider>
    <App />
    <Toaster />
  </QueryClientProvider>
</SessionProvider>
```

* `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:

```tsx theme={null}
const AdminStoresPage = lazy(() => import('@/pages/admin/stores'));
const ManagerOrdersPage = lazy(() => import('@/pages/manager/orders'));
```

### Route guards

| Guard             | Purpose                                                                             |
| ----------------- | ----------------------------------------------------------------------------------- |
| `PublicOnlyRoute` | Redirects authenticated users away from login and password-reset pages              |
| `ProtectedRoute`  | Requires any valid session; redirects unauthenticated users to `/login`             |
| `AdminRoute`      | Requires MUZE\_ADMIN or SUPER\_ADMIN (used for the Data Import Hub and admin pages) |
| `RoleBasedRoute`  | Generic guard accepting allowed roles                                               |

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

### Route groups

| Path prefix                                     | Access                 | Pages                                                                                                                                      |
| ----------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `/login`, `/forgot-password`, `/reset-password` | Public only            | Authentication pages                                                                                                                       |
| `/dashboard`                                    | Any authenticated user | Dashboard                                                                                                                                  |
| `/manager/*`                                    | Store Manager scope    | Store overview, employees, orders, order for employee, approvals                                                                           |
| `/admin/*`                                      | Admin roles            | Orders, employees, stores, clients, departments, catalog, entitlement rules, reports, account management, import hub, audit logs, settings |
| `/notifications`, `/profile`                    | Any authenticated user | Shared pages                                                                                                                               |

## 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](/engineering/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 links

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.
