Frontend Local Setup
The UniPulse web app is a React 19 SPA built with Vite 7. It communicates with the Express API and uses Zustand for client state and TanStack Query for server state.
Prerequisites
| Requirement | Version | Purpose |
|---|---|---|
| Node.js | >= 20 | Runtime |
| npm | >= 9 | Package manager |
| Backend API | Running on port 3000 | See Backend Setup |
Quick Start
# From the Pulse root directory
npm install
# Build the shared package first (required for type imports)
npm run build --filter=shared
# Start the web app
npm run dev --filter=web
The frontend starts on http://localhost:5173 and proxies API requests to http://localhost:3000.
Environment Configuration
cp apps/web/.env.example apps/web/.env
| Variable | Default | Description |
|---|---|---|
VITE_API_URL | http://localhost:3000 | Backend API base URL |
Vite Environment Variables
Only variables prefixed with VITE_ are exposed to the client bundle. Never put secrets in VITE_* variables.
Tech Stack
| Technology | Version | Purpose |
|---|---|---|
| React | 19 | UI framework |
| Vite | 7 | Build tool with HMR |
| TypeScript | 5.9 | Type safety |
| Tailwind CSS | v4 | Utility-first styling |
| shadcn/ui + Radix UI | Latest | Component library (accessible primitives) |
| Zustand | 5 | Client state management |
| TanStack Query | 5 | Server state (data fetching, caching, sync) |
| React Router | 7 | Client-side routing |
| Recharts | Latest | Data visualization / charts |
| @xyflow/react | Latest | Workflow visual builder (React Flow) |
| react-i18next | Latest | Internationalization (EN + AR) |
| Lucide React | Latest | Icon library |
| React Hook Form | Latest | Form state management |
| zod | Latest | Validation (via @unipulse/shared) |
| Alexandria | - | Font family (Latin + Arabic support) |
Development Commands
# Start dev server with HMR
npm run dev --filter=web
# Type check
npm run typecheck --filter=web
# Build for production
npm run build --filter=web
# Preview production build
npm run preview --filter=web
# Add a new shadcn/ui component
cd apps/web && npx shadcn@latest add button
Project Structure
apps/web/src/
├── pages/ # 58+ page components (26 directories)
│ ├── dashboard/
│ ├── composer/
│ ├── analytics/
│ ├── ...
│ └── admin/
├── components/ # Shared UI components
│ ├── ui/ # shadcn/ui components
│ ├── layout/ # AppLayout, AuthLayout, SettingsLayout
│ ├── data-table/ # DataTable with sort/filter/pagination
│ └── charts/ # Recharts wrappers
├── stores/ # Zustand stores
│ ├── auth.store.ts
│ ├── workspace.store.ts
│ ├── ui.store.ts
│ └── composer.store.ts
├── hooks/ # Custom React hooks
├── lib/ # API client, utilities, cn() helper
├── i18n/ # Internationalization
│ ├── en/ # English JSON translation files
│ └── ar/ # Arabic JSON translation files
├── App.tsx # Root component with providers
└── main.tsx # Entry point
API Client
The API client is configured in apps/web/src/lib/api.ts. It automatically:
- Attaches the JWT access token to every request
- Refreshes expired tokens via
/api/v1/auth/refresh - Handles workspace context headers
- Types responses using
@unipulse/sharedtypes
Common Issues
| Issue | Solution |
|---|---|
@unipulse/shared types not found | Build shared: npm run build --filter=shared |
| API requests failing with CORS | Ensure backend is running on port 3000 |
| Blank page after login | Clear localStorage and retry (token format may have changed) |
| Tailwind classes not applying | Restart dev server (Vite HMR sometimes misses Tailwind config changes) |
| Arabic text not rendering correctly | Ensure Alexandria font is loaded and dir="rtl" is set |
Cross-Reference
- Backend Local Setup -- getting the API running
- Page Map -- all 58+ frontend pages
- Components -- UI component library
- State Management -- Zustand and TanStack Query
- Routing -- React Router configuration
- i18n -- internationalization setup