From 94ccbf6fe42847178747fd0becc8150037be1ac5 Mon Sep 17 00:00:00 2001 From: Replit Agent Date: Sat, 6 Jun 2026 16:23:08 +0300 Subject: [PATCH] docs: add project brief and fix request route types --- artifacts/api-server/src/routes/requests.ts | 2 +- hammam-dev.md | 65 ++++++++++++++++++--- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/artifacts/api-server/src/routes/requests.ts b/artifacts/api-server/src/routes/requests.ts index 48fcc28..69ebc70 100644 --- a/artifacts/api-server/src/routes/requests.ts +++ b/artifacts/api-server/src/routes/requests.ts @@ -116,7 +116,7 @@ router.get("/requests/:id", (req: Request, res: Response): void => { // ─── Helper: find & update ──────────────────────────────────────────────────── function findAndUpdate( - id: string, + id: string | string[], updater: (r: DonationRequest) => void, res: Response ): void { diff --git a/hammam-dev.md b/hammam-dev.md index b25fafa..53b887b 100644 --- a/hammam-dev.md +++ b/hammam-dev.md @@ -3,6 +3,9 @@ > Onboarding brief for any developer or AI coding agent (e.g. OpenClaw) taking over > this project. Read it fully before editing. The repo is hosted on a self-hosted > **Gitea** server and runs on a **Mac Mini** via Docker. +> +> **تنويه:** مشروع **تجريبي** مستوحى من فكرة منصة «إحسان»، وليس منصة إحسان الرسمية +> ولا تابعاً لها. ## 1. What this product is A bilingual (Arabic / English, full RTL + LTR) **proof-of-concept charity donation web @@ -40,15 +43,20 @@ This is a POC/demo: data is **in-memory mock data** (no real database, no real p (multi-case donation cart), `AuthContext` (mock admin login). - `artifacts/ehsan-poc/src/components/` — `Riyal.tsx` (renders the NEW Saudi Riyal symbol via an image mask), `layout/` (Header, AppLayout), `ui/` (shadcn components). +- `artifacts/ehsan-poc/src/App.tsx` — UI root: routing (wouter) + all context providers. - `artifacts/api-server/src/routes/` — `health`, `requests`, `donors`, `stats`, `whatsappLog`. Mounted in `routes/index.ts`. +- `artifacts/api-server/src/routes/requests.ts` — **the closed-loop logic and every + status transition** live here. +- `artifacts/api-server/src/lib/mockDb.ts` — types, in-memory seed data, the + `STATUS_STEP` map, and `checkEligibility`. - Root deploy files: `Dockerfile.web`, `Dockerfile.api`, `docker/nginx.conf`, `docker-compose.yml`, `deploy.sh`, `scripts/push-to-gitea.sh`, `DEPLOYMENT.md`. ## 4. How to run locally (development) ```bash pnpm install -pnpm --filter @workspace/api-server run dev # API +pnpm --filter @workspace/api-server run dev # API (port 5000 in dev) pnpm --filter @workspace/ehsan-poc run dev # Web (needs PORT and BASE_PATH env) pnpm run typecheck # full typecheck pnpm run build # typecheck + build everything @@ -57,6 +65,13 @@ Note: `vite.config.ts` REQUIRES `PORT` and `BASE_PATH` env vars even for `build` (it throws otherwise), e.g. `PORT=8080 BASE_PATH=/ pnpm --filter @workspace/ehsan-poc run build`. +> **Apple Silicon caveat:** a full `pnpm run build` typechecks all packages but the +> `vite build` step fails locally on darwin-arm64 with +> `Cannot find module @rollup/rollup-darwin-arm64`. That is **by design** — the +> workspace `overrides` in `pnpm-workspace.yaml` strip every non-`linux-x64-gnu` +> native binary so the Docker (`linux/amd64`) build stays clean. The real build runs +> inside Docker; locally the `typecheck` result is the meaningful gate. + ## 5. How it runs in production (Mac Mini, Docker) Two services in `docker-compose.yml`: - `api` — Express, internal only, listens on `PORT=8080`, healthcheck `/api/healthz`. @@ -72,24 +87,58 @@ App is then served on the Mac Mini at `http://localhost:8080` (override with `WE ## 6. Deployment flow (how code travels) ``` -Edit code → commit → push to Gitea (remote name: gitea, branch: main) +Edit code → commit → push to Gitea (branch: main) → on Mac Mini run ./deploy.sh → Docker rebuilds & restarts ``` -- Central repo is **Gitea** (no GitHub). Remote name everywhere is `gitea`, branch `main`. +- Central repo is **Gitea** (no GitHub), branch `main`. The documented remote name is + `gitea`; on this Mac Mini checkout the remote may be named `origin` but still points + at the Gitea host — verify with `git remote -v`. - `scripts/push-to-gitea.sh` pushes from a dev machine; `deploy.sh` redeploys on the Mac Mini. - An AI agent working directly on the Mac Mini clone should use this loop: - edit → test → `git commit` → `git push gitea main` → `./deploy.sh`. + edit → test → `pnpm run build` → `git commit` → `git push main` → `./deploy.sh`. -## 7. Hard rules / gotchas — do NOT break these +## 7. Status lifecycle (create → close) +The case `status` (`RequestStatus`) and its step number (`STATUS_STEP` in `mockDb.ts`): + +``` +new (1) + → pending_review (2) + → verified (3) + → published (4) + → donated (5) ← reached ONLY when fully funded + → delivered (6) + → receipt_confirmed (7) + → thank_you_submitted (8) + → whatsapp_sent (9) + → closed (10) + +rejected (side path, step 2) +``` + +- **Eligibility on creation:** `POST /requests` checks `nationalId` via + `checkEligibility` → eligible ⇒ `verified`, not eligible ⇒ `rejected`, unknown ⇒ + `pending_review`. +- **API transition endpoints:** `verify`, `publish`, `donate`, `deliver`, + `confirm-receipt`, `thank-you`, `send-whatsapp`, `close`, `reject` + (`POST /requests/:id/`). +- **`donate` is the heart of the closed loop:** it only accepts donations while the + case is `published`, clamps each donation to the remaining amount + (`applied = min(amount, requestedAmount − collectedAmount)`), and advances the case + to `donated` only once `collectedAmount >= requestedAmount`. + +## 8. Hard rules / gotchas — do NOT break these - **amd64 / glibc ONLY.** The pnpm workspace strips every native binary that is not `linux-x64-gnu`. Docker build stages MUST use `node:24-bookworm-slim` (glibc, not alpine) and `platform: linux/amd64` (runs under Rosetta on Apple Silicon). Do not switch the build base to alpine or arm64 — rollup / tailwind-oxide / lightningcss will fail to find native binaries. -- **Funding rule.** Donations accumulate and clamp to the target; a case enters the - fulfillment pipeline only when fully funded. Preserve this. +- **Funding rule (Closed Donation Loop).** Donations accumulate and clamp to the + target; a case can never be over-funded and enters the fulfillment pipeline + (`donated` and beyond) only when fully funded. Preserve this. - **Bilingual + RTL.** Every user-facing string must exist in both Arabic and English via `LanguageContext`. Don't hardcode single-language text. Keep RTL layout working. +- **Same-origin API.** The browser calls `/api/...` on the same domain (nginx proxies + it to the `api` service). Never add a separate frontend API URL. - **Currency.** Saudi Riyal uses the new official symbol rendered by the `` component (image mask), not the old "ر.س"/"SAR" text. Reuse ``. - **Routing base.** The app is mounted under a base path via `import.meta.env.BASE_URL`. @@ -101,7 +150,7 @@ Edit code → commit → push to Gitea (remote name: gitea, branch: main) while the code is correct, it's stale Fast Refresh state — restart the web dev server. - Always run `pnpm run build` (typecheck + build) before pushing to catch type errors. -## 8. Suggested first task for a new agent +## 9. Suggested first task for a new agent Read `DEPLOYMENT.md`, `artifacts/ehsan-poc/src/App.tsx`, and `artifacts/api-server/src/routes/index.ts` to confirm the routes and data model, then summarize your understanding of the funding / closed-loop flow before making any change.