
Platform Review & Changelog
Internal — last reviewed 2026-05-23
Honest Assessment — May 2026
May 2026 — EMR v2 shipped. The platform has matured significantly. The core patient loop (assessment → Paystack payment → Zoom confirmation → tiered reminders → join call) works end-to-end. The new integrated EMR (IScriptEMRPatientRecord) now hosts 17 clinical tool panels — SOAP, Vitals, Diagnosis+ICD10, EnhancedPrescription, LabRequest, Radiology, Referral, SickNote, Medicus Opus, ZoomDock, AI CDS, MedAid, Billing, Close Visit, Docs, Audit — all within a single unified workspace. Visit lifecycle (Open → Active → Closed) is enforced at the database level.
Genuinely working, end-to-end: Assessment engine (8 verticals), booking wizard with availability-aware slot picker, Paystack payment, finalisePaidAppointment (atomic invoice/receipt), doctor confirm/reject, Zoom meeting creation, ZoomDock in-EMR call, tiered reminder chain, patient prescriptions (via EnhancedPrescription), lab requests, visit lifecycle, Medicus Opus AI wizard.
2 real P0 gaps remaining: (1) No automated Paystack refund when a doctor declines — critical legal risk. (2) EMR BillingSection "Submit Billing" is still a setTimeout mock — doctor post-consult charges are silently lost.
MediKredit deferred: Excluded from this review cycle. Will be addressed separately as Path 5.
Overall Health
81%
Working
34
of 51 items
Partial
15
of 51 items
Fix Needed
2
of 51 items
Planned
0
of 51 items
Clinical Assessment Engine (8 verticals)
All 8 assessments functional with AI analysis. Anonymous → user linking on login works via anonId in localStorage. Assessment drafts stored in per-vertical entities.
Account creation / login flow after assessment
CreateAccount page exists. linkAnonAssessmentToUser backend runs on dashboard load. Fragile: if user closes between assessment and login, GLP-1 anon_id is cleared but other vertical drafts may not be linked if the correct vertical entity is not queried.
In-Dashboard Booking Wizard (5-step)
Vertical → Assessment → Doctor selection (with vertical keyword filter + fallback) → Date/Time → Payment (Paystack or PayFast gateway selector) all wired end-to-end.
Doctor calendar slot availability
DateTimePicker checks DoctorProfile.availability (synced from Google Calendar via syncDoctorCalendarAvailability). If availability is set, ONLY those slots show and days with no slots are greyed out. If NOT set, all 08:00–16:30 Mon-Fri slots show with a warning banner. Partial: Google Calendar sync exists but doctors must actively connect it to protect against double-booking.
Paystack payment (primary gateway) — LIVE ✅
Paystack Inline.js integrated. initializePaystackTransaction + finalisePaidAppointment (idempotent). Webhook fallback via paystackWebhook. ZAR card, bank transfer, EFT. Reference stored on Appointment.
PayFast payment (secondary gateway)
PayFast checkout component exists and calls finalisePaidAppointment on success. Race condition risk: PayFast ITN webhook and frontend callback may collide. No deduplication on PayFast path. Consider removing now Paystack is live.
Post-payment dashboard redirect & polling
?payment=ok&ref= redirect handled. Green banner shown. Auto-refresh polls every 3s until appointment appears (up to ~30s). postPaymentWarning localStorage fallback if finalisation fails silently.
Patient Dashboard — Upcoming Appointments
AppointmentCard shows status, Join Now (unlocks 10 min before), Add to Calendar (Google/Outlook/.ics), PDF summary download, and doctor profile block. Rebook flow for declined appointments fully wired.
Patient Dashboard — Prescriptions tab
Prescriptions tab reads from the Prescription entity and renders download links. HOWEVER — the EMR EnhancedPrescription component does write to the Prescription entity, so prescriptions DO appear here for consults done through the new EMR. Old SimplePrescription path was PDF-only and did not save to entity.
Patient Profile & Intake Form
PatientIntakeForm: SA ID Luhn validation, DoB auto-derived and locked, phone mandatory with red-border error, OSM Nominatim address autocomplete, AI allergy/medication suggestions. Accessible from both profile tab and EMR provider view.
Patient Medical Aid Tab
MedicalAidTab UI complete — shows scheme, number, plan, dependent code. MediKredit eligibility lookup is deferred (mTLS blocker). Currently displays saved data only with no live verification.
Billing Tab — Invoices & Receipts
AccountsBillingTab shows invoices, receipts, and incomplete payment recovery. PDF links attached from finalisePaidAppointment. Invoice + Receipt entities populated atomically.
Assessment draft resume (Incomplete Assessments)
IncompleteAppointments fetches all 8 draft entity types. Resume passes draft into InDashboardBooking wizard starting at step 1. Edge case: if vertical field is missing on draft, step starts at 0.
Patient receives prescription / results after consult
Prescriptions entity is written by EnhancedPrescription. Patient-facing prescriptions tab reads it. However: no push notification or email to patient saying "your prescription is ready". Patient must check the tab themselves.
Post-consultation follow-up (7-day thread)
Booking confirmation email mentions "7-day follow-up message thread" as an included feature. The PatientCommunicationTab exists and shows communication logs, but there is no automated post-consult message sent to open a follow-up thread with the doctor.
Doctor registration & role assignment
setDoctorRole backend function assigns role="doctor". ChooseRole page guides new users. DoctorProfile auto-created.
Onboarding wizard (photo, bio, practice details)
OnboardingWizard 4-step modal: photo upload + AI enhance, AI bio writer, SA ID + phone, practice details. confetti on complete. Progress tracked via onboarding_step.
Independent Contractor Agreement signing
ContractorAgreementSigning renders full agreement text. E-signature supports drawn (canvas), typed name, and upload. Canvas-drawn signature stored as base64 URL — not legally DocuSign-grade. Signed PDF URL stored on DoctorProfile.
Admin approval of doctor profile
approveDoctorProfile backend sets status="approved" + profile_complete=true. Admin has DoctorProfileEditor, approval toggle, and email notification. Doctor unlocks booking availability post-approval.
Doctor public profile page
/doctors/:doctorId renders full profile, languages, specialties, bio, reviews, Google rating. Book Now button links to booking wizard pre-filtered to this doctor.
Google Calendar sync for availability
CalendarSyncManager + iScript Doctor Calendar OAuth connector + syncDoctorCalendarAvailability function all built. Syncs busy events to DoctorProfile.availability. DateTimePicker reads this. Gap: doctors must actively connect their calendar — no prompt to remind them.
Doctor dashboard — Awaiting Consultations tab
AwaitingConsultationTab shows confirmed appointments grouped by date with date filters. AI clinical summary expand/collapse per patient, PDF download. "Start Consultation" opens CreateVisitModal. All wired.
Doctor dashboard — EMR
Full IScriptEMRPatientRecord: 17 tool panels (SOAP, Vitals, Diagnosis+ICD10, EnhancedPrescription, LabRequest, Radiology, Referral, SickNote, Medicus Opus, AI Report, CDS, MedAid, Billing, Close Visit, Docs, Audit). VisitBanner and ZoomDock integrated.
Visit lifecycle (Open → Close)
CreateVisitModal creates ClinicalRecord with visit_status=open. VisitBanner tracks status. updateVisitStatus backend enforces guards. VisitClosePanel handles close with reason. Read-only lock on closed visits.
Doctor notifications for new pending bookings
Confirmation email sent via finalisePaidAppointment. No in-app push notification or real-time subscription. Doctor must manually refresh to see new bookings in queue.
Doctor earnings / financial overview
DoctorDashboard Overview tab shows appointment stats and estimated earnings based on consultation fees. Not connected to actual Invoice/Receipt entities — uses appointment.consultation_fee × count as an estimate only.
Appointment creation on payment
finalisePaidAppointment atomically creates: Appointment + ClinicalRecord + Invoice + Receipt. Idempotency guard via payment_reference lookup prevents duplicates. Paystack webhook as fallback path.
Doctor confirm / reject workflow
doctorConfirmAppointment: confirm → Zoom meeting created → emails to both parties + WhatsApp. Reject → status=declined + patient email. Awaiting Consultation tab shows pending queue with Confirm/Decline actions.
Zoom meeting auto-creation on doctor confirm
S2S Zoom OAuth, 20-min scheduled meeting, waiting room ON, cloud recording ON, Africa/Johannesburg timezone. Zoom credentials stored on both Appointment and ClinicalRecord.
Patient joins Zoom via AppointmentCard
AppointmentCard Join Now button unlocks 10 min before. Uses teleconsult_link (zoom_join_url). Works on mobile. Calendar export (.ics, Google, Outlook) also available.
Doctor starts Zoom call (ZoomDock in EMR)
VisitBanner "Start Zoom Call" button triggers createZoomMeetingForVisit (idempotent). ZoomDock renders as persistent bottom-right panel within EMR. Doctor never leaves the EMR to take the call.
Appointment reminder chain (tiered)
sendAppointmentReminders: immediate confirmation, 8am day-before, 6am day-of, 1h before, 10min before. Flags on Appointment entity prevent double-sends. Scheduler automation runs every 5 min.
Zoom recording → transcript → clinical notes
zoomRecordingWebhook + generateTranscriptSummary built and deployed. Not end-to-end validated on a live consult. Zoom cloud recording must be enabled on the account and webhook URL registered in Zoom Developer Console.
Automated refund on doctor cancellation
CRITICAL P0. When doctor declines/cancels, there is NO automated Paystack refund call. AppointmentCard shows refund_status UI (processed/pending/none) but nothing actually triggers the Paystack refunds API. Must be fixed before first live patient.
WhatsApp appointment automation
appointmentWhatsappAutomation sends WhatsApp on confirmation, reminders, and cancellation. whatsappSend backend calls Meta API. In-app patient-doctor messaging thread not linked to the appointment confirmation flow.
Zoom host start_url regeneration
zoom_start_url saved on Appointment/ClinicalRecord. Time-limited (~24-48h). No UI button to regenerate fresh host URL if it expires before the consult. createZoomMeetingForVisit is idempotent — already returns existing meeting data. Doctor could use VisitBanner to re-trigger on the day.
Visit lifecycle management (Open/Close/Lock)
CreateVisitModal opens ClinicalRecord with visit_status=open. VisitBanner tracks state. updateVisitStatus backend enforces: no edits to closed visits, only assigned doctor can update, close_reason required.
Patient profile & problem list in EMR
EMR shows PatientProfile with allergies/medications/chronic conditions. AllergyAlertBanner + MedicationBanner shown at top of profile AND at prescription point. ProblemListPanel, PatientIntakeForm, PatientTimeline all accessible as tabs.
AI pre-consultation report (from assessment)
ClinicalReportCompact renders likely diagnoses, risk flags, urgency level, recommended investigations from ClinicalRecord.ai_analysis.
Medicus Opus wizard (AI clinical workflow)
Full MedicusOpusWizard integrated as EMR panel. History → Diagnosis → Management → Education → Billing. Auto-save via debounced saveToVisit(). read-only when visit is closed. Runs medicusOpusDiagnosis, medicusOpusPrescriptionValidator, medicusOpusBillingCoding backend functions.
SOAP notes (structured, AI-assisted)
SOAP tab with 8 vertical-specific prompt templates. AI assist buttons (Generate SOAP, Suggest Diagnosis, Draft Plan) via Claude Sonnet. Notes saved to ClinicalRecord.
Vitals panel
VitalSignsPanel is a fully wired EMR tab — BP, HR, Temp, SpO2, RR, Weight, Height, BMI, Blood Glucose. Trend graph across past visits. Writes to ClinicalRecord vitals fields.
Diagnosis + ICD-10 autocomplete
ICD10Autocomplete rendered in EMR Diagnosis tab. AI diagnosis recommender with AIDiagnosisRecommender + confidence badge + audit trail for doctor override. AIAuditTrail records AI-generated vs doctor-accepted diagnoses.
E-Prescription (EnhancedPrescription)
EnhancedPrescription writes to Prescription entity with patient/doctor snapshots, medications, ICD10, PDF. AllergyAlertBanner shown at prescribing point. dispatchPrescription backend function for pharmacy dispatch.
Lab request (LabRequestModule)
LabRequestModule is a wired EMR tab — writes to LabRequest entity, sends via sendLabRequest backend. AmpathOrderPanel available for Ampath e-lab orders (sandbox-tested).
Radiology / Imaging requests
RadiologyRequestModule is a wired EMR tab — generates radiology request with modality, body part, clinical indication. AI-suggested imaging based on diagnosis. PDF export.
Referral letter & sick note
ReferralLetterGenerator + SickNoteGenerator both surfaced as EMR tabs. AI-generated content, doctor letterhead, PDF export. Both write to relevant entities.
Clinical decision support (CDS)
ClinicalDecisionSupport tab uses full patient context + SOAP + vertical to suggest diagnoses, red flags, contraindications, prescriptions via Claude Sonnet. CriticalValueAlert for abnormal vitals.
Post-consultation billing (BillingSection)
P0. BillingSection "Submit Billing" still uses a setTimeout mock — does NOT write to Invoice entity or call any backend. Doctor post-consult charges are lost. Must wire to submitConsultationBilling backend before go-live.
POPIA consent & audit trail
AuditTrail tab in EMR. auditLog backend called on SOAP save. POPIAConsentModal on appointment creation. Consent snapshot stored on Appointment entity.
Document manager (EMRDocumentManager)
Documents tab in EMR. managePatientDocuments backend for upload/list/delete. Supports attachments + lab results array on ClinicalRecord.