1. 📌 Overview & Purpose
Goal: Pesakit dpt clarity ke klinik mana terdekat dgn capacity sedia ada. Klinik dpt pre-qualified leads (already triaged + intent confirmed). Hospital ED dpt pre-arrival packet untuk MERAH triage emergency cases.
Network effect: Lebih banyak klinik join rangkaian = lebih bermakna untuk pesakit. M3 = the magnet that grows network value beyond single-clinic SaaS.
Per Doc Zam mock: "Guided to Nearest Hospital / 1st Responder · System Assist · Doctor Assist" workflow direct integration.
2. 👤 User Stories
Sebagai pesakit Kuning triage dari M1, saya nak top 3 klinik berhampiran dgn queue indicator + jarak + ETA — pilih, book, gerak.
Saya MERAH triage (sakit dada) — saya nak hospital ED nearest dgn cath-lab + pre-arrival packet auto-hantar ke ED supaya cardio standby.
Sebagai klinik admin, saya nak update klinik info (services · doctor · hours · queue cap) dlm panel mudah.
Pesakit perlu specialist · saya nak generate digital referral letter terus dari M4 encounter, hantar via M3 ke klinik specialist receiving.
Saya nak dashboard: berapa lead M3 hantar bulan ni · conversion rate (lead → encounter) · NPS pesakit referred.
3. ✅ Functional Requirements
4. ⚙️ Non-Functional Requirements
| Aspect | Target | Notes |
|---|---|---|
| Geo search latency | <500ms p99 | PostGIS HNSW + GIN index |
| Live queue refresh | 60s cache TTL | Real enough; reduce M7 load |
| ETA freshness | 5min cache | Maps API call cost concern |
| Klinik directory size | ≥1000 klinik searchable | Phase 3 ambition |
| Maps API cost | ≤RM 0.05/search | Aggressive caching · ETA only on book |
| Pre-arrival packet delivery | <30s | MERAH critical path |
| Referral letter generation | <5s | Doctor in-encounter usage |
5. 🗄️ Data Model
| Table | Key fields | Purpose |
|---|---|---|
clinics | id, tenant_id, name, address, lat, lng (PostGIS POINT), phone, email, hours_json, services_json, doctors_json, panel_json, halal_cert, walk_in, telemedicine, established_year | Master directory |
clinic_doctors | clinic_id, user_id, specialty, mmc_no, available_hours_json | Doctor roster |
clinic_queue_snapshot | clinic_id, snapshot_at, queue_count, avg_wait_min | FR-3.3 cached snapshot |
referrals | id, encounter_id, from_doctor_id, to_clinic_id?, to_specialty, urgency, summary, pdf_url, status (sent · acknowledged · accepted · declined) | FR-3.7 referral records |
prearrival_packets | id, session_id (M1 RED), patient_id, hospital_id, ed_unit, summary_json, eta_min, sent_at, acknowledged_at | FR-3.8 ED notify |
clinic_leads | clinic_id, source (M1 patient_pa · M3 self-search), lead_type, generated_at, converted_to_encounter_id? | FR-3.10 analytics |
5a. PostGIS Index
CREATE EXTENSION postgis; ALTER TABLE clinics ADD COLUMN location geography(POINT, 4326); UPDATE clinics SET location = ST_MakePoint(lng, lat)::geography; CREATE INDEX clinics_location_gix ON clinics USING GIST (location); CREATE INDEX clinics_services_gin ON clinics USING GIN (services_json); CREATE INDEX clinics_panel_gin ON clinics USING GIN (panel_json); -- Geo query example SELECT id, name, ST_Distance(location, ST_MakePoint(101.6869, 3.1390)::geography) AS distance_m FROM clinics WHERE ST_DWithin(location, ST_MakePoint(101.6869, 3.1390)::geography, 10000) AND services_json @> '["gp"]' ORDER BY distance_m LIMIT 10;
6. 🔌 API + Maps Integration
GET /api/v1/clinics/nearby
Query: lat, lng, radius_km=10, service_type=gp, panel=mysalam
Returns: [{ clinic_id, name, distance_km, queue_count, eta_min, booking_url }, ...]
GET /api/v1/clinics/{id} # detail (services · doctors · hours)
POST /api/v1/clinics # create (clinic admin)
PATCH /api/v1/clinics/{id} # update
DELETE /api/v1/clinics/{id} # soft delete
POST /api/v1/referrals # doctor compose referral
Body: { encounter_id, to_clinic_id?, to_specialty, urgency, summary }
Returns: { referral_id, pdf_url, sent_at }
GET /api/v1/referrals/incoming # for receiving clinic
POST /api/v1/referrals/{id}/acknowledge
POST /api/v1/prearrival/dispatch # M1 RED triage trigger
Body: { session_id, hospital_id, ed_unit }
Returns: { packet_id, eta_min, ed_acknowledged }
GET /api/v1/clinics/{id}/analytics # lead · conversion · NPS
# MCP Tools
clinic_finder Geo-search + filter
queue_lookup Live queue per clinic
eta_calc Maps API · cached
referral_compose Letter generator
prearrival_dispatch ED packet send
6a. Maps API Strategy
- Primary: Google Maps Distance Matrix API (real-time traffic) — RM 0.005/element
- Fallback: Mapbox Directions API (cheaper bulk · no traffic)
- Caching: 5-minute TTL · key =
{from_h3_index}_{to_clinic_id}· H3 hex grid for spatial caching - Cost cap: per-tenant monthly budget · alert at 80% · soft-fail to straight-line distance
7. 🔁 State Machine
Patient flow:
PATIENT_TRIAGED ──► CLINIC_SEARCH ──► RESULTS_SHOWN ──► SELECTION ──► BOOK_VIA_M7
IF triage = RED:
──► EMERGENCY_ROUTING ──► HOSPITAL_ED_NEAREST ──► PREARRIVAL_DISPATCH
+ 999 advisory shown to patient
Referral flow (doctor-initiated):
ENCOUNTER_DECISION ──► COMPOSE_REFERRAL ──► PDF_GENERATE ──► DISPATCH ──► ACKNOWLEDGE
Klinik admin flow:
CLINIC_SETUP ──► HOURS/SERVICES/DOCTORS ──► PUBLISH ──► [LEAD_RECEIVE loop]
8. 🤖 Agent Specification
M3 mostly rule-based + Maps API. LLM hanya untuk: (a) referral letter compose, (b) clinic recommendation explanation ke pesakit ("klinik ni nearest, queue 3 orang, ETA 12 min"), (c) pre-arrival packet structuring untuk ED.
- Model: Llama 8B (rule-based heavy) untuk recommendation explanation; Llama 70B untuk referral letter compose (clinical quality)
- Memory: klinik directory (read-heavy cache) · patient preferences (last clinic visited)
9. 🎨 UI/UX
- Patient PWA: Map view (Mapbox GL atau Google Maps embedded) + list view toggle · clinic cards dgn name · distance · queue · "Book now" button
- Emergency screen (RED): Big red banner · 999 phone button · Hospital ED card dgn "Tap to share GPS + send pre-arrival" · live ETA
- Klinik admin (Filament): CRUD form · location picker (map drag) · services multi-select · hours week-grid · panel insurer multi-select
- Doctor referral compose: One-click from M4 encounter · LLM draft + edit · target clinic search · digital signature · send
- Klinik analytics dashboard: Cards (leads received · conversion % · NPS) · trend chart · per-source breakdown
10. ✔️ Acceptance Criteria
- AC-3.1: Geo search returns top-10 results <500ms pada 1000-clinic database
- AC-3.2: RED triage emergency routing: pre-arrival packet ke ED <30s
- AC-3.3: Clinic directory CRUD: 95%+ form completion success (no validation friction)
- AC-3.4: Referral letter PDF gen <5s · clinical content accurate
- AC-3.5: Maps API cost <RM 100/bulan @ 1000 searches/day
- AC-3.6: Cross-tenant consent flow tested · zero unauthorised access
- AC-3.7: Klinik analytics 30-day metrics within 24h refresh
- AC-3.8: Mobile PWA Map view 60fps pan/zoom
11. 🧪 Test Plan
| Tier | Cases | Coverage |
|---|---|---|
| Unit | Geo distance calc · service filter · queue snapshot fetch · referral letter template | ≥80% |
| Integration | API endpoints · Maps API integration (mocked + real) · cross-tenant referral | 100% endpoints |
| E2E | 3 patient journeys: nearby search · RED emergency · doctor referral | 3/3 pass |
| Load | 100 concurrent geo searches · 10 simultaneous referrals | p99 <1s |
| Geographic | 50 clinic samples KL/Selangor/Johor — verify accuracy distance + ETA | ±5% accuracy |
| Cost | Maps API budget tracking · soft-fail when exceeded | Budget control verified |
12. 🔗 Dependencies
- Hard: M9 (audit + RBAC · cross-tenant consent) · M7 ADPA (queue + appointment API)
- Soft: M1 PSPA (lead source · trigger emergency routing) · M4 DRPA (referral consumer)
- External: Google Maps API atau Mapbox GL · MOH directory of registered clinics (data seed) · postal services API (for ED hospital list)
13. 🏃 Sprint Allocation
- Day 1-3: Klinik directory schema · CRUD admin UI · seed data 100 sample clinics
- Day 4-5: PostGIS geo-search · service filter · panel filter
- Day 6-7: Maps API integration · ETA · live queue snapshot
- Day 8-9: Patient PWA Map view · list view · booking deep-link
- Day 10-11: Referral letter compose (M4 integration) · PDF gen
- Day 12: Pre-arrival packet workflow (RED triage path)
- Day 13: Klinik analytics dashboard
- Day 14: E2E test · sprint review
14. ⚠️ Module-Specific Risks
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Klinik gaming queue counts (always show "0 waiting") | Med | 🟠 Trust loss | Audit trail queue updates · M7 ADPA single source · spot check via 30-min refresh |
| Maps API cost explosion | Med | 🟢 Budget | Aggressive caching · H3 spatial index · per-tenant budget cap · soft-fail to straight line |
| Pre-arrival packet not received by ED | Med | 🔴 Patient harm | Multi-channel: API + email + WAHA + SMS · ED ack required · audit FN rate |
| False advertising klinik (services not actually available) | Med | 🟠 Patient frustration | Patient feedback loop · NPS · inactive clinic auto-hide · admin verify checkbox |
| Cross-tenant data leak (referral) | Low | 🔴 PDPA breach | Explicit consent per referral · audit cross-tenant access · receiving clinic acknowledge |
| Geocoding inaccurate (rural Malaysia) | Med | 🟢 UX | Manual address override · clinic admin set GPS pin · MOH directory cross-ref |