feat: Phase 5.10 encounter templates + Phase 7B bulk onboarding (DAY-14)#59
Open
Mlu-ctrl-alt-design wants to merge 79 commits into
Open
feat: Phase 5.10 encounter templates + Phase 7B bulk onboarding (DAY-14)#59Mlu-ctrl-alt-design wants to merge 79 commits into
Mlu-ctrl-alt-design wants to merge 79 commits into
Conversation
… 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>
8 tasks
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>
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>
…for timedelta safety
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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)
chief_complaint,hopichief_complaint,medication_adherencechief_complaint,BP,weight,AVPUchief_complaint,planAll
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 isolationregister_yoco_webhook(url)— captures the signing secret from Yoco's API; handles the 3-webhook limitlist_yoco_webhooks()+delete_yoco_webhook(id)— webhook lifecycle helpersTestInviteStaffBulk(4 cases) +TestPatientPracticeStamping(1 case)Pre-merge checklist
git pull origin developon staging +bench --site medic-demo-staging.thedaystar.co.za migratebench run-tests --app medic_plus --module medic_plus.api.test_invitationspassesBackup Drill LogrowTest plan
TestInviteStaffBulk: happy path, per-row isolation, missing-column rejection, blank-line skippingTestPatientPracticeStamping:before_insertstampscustom_practicebench migrate→ all 7Encounter Templaterows present🤖 Generated with Claude Code