import { useState } from 'react'; import { Item, ItemGroup } from "../../../../components/ui/item-group"; import { useNavigate } from 'react-router-dom'; import { Icon } from "../../../../components/ui/icon"; import { Flex, FlexItem, FlexBlock } from '../../../../components/ui/flex'; import { Text } from '../../../../components/ui/text'; import { Spacer } from '../../../../components/ui/spacer'; // `info` is KEPT: it feeds the WordPress icon component on each row as well as the Help button. import { Pencil, Plus, Info as info } from 'lucide-react'; import { Procedure } from 'fhir/r4'; import { AppSpinner, FhirProcedureDisplay, ScrollableTab } from '../../../../components'; import { ResourceListStatus } from '../../../../components/ui/resource-list-status'; import { formatFhirCodeableConcept } from '../../../../utils/fhirCodeableConcept.tsx'; import { useFhirProcedureBroadcast, useFhirProcedures, useFhirProcedureTargetedBroadcast, useRequestRecordCorrection } from '../../../../hooks'; import { resolveProcedureAuthorReference } from '../../../../utils/messaging/requestCorrection'; import { useAuthServiceContext, useProcedureServiceContext } from '../../../../services'; import { mayEditRecord } from '../../../../utils/recordEditability'; import { useSeatStanding } from '../../../../utils/seatStanding'; import { SourcePractice } from '../../../../components/ui/source-practice'; import { RecordPracticeNote, recordPracticeId, useRecordPracticeView, useRetireOnPracticeChange } from '../../../../contexts/RecordPracticeContext'; const Procedures = () => { const navigate = useNavigate(); const { userRole, USER_ROLES, practitionerRoles } = useAuthServiceContext(); const seatStanding = useSeatStanding(); const { getProcedureDisplay } = useProcedureServiceContext(); const { loading, error, clearError, proceduresList, attempted } = useFhirProcedures(); const [selectedProcedure, setSelectedProcedure] = useState(null); const [showDisplayModal, setShowDisplayModal] = useState(false); // The record's practice context: its own procedures under a named practice; the empty state speaks for the whole record. const practiceView = useRecordPracticeView(proceduresList, recordPracticeId); useRetireOnPracticeChange(() => { setShowDisplayModal(false); setSelectedProcedure(null); }); useFhirProcedureBroadcast(); useFhirProcedureTargetedBroadcast({ procedure: selectedProcedure || undefined, isOpen: showDisplayModal, onUpdate: (updatedProcedure) => { setSelectedProcedure(updatedProcedure); }, onDelete: () => { setShowDisplayModal(false); setSelectedProcedure(null); } }); const viewProcedure = (procedure: Procedure) => { setSelectedProcedure(procedure); setShowDisplayModal(true); }; const editProcedure = (event: React.MouseEvent, procedure: Procedure) => { event.stopPropagation(); navigate(procedure.id as string, { relative: 'route' }); }; const addProcedure = () => { navigate('new', { relative: 'route' }); }; const roleAllowsEdit = () => { return userRole && [USER_ROLES.PRACTITIONER].includes(userRole as string); }; const isPatient = Boolean(userRole) && [USER_ROLES.PATIENT, USER_ROLES.RELATED_PERSON].includes(userRole as string); const { requestCorrection, error: correctionError, recovery: correctionRecovery } = useRequestRecordCorrection( selectedProcedure ? resolveProcedureAuthorReference(selectedProcedure) : undefined); const handleRequestCorrection = async (note: string) => { if (!selectedProcedure) return { ok: false }; const label = formatFhirCodeableConcept(selectedProcedure.code) || 'this procedure'; return requestCorrection(resolveProcedureAuthorReference(selectedProcedure), label, note); }; /* * AND THE ROLE IS NOT THE RULE THE SERVER APPLIES. * * Writing a Procedure needs a paid clinician seat, resolved per request by `callerMayWrite`. The role * check above passes for an unseated practitioner - a medical assistant, a nurse rooming patients, * somebody whose seat was turned off this morning - so the page offered Add and Edit, and the save * came back refused. A refusal that arrives after the typing has already spent the work. */ const seat = mayEditRecord({ seatStanding, resourceType: 'Procedure', viewerIsStaff: Boolean(userRole) && ![USER_ROLES.PATIENT, USER_ROLES.RELATED_PERSON].includes(userRole as string), practitionerRoles, }); const isAuthorizedToEdit = () => roleAllowsEdit() && seat.mayEdit; return (
{/* SAID, NOT WITHHELD. A control that simply disappears reads as a broken screen or as a permission the reader has lost, and both send them to an administrator to check permissions that are already correct. A seat is nameable, and a practice can enable one in a minute. */} {roleAllowsEdit() && !seat.mayEdit && (

{seat.reason}

)} {isAuthorizedToEdit() && ( )}
<> {loading ? ( ) : ( {practiceView.shown.length !== 0 && ( {practiceView.shown.map((procedure: Procedure) => { const displayTitle = getProcedureDisplay(procedure); const category = procedure.category ? formatFhirCodeableConcept(procedure.category) : ''; return ( viewProcedure(procedure)}> {displayTitle} {category && ( Category: {category} )} {isAuthorizedToEdit() && ( )} ); })} )} )} {selectedProcedure && ( { setShowDisplayModal(false); setSelectedProcedure(null); }} onRequestCorrection={isPatient ? handleRequestCorrection : undefined} correctionError={correctionError} correctionRecovery={correctionRecovery} /> )}
); }; export default Procedures;