135 lines
8.9 KiB
Markdown
135 lines
8.9 KiB
Markdown
# 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)
|
|
|
|
```bash
|
|
# 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.
|