geocrop-platform./apps/nextgen/AGENTS.md

8.9 KiB

AGENTS.md — Africa Alert PWA

Compact orientation for AI coding agents. Update this file when commands, structure, or load-bearing conventions change.

What this repo is

Offline-first PWA school-management system. React 18 + Vite + TypeScript client, Node 20 + Express + SQLite server, bidirectional sync to Supabase, Paynow (Zimbabwe) for fee payments. Deployed as a single Docker container.

Quickstart (local dev, two terminals)

# Terminal 1 — API on :3001
cd server
npm install
npm run db:init        # one-off; creates server/data/school.db
npm run dev

# Terminal 2 — client on :3000 (proxies /api → :3001)
cd client
npm install
npm run dev

Login with admin@school.com / admin123. Other demo accounts (seeded by server/src/database/seeds/demo.js): teacher@school.com / teacher123, student@school.com / student123, parent@school.com / parent123, principal@school.com / principal123, bursar@school.com / bursar123, hr@school.com / hr123, librarian@school.com / librarian123.

For production: docker-compose up --build (single port 3001). The Dockerfile and docker-compose.yml are now a single deploy story — node:20-slim runtime with the React client served from Express, healthcheck against /api/health.

Repository layout (don't deviate)

client/src/
  App.tsx          router, ProtectedRoute, role-based getRoutes()
  main.tsx
  components/      shared UI (Nav, PaynowPayment)
  pages/<role>/    role-specific pages
  pages/dashboard/ role landing pages
  store/           Zustand slices — use client/src/store/api.ts as the axios instance
server/src/
  index.js         Express bootstrap, registers controllers under /api/<x>
  controllers/     one file per resource, inline auth middleware
  services/        SyncEngine.js, AuditService.js
  database/migrations/knex/  Knex migration files (source of truth; `init.js` just runs them)
data/              SQLite file (gitignored, WAL mode → .db-shm + .db-wal alongside)
uploads/           multer uploads (gitignored)
.harness/          team memory (AGENTS.md, docs/conventions.md, reins/, changelogs/)

There is no root package.json. Run npm install and scripts inside client/ and server/.

Hard rules

  • All code changes go in a worktree at .worktrees/<name>/, branch feature/<kebab> or fix/<kebab>, branched from dev. Never edit the main checkout.
  • Branch model is Git Flow. Work and merge on dev. Promote to main only when dev is green and deployable. No direct commits to main.
  • Single axios instance lives at client/src/store/api.ts. Don't create a second one in a store or component.
  • All tables must have uid TEXT UNIQUE, last_synced_at, sync_status CHECK in ('synced','pending','conflict') DEFAULT 'pending', is_deleted INTEGER DEFAULT 0. Add to init.js and append the table name to tablesToSync in SyncEngine.js (in FK dependency order — parents first).
  • Route gating is done in App.tsx via <ProtectedRoute allowedRoles={…}> — do not gate inside page components.
  • Demo accounts are seeded in server/src/database/init.js. Don't add new ones in a migration; update init.js.
  • .env is gitignored. Never commit secrets, never paste .env contents into chat. The repo's actual remote is Gitea (git.techarvest.co.zw), not GitHub.

Port allocation

Both stacks (tenant + SuperAdmin) can run simultaneously without collision. See PORTS.md for the canonical plan.

Port Service Stack Env var
3000 Tenant client (Vite) tenant VITE_PORT
3001 Tenant API (Express) tenant PORT
3002 SuperAdmin API (Express) superadmin SUPERADMIN_PORT
3003 SuperAdmin client (Vite) superadmin VITE_PORT (superadmin)

Vite dev servers run with strictPort: true so a port already in use fails fast instead of silently picking a different port.

Commands cheat sheet

What Command Where
Install deps npm install client/ and server/ separately
Dev server (client) npm run dev client/ — Vite, port 3000, proxies /api → :3001
Dev server (server) npm run dev server/ — nodemon, port 3001
Init / migrate DB npm run db:init server/ — runs src/database/init.js
Production build npm run build client/ — outputs client/dist/, generates the PWA service worker
Preview build npm run preview client/
Full stack (prod-like) docker-compose up --build repo root
Stop stack docker-compose down repo root
Lint / typecheck not configured n/a — see Open follow-ups
E2E tests (Playwright) npm run test:e2e client/ — Playwright config + e2e/*.spec.ts; needs both client (3000) and server (3001) running; first run also: npx playwright install chromium

npm run build (not npm run dev) is required to exercise the PWA service worker — vite-plugin-pwa only ships the SW in production builds.

Canonical reference for new features

The exams module is the vertical slice to mimic: server/src/controllers/exams.controller.js, client/src/store/exams.ts, client/src/pages/exams/, route + nav entry in App.tsx. See IMPLEMENTATION_SUMMARY.md for the design notes.

Things that will trip you up

  • Hardcoded JWT secret 'africa-alert-secret-key-2024' is the fallback in server/src/index.js and duplicated across controllers. Set JWT_SECRET env var in any non-dev environment.
  • CORS is wide open (app.use(cors()) in server/src/index.js). Don't add more routes without a CORS review.
  • outputs/ and logo/ at the repo root are unused build artifacts — don't import from them. Real brand assets live in client/public/.
  • Sync engine uses raw http/https, not fetch or axios. 10s timeout, 100 rows/table push, 50/table delete, server-wins conflict, 30s default interval. SUPABASE_KEY empty → sync no-ops, app keeps working.
  • Paynow integration in server/src/controllers/payments.controller.js. Webhook is idempotent. Payment + student_fees.status update wrapped in db.transaction().
  • Auth is JWT only (7-day expiry). No session cookies, no refresh tokens. Logout is client-side only (clears Zustand store).
  • Roles in active UI: school_admin, systems_admin, principal, hr, bursar, teacher, student, parent, librarian, clubs_head. Schema also defines accountant and nurse but those have no UI.
  • index.js exposes module.exports = app AND starts the server with app.listen. Importing it in tests will bind a port — use a separate bootstrap or guard the listen call.

Where to read more

  • .harness/AGENTS.md — project-level memory (demo accounts, branch model, "exams" as canonical reference).

Team (2026-07-22)

  • fchin (fchinembiri24) — orchestrator / reviewer / merge owner. Owns Phase 1 (auxiliary roles + cohorts + class/teacher assignments).
  • Arthur (PhaseOfficial / panashearthurmhonde@gmail.com) — 163 commits, heaviest contributor. Owns Phase 2 (analytics dashboard).
  • Craig (CraigDataNerd / craig.chadiwa@students.uz.ac.zw) — 7 commits, lighter (newer or narrower scope). Owns Phase 3 (offboarding).

Phase 2 + Phase 3 rollout: decide after Phase 1 lands (deferred 2026-07-22).

Commit volume is signal, not strength. Roles/skills for Craig and Arthur are not on record yet; record them here once known.

  • .harness/docs/conventions.md — SQL contract, controller/store templates, auth+RBAC, sync contract, Paynow flow, env vars.
  • .harness/docs/testing.md — test stack and manual recipes (when set up).
  • .harness/reins/ — agent role definitions (backend-expert, frontend-expert, sync-expert, tester, etc.).
  • IMPLEMENTATION_SUMMARY.md — modules and their tables; rationale for sync engine.
  • DATABASE_SCHEMA.md — full schema reference.
  • SCREENS.md — UI map and role/permission matrix (canonical for screen-level access).
  • GEMINI.md — high-level overview (older, points here for details).

Open follow-ups (do not silently take these on — ask first)

  • No test framework is installed or configured anywhere. The tester rein owns adding Vitest/Jest/Playwright.
  • No CI — no GitHub Actions, no Gitea Actions workflow. Add when the user asks.
  • Schema lives in Knex migrations under server/src/database/migrations/knex/ (since Track D, P1-9). npm run db:init calls runMigrations()knex.migrate.latest(). New tables: drop a new file in that folder with the next timestamp. No ALTER-in-init.js — the migration is the source of truth.
  • No lint or typecheck script in either package.json. tsconfig.json has "strict": false — the client typechecks leniently.
  • JWT secret, CORS, request-size limit (express.json({ limit: '50mb' })) are all loose. Don't relax further; tighten when touching the area.