# Panel ## Stack - React 19 - React Router 7 - Vite 6 - Tailwind CSS v4 via `@tailwindcss/vite` - Local utilities: `clsx`, `tailwind-merge`, `class-variance-authority` - Icons: `lucide-react` The panel lives in `panel/src` and is built to `panel/dist`. ## Dev and runtime - `bun run panel:dev` starts Vite on `http://localhost:5173` - Vite proxies `/api`, `/auth`, `/assets`, and `/ws` to `http://localhost:3000` - The Bun server serves the built panel from `panel/dist` in integrated mode ## Auth model - `useAuth()` calls `GET /auth/me` - unauthenticated users are sent through `/auth/discord` - logout uses `POST /auth/logout` - non-enrolled users see the `NotEnrolled` page Roles: - `admin`: admin routes plus player/game routes - `player`: player/game routes only ## Active routes - `/dashboard` - `/leaderboards` - `/games` - `/:gameSlug/:roomId` - `/admin` - `/admin/users` - `/admin/items` - `/admin/classes` - `/admin/quests` - `/admin/lootdrops` - `/admin/moderation` - `/admin/transactions` - `/admin/settings` ## Data layer Shared hooks in `panel/src/lib`: - `useAuth` - `useDashboard` - `useUsers` - `useItems` - `useSettings` - `useWebSocket` - `useGameRoom` `panel/src/lib/api.ts` is a thin fetch wrapper: - base path is empty because the panel is usually same-origin with the Bun server - 401 triggers a redirect back into `/auth/discord` - 204 and empty responses return `undefined` ## WebSocket and games - `useWebSocket()` keeps a singleton browser WebSocket connection - reconnects with exponential backoff up to 30 seconds - `useGameRoom()` multiplexes room traffic over that shared socket - current built-in game UIs are chess and blackjack ## Styling - Theme tokens live in `panel/src/index.css` - Fonts currently loaded: - Noto Serif - Manrope - Space Grotesk - JetBrains Mono - The implemented visual system is the "Stellar Editorial" direction documented in `docs/new-design/DESIGN.md` ## Current patterns - Admin pages tend to use explicit `refetch()` after mutations instead of a shared cache layer - Search inputs use a 300 ms debounce in hooks such as `useUsers()` and `useItems()` - Layout and sidebar ownership lives in `panel/src/components/Layout.tsx`