The frontend is a Single Page Application (SPA) built with React 19 and Vite. It serves as the user interface for claiming roles and managing wallets.
- Framework: Vite + React (TypeScript)
- Routing: @tanstack/react-router
- State/Data Fetching: @tanstack/react-query
- Web3: @mysten/dapp-kit for Sui Wallet connection.
- Styling: Vanilla CSS with strict Design System variables (see
design-system.md) + Lucide React for icons. - Validation: Zod.
src/
├── components/ # Reusable UI components (Buttons, Cards, Modals)
├── hooks/ # Custom React hooks
├── routes/ # Route definitions (part of TanStack router)
├── lib/ # Utilities and helper functions
├── main.tsx # Entry point
├── App.tsx # Main App component & Provider setup
└── index.css # Global styles and CSS variables
Uses dapp-kit's ConnectButton and useWallet hook to manage wallet state.
- Provider: Wrapped in
SuiClientProviderandWalletProvider. - Auto-connect: Configured to attempt auto-connection on load.
Authentication state is managed via React Query.
- Login: Redirects to backend Discord endpoint.
- Session: Checks
/api/meto validate session and get user details.
We use file-based routing features or code-based routing from TanStack Router.
routes/directory contains route definitions.__root.tsxusually defines the layout shell.
The application uses a strict set of CSS variables defined in index.css.
- Design Philosophy: High-contrast, technical, "sci-fi industrial" aesthetic.
- Rules:
- No border-radius (0px).
- Monospace fonts for headers/data.
- Specific "stone" and "nebula" color palettes.
See Design System for variable references.
# Start dev server
bun run dev
# Build for production
bun run build
# Preview production build
bun run preview
# Lint code
bun run lintCreate a .env file in src/sui if needed (Vite requires VITE_ prefix for client-exposed variables).
| Variable | Description |
|---|---|
VITE_API_URL |
URL of the backend API (if not proxying) |
Symptom: After Discord OAuth callback, you see a 400 error for /api/auth/exchange and get redirected back to login.
Cause: React 19 Strict Mode double-invokes effects in development. The auth code is consumed on first call, second call fails.
Fix: The callback route (src/routes/auth/callback.tsx) uses a useRef to prevent duplicate exchange attempts. If you see this, ensure the ref pattern is implemented:
const hasExchangedRef = useRef(false);
// ... in useEffect:
if (hasExchangedRef.current) return;
hasExchangedRef.current = true;Note: Auth codes have a 2-minute TTL from creation. If you see "Code expired" errors, the code took longer than 2 minutes to exchange (unlikely in normal flow).