Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8be71c8
Implement backend: API, algorithms, data layer, docs, CI
SarmaHighOnCode Jun 17, 2026
ff23c36
Add missing env keys to render.yaml
SarmaHighOnCode Jun 17, 2026
7a7b84e
Harden DB path: service-role key, SQL seed, /ready, deploy docs
SarmaHighOnCode Jun 18, 2026
c63155e
Align backend behavior with the pitch deck
SarmaHighOnCode Jun 18, 2026
252bd9f
Validate GPS coordinate ranges on request
SarmaHighOnCode Jun 18, 2026
b0b30dc
Add README diagrams (SVG) and enrich API metadata
SarmaHighOnCode Jun 18, 2026
9a40e14
feat(frontend): Implement premium dark-mode UI/UX for all 4 screens b…
RishabhRana37 Jun 18, 2026
fddd9cb
chore(design): Scaffold React+Vite+TS, define visual design tokens, a…
RishabhRana37 Jun 19, 2026
0e86ff6
feat(frontend): Implement Screen 1 (Emergency Intake View) with Geolo…
RishabhRana37 Jun 19, 2026
e6cd515
feat(frontend): Implement Screen 2 (Live Results View) with dynamic s…
RishabhRana37 Jun 19, 2026
d536dbe
feat(frontend): Implement Screen 3 (Donor Registration View) with cov…
RishabhRana37 Jun 19, 2026
caa717b
feat(frontend): Implement Screen 4 (Hospital Confirmation View) with …
RishabhRana37 Jun 19, 2026
75d98a5
feat: add location fallbacks and demo bypass options
RishabhRana37 Jun 19, 2026
1ff7621
feat: implement live status polling and mock simulation on results sc…
RishabhRana37 Jun 19, 2026
f38f6a2
Merge backend-dev and frontend-dev for integration testing
SarmaHighOnCode Jun 19, 2026
638415e
style: final visual polish, accessibility audit, mobile responsivenes…
RishabhRana37 Jun 19, 2026
0fdd730
feat: wire real backend API client, add .env.example, and configure S…
RishabhRana37 Jun 19, 2026
a952854
Merge latest frontend-dev into integration (API wiring, Realtime, pol…
SarmaHighOnCode Jun 19, 2026
b4b2f3e
Move @supabase/supabase-js to runtime dependencies
SarmaHighOnCode Jun 19, 2026
232e102
Default results view to real API mode instead of mock
SarmaHighOnCode Jun 19, 2026
44aead7
Lazy-fetch hospitals from OSM when none found near patient GPS
SarmaHighOnCode Jun 19, 2026
d9635d9
Use faster OSM mirror, 20s timeout, startup pre-warm via OSM_SEED_COORDS
SarmaHighOnCode Jun 19, 2026
6b74861
feat: wire real backend API, make hospital confirm page dynamic, and …
RishabhRana37 Jun 19, 2026
f588a4a
style: redesign frontend layout and branding to replicate Medulance U…
RishabhRana37 Jun 19, 2026
0f68051
style: rebrand application back to standalone GoldenHour visual identity
RishabhRana37 Jun 19, 2026
c8d14d5
Add Google Places as primary hospital source, Overpass as free fallback
SarmaHighOnCode Jun 19, 2026
bd4bed1
feat: Implement premium cinematic editorial frontend using Lenis, GSA…
RishabhRana37 Jun 19, 2026
f16252c
Load .env automatically via python-dotenv in config
SarmaHighOnCode Jun 19, 2026
1f33fb5
feat: Add volumetric postprocessing shader, custom magnetic cursor, a…
RishabhRana37 Jun 19, 2026
9f37b72
Format backend codebase, update create_confirmation signatures, and u…
SarmaHighOnCode Jun 20, 2026
73ce4b1
Merge branch 'origin/frontend-dev' into integration
SarmaHighOnCode Jun 20, 2026
5276760
Switch to Places API (New), add venue-local donor synthesis
SarmaHighOnCode Jun 20, 2026
5bc14c7
Use npm install in frontend CI to tolerate cross-platform lock drift
SarmaHighOnCode Jun 20, 2026
96ace62
Add GET /confirm/{token} for hospital confirmation page details
SarmaHighOnCode Jun 20, 2026
95a5280
Wire real donor count and make the app an installable PWA
SarmaHighOnCode Jun 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/backend-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ jobs:
run: |
cd backend
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run Tests
run: |
cd backend
# pytest (if tests are added)
pytest -q
5 changes: 4 additions & 1 deletion .github/workflows/frontend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: cd frontend && npm ci
# npm install (not npm ci) tolerates cross-platform optional-dependency
# lock drift — the lock is generated across Windows/macOS/Linux dev boxes
# and native bindings (Tailwind oxide, rolldown) differ per platform.
run: cd frontend && npm install
- name: Build
run: cd frontend && npm run build
# - name: Test
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/pr-guard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check for merge conflicts
- name: Fail if leftover conflict markers are present
run: |
echo "Checking merge conflicts..."
# Add conflict check logic here
echo "Scanning for unresolved merge conflict markers..."
if git grep -nE '^(<<<<<<<|=======|>>>>>>>)' -- . ':(exclude).github/**'; then
echo "::error::Unresolved merge conflict markers found. Resolve them before merging."
exit 1
fi
echo "No conflict markers found."
53 changes: 53 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
serveo.log
surge.log
surge_redeploy.log
tunnel.log

# Dependency directories
node_modules/
jspm_packages/

# Build outputs
dist/
dist-ssr/
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea/
.DS_Store
*.suo
*.ntvsca*
*.njsproj
*.sln
*.sw?

# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env.production

# Python Virtual Environments & Caches
venv/
.venv/
env/
ENV/
__pycache__/
*.pyc
*.pyo
*.pyd
.pytest_cache/
.mypy_cache/
.ipynb_checkpoints/
5 changes: 5 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"tomoki1207.pdf"
]
}
41 changes: 37 additions & 4 deletions API_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This is the single most important document for the team. Both the backend (You)
- The frontend **never** talks to the database directly. It only ever calls these endpoints.
- Blood groups are always strings: `"O+"`, `"O-"`, `"A+"`, `"A-"`, `"B+"`, `"B-"`, `"AB+"`, `"AB-"`.
- `status` for a hospital is always one of: `"pending"`, `"confirmed"`, `"declined"`.
- Coordinates: `lat` and `lng` are decimal numbers (e.g. `26.9124`, `75.7873` for Jaipur).
- Coordinates: `lat` and `lng` are decimal numbers (e.g. `26.9124`, `75.7873` for Jaipur). `lat` must be −90..90 and `lng` −180..180 — out-of-range coordinates (e.g. a failed browser geolocation) are rejected with **422**.

## Endpoint 1 — Trigger an emergency
`POST /emergency`
Expand Down Expand Up @@ -41,11 +41,14 @@ This is the single most important document for the team. Both the backend (You)
"phone": "+91xxxxxxxxxx"
}
],
"donors_alerted": 5
"donors_alerted": 5,
"rare_group": false
}
```
- `hospitals` is an array, already sorted best-first by the backend.
- `donors_alerted` is just a count (integer) for the UI to display.
- `rare_group` (boolean) is `true` when the needed group is Rh-negative (scarce
supply); the UI can warn that compatible donors may be few. Safe to ignore.

## Endpoint 2 — Poll / subscribe to emergency status
`GET /emergency/{request_id}/status`
Expand All @@ -59,9 +62,35 @@ This is the single most important document for the team. Both the backend (You)
{ "hospital_id": "h2", "name": "Fortis Jaipur", "eta_minutes": 9, "status": "pending" }
],
"donors_alerted": 5,
"donors_responded": 2
"donors_responded": 2,
"unconfirmed_fallback": false
}
```
- `unconfirmed_fallback` (boolean) becomes `true` once ~3 minutes pass with no
hospital confirmed. Cue for the UI to surface the nearest hospitals with a
1-tap call button (the phones from the `POST /emergency` response). Safe to ignore.

## Endpoint 3a — Hospital confirmation page details
`GET /confirm/{token}`

Hit when the hospital contact **opens** their link, so the page can show the real
emergency details (instead of placeholders).

**Backend returns:**
```json
{
"hospital_name": "SMS Hospital",
"emergency_type": "trauma",
"blood_group": "O+",
"eta_minutes": 6,
"already_confirmed": false,
"responded": false,
"accepted": false
}
```
- `already_confirmed` is `true` if another hospital already took this patient.
- `responded` is `true` if this hospital has already replied; `accepted` is its choice.
- Unknown token → **404**.

## Endpoint 3 — Hospital taps Accept / Decline
`POST /confirm/{token}`
Expand Down Expand Up @@ -91,10 +120,14 @@ This is hit by the hospital confirmation page when the contact taps a button.
"blood_group": "B+",
"lat": 26.9200,
"lng": 75.8000,
"last_donated": "2025-01-15"
"last_donated": "2025-01-15",
"sex": "female"
}
```
`last_donated` is a date string "YYYY-MM-DD", or null if never donated.
`sex` is **optional** — `"male"` or `"female"`. It only sets the donation
cooldown (120 days for women, 90 otherwise). Omit it and the backend defaults to
90 days; existing forms need no change.

**Backend returns:**
```json
Expand Down
109 changes: 109 additions & 0 deletions DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Deploying GoldenHour (backend + database)

Two ways to run in production. Pick one.

- **Demo mode** — deploy the backend with no Supabase variables. It serves the
seeded in-memory store (25 hospitals, 180 donors). Every endpoint works;
nothing persists. Lowest-risk path for a hackathon demo.
- **Database mode** — back it with Supabase (PostgreSQL + PostGIS) for real
persistence and Supabase Realtime. Do the database steps below first.

Check which mode is live any time with `GET /health` (`"mode": "supabase"` vs
`"in-memory"`) and confirm the DB is actually reachable with `GET /ready`.

---

## 1. Database — Supabase (skip for demo mode)

1. Create a project at [supabase.com](https://supabase.com) (remember the DB password).
2. **SQL Editor** → run [`backend/sql/schema.sql`](backend/sql/schema.sql) — creates
the 4 tables, the PostGIS spatial indexes, and the `donors_nearby` RPC.
3. **SQL Editor** → run [`backend/sql/seed.sql`](backend/sql/seed.sql) — loads the
25 hospitals + 180 donors. (Runs as table owner, so it bypasses RLS — more
reliable than the anon-key Python seeder.)
4. Enable **Realtime** on the `confirmation_requests` table
(Database → Replication, or the table's Realtime toggle). This is what makes
the patient's screen flip live when a hospital accepts.
5. Collect credentials from **Project Settings**:
- **API** → Project URL → `SUPABASE_URL`
- **API** → `service_role` secret → `SUPABASE_SERVICE_KEY` (backend uses this)
- **API** → `anon` public key → `SUPABASE_ANON_KEY` (frontend uses this)
- **Database** → Connection string (URI) → `DATABASE_URL`

> Alternative to step 3: seed from your machine with
> `cd backend && python seed_supabase.py` (needs `SUPABASE_URL` +
> `SUPABASE_SERVICE_KEY` in `.env`). Prefer the SQL editor if anon RLS blocks inserts.

---

## 2. Backend — Render

The repo ships a blueprint at [`render.yaml`](render.yaml).

1. [render.com](https://render.com) → **New → Blueprint** → connect the
`GoldenHour` repo. Render reads `render.yaml` automatically.
2. Set the `sync: false` environment variables:

| Variable | Demo mode | Database mode |
| --- | --- | --- |
| `SUPABASE_URL` | *(blank)* | from Supabase |
| `SUPABASE_SERVICE_KEY` | *(blank)* | service_role secret |
| `SUPABASE_ANON_KEY` | *(blank)* | anon key (optional for backend) |
| `DATABASE_URL` | *(blank)* | Supabase connection URI |
| `GOOGLE_MAPS_API_KEY` | *(blank → estimated ETAs)* | optional, real ETAs |
| `MSG91_AUTH_KEY` | *(blank)* | optional, SMS |
| `FRONTEND_URL` | your frontend URL | your frontend URL |
| `CORS_ORIGINS` | `*` or frontend URL | frontend URL |

3. **Create** and wait for the build. The API is live at
`https://goldenhour-api.onrender.com` (or your chosen name).

The same image runs on Fly.io, or locally: `docker compose up --build`.

---

## 3. Verify

```bash
# Liveness + which mode is active
curl https://goldenhour-api.onrender.com/health
# {"status":"ok","version":"0.1.0","mode":"supabase"}

# Readiness — actually pings the database (503 if unreachable)
curl https://goldenhour-api.onrender.com/ready
# {"ready":true,"backend":"supabase"}

# Smoke-test the main flow
curl -X POST https://goldenhour-api.onrender.com/emergency \
-H "Content-Type: application/json" \
-d '{"lat":26.9124,"lng":75.7873,"emergency_type":"trauma","blood_group":"O+"}'
```

Interactive API docs are at `…/docs`.

---

## 4. Git flow (you are the Git owner)

Backend work lands on `backend-dev`; you merge to `main`:

```bash
git checkout main
git pull origin main
git merge backend-dev
git push origin main
```

Your teammate works only on `frontend-dev` and pushes there; you merge it into
`main` the same way. Their app reaches this backend via
`VITE_API_BASE_URL=https://goldenhour-api.onrender.com`.

---

## Security note

The backend connects with the **service-role** key (`SUPABASE_SERVICE_KEY`),
which bypasses RLS — keep it server-side only, never in the frontend. To harden
the database, enable RLS with read-only `anon` policies (see the commented block
at the end of [`backend/sql/schema.sql`](backend/sql/schema.sql)); backend writes
still succeed via the service-role key.
50 changes: 50 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# GoldenHour Visual Identity & Design System

GoldenHour's visual language balances **calm urgency**—trustworthy like a medical device, fast like a 911 dispatch console. The interface is optimized for mobile-first, one-handed, glove-friendly usage during critical moments.

---

## 🎨 Color Palette

| Token | CSS Variable | Hex Code | Purpose |
| :--- | :--- | :--- | :--- |
| **bg** | `--color-bg` | `#FBFAF8` | Warm paper background for readability |
| **surface** | `--color-surface` | `#FFFFFF` | Card backgrounds, separated by soft shadows (no harsh borders) |
| **ink** | `--color-ink` | `#1A1714` | High-contrast readable typography |
| **muted** | `--color-ink-muted` | `#6B6560` | Secondary metadata and labels |
| **emergency** | `--color-emergency` | `#DC2626` | Primary CTAs, urgent warnings |
| **pressed** | `--color-emergency-pressed` | `#B91C1C` | Active/Pressed states for emergency actions |
| **success** | `--color-success` | `#059669` | Confirmed responses, lock status |
| **pending** | `--color-pending` | `#9CA3AF` | Warm gray for loading, pending, inactive states |
| **golden hour** | `--color-accent` | `#F59E0B` | Brand mark and subtle highlighting |

---

## 📐 Shape & Spacing

* **Border Radius:**
* Cards: `rounded-2xl` (`1rem`)
* Buttons: `rounded-xl` (`0.75rem`)
* **Touch Targets:**
* Primary Action Buttons: Minimum height of `56px` (`h-14`) for easy tap states.
* **Shadows:**
* Soft layered shadows to create depth without borders:
```css
box-shadow: 0 1px 3px rgba(26, 23, 20, 0.02), 0 8px 24px rgba(26, 23, 20, 0.04);
```

---

## ✍️ Typography

* **Font Family:** Inter throughout (tightly tracked headings).
* **Headings:** Bold, tight-tracked (`tracking-tight`), text sizes `text-3xl` or `text-4xl`.
* **Body:** Generous line heights for readability.

---

## 🎬 Motion

* **Buttons:** Click/tap feedback scale down (`scale-95`).
* **Transitions:** Spring transitions for status/state changes (`framer-motion`).
* **Card Entrances:** Staggered fade and slide-up transition.
Loading
Loading