Skip to content

feat: Phase 5.10 encounter templates + Phase 7B bulk onboarding (DAY-14)#59

Open
Mlu-ctrl-alt-design wants to merge 79 commits into
mainfrom
develop
Open

feat: Phase 5.10 encounter templates + Phase 7B bulk onboarding (DAY-14)#59
Mlu-ctrl-alt-design wants to merge 79 commits into
mainfrom
develop

Conversation

@Mlu-ctrl-alt-design

Copy link
Copy Markdown
Owner

Summary

Closes out DAY-14 — Phase 5.10 encounter templates + Phase 7B practice onboarding.

Phase 5.10 — Encounter templates for all appointment types (4 new)

Template Appointment Type Required Fields Notes
General Consultation (ET-00004) Consultation chief_complaint, hopi Smart orders for I10/E11/N18
Follow-up Consultation (ET-00005) Follow-up chief_complaint, medication_adherence
Emergency Encounter (ET-00006) Emergency chief_complaint, BP, weight, AVPU FBC/glucose/dipstick auto-ordered
Procedure Encounter (ET-00007) Procedure chief_complaint, plan

All is_platform_template=1, practice=null. Records created in staging DB and fixture updated.

Phase 7B — Bulk staff invite + patient import + Yoco webhook registration

  • invite_staff_bulk(practice, csv_data) — CSV with email/full_name/role, per-row savepoints for isolation
  • Practice form — "Bulk Invite Staff (CSV)" + "Import Patients" action buttons
  • register_yoco_webhook(url) — captures the signing secret from Yoco's API; handles the 3-webhook limit
  • list_yoco_webhooks() + delete_yoco_webhook(id) — webhook lifecycle helpers
  • TestsTestInviteStaffBulk (4 cases) + TestPatientPracticeStamping (1 case)

Pre-merge checklist

  • git pull origin develop on staging + bench --site medic-demo-staging.thedaystar.co.za migrate
  • Run bench run-tests --app medic_plus --module medic_plus.api.test_invitations passes
  • Manual smoke test: Practice form shows both new action buttons
  • Phase 5.11 HITL gate: perform first backup-restore drill on staging + log a Backup Drill Log row

Test plan

  • TestInviteStaffBulk: happy path, per-row isolation, missing-column rejection, blank-line skipping
  • TestPatientPracticeStamping: before_insert stamps custom_practice
  • Fixture sync: bench migrate → all 7 Encounter Template rows present

🤖 Generated with Claude Code

Mlu-ctrl-alt-design and others added 6 commits May 12, 2026 00:02
… Procedure (Phase 5.10)

Adds ET-00004 through ET-00007 to fixtures so that all appointment types
have a platform template. Templates were already present in staging DB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Accepts CSV with email/full_name/role columns, processes each row in its own
savepoint so partial failures don't block the rest.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
register_yoco_webhook() captures the signing secret from Yoco's API;
list_yoco_webhooks() and delete_yoco_webhook() manage subscription limits.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…orm (Phase 7B)

Two new action buttons: CSV editor for bulk staff invites with per-row
error table, and a redirect to Frappe Data Import for patient import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…boarding

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
root and others added 23 commits May 14, 2026 19:50
Replace the placeholder blue-gradient SVG in MeridianLogo with the
official Daystar Medical icon PNG (@2x). The icon now ships in the
SPA's public dir and renders via <img> across auth, layout, and nav.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MTimePicker rendered <window.MIcons.Clock /> but the Clock entry was
never defined in Icons (parent-icons.jsx) or extended in MIcons. The
new-visit drawer crashed with "Element type is invalid ... Check the
render method of `MTimePicker`" whenever it tried to render the
time-picker input prefix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
practice_member.py:21 read self.status, but the deployed practice_member.json
defines only practice/user/role/practitioner — no status column. Direct
provisioning paths (and admin Desk Save) crashed with AttributeError before
the row could be inserted.

Use getattr to default status to None when the field is absent; the
invitation flow stays inert and the role-assignment path runs as intended.
The invitation schema (status/email/full_name/mobile_number/...) can be
re-introduced via a proper JSON + migrate when that feature is reactivated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MSelect/MDatePicker/MTimePicker shipped in PR #52 without a stylesheet —
their portaled popovers had no position/z-index/background rules, so
opening a dropdown in the New Visit drawer dumped the listbox at the
end of <body> as an unstyled static block and broke the page layout.

Adds layout-critical CSS:
- .msel/.mdp/.mtp triggers: flex row, chevron pinned right
- .msel-popover/.mdp-popover/.mtp-popover: position: fixed, z-index 10000,
  surface bg, border, shadow, scrollable list
- MDatePicker calendar grid + month nav
- MTimePicker hour/minute columns
- Mobile popover width clamp

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s_on

Patient Encounter -> custom_head_circumference shipped with
depends_on="eval:frappe.utils.age_in_days(doc.dob || '') < 730", but
frappe.utils.age_in_days is not a function on the client or the server.
At form-load time the JS evaluator throws TypeError, which Frappe's
catch surfaces as 'Invalid "depends_on" expression', preventing the
form from rendering.

Use the real Frappe date utils: only show the field when DOB is set
AND the patient is under 730 days (2 years) old.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UX fixes for the daystar-health SPA, plus removal of unreachable Practice
Member invitation code that referenced non-existent schema fields.

new-visit drawer:
- NVField gains optional `required` prop; renders a red asterisk
- Mark Patient, Practitioner, Date, Chief Complaint as required
- Click anywhere in the date/time input opens the picker (typists keep
  keyboard-only via ArrowDown; focus alone still doesn't auto-open)

date/time pickers (CSS):
- Calendar / Clock icon overlays inside the field via absolute positioning
  (right: 6px, vertically centered) — no longer flush against a chevron
- Reserve 36px right-padding on the input so text doesn't slide under
  the icon

login form:
- Password show/hide eye is now vertically centered against the 38px input
  and rounded so hover looks intentional. Uses transform: translateY(-50%)
  and a 28×28 hit target.

Practice Member controller:
- Remove _run_invitation_flow / _invite_staff / _invite_patient /
  _send_*_email / _split_name / _get_practitioner_name. These accessed
  self.email / self.full_name / self.mobile_number / self.invitation_sent_on
  / self.patient_record which never existed on the doctype JSON; they
  would crash with AttributeError if ever reached. After v2.2.6 they were
  unreachable anyway (status field also missing). Net: dormant code
  removed; behavior unchanged — direct provisioning still assigns the
  Frappe role on insert.
- Simplify _prevent_duplicate to check (practice, user) since email isn't
  on the schema.
- Drop unused imports of on_staff_accepted / on_patient_invited /
  now_datetime / get_url.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Login password field: anchor the eye toggle with explicit top:5 (not
  top:50%/translateY) and pin the parent wrapper to height:38 with
  matching height/box-sizing on the input, so the 28×28 toggle sits flush
  centered (5px slack each side) regardless of browser line-height quirks.
- Add the Lock icon pointerEvents:none so clicks pass through to the input.
- Add <link rel="icon"> + <link rel="apple-touch-icon"> in
  daystar-health.html pointing at the Daystar Medical PNG so the
  browser tab and iOS home-screen icon use the brand mark.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tal register-patient drawer

- New whitelisted endpoint medic_plus.api.daystar_health.list_appointment_types
  returns the list as a flat list[str] using frappe.get_all with
  ignore_permissions (Appointment Type is platform-wide reference data —
  no tenant scoping needed; auth is enforced by the session).
- New Visit drawer now calls that endpoint via api.call() instead of the
  REST /api/resource/Appointment Type path, which was returning an empty
  list for some practice-doctor sessions despite having read permission.
- Register Patient drawer is now portaled to document.body via
  ReactDOM.createPortal so it can't be reparented under .page.fade-in
  which animates with `transform: translateY(...)`. A transformed
  ancestor makes `position: fixed` relative to that ancestor instead of
  the viewport — that's what was letting the patients table bleed through.
  Also clamp drawer maxWidth: 100vw for narrow phones.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…w Visit)

Replace the hand-rolled fixed-position drawer with the shared
window.MDrawer used by the New Visit flow. Identical visual chrome
(backdrop, header, body, footer, Escape-to-close, transition) and we get
the .drawer / .drawer-backdrop CSS for free.

- Convert RegisterPatientDrawer to accept `open` prop; MDrawer handles
  show/hide. Reset form/error/saving on each reopen so the next session
  starts clean.
- Move Cancel + submit button into MDrawer's `footer` prop. Wire the
  submit button to the form via `form="register-patient-form"` since
  the button now lives outside the <form>.
- MPatientsScreen stops gating with `{showRegister && practice && ...}`
  — the drawer mounts permanently, MDrawer toggles visibility via the
  `open` prop. Practice presence is checked in the open expression.
- Drop the custom ReactDOM.createPortal wrapper; MDrawer's fixed-position
  chrome is already at the layout root via the .drawer class.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Native <select> and <input type="date"> are explicitly banned by the
Daystar UI standard — they were the only browser-default chrome left in
the SPA. Swap to the same branded components New Visit uses.

- Sex → MSelect
- Date of Birth → MDatePicker (with onChange callback also running the
  existing duplicate check)
- ID Type → MSelect
- Race → MSelect
- Home Language → MSelect (searchable — 11 SA languages)
- Preferred Language → MSelect (searchable)

For the optional selects (Race / Languages) the previous empty-string
sentinel option is replaced with MSelect's placeholder; we filter the
empty entry out of the options list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… + Practice screen

Three changes for the live demo:

1. Patient Encounter "Value missing: Appointment Type"
   meridian-api.js's api.call already unwraps payload.message, so the
   v2.2.11 list-types handler doing `(res && res.message) || []` was
   double-unwrapping → empty list → form.appointment_type never
   auto-populated → Frappe rejected on submit. Use the unwrapped value
   directly: `Array.isArray(res) ? res : []`.

2. Top-nav search bar cramped + ⌘K hint removed
   Drop the <kbd>⌘K</kbd> element from MTopbar (cmd-K isn't wired and
   the badge was eating horizontal room). Widen .topbar-search:
   flex:1; max-width:480px on desktop, 320px on small screens, so the
   search bar can breathe instead of clamping to 220px.

3. ?screen=practice — practice details screen
   New whitelisted endpoint daystar_health.get_active_practice_details
   returns the active practice's safe Desk fields (identity,
   subscription, contact, branding, doctors). New
   <MPracticeScreen> renders Identity header + Contact / Subscription /
   Branding / Doctors cards in a responsive grid. Sidebar already had a
   'Practice' nav item that fell through to <Placeholder>; routing now
   dispatches to MPracticeScreen instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ePicker

After Register Patient + New Visit, four native date inputs were still
sitting in the Appointments and Medical Records filter toolbars. Replace
all four so every date field across the SPA uses the same branded picker
(MDatePicker) with the calendar icon inside the field, click-to-open,
and consistent popover chrome.

- Appointments toolbar From/To range filters
- Medical Records filter From/To

No remaining type="date" / type="time" inputs anywhere under public/daystar-health.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
root and others added 30 commits May 15, 2026 21:19
Adds a SchedulePanel card below the week grid that renders the
practitioner's recurring working hours day by day (Mon–Sun).  Each row
shows the day, one or more time-slot ranges (supports split shifts), and
the source Practitioner Schedule name.  Non-working days are shown with
a muted em-dash and a subtle background so the pattern is immediately
readable.  Clicking the schedule name badge opens the Practitioner
Schedule document in Desk.  Days with no schedule configured show a
"Set up schedule" link instead.  The API now also returns doc_name per
slot so Desk links are correct.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace frappe.user.get_roles() (non-existent) with frappe.get_roles()
  in the Patient Mask Sensitive Fields client script
- Add read-only Custom DocPerms for Mode of Payment and Medical Department
  for Practice Admin, Practice Doctor, Practice Receptionist (fixes 403s)
- Add patient_history page to fixtures with Practice roles (fixes 403
  on Patient History page)
- Add patient_history page fixture filter to hooks.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Practice-scoped React SPA at /portal/<slug> reusing the Meridian
design system. Email-OTP auth, authed booking via shared rules
helper, 7-screen patient parity, new Patient Portal User role with
PQC scoping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refines the spec during planning: reuse existing Patient role (PQCs
already scope correctly) instead of adding a Patient Portal User role.
Adds _get_customer_for_user helper and four PQC entries (Patient
Encounter patient branch + new PQCs for Patient Problem List,
Medication Request, Sales Invoice).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merges 27 commits implementing the Patient Portal at /portal/<slug>:
- Backend: medic_plus.api.patient_portal (15 whitelisted endpoints)
- Frontend: medic_plus/public/portal/ (13 JSX/JS/CSS files)
- Boot: medic_plus/www/portal/{index.py,index.html}
- Tests: 18 Python + 5 Playwright
- PQCs: Medication Request + Patient role branches on Encounter/Problem List/Sales Invoice
- Shared _book_slot helper between guest and authed booking

See docs/releases/v2.4.0.md for full release notes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant