Local development
git clone https://github.com/Every-Man-A-Warrior/emaw-crm-app.git
cd emaw-crm-app
git checkout develop
npm install
cp .env.example .env.local # optional overrides
npm run dev # http://localhost:5173
Sign in with a dev backend account that is in the Django group CRM Staff (or has capabilities granted). Create the account on the dev website first, then ask a superuser to add the group in Django admin at https://dev.emaw-app.com/admin/.
How requests reach the backend
.env.development sets VITE_API_BASE_URL=https://dev.emaw-app.com and VITE_API_PROXY=true. With the proxy on, the app calls its own origin at /rest/...:
- In
npm run dev, Vite's dev server proxies/resttoVITE_API_BASE_URL(vite.config.ts). - In deployment, the Worker does the same.
So the backend sees a same-origin client, the refresh cookie is first-party, and there is no CORS to configure. Locally there is no Cloudflare Access in front of you; the dev backend only enforces the Access JWT when CF_ACCESS_* are set on it, and the Worker is what forwards that header in deployed environments. If a staff endpoint returns 403 locally, the account lacks the group or capability.
Scripts
| Command | Does |
|---|---|
npm run dev | Vite on 5173 (ngrok hosts allowed) |
npm run build | tsc -b && vite build → dist/ |
npm run preview | Serve the production build |
npm run lint | ESLint (no-console is an error except warn/error, because bodies can contain personal data) |
npm test / npm run test:watch | Vitest |
npm run deploy:develop / deploy:production | Build with the mode's env file and wrangler deploy (Deployment) |
Development rules
These come from README.md and src/api/README.md; reviewers enforce them.
- Dev sends go to the "Email Testing" group only (group 36). The dev database holds about 4,000 real, mailable people. The app shows a banner and disables the send button unless the audience is limited to
VITE_DEV_SEND_GROUP_IDS; the backend has the matchingCAMPAIGN_SEND_ALLOWED_GROUP_IDSguard. - Access tokens live in memory only. Refresh uses the backend's HttpOnly cookie through the proxy. Nothing auth-related goes in
localStorage; only the display email is insessionStorage. - No email address in a URL except the
qsearch parameter. Never log request or response bodies. ShowX-Request-IDon error toasts. - CSV exports run only on an explicit click and are never cached.
- Env vars are read in exactly one place,
src/config.ts. - Each feature owns its API functions (
features/<area>/api.ts), types and screens; routes are lazy modules exportingComponent. - Paths must end with a trailing slash;
apiFetchenforces it.
Working against a local backend
Set VITE_API_BASE_URL=http://localhost:8000 in .env.local. Keep VITE_API_PROXY=true so cookies stay first-party. Your local backend must have the account in CRM Staff and CF_ACCESS_* unset.
Map tiles
VITE_CARTO_BASEMAP_KEY enables the CARTO Light/Dark basemaps; without it they render with a watermark. OSM and Esri World Imagery need no key (features/map/layers.ts).