Skip to main content
Glama
PatientPage.tsx4.07 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { Loader, Modal, ScrollArea } from '@mantine/core'; import { getReferenceString, isOk } from '@medplum/core'; import type { OperationOutcome } from '@medplum/fhirtypes'; import { Document, OperationOutcomeAlert, PatientSummary, useMedplum } from '@medplum/react'; import { useCallback, useEffect, useState } from 'react'; import type { JSX } from 'react'; import { Outlet, useLocation, useNavigate } from 'react-router'; import type { Location } from 'react-router'; import { usePatient } from '../../hooks/usePatient'; import classes from './PatientPage.module.css'; import { formatPatientPageTabUrl, getPatientPageTabs } from './PatientPage.utils'; import type { PatientPageTabInfo } from './PatientPage.utils'; import { PatientTabsNavigation } from './PatientTabsNavigation'; import { OrderLabsPage } from '../labs/OrderLabsPage'; function getTabFromLocation(location: Location, tabs: PatientPageTabInfo[]): PatientPageTabInfo | undefined { const tabId = location.pathname.split('/')[3] ?? ''; const tab = tabId ? tabs.find((t) => t.id === tabId || t.url.toLowerCase().startsWith(tabId.toLowerCase())) : undefined; return tab; } export function PatientPage(): JSX.Element { const navigate = useNavigate(); const location = useLocation(); const medplum = useMedplum(); const membership = medplum.getProjectMembership(); const [outcome, setOutcome] = useState<OperationOutcome>(); const patient = usePatient({ setOutcome }); const [isLabsModalOpen, setIsLabsModalOpen] = useState(false); const tabs = getPatientPageTabs(membership); const [currentTab, setCurrentTab] = useState<string>(() => { return (getTabFromLocation(location, tabs) ?? tabs[0]).id; }); /** * Handles a tab change event. * @param newTabName - The new tab name. */ const onTabChange = useCallback( (newTabName: string | null): void => { if (!patient?.id) { console.error('Not within a patient context'); return; } const tab = newTabName ? tabs.find((t) => t.id === newTabName) : tabs[0]; if (tab) { setCurrentTab(tab.id); navigate(formatPatientPageTabUrl(patient.id, tab))?.catch(console.error); } }, [navigate, patient?.id, tabs] ); // Rectify the active tab UI with the current URL. This is necessary because the active tab can be changed // in ways other than clicking on a tab in the navigation bar. useEffect(() => { const newTab = getTabFromLocation(location, tabs); if (newTab && newTab.id !== currentTab) { setCurrentTab(newTab.id); } }, [currentTab, location, tabs]); const handleCloseLabsModal = useCallback(() => { setIsLabsModalOpen(false); }, []); if (outcome && !isOk(outcome)) { return ( <Document> <OperationOutcomeAlert outcome={outcome} /> </Document> ); } const patientId = patient?.id; if (!patientId) { return ( <Document> <Loader /> </Document> ); } return ( <> <div key={getReferenceString(patient)} className={classes.container}> <div className={classes.sidebar}> <ScrollArea className={classes.scrollArea}> <PatientSummary patient={patient} onClickResource={(resource) => navigate(`/Patient/${patientId}/${resource.resourceType}/${resource.id}`)?.catch(console.error) } onRequestLabs={() => { setIsLabsModalOpen(true); }} /> </ScrollArea> </div> <div className={classes.content}> <PatientTabsNavigation tabs={tabs} currentTab={currentTab} onTabChange={onTabChange} /> <Outlet /> </div> </div> <Modal opened={isLabsModalOpen} onClose={handleCloseLabsModal} size="xl" centered title="Order Labs"> <OrderLabsPage onSubmitLabOrder={handleCloseLabsModal} /> </Modal> </> ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server