This project is a React 18 + Vite app using TailwindCSS and Supabase (Auth/Postgres/Storage).
pnpm start– start Vite dev serverpnpm dev:5174– start Vite on port 5174 (strict) to avoid conflictspnpm build– build for productionpnpm serve– preview the production buildpnpm test– run unit tests (Vitest)pnpm e2e– run end-to-end tests (Playwright)pnpm exec playwright test --list– list discovered Playwright tests
For automated coding agents (Copilot, MCP agents), see the comprehensive one-page execution guide:
- AGENT_RUN_PROMPT_ONEPAGE.md — Complete agent execution prompt with guardrails, stack lock, and phased execution plan
Canonical / start-here docs:
AGENTS.md.github/copilot-instructions.md.github/instructions/Aftermarket – Workspace Guardrails (DO NOT DEVIATE).instructions.mdMASTER_EXECUTION_PROMPT.md- docs/QUICK_START_DEVELOPMENT.md
- docs/DEPLOY_CHECKLIST.md
DEPLOYMENT_GUIDE.md
Historical one-off reports and fix summaries live under docs/archive/.
Canonical local setup is WSL/Linux/macOS with Node 22 and pnpm via corepack.
- Ensure Node 22 is active (
node -vshould bev22.x)- If using nvm:
nvm use(reads.nvmrc)
- If using nvm:
- Enable corepack + pinned pnpm:
corepack enablecorepack prepare pnpm@10.15.0 --activate
- Install dependencies:
pnpm install - Install recommended VS Code extensions from
.vscode/extensions.json(optional)
Alternatively, run the setup script on Windows:
pwsh scripts/setup-workspace.ps1Backup Copilot Chat State (Windows):
pwsh scripts/backup-copilot-chat.ps1This backs up your Copilot chat history to .vscode_state/ so you can restore it on another machine.
One-Click Workspace Setup (Windows):
pwsh scripts/setup-workspace.ps1This installs all dependencies and recommended extensions automatically.
By default the app runs at http://localhost:5173 in development.
If you already have another app on 5173, use pnpm dev:5174 to run this repo at http://localhost:5174.
- Use
http://for local dev URLs (nothttps://). - If you use the
antfu.viteextension, it defaults to port4000and may auto-bump to4001if4000is already in use.
Create a .env.local with your Supabase config and any feature flags.
- Template:
.env.example→ copy to.env.local - Setup guide: docs/LOCAL_DEV_SUPABASE.md
If VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY are missing, the dev server intentionally falls back to an in-memory stub and data-driven screens may look empty.
Optional feature flags:
VITE_DEAL_FORM_V2(default:truein development) – Whentrue, deal creation and editing flows use unified form adapters for safer data handling. Set tofalseto revert to legacy behavior without modifying services or database. Recommended to keeptruefor local and preview environments. See Feature Flag Guide for detailed usage instructions.VITE_SIMPLE_CALENDAR(default:false) – When unified shell is OFF, this controls whether/calendarredirects to/calendar/agenda(true) or/calendar/grid(false).VITE_ACTIVE_SNAPSHOT(default:false) – Replaces the "Currently Active Appointments" workflow center with a vendor‑centric snapshot list (schedule + in‑progress). Safe rollback: set tofalseor remove from env; legacy workflow center code path is preserved.VITE_FF_CALENDAR_UNIFIED_SHELL(default:false) – EnablesCalendarShellon/calendarand redirects/calendar/grid,/calendar/agenda, and/calendar-flow-management-centerinto unified shell views.- Deprecated:
VITE_ORG_SCOPED_DROPDOWNS– previously scoped dropdowns via a database helper. This flag is now ignored and dropdowns are unscoped by default. Prefer tenant-aware lists viatenantServiceor Admin filters where applicable.
Snapshot + Agenda notes:
- Snapshot view adds an accessible conflict indicator (⚠) when two active appointments for the same vendor overlap locally (pure client check). For authoritative vendor conflict detection (server-side), use scheduling flows that call
calendarService.checkSchedulingConflictduring edits. The snapshot avoids direct Supabase queries and relies on already fetched job data.
To speed up the first render of forms (like Deals), dropdowns are cached in-memory for a short period and prefetched on app start.
- Where:
src/services/dropdownService.js - What: A 5‑minute TTL cache covers staff lists, vendors, and products. Keys include request filters.
- Prefetch:
prefetchDropdowns()is invoked insrc/App.jsxon mount (fire‑and‑forget, non‑blocking).
Adjust TTL
- Update
CACHE_TTL_MSnear the top ofdropdownService.js.
Clear cache (for tests/troubleshooting)
- Call
clearDropdownCache()fromdropdownService.jsto reset the in‑memory cache.
The cache is per tab/session and never persisted. Server‑side changes will naturally be picked up after the TTL expires or when the cache is cleared.
Admin → Staff Records and Admin → User Accounts both support:
- “Only my org” toggle to filter lists to your organization.
- A bulk “Assign Org …” action to set
org_idfor active records that are currently missing it.
These tools help keep tenant data clean without requiring additional logins for staff records.
- Deals list supports Card View and Sheet View (spreadsheet-style intake layout) so teams can scan sales, categories, and profit at a glance.
- Loaners are considered returned when the customer picks up their vehicle. Set the expected return date in the Deal form and manage loaners in the Loaner drawer.
- Customer claims intake is available at
/guest-claims-submission-form(linked from the sign-in page) to keep walk-ins from bypassing the claims process.
/calendarVITE_FF_CALENDAR_UNIFIED_SHELL=true→ rendersCalendarShell- otherwise redirects to
/calendar/agendawhenVITE_SIMPLE_CALENDAR=true, else/calendar/grid
/calendar/grid- unified shell ON → redirects to unified
view=calendar - unified shell OFF → renders
src/pages/calendar/index.jsx
- unified shell ON → redirects to unified
/calendar/agenda- unified shell ON → redirects to unified
view=list - unified shell OFF +
VITE_SIMPLE_CALENDAR=true→ renderssrc/pages/calendar-agenda/index.jsx - unified shell OFF +
VITE_SIMPLE_CALENDARunset/false → redirects to/calendar-flow-management-center
- unified shell ON → redirects to unified
/calendar-flow-management-center- unified shell ON → redirects to unified
view=board - unified shell OFF → renders
src/pages/calendar-flow-management-center/index.jsx
- unified shell ON → redirects to unified
Defaults when flags are unset:
VITE_FF_CALENDAR_UNIFIED_SHELLdefaults OFF unless explicitlytrueVITE_SIMPLE_CALENDARdefaults false unless explicitlytrue
This application includes comprehensive error handling for PostgREST/Supabase 400 and 403 HTTP errors:
- Quick Reference: See QUICK_REFERENCE_ERROR_HANDLING.md for common scenarios and quick fixes
- Full Guide: See ERROR_HANDLING_GUIDE.md for complete architecture documentation
Key features:
- Automatic detection of missing database columns and relationships
- Graceful degradation with capability flags
- Telemetry tracking for monitoring
- Health endpoints for proactive monitoring
- Migration guidance for permanent fixes
Some Supabase tables are treated as optional for local/dev environments (for example, when you are connected to a Supabase project that has not had the repo migrations applied yet). To avoid repeated PGRST205 / missing-table spam, the app will disable the related feature for the remainder of the browser tab session after the first missing-table error.
Lifecycle / source of truth:
- Storage:
sessionStorage(per-tab, cleared when the tab/browser session ends) - Keys:
cap_smsTemplatesTablecap_notificationOutboxTable
- Set when: a PostgREST missing-table error is detected for that table (first failure disables the feature)
- Behavior when disabled: queries short-circuit to empty results and related UI is hidden/soft-disabled
Reset (manual):
- In DevTools → Application → Session Storage, delete the keys above (or run
sessionStorage.clear()in the console), then refresh.
- Unit tests:
pnpm test - E2E tests:
pnpm e2e(requiresE2E_EMAIL/E2E_PASSWORDfor the E2E user) - E2E smoke:
pnpm e2e:smoke(4-page flow + mobile fetch-resilience guard + month-layout guard) - List E2E tests:
pnpm exec playwright test --list - Error handling tests:
pnpm test src/tests/schemaErrorClassifier.test.js src/tests/capabilityTelemetry.test.js
To view the last Playwright report:
pnpm exec playwright show-reportMigrations
- Apply migrations to your local Supabase database:
pnpm run db:pushSeed org data (optional)
- Populate baseline org/vendor/product/staff data into your Supabase project:
pnpm run db:seed-orgE2E seed (Node-based)
- For deterministic E2E data (org, vendor, products, a scheduled job with a loaner), use the Node seed runner. It requires a Postgres connection string.
Environment variables accepted by the seed runner:
E2E_DATABASE_URL(preferred for local E2E), orDATABASE_URL, orSUPABASE_DB_URL— a Postgres connection string with credentials that can create/insert into your Supabase database.- Tip (WSL/network-restricted): prefer the Supabase "Session pooler" connection string and include
sslmode=require.
Run locally:
pnpm run db:seed-e2eRun in CI (examples):
- GitHub Actions: set
DATABASE_URLas an encrypted repository secret and callpnpm run db:seed-e2ein a step before E2E. - Vercel/other: not required for deployments; this seed is only for test data.
E2E reporting/cleanup (local)
- Report what E2E-ish rows exist:
pnpm -s db:report-e2e
- Preview what would be deleted (no changes):
pnpm -s db:cleanup-e2e -- --dry-run
Both scripts accept E2E_DATABASE_URL (preferred), DATABASE_URL, or SUPABASE_DB_URL.
If you have pre-exported env vars in your shell, force dotenv to win:
env -u E2E_DATABASE_URL -u DATABASE_URL -u SUPABASE_DB_URL \
DOTENV_CONFIG_PATH=.env.e2e.local DOTENV_CONFIG_OVERRIDE=true \
node -r dotenv/config scripts/reportE2E.jsE2E test auth
- Playwright’s global setup supports two modes:
- Storage state present at
e2e/storageState.json: tests reuse it. - Or environment-based login. Set:
E2E_EMAILE2E_PASSWORD
- Storage state present at
Recommended local setup for E2E (to avoid accidentally mixing dev/prod-ish env with tests):
- Put E2E-only settings in
.env.e2e.local(preferred). Playwright loads it first and overrides.env.local. - If you change E2E credentials, delete
e2e/storageState.jsonbefore rerunningpnpm e2e.
Which Supabase project is “E2E” vs “production”?
- Supabase identifies a project by its project ref — the subdomain in
https://<ref>.supabase.co. - Current E2E project ref (from
.env.e2e.local):ntpoblmjxfivomcwmjrj. - For Playwright E2E runs, the
VITE_SUPABASE_URLandVITE_SUPABASE_ANON_KEYin.env.e2e.localshould point at your E2E Supabase project (non-production). - Production values are configured in Vercel → Project Settings → Environment Variables (not in
.env.e2e.local). - Safety check:
pnpm release:checkrefuses to run ifVITE_SUPABASE_URL(or any DB connection string) contains the production refogjtmtndgiqqdtwatsue.
Optional Playwright settings:
PLAYWRIGHT_BASE_URL— defaults tohttp://127.0.0.1:5174.
Notes:
- Dropdowns are cached briefly in-memory; tests use explicit testids to remain stable.
- Multi-tenant writes: services will include
org_id(from the form or inferred from the signed-in user profile) to satisfy org-scoped RLS policies.
We use Vercel's native Git integration for auto-deploys (recommended):
- Production: any push to
mainauto-deploys - Preview: every PR targeting
maingets a Preview URL
Ensure the Vercel project is linked to this GitHub repo and main is set as the Production Branch. Set environment variables in Vercel → Project Settings → Environment Variables:
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEY
Routing and CSP headers are defined in vercel.json.
Notes:
- A manual fallback GitHub Action exists at
.github/workflows/deploy-vercel.yml(manual only) for emergencies. Normally, you won't need it, as Vercel Git integration handles auto-deploys.
Workflows are split by responsibility:
.github/workflows/ci.yml— build + typecheck + unit tests + guard checks.github/workflows/ci-pnpm.yml— pnpm-focused lint/test guard lane.github/workflows/e2e.yml— Playwright E2E smoke/full with artifacts
Build/test env names used in CI:
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEY
Local E2E required env vars (canonical names):
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEYE2E_EMAILE2E_PASSWORD
CI workflow alias mapping (names only; mapped by workflow/job env):
E2E_VITE_SUPABASE_URL/VITE_SUPABASE_URLE2E_VITE_SUPABASE_ANON_KEY/VITE_SUPABASE_ANON_KEYE2E_EMAIL/E2E_TEST_EMAILE2E_PASSWORD/E2E_TEST_PASSWORDE2E_DATABASE_URL/DATABASE_URL/SUPABASE_DB_URL
Migration workflows use:
SUPABASE_ACCESS_TOKENSUPABASE_DB_PASSWORDSUPABASE_PROJECT_REF
Security reminder: never commit secrets. Use local env vars for local E2E and GitHub Secrets for CI.
- Profile name fallback capabilities and display name resolution: see
docs/PROFILE_NAME_FALLBACK.md. - E2E degraded profile capability coverage:
e2e/profile-name-fallback.spec.tsseeds capability flags to validate UI resilience without relying on live column presence.
Notes:
- Playwright uses
webServerto start Vite directly (viapnpm exec vite) on port 5174 (and does not reuse an existing server). - If you change the dev server port, update
PLAYWRIGHT_BASE_URLin the workflow orplaywright.config.ts.
- Terminal shows
rob@...(WSL), notPS C:\... - Node 20:
node -v→ v20.x - pnpm present:
pnpm -v(>=10) - Build & test:
pnpm i && pnpm run build && pnpm run test→ all green
- Disable PowerShell extension for this workspace: Extensions (Ctrl+Shift+X) → search "PowerShell" → gear icon → Disable (Workspace).
- Terminal profiles are locked to WSL/bash in this repo: see
.vscode/settings.jsonwith"terminal.integrated.defaultProfile.windows": "Ubuntu (WSL)""terminal.integrated.defaultProfile.linux": "bash"