Skip to main content
Glama
Labs.tsx6.43 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { Box, Flex, Group, Modal, Text } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { formatDate, getDisplayString } from '@medplum/core'; import type { CodeableConcept, DiagnosticReport, Patient, Resource, ServiceRequest } from '@medplum/fhirtypes'; import type { JSX } from 'react'; import { useState } from 'react'; import { DiagnosticReportDisplay } from '../DiagnosticReportDisplay/DiagnosticReportDisplay'; import { StatusBadge } from '../StatusBadge/StatusBadge'; import { CollapsibleSection } from './CollapsibleSection'; import SummaryItem from './SummaryItem'; import styles from './SummaryItem.module.css'; export interface LabsProps { readonly patient: Patient; readonly serviceRequests: ServiceRequest[]; readonly diagnosticReports: DiagnosticReport[]; readonly onClickResource?: (resource: Resource) => void; readonly onRequestLabs?: () => void; } export function Labs(props: LabsProps): JSX.Element { const { serviceRequests, diagnosticReports, onClickResource, onRequestLabs } = props; const [selectedReport, setSelectedReport] = useState<DiagnosticReport | undefined>(); const [reportDialogOpened, { open: openReportDialog, close: closeReportDialog }] = useDisclosure(false); // Get all Diagnostic Reports that are code LAB. // Build a set of all Service Requests that are based on these Diagnostic Reports. const diagnosticReportsRequests = new Set<string>(); const filteredDiagnosticReports = diagnosticReports.filter((report) => { const flag = isLaboratoryReport(report); if (flag && report.basedOn) { report.basedOn.forEach((basedOn) => { if (basedOn.reference?.startsWith('ServiceRequest/')) { const [, id] = basedOn.reference.split('/'); diagnosticReportsRequests.add(id); } }); } return flag; }); // Filter out Service Requests that are based on Diagnostic Reports. // Filter out multiple service requests with the same requisition number. const completedRequisitionNumbers = new Set<string>(); const filteredServiceRequests = serviceRequests.filter((request) => { if (request.id && diagnosticReportsRequests.has(request.id)) { return false; } // If the ServiceRequest is also based on a parent ServiceRequest, skip it. if (request.basedOn) { const basedOn = request.basedOn.find((basedOn) => { if (basedOn.reference?.startsWith('ServiceRequest/')) { const [, id] = basedOn.reference.split('/'); return diagnosticReportsRequests.has(id); } return false; }); if (basedOn) { return false; } } const shouldFilter = shouldFilterRequest(request, completedRequisitionNumbers); if (!shouldFilter && request.requisition?.value) { completedRequisitionNumbers.add(request.requisition?.value); } return !shouldFilter; }); const handleDiagnosticReportClick = (report: DiagnosticReport): void => { setSelectedReport(report); openReportDialog(); }; return ( <> <CollapsibleSection title="Labs" onAdd={() => onRequestLabs?.()}> <Flex direction="column" gap={8}> {filteredServiceRequests.map((serviceRequest) => ( <SummaryItem key={serviceRequest.id} onClick={() => onClickResource?.(serviceRequest)}> <Box> <Text fw={500} className={styles.itemText}> {getDisplayString(serviceRequest)} </Text> <Group mt={2} gap={4}> {serviceRequest.status && ( <StatusBadge color={getStatusColor(serviceRequest.status)} variant="light" status={serviceRequest.status} /> )} <Text size="xs" fw={500} c="dimmed"> {formatDate(serviceRequest.authoredOn)} </Text> </Group> </Box> </SummaryItem> ))} {filteredDiagnosticReports.map((report) => ( <SummaryItem key={report.id} onClick={() => handleDiagnosticReportClick(report)}> <Box> <Text fw={500} className={styles.itemText}> {getDisplayString(report)} </Text> <Group mt={2} gap={4}> {report.status && ( <StatusBadge color={getStatusColor(report.status)} variant="light" status={report.status} /> )} <Text size="xs" fw={500} c="dimmed"> {formatDate(report.issued)} </Text> </Group> </Box> </SummaryItem> ))} {filteredServiceRequests.length === 0 && filteredDiagnosticReports.length === 0 && <Text>(none)</Text>} </Flex> </CollapsibleSection> <Modal opened={reportDialogOpened} onClose={closeReportDialog} size="80%"> {selectedReport && <DiagnosticReportDisplay value={selectedReport} hideSubject={true} />} </Modal> </> ); } const getStatusColor = (status: string): string => { switch (status) { case 'active': return 'indigo'; case 'final': return 'teal'; case 'cancelled': return 'red'; default: return 'gray'; } }; function hasLaboratoryCategory(category: CodeableConcept): boolean { if (!category.coding || !Array.isArray(category.coding)) { return false; } for (const coding of category.coding) { if (coding.code === 'LAB') { return true; } } return false; } function isLaboratoryReport(report: DiagnosticReport): boolean { if (!report.category || !Array.isArray(report.category)) { return false; } for (const category of report.category) { if (hasLaboratoryCategory(category)) { return true; } } return false; } function shouldFilterRequest(request: ServiceRequest, completedRequisitionNumbers: Set<string>): boolean { if (['completed', 'draft', 'entered-in-error'].includes(request.status)) { return true; } const requisitionNumber = request.requisition?.value; if (requisitionNumber && completedRequisitionNumbers.has(requisitionNumber)) { return true; } return false; }

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