The department-side Resolve button is disabled (greyed out) until the
department records a patient contact outcome — the contact_status gate
in inquiry_change_status enforces this. Previously only a hover tooltip
explained why; now an inline amber hint appears next to the actions bar
so the reason is immediately visible.
Per the workflow realignment, the department owns inquiry resolution
end-to-end. The PX-side respond modal and its 'Response to Patient'
tile button are removed; the 'Resolve' button in the 'Department has
responded' banner now POSTs directly to inquiry_change_status (with a
confirm dialog), which already enforces the contact_status gate.
The inquiry_respond view and URL are kept for backward compatibility
but no UI surfaces them. Two regression tests lock in the new behavior:
- showRespondModal JS function and respondModal div are gone
- Resolve form posts directly to inquiry_change_status with status=resolved
- department_record_complaint: broaden permission to include any involved
department (mirrors the OR-filter used to surface complaints in the list)
- send_to_modal: simplify — sends directly to champion+manager, drop the
contact-person picker (loadDepartmentContacts is now a no-op stub)
- department_inquiry_detail: confirm dialogs on Resolve/Close; fold No-Response
into the contact_status dropdown
- department_detail: JS string-escaping fixes
1. /complaints/templates/new/ — TemplateDoesNotExist: template_form.html was
missing. Created a functional template form (name, category, title pattern,
description).
2. /complaints/oncall/schedules/new/ — NoReverseMatch: redirect('dashboard')
should be redirect('/') since the URL name 'dashboard' doesn't exist.
3. /complaints/settings/escalation-rules/new/ — RelatedObjectDoesNotExist:
EscalationRuleForm accessed self.instance.hospital on an unsaved instance.
Changed to self.instance.hospital_id (safe FK ID check).
Root cause: onboarding_complete existed as dead code (a module-level function
with 'self' param, never registered as a DRF @action). The template JS posted
to /accounts/users/onboarding/complete/ which 404'd.
Fix: added a POST handler to the existing ui_views.onboarding_complete view
that accepts JSON {username, password, password_confirm, signature}, calls
OnboardingService.complete_wizard (sets password, clears is_provisional,
sets acknowledgement_completed), and notifies admins. Updated the template
JS fetch URL to /accounts/onboarding/complete/ (the working endpoint).
Re-verified (headed): token activation → welcome → wizard steps → checklist →
activation form → POST completion → is_provisional=False, ack=True. ✅
Remaining: the browser JS submitActivation() has a timing/CSRF issue that
prevents the fetch from completing on button-click (the direct POST works).
Needs separate investigation.
Tests the full onboarding flow: token activation (auto-login) → welcome → wizard
content steps → checklist → activation (password) → completion → invalid token
→ admin provisional list.
Key finding: the wizard UI renders but the onboarding flow doesn't actually
COMPLETE — after walking all steps + submitting the password form, the user is
still is_provisional=True with no password set. The activation step lacks a
POST handler to finalize onboarding. Details in the test observations.
Harness: seed_e2e_provisional + get_e2e_onboarding_state CLI + spec.
Fixed 3 gaps in the QI Projects module:
Gap 1 (critical): team members couldn't manage their own tasks — the toggle
checkbox/edit/delete were gated behind admin-only can_edit. Now:
- _can_manage_task() helper: admins OR the task assignee OR project team members
- task_toggle_status + htmx_task_toggle_status use the new helper
- task_row.html shows the toggle for assignees (task.assigned_to.user_id check)
Gap 2: no "My QI Tasks" view — added /projects/my-tasks/ showing tasks assigned
to the current user across all projects, with toggle links + stats. Sidebar link.
Gap 3: no notification on task assignment — added apps/projects/signals.py
(post_save on QIProjectTask → create_in_app_notification). apps.py ready() wired.
Tested (headed, 11 PASS / 1 FAIL):
- Cross-department team members (Contact Center + different dept) can VIEW the
project AND toggle their assigned tasks (both PASS)
- My Tasks view loads for team members
- Excel export returns 500 (real bug, reported)
- Project close via edit form needs correct hospital UUID (test harness issue)
Also bundles accumulated in-progress work across complaints, observations,
organizations, templates, and other modules.
Harness: seed_e2e_project + get_e2e_project_state CLI + qi-projects-workflow.spec.ts
Behavioral consistency across complaint/inquiry/observation (re-run: 0 FAIL):
- activation gate on complaints: complaint_send_to + send_to_department_form now
reject status=open ("Activate this complaint before sending it to a department")
- observation resolve dead-end fixed: observation_change_status now allows
hospital_admin (was triage_perm/px_admin only) + added a Resolve action on the
observation detail page -> accepted dept responses can be resolved from the flow
- status validation: Observation.clean() rejects invalid statuses + a DB
CheckConstraint (migration 0016 normalizes legacy "new"->"open" first)
- inquiry sent_to_department consistency: inquiry_transfer_to_department now also
sets sent_to_department=True/At (matches observation/complaint for cross-module queries)
Lucide icon-race mitigation: added a defensive `window.lucide || {createIcons:noop}`
shim to the three base layouts + standalone CDN pages (login, select_hospital,
password reset) so the "lucide is not defined" ReferenceError can't throw during
navigation races. Audit listener classifies any residual occurrence as WARN (cosmetic).
Docs: docs/workflows.md records the intentional complaint vs inquiry/observation
differences (multi-dept join + manager review vs single-dept flat) + the field-name map.
Specs: champion spec now activates before send (matches the new gate).
Bugs 4-8 from the QA audit, all re-verified (re-run: 0 FAIL):
- token-response form pages (inquiry + observation, 8 templates) extended the
static dashboard base (no {% block content %}) -> form was dropped. Switched
to layouts/public_base.html; form + csrf now render, anonymous token POST works
- inquiry dept-response GET 500 (missing template) -> created
complaints/inquiry_department_response.html (mirrors the observation one)
- NameError get_email_header_html in notifications/settings_service.py
(send_inquiry_department_assigned/_resolved/_reopened) -> added to the imports
- "lucide is not defined" -> guarded the unguarded lucide.createIcons() in
layouts/base.html and added a guarded init to layouts/public_base.html
- dept analytics XHR ERR_ABORTED -> verified endpoint returns 200 (test artifact)
Report updated: §13 issues marked fixed.