108 lines
6.1 KiB
Markdown
108 lines
6.1 KiB
Markdown
|
|
# Hammam Dev — EHSAN "Closed Donation Loop" (POC)
|
||
|
|
|
||
|
|
> 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
|
||
|
|
app** inspired by the Saudi "EHSAN" (إحسان) platform. Its core idea is a **Closed
|
||
|
|
Donation Loop**:
|
||
|
|
|
||
|
|
- Beneficiaries submit support requests (housing, food, electricity, water, health,
|
||
|
|
court-ordered debt, appliances like A/C & refrigerator, etc.).
|
||
|
|
- Each request/opportunity has a **funding target**. Donations from multiple donors
|
||
|
|
**accumulate and are clamped to the target** — a case can never be over-funded.
|
||
|
|
- A case only **enters the "closed loop" fulfillment pipeline once it is fully funded**.
|
||
|
|
This is the central business rule. Do not break it.
|
||
|
|
- After funding, the flow continues to confirmation / tracking, and a simulated
|
||
|
|
WhatsApp notification log records donor/beneficiary messaging.
|
||
|
|
|
||
|
|
This is a POC/demo: data is **in-memory mock data** (no real database, no real payments).
|
||
|
|
|
||
|
|
## 2. Tech stack
|
||
|
|
- Monorepo: **pnpm workspaces**, Node.js 24, TypeScript 5.9.
|
||
|
|
- Web app (`artifacts/ehsan-poc`): React + Vite + **wouter** (routing) +
|
||
|
|
**TanStack Query** (data) + **Tailwind CSS** + shadcn/ui components.
|
||
|
|
- API (`artifacts/api-server`): **Express 5**, all routes mounted under `/api`,
|
||
|
|
health at `/api/healthz`. Data lives in `src/lib/mockDb.ts` (in-memory; resets on restart).
|
||
|
|
- Shared libs under `lib/`: `api-spec` (OpenAPI source of truth), `api-client-react`
|
||
|
|
(generated React Query hooks), `api-zod` (Zod schemas), `db` (Drizzle schema, not
|
||
|
|
active at runtime since data is in-memory).
|
||
|
|
- All workspace libs export TypeScript source directly (`./src/index.ts`) — no lib
|
||
|
|
pre-build step; Vite/esbuild consume the source.
|
||
|
|
|
||
|
|
## 3. Repo map
|
||
|
|
- `artifacts/ehsan-poc/src/pages/` — screens: home, about, waqf, baraem, request,
|
||
|
|
opportunities, donate/:id, cart, login, admin, track/:id, thank-you/:id,
|
||
|
|
whatsapp-log, not-found.
|
||
|
|
- `artifacts/ehsan-poc/src/contexts/` — `LanguageContext` (ar/en + RTL), `CartContext`
|
||
|
|
(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/api-server/src/routes/` — `health`, `requests`, `donors`, `stats`,
|
||
|
|
`whatsappLog`. Mounted in `routes/index.ts`.
|
||
|
|
- 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/ehsan-poc run dev # Web (needs PORT and BASE_PATH env)
|
||
|
|
pnpm run typecheck # full typecheck
|
||
|
|
pnpm run build # typecheck + build everything
|
||
|
|
```
|
||
|
|
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`.
|
||
|
|
|
||
|
|
## 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`.
|
||
|
|
- `web` — nginx that serves the built Vite SPA **and reverse-proxies `/api/` to
|
||
|
|
`api:8080`**. The browser always calls **same-origin `/api/...`**, so there is NO
|
||
|
|
frontend API URL to configure.
|
||
|
|
|
||
|
|
Deploy / redeploy on the Mac Mini:
|
||
|
|
```bash
|
||
|
|
./deploy.sh # git pull gitea main → docker compose down → build → up -d
|
||
|
|
```
|
||
|
|
App is then served on the Mac Mini at `http://localhost:8080` (override with `WEB_PORT`).
|
||
|
|
|
||
|
|
## 6. Deployment flow (how code travels)
|
||
|
|
```
|
||
|
|
Edit code → commit → push to Gitea (remote name: 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`.
|
||
|
|
- `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`.
|
||
|
|
|
||
|
|
## 7. 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.
|
||
|
|
- **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.
|
||
|
|
- **Currency.** Saudi Riyal uses the new official symbol rendered by the `<Riyal/>`
|
||
|
|
component (image mask), not the old "ر.س"/"SAR" text. Reuse `<Riyal/>`.
|
||
|
|
- **Routing base.** The app is mounted under a base path via `import.meta.env.BASE_URL`.
|
||
|
|
Use it for routes/links; never hardcode root-relative `/api` in a way that escapes
|
||
|
|
the base — call same-origin `/api/...` through the proxy.
|
||
|
|
- **Data is ephemeral.** mockDb is in-memory; restarting the `api` container resets it.
|
||
|
|
If you add persistence, add a database service to `docker-compose.yml` accordingly.
|
||
|
|
- **HMR quirk (dev):** if you see "useLanguage must be used within a LanguageProvider"
|
||
|
|
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
|
||
|
|
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.
|