Update physical exam with modal-based detailed findings and data migration#6750
Update physical exam with modal-based detailed findings and data migration#6750
Conversation
- Add freeSolo prop to AutocompleteInput component for freetext input - Enable freeSolo on Procedure Type field so users can type custom procedure names not in the ValueSet - Fall back to raw procedure name when saving/applying quick picks for custom procedure types without a ValueSet code - Auto-suggest quick pick name from current form: procedure name | site location | side of body | complications | cpt codes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce a new modal-exam exam component that renders a checkbox with a pencil icon to open a detailed findings dialog. Sub-items are stored as FHIR Observation.component[] on the parent observation. Changes include: - New ExamCardModalExamComponent type with sections/groups/options - ExamModalCheckbox React component with batched save on modal close - L/R body part pairing in ExamTable for compact layout - Extremities: 16 body parts (L/R) with Inspection, Palpation, ROM, Neurovascular, and body-part-specific Special Tests - Nose: External nose, Anterior rhinoscopy L/R, Epistaxis, Sinus tenderness - Lymph Nodes: 7 node groups (L/R) with Status and Node Characteristics - Skin: Common findings picker with descriptions, Lesion characteristics, Distribution, Location, Wound/laceration, Abscess/cellulitis, Burn, Bite/sting, Associated findings - New sections: GU Male, GU Female, separated from Rectal - Added checkboxes: General distress levels, Head bony tenderness/hematoma, Eye lid erythema/tenderness, Neck meningismus, Heart murmur grades, Extremities normal gait, Neurologic focal deficit/sensory loss/tremor, Psychiatric normal affect/anxious - Progress note and review tab support for modal-exam observations - Template compatibility warning when unrecognized exam fields detected Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e-if-no-existing-match' into feature/physical-exam-improvements
…vements Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, and stale data warnings - Convert multi-select exam items (murmur, wheezing, crackles, breath sounds, retractions, abdomen tender) to modal-exam with component storage - Add paired L/R modal for all bilateral body parts (single row, two-column modal with shared sections) - Add exam data migration framework with version numbering on encounters - Add migrate-exam-data zambda for on-demand migration triggered by user - Add ExamMigrationWarning component with migrate button on Exam tab and Review & Sign page - Add "Other findings" fallback in Review & Sign and PDF for unmatched observations with stored labels - Add label field to ExamObservationDTO populated from FHIR Observation code.text for backward-compatible display - Add "Scan for Stale Templates" to global templates admin page - Add new exam sections (GU Male, GU Female) and checkboxes across General, Head, Eyes, Neck, Heart, Extremities, Neurologic, Psychiatric - Add LLQ to abdomen tender options - Fix deleteField crash on never-saved observations in modal components Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR expands the EHR UI with a new Admin area (including billing/payment-location management), broad reporting UX updates, and significant visit-note enhancements (exam rendering/migration warnings, orders, and various UI label/validation changes).
Changes:
- Introduces
/adminwith consolidated tabs (Schedules, Virtual Locations, Employees/Providers, Insurance, Billing Configuration, Quick Picks, Global Templates) and new RCM payment-location query/api wiring. - Updates Exam rendering to support
modal-exam, adds stale/unmatched exam detection + migration warning UI, and adjusts review/progress-note rendering. - Improves multiple workflows (patient documents delete, ZIP validation, eRx enrollment pending dialog, immunization orders display, reports refresh behavior) and CI secret population via a composite action.
Reviewed changes
Copilot reviewed 168 out of 610 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/ehr/tests/component/BillingConfiguration.test.tsx | Adds component coverage for BillingConfiguration tabs & payment locations |
| apps/ehr/src/state/nav.store.ts | Renames Telemedicine tab to Virtual |
| apps/ehr/src/rcm/state/payments/payments.queries.ts | Adds react-query hooks for payment/stripe/terminal endpoints |
| apps/ehr/src/rcm/state/payments/payments.api.ts | Adds zambda API wrappers for payments admin |
| apps/ehr/src/rcm/state/payments/index.ts | Re-exports payments API/query modules |
| apps/ehr/src/rcm/state/index.ts | Reorders/extends RCM exports |
| apps/ehr/src/rcm/state/fee-schedules/index.ts | Re-exports fee schedule API/query modules |
| apps/ehr/src/pages/reports/index.ts | Exports new CompleteEncounters report |
| apps/ehr/src/pages/reports/VisitsOverview.tsx | Prevents auto-fetch for custom dates; clears stale report UI |
| apps/ehr/src/pages/reports/RecentPatients.tsx | Adds Refresh button, new “Source” column, custom-date fetch behavior |
| apps/ehr/src/pages/reports/PracticeKpis.tsx | Adds Refresh button; custom-date fetch behavior |
| apps/ehr/src/pages/reports/IncompleteEncounters.tsx | Switches to getEncountersReport + disables auto-fetch for custom dates |
| apps/ehr/src/pages/reports/DailyPayments.tsx | Stops auto-fetch for custom; clears stale report UI |
| apps/ehr/src/pages/reports/AiAssistedEncounters.tsx | Disables auto-fetch for custom dates |
| apps/ehr/src/pages/VisitDetailsPage.tsx | Adds fax history query + “Legacy Data” button + activity log wiring |
| apps/ehr/src/pages/TelemedAdminPage.tsx | Removes old telemed admin page (replaced by AdminPage) |
| apps/ehr/src/pages/Schedules.tsx | Replaces tab UI with button group; adapts schedule type handling |
| apps/ehr/src/pages/SchedulePage.tsx | Updates routes/breadcrumbs to new /admin schedule paths |
| apps/ehr/src/pages/Reports.tsx | Adds “Complete Encounters” report tile |
| apps/ehr/src/pages/PatientDocumentsExplorerPage.tsx | Wires delete action into patient docs explorer |
| apps/ehr/src/pages/GroupPage.tsx | Updates breadcrumbs for new /admin structure |
| apps/ehr/src/pages/Employees.tsx | Refactors into param-driven page; updates routes to /admin |
| apps/ehr/src/pages/EditEmployee.tsx | Updates breadcrumbs and schedule links to /admin |
| apps/ehr/src/pages/AdminPage.tsx | Adds consolidated /admin tab page |
| apps/ehr/src/pages/AddSchedulePage.tsx | Updates navigation/breadcrumbs to /admin schedule paths |
| apps/ehr/src/pages/AddEmployeePage.tsx | Updates post-create navigation/breadcrumbs to /admin |
| apps/ehr/src/hooks/useMergedQuickPicks.ts | Adds merged quick-picks fetching hook |
| apps/ehr/src/hooks/useGetPatientDocs.ts | Adds deleteDocumentAction and query refresh logic |
| apps/ehr/src/hooks/useGetOrdersForTrackingBoard.tsx | Adds immunization orders into tracking-board aggregation |
| apps/ehr/src/hooks/useAppClients.tsx | Adjusts zambda client config, local services override |
| apps/ehr/src/helpers/activityLogsUtils.ts | Adds fax activity log entries + activity link support |
| apps/ehr/src/features/visits/telemed/utils/appointments.ts | Adds appointmentId to request params type |
| apps/ehr/src/features/visits/telemed/components/tracking-board/TrackingBoardBody.tsx | Removes provider notification settings UI from tracking board |
| apps/ehr/src/features/visits/telemed/components/appointment/AppointmentSidePanel.tsx | Switches to useGetTelemedAppointments for hasUnread messaging |
| apps/ehr/src/features/visits/telemed/components/admin/VirtualLocationsPage.tsx | Moves queries import + adjusts layout spacing |
| apps/ehr/src/features/visits/telemed/components/admin/RadiologyQuickPicksPage.tsx | Adds radiology quick picks admin page |
| apps/ehr/src/features/visits/telemed/components/admin/Insurance.tsx | Moves queries import + adjusts layout spacing |
| apps/ehr/src/features/visits/telemed/components/admin/EditVirtualLocationPage.tsx | Updates breadcrumbs and schedule edit route to /admin |
| apps/ehr/src/features/visits/telemed/components/admin/BillingConfiguration.tsx | Adds billing sub-tabs + payment locations list |
| apps/ehr/src/features/visits/shared/utils/appointment-accessibility.helper.ts | Guards encounter access (avoid crash when undefined) |
| apps/ehr/src/features/visits/shared/stores/tracking-board/tracking-board.queries.ts | Adds appointmentId support into telemed appointments query |
| apps/ehr/src/features/visits/shared/stores/appointment/appointment.store.tsx | Fixes query invalidation dependency on encounter id |
| apps/ehr/src/features/visits/shared/stores/appointment/appointment.queries.ts | Removes SMS model query hook implementation |
| apps/ehr/src/features/visits/shared/components/vitals/hooks/visionCptAutoAdd.ts | Adds vision CPT auto-add helper |
| apps/ehr/src/features/visits/shared/components/vitals/hooks/useSaveVitals.ts | Auto-adds vision CPT codes after vitals save |
| apps/ehr/src/features/visits/shared/components/vitals/hooks/useBatchSaveVitals.ts | Auto-adds vision CPT codes after batch vitals save |
| apps/ehr/src/features/visits/shared/components/templates/useListTemplates.ts | Changes template options to include ids + filters missing titles |
| apps/ehr/src/features/visits/shared/components/staff-add-visit/AddVisitPatientSearchFields.tsx | Adds DOB search field (editable + readonly modes) |
| apps/ehr/src/features/visits/shared/components/staff-add-visit/AddVisitPatientInformationCard.tsx | Adds DOB search filter + updates patient search flow |
| apps/ehr/src/features/visits/shared/components/review-tab/components/ProceduresContainer.tsx | Joins technique array for display |
| apps/ehr/src/features/visits/shared/components/review-tab/components/MedicationsContainer.tsx | Adds dose/confirmation info to medication display |
| apps/ehr/src/features/visits/shared/components/review-tab/components/ExaminationContainer.tsx | Adds modal-exam rendering + “Other findings” fallback |
| apps/ehr/src/features/visits/shared/components/review-tab/components/ChiefComplaintContainer.tsx | Renames header to “Additional information” |
| apps/ehr/src/features/visits/shared/components/review-tab/ReviewAndSignButton.tsx | Removes in-person Chief Complaint gating requirement |
| apps/ehr/src/features/visits/shared/components/review-tab/MissingCard.tsx | Removes missing Chief Complaint prompt logic |
| apps/ehr/src/features/visits/shared/components/plan-tab/ERxContainer.tsx | Uses local timezone in date formatting |
| apps/ehr/src/features/visits/shared/components/patient/patientRecordValidation.ts | Uses shared zipRegex; updates ZIP message |
| apps/ehr/src/features/visits/shared/components/patient/patientRecordValidation.test.ts | Adjusts tests for enable/disable trigger values |
| apps/ehr/src/features/visits/shared/components/patient/info/IdentifiersRow.tsx | Adds click-to-copy PID with icon feedback |
| apps/ehr/src/features/visits/shared/components/patient/docs/PatientDocumentsExplorerTable.tsx | Adds delete dialog/action to docs table |
| apps/ehr/src/features/visits/shared/components/patient/PatientRecordFormSection.tsx | Moves config-types imports |
| apps/ehr/src/features/visits/shared/components/patient/PatientRecordFormField.tsx | Moves config-types imports; adds ZIP placeholder/mask |
| apps/ehr/src/features/visits/shared/components/patient/AddInsuranceModal.tsx | Updates ZIP validation error message |
| apps/ehr/src/features/visits/shared/components/medical-history-tab/MedicalConditions/MedicalConditionsProviderColumn.tsx | Switches medical condition quick picks to merged hook |
| apps/ehr/src/features/visits/shared/components/medical-history-tab/CurrentMedications/CurrentMedicationsProviderColumn.tsx | Adds “could not confirm dosage” + merged quick picks |
| apps/ehr/src/features/visits/shared/components/medical-history-tab/CurrentMedications/CurrentMedicationGroup.tsx | Displays added medication intake info |
| apps/ehr/src/features/visits/shared/components/known-allergies/KnownAllergiesProviderColumn.tsx | Switches allergy quick picks to merged hook |
| apps/ehr/src/features/visits/shared/components/exam-tab/useUnmatchedExamFields.ts | Adds hook to detect stale/unmatched exam fields |
| apps/ehr/src/features/visits/shared/components/exam-tab/ExamTable.tsx | Adds modal-exam rendering + L/R paired modal rows |
| apps/ehr/src/features/visits/shared/components/exam-tab/ExamTab.tsx | Shows migration banner when unmatched exam fields exist |
| apps/ehr/src/features/visits/shared/components/exam-tab/ExamMigrationWarning.tsx | Adds user-triggered exam migration UI |
| apps/ehr/src/features/visits/shared/components/QuickPicksButton.tsx | Adds optional search + admin “Add/Update” item |
| apps/ehr/src/features/visits/shared/components/OrdersIconsToolTip.tsx | Adds immunization orders to orders tooltip |
| apps/ehr/src/features/visits/shared/components/HpiSection.tsx | Renames Chief Complaint label to Additional information |
| apps/ehr/src/features/visits/shared/components/ERX.tsx | Adds pending enrollment dialog + sentry warning message |
| apps/ehr/src/features/visits/shared/components/AiPotentialDiagnosesCard.tsx | Handles alternate “seen in last 3 years” field |
| apps/ehr/src/features/visits/in-person/layout/InPersonLayout.tsx | Uses fixed height layout |
| apps/ehr/src/features/visits/in-person/hooks/useTasks.ts | Adds ERX task handling + completable flag |
| apps/ehr/src/features/visits/in-person/hooks/useImmunization.ts | Switches enabled condition to encounterIds input |
| apps/ehr/src/features/visits/in-person/components/screening/useScreeningQuestionsHandler.ts | Adds debounced field support |
| apps/ehr/src/features/visits/in-person/components/screening/AskThePatient.tsx | Adds single date question type + ISO date range outputs |
| apps/ehr/src/features/visits/in-person/components/progress-note/ProgressNoteDetails.tsx | Adds exam migration banner + immunization & labs refinements |
| apps/ehr/src/features/visits/in-person/components/medication-administration/medication-history/MedicationHistoryEntity.tsx | Adds dosage confirmation display |
| apps/ehr/src/features/visits/in-person/components/medication-administration/mar/MarTable.tsx | Disables hyphenation in MAR table cells |
| apps/ehr/src/features/visits/in-person/components/follow-up-note/FollowUpNoteDetails.tsx | Updates immunization input + labs refinements |
| apps/ehr/src/features/visits/in-person/components/EncounterSwitcher.tsx | Expands encounter switcher by default |
| apps/ehr/src/features/visits/ChiefComplaintField.tsx | Renames “Chief Complaint” to “Additional information” |
| apps/ehr/src/features/tasks/pages/Tasks.tsx | Adds DoseSpot task action with SSO open |
| apps/ehr/src/features/tasks/components/CreateTaskDialog.tsx | Refreshes order options on open; adjusts option labeling |
| apps/ehr/src/features/tasks/common.ts | Adds refreshKey to orders hooks; adds ERX category label |
| apps/ehr/src/features/radiology/pages/RadiologyOrdersListPage.tsx | Adds “studyName” column |
| apps/ehr/src/features/radiology/pages/RadiologyOrderDetails.tsx | Shows study name + read-only x-ray consent checkbox |
| apps/ehr/src/features/radiology/components/RadiologyTableRow.tsx | Renders studyName cell |
| apps/ehr/src/features/radiology/components/RadiologyTable.tsx | Extends column type + widths + headers |
| apps/ehr/src/features/notifications/notifications.store.ts | Splits notification toggles + adds phoneNumber |
| apps/ehr/src/features/notifications/notifications.queries.ts | Updates practitioner notification patching + phone telecom update |
| apps/ehr/src/features/notifications/ProviderNotifications.tsx | Updates store wiring to new notification fields |
| apps/ehr/src/features/in-house-labs/components/details/InHouseLabResults.tsx | Hides repeat/reflex actions when read-only |
| apps/ehr/src/features/in-house-labs/components/details/CollectSampleView.tsx | Removes sourceType gating from collect button |
| apps/ehr/src/features/immunization/components/VaccineDetailsCardList.tsx | Switches immunizations query to encounterIds input |
| apps/ehr/src/features/immunization/components/OrderStatusChip.tsx | Prevents status chip text wrapping |
| apps/ehr/src/features/immunization/components/OrderHistoryTable.tsx | Switches immunizations query to encounterIds input |
| apps/ehr/src/features/external-labs/pages/CreateExternalLabOrder.tsx | Renames CPT-add input + only adds when not PSC |
| apps/ehr/src/constants/index.ts | Extends ZIP regex to allow ZIP+4 |
| apps/ehr/src/constants/feature-flags.ts | Adds legacy data + mailing paper statements flags |
| apps/ehr/src/constants/data-test-ids.ts | Adds procedureCodes test ids |
| apps/ehr/src/components/navigation/UserMenu.tsx | Adds Settings link + pending eRx review dialog |
| apps/ehr/src/components/navigation/Navbar.tsx | Updates nav tabs (Virtual/Admin) + URL mappings |
| apps/ehr/src/components/input/AutocompleteInput.tsx | Adds freeSolo support (type casts) |
| apps/ehr/src/components/dialogs/index.ts | Exports SendStatementToPatientDialog |
| apps/ehr/src/components/dialogs/PendingErxEnrollmentDialog.tsx | Adds dialog for pending enrollment review |
| apps/ehr/src/components/dialogs/EditPatientDialog.tsx | Switches ZIP input to PatternFormat ZIP+4 |
| apps/ehr/src/components/ScheduleInformation.tsx | Updates schedule routes to /admin |
| apps/ehr/src/components/PatientEncountersGrid.tsx | Adds service category filtering + display |
| apps/ehr/src/components/GroupSelect.tsx | Removes GroupSelect component |
| apps/ehr/src/components/EmployeeInformation/BasicInformation.tsx | Updates ZIP validation message |
| apps/ehr/src/components/AppointmentTabs.tsx | Replaces groups filter prop with serviceCategories; renames tab label |
| apps/ehr/src/components/AppointmentTableRow.tsx | Refactors status/type/time display; always shows time column |
| apps/ehr/src/components/AppointmentTableHeader.tsx | Removes showTime/visible props; always renders Time column |
| apps/ehr/src/components/AppointmentTable.tsx | Adds immunizations to orders + always shows time header |
| apps/ehr/src/components/ActivityLogRow.tsx | Links activity-by text when activityLink provided |
| apps/ehr/src/App.tsx | Adds /admin routes + billing/payment/global templates routes + legacy data flag route |
| apps/ehr/public/fee-schedule-template.csv | Adds fee schedule CSV template |
| apps/ehr/playwright.config.ts | Stops ignoring employees.spec.ts |
| apps/ehr/package.json | Updates version; builds tsc+vite concurrently |
| apps/ehr/env/.env.template-iac | Adds new feature flag env vars |
| apps/ehr/env/.env.local-template | Adds mailing paper statements feature flag |
| E2E_README.md | Updates config repo naming/details |
| CLAUDE.md | Adds repo guidance for Claude Code |
| .vscode/settings.json | Pins TypeScript SDK path |
| .github/workflows/iac-deploy-services.yml | Lowers runner size; uses populate-secrets action |
| .github/workflows/e2e-intake.yml | Uses populate-secrets action + OIDC permissions |
| .github/workflows/e2e-ehr.yml | Uses populate-secrets action + OIDC permissions |
| .github/workflows/deploy-and-test-reusable.yml | Uses populate-secrets action + passes aws_deploy_role |
| .github/workflows/automated-tests.yml | Uses populate-secrets action + OIDC permissions |
| .github/actions/populate-secrets/action.yml | Adds composite action for secrets + optional TF state pull |
| .gitattributes | Updates union-merge paths to config/oystehr-core |
Comments suppressed due to low confidence (1)
apps/ehr/src/features/immunization/components/VaccineDetailsCardList.tsx:1
- This will crash if
encounteris undefined during initial render/loading becauseencounter.idis accessed unconditionally. Passencounter?.idsafely (and omit/empty the array when missing) so the hook can remain disabled until the encounter is available.
import { Box, Stack, Typography } from '@mui/material';
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const locations: PaymentLocation[] = React.useMemo(() => data || [], [data]); | ||
|
|
||
| const filteredLocations = React.useMemo( | ||
| () => locations.filter((item) => item.location.name?.toLowerCase().includes(searchText.toLowerCase())), |
There was a problem hiding this comment.
This can throw when item.location.name is undefined: name?.toLowerCase() yields undefined, then .includes(...) is invoked on undefined. Use a null-safe string fallback (e.g., (item.location.name ?? '')...) or optional-chain the includes call as well.
| () => locations.filter((item) => item.location.name?.toLowerCase().includes(searchText.toLowerCase())), | |
| () => locations.filter((item) => (item.location.name ?? '').toLowerCase().includes(searchText.toLowerCase())), |
There was a problem hiding this comment.
Reply from Claude Code: Valid concern, but this file was brought in via a merge from develop and is not part of the exam changes in this PR. Noted for follow-up.
There was a problem hiding this comment.
This code is from the develop merge, not changes in this PR.
This comment is from Claude (claude-opus-4-6).
| const handleCopy = (): void => { | ||
| if (!id) return; | ||
| void navigator.clipboard.writeText(id); | ||
| setCopied(true); | ||
| setTimeout(() => setCopied(false), 2000); | ||
| }; |
There was a problem hiding this comment.
Clipboard writes can reject (permissions/insecure context), and the setTimeout can update state after unmount. Consider awaiting writeText with a try/catch (and showing feedback on failure) and clearing the timeout in an effect cleanup to avoid potential unhandled rejections and memory leaks.
There was a problem hiding this comment.
Reply from Claude Code: This file was brought in via a merge from develop — not part of the exam changes in this PR. Valid point about error handling, noted for follow-up.
There was a problem hiding this comment.
This code is from the develop merge, not changes in this PR.
This comment is from Claude (claude-opus-4-6).
| <TabList onChange={() => {}} aria-label={`${pageTab} page`}> | ||
| <Tab | ||
| label="Schedules" | ||
| value={PageTab.schedules} | ||
| sx={{ textTransform: 'none', fontWeight: 500 }} | ||
| onClick={() => navigate(`/admin/${PageTab.schedules}`)} | ||
| /> | ||
| <Tab | ||
| label="Virtual Locations" | ||
| value={PageTab['virtual-locations']} | ||
| sx={{ textTransform: 'none', fontWeight: 500 }} | ||
| onClick={() => navigate(`/admin/${PageTab['virtual-locations']}`)} | ||
| /> | ||
| <Tab | ||
| label="Employees" | ||
| value={PageTab.employees} | ||
| sx={{ textTransform: 'none', fontWeight: 500 }} | ||
| onClick={() => navigate(`/admin/${PageTab.employees}`)} | ||
| /> | ||
| <Tab | ||
| label="Providers" | ||
| value={PageTab.providers} | ||
| sx={{ textTransform: 'none', fontWeight: 500 }} | ||
| onClick={() => navigate(`/admin/${PageTab.providers}`)} | ||
| /> | ||
| <Tab | ||
| label="Insurance" | ||
| value={PageTab.insurance} | ||
| sx={{ textTransform: 'none', fontWeight: 500 }} | ||
| onClick={() => navigate(`/admin/${PageTab.insurance}`)} | ||
| /> | ||
| <Tab | ||
| label="Billing Configuration" | ||
| value={PageTab.billing} | ||
| sx={{ textTransform: 'none', fontWeight: 500 }} | ||
| onClick={() => navigate(`/admin/${PageTab.billing}`)} | ||
| /> | ||
| <Tab | ||
| label="Quick Picks" | ||
| value={PageTab['quick-picks']} | ||
| sx={{ textTransform: 'none', fontWeight: 500 }} | ||
| onClick={() => navigate(`/admin/${PageTab['quick-picks']}`)} | ||
| /> | ||
| <Tab | ||
| label="Global Templates" | ||
| value={PageTab['global-templates']} | ||
| sx={{ textTransform: 'none', fontWeight: 500 }} | ||
| onClick={() => navigate(`/admin/${PageTab['global-templates']}`)} |
There was a problem hiding this comment.
Using a no-op TabList onChange and relying on per-Tab onClick breaks expected keyboard tab behavior (arrow keys / selection) and can reduce accessibility. Prefer handling navigation in onChange (using newValue) and remove per-Tab onClick handlers so MUI Tabs behave correctly.
| <TabList onChange={() => {}} aria-label={`${pageTab} page`}> | |
| <Tab | |
| label="Schedules" | |
| value={PageTab.schedules} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| onClick={() => navigate(`/admin/${PageTab.schedules}`)} | |
| /> | |
| <Tab | |
| label="Virtual Locations" | |
| value={PageTab['virtual-locations']} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| onClick={() => navigate(`/admin/${PageTab['virtual-locations']}`)} | |
| /> | |
| <Tab | |
| label="Employees" | |
| value={PageTab.employees} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| onClick={() => navigate(`/admin/${PageTab.employees}`)} | |
| /> | |
| <Tab | |
| label="Providers" | |
| value={PageTab.providers} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| onClick={() => navigate(`/admin/${PageTab.providers}`)} | |
| /> | |
| <Tab | |
| label="Insurance" | |
| value={PageTab.insurance} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| onClick={() => navigate(`/admin/${PageTab.insurance}`)} | |
| /> | |
| <Tab | |
| label="Billing Configuration" | |
| value={PageTab.billing} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| onClick={() => navigate(`/admin/${PageTab.billing}`)} | |
| /> | |
| <Tab | |
| label="Quick Picks" | |
| value={PageTab['quick-picks']} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| onClick={() => navigate(`/admin/${PageTab['quick-picks']}`)} | |
| /> | |
| <Tab | |
| label="Global Templates" | |
| value={PageTab['global-templates']} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| onClick={() => navigate(`/admin/${PageTab['global-templates']}`)} | |
| <TabList | |
| onChange={(_, newValue) => navigate(`/admin/${newValue}`)} | |
| aria-label={`${pageTab} page`} | |
| > | |
| <Tab | |
| label="Schedules" | |
| value={PageTab.schedules} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| /> | |
| <Tab | |
| label="Virtual Locations" | |
| value={PageTab['virtual-locations']} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| /> | |
| <Tab | |
| label="Employees" | |
| value={PageTab.employees} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| /> | |
| <Tab | |
| label="Providers" | |
| value={PageTab.providers} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| /> | |
| <Tab | |
| label="Insurance" | |
| value={PageTab.insurance} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| /> | |
| <Tab | |
| label="Billing Configuration" | |
| value={PageTab.billing} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| /> | |
| <Tab | |
| label="Quick Picks" | |
| value={PageTab['quick-picks']} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} | |
| /> | |
| <Tab | |
| label="Global Templates" | |
| value={PageTab['global-templates']} | |
| sx={{ textTransform: 'none', fontWeight: 500 }} |
There was a problem hiding this comment.
Reply from Claude Code: The AdminPage tab structure was pre-existing — our merge resolution kept both sets of tabs (billing + global templates). The accessibility suggestion is valid and could be addressed in a follow-up.
There was a problem hiding this comment.
This code is from the develop merge, not changes in this PR.
This comment is from Claude (claude-opus-4-6).
| getOptionLabel={getOptionLabel as any} | ||
| isOptionEqualToValue={isOptionEqualToValue as any} | ||
| freeSolo={freeSolo} | ||
| onChange={(_e, option: any) => field.onChange(option ?? null)} |
There was a problem hiding this comment.
@mui/material's Autocomplete does not support a getOptionKey prop, and the added as any casts will hide type/prop issues (including potentially forwarding unknown props). Consider removing getOptionKey and instead ensuring stable keys via renderOption (or by normalizing options) and tightening types for freeSolo so the component doesn't accept incompatible prop combinations.
| onChange={(_e, option: any) => field.onChange(option ?? null)} | |
| onChange={(_e, option) => field.onChange(option ?? null)} |
There was a problem hiding this comment.
Reply from Claude Code: This came from the procedure naming branch merge (dabrams/otr-2077). The as any cast was needed because MUI Autocomplete types do not expose getOptionKey but it works at runtime for freeSolo mode. Can revisit in a follow-up.
There was a problem hiding this comment.
The getOptionKey prop is supported in our MUI version (v5.14+). The type casts were narrowed from any to explicit typed casts in a recent commit to address type safety. A few remain because MUI's Autocomplete widens the option type to string | Value when freeSolo is a boolean prop.
This comment is from Claude (claude-opus-4-6).
| {filteredQuickPicks.map((item, index) => ( | ||
| <MenuItem | ||
| key={getLabel(item)} | ||
| onClick={() => handleSelect(item)} | ||
| selected={searchable && index === highlightedIndex} | ||
| > | ||
| {getLabel(item)} | ||
| </MenuItem> | ||
| ))} |
There was a problem hiding this comment.
Using getLabel(item) as the React key can cause collisions if labels are duplicated (which can lead to incorrect item reuse/rendering). Prefer a stable unique identifier (e.g., an id field) or allow callers to pass a getKey function; falling back to index is safer than non-unique keys.
There was a problem hiding this comment.
Reply from Claude Code: This file was brought in via a merge from develop — not part of the exam changes. The key uniqueness concern is valid and noted for follow-up.
There was a problem hiding this comment.
Labels are unique within each quick pick category in practice. Switched from index to getLabel(item) to fix a React key stability issue when items are filtered via search.
This comment is from Claude (claude-opus-4-6).
| label="Order" | ||
| options={orderOptions.map((order) => order.id)} | ||
| getOptionLabel={(option) => orderOptions.find((opt) => opt.id === option)?.label ?? option} | ||
| getOptionLabel={(option) => orderOptions.find((opt) => opt.id === option)?.label ?? ''} |
There was a problem hiding this comment.
Returning an empty string when the option label can't be resolved can make the selected value appear blank and can also make the dropdown hard to use while options are loading. A safer fallback is to return option (or a short placeholder) so the UI remains intelligible even if orderOptions hasn't populated yet.
| getOptionLabel={(option) => orderOptions.find((opt) => opt.id === option)?.label ?? ''} | |
| getOptionLabel={(option) => orderOptions.find((opt) => opt.id === option)?.label ?? option} |
There was a problem hiding this comment.
Reply from Claude Code: This file was brought in via a merge from develop — not part of the exam changes. Good suggestion to return option as fallback instead of empty string.
There was a problem hiding this comment.
This code is from the develop merge, not changes in this PR.
This comment is from Claude (claude-opus-4-6).
| try { | ||
| if (GET_PAYMENT_LOCATIONS_ZAMBDA_ID == null) { | ||
| throw new Error('get-payment-locations zambda ID could not be loaded'); | ||
| } | ||
|
|
||
| const response = await oystehr.zambda.execute({ | ||
| id: GET_PAYMENT_LOCATIONS_ZAMBDA_ID, | ||
| }); | ||
| return chooseJson(response); | ||
| } catch (error: unknown) { | ||
| console.log(error); | ||
| throw error; | ||
| } |
There was a problem hiding this comment.
The *_ZAMBDA_ID == null checks are dead code since these IDs are hard-coded string constants; they add noise without providing safety. Also console.log in catch blocks makes error triage harder than console.error (and can be inconsistent with structured logging). Consider removing the null checks and logging with console.error or a centralized logger.
There was a problem hiding this comment.
Reply from Claude Code: This file was brought in via a merge from develop — not part of the exam changes. Agreed the null checks on hardcoded constants are dead code.
There was a problem hiding this comment.
This code is from the develop merge, not changes in this PR.
This comment is from Claude (claude-opus-4-6).
| export const INSURANCES_URL = '/admin/insurances'; | ||
| export const FEE_SCHEDULES_URL = '/admin/fee-schedule'; | ||
| export const CHARGE_MASTERS_URL = '/admin/charge-masters'; | ||
| export const VIRTUAL_LOCATIONS_URL = '/admin/virtual-locations'; | ||
| export const BILLING_URL = '/admin/billing'; | ||
| export const PAYMENT_LOCATIONS_URL = '/admin/billing/payments/locations'; | ||
| export const GLOBAL_TEMPLATES_URL = '/admin/global-templates'; |
There was a problem hiding this comment.
The PR description is focused on physical exam modal findings and exam data migration, but this PR also introduces a large /admin restructuring, billing/payment-location features, reporting changes, notifications refactors, and CI workflow updates. Please update the PR description (or split the PR) so reviewers and release notes accurately reflect the full scope of changes.
There was a problem hiding this comment.
Reply from Claude Code: Fair point — this PR includes merge commits from develop, feature/global-templates-admin, and dabrams/otr-2077 which bring in unrelated changes (billing, payment locations, admin restructuring, etc.). The exam-specific changes are in the two non-merge commits: ae1c86b and a6d6a88. The PR description focuses on the exam work which is the primary intent of this branch.
There was a problem hiding this comment.
The develop branch was merged into this feature branch, which brought in unrelated changes. The PR description and scope cover only the exam module improvements. The other changes are from develop and will appear in their own PRs.
This comment is from Claude (claude-opus-4-6).
… reorder skin modals - Add abnormal flag to ExamObservationComponentDTO and modal-exam config options - Classify modal options as normal/abnormal via NORMAL_LABELS set and opt() helper - Style indented sub-items with red/bold (abnormal) or green (normal) in exam tab, review tab, and PDF - Add hip special tests (FABER, FADIR, Log roll, Trendelenburg, Greater trochanter) - Remove Soft, Mobile, Non-tender from NORMAL_LABELS (not normal in lymph node context) - Move skin distribution pattern below location to group modifier modals together Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pdate-general-exam-with-new-sections-and-more-detail
- Unit tests (32): exam config structure, NORMAL_LABELS classification, extremity/lymph node/skin modal validation, L/R pairing, migration map contents, V0→V1 conversion, idempotency - Integration tests (4): component round-trip save/get, clear components, paired L/R independence, label persistence - E2E tests (6): single modal open/close, select/deselect items, paired L/R two-column layout, Review & Sign verification - Remove disablePortal from exam Dialog components (invisible to Playwright when rendered inside table cells) - Export migration functions for unit test access Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… flag, rectal findings, skin location reorder - Split Hand/fingers into Hand/Wrist (with wrist special tests) and Fingers (organized by joint: DIP, PIP, MCP) - Add legacy flag to ExamCardCheckboxComponent to hide deprecated fields unless existing data is present - Mark Swelling/tenderness/decreased ROM and Point tenderness over bone as legacy - Add left/right side checkboxes for scrotal edema and hernia - Add rectal abnormal findings (hemorrhoids, skin tags, fissure, abscess) - Move Location directly below Common skin findings in Skin section Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pdate-general-exam-with-new-sections-and-more-detail
…and exam config updates - Parent checkbox saves non-lateralized observation; modal saves L/R - Progress note consolidates as "Hand/Wrist L: Appearance: Swelling, Deformity; R: Tenderness: Joint" - Normal-only modal selections display as normal on progress note - Clearing all modal items unchecks parent - Skin modal shows description on hover; diagnosis labels show exam findings on note - Add legacy flag, rectal findings, skin location reorder, hand/wrist+fingers split - Register base keys for L/R pairs in exam field metadata Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pdate-general-exam-with-new-sections-and-more-detail
…into shared utilities - Extract extractObservationsFromExamComponents and collectKnownExamFields to packages/utils for shared use by review page, PDF, and admin templates - Add L/R paired modal-exam handling to PDF to match review page display - Extract buildAllOptions, buildDescriptionMap, buildAbnormalMap to exam-modal-helpers for ExamModalCheckbox and ExamPairedModalCheckbox - Move template detail types (TemplateExamFinding, TemplateCodeInfo, etc.) to packages/utils/lib/types/data/list-template.types.ts - Extract verifyIsTemplate and findHolderList to shared template-helpers for admin zambdas Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove duplicate template types from list-template.types.ts in favor of admin-template.types.ts from the release branch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
buildAbnormalFieldCodes and buildFieldLabels now handle modal-exam, dropdown, multi-select, form, and column types so template admin detail page correctly labels and classifies all exam findings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No thyroid conditions are asked about in this section. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
This branch was iterated on and merged in another PR #7030 |
Summary
Observation.component[]on a single parent observation rather than separate resources.code.text.code.textfrom FHIR for backward-compatible display of old findings.Test plan
🤖 Generated with Claude Code