Skip to main content
Glama
HomePage.tsx9.21 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { Button, Code, Container, Group, Input, Radio, Stack, Text, TextInput } from '@mantine/core'; import { ContentType } from '@medplum/core'; import type { MedplumClient } from '@medplum/core'; import type { Bundle, Patient, Practitioner, ServiceRequest } from '@medplum/fhirtypes'; import { LabOrderValidationError, NPI_SYSTEM } from '@medplum/health-gorilla-core'; import type { BillingInformation, DiagnosisCodeableConcept, LabOrderInputErrors, TestCoding, } from '@medplum/health-gorilla-core'; import { HealthGorillaLabOrderProvider, useHealthGorillaLabOrder } from '@medplum/health-gorilla-react'; import { AsyncAutocomplete, DateTimeInput, Panel, ResourceInput, useMedplum, ValueSetAutocomplete, } from '@medplum/react'; import type { AsyncAutocompleteOption } from '@medplum/react'; import { useState } from 'react'; import type { JSX } from 'react'; import { CoverageInput } from '../components/CoverageInput'; import { PerformingLabInput } from '../components/PerformingLabInput'; import { TestMetadataCardInput } from '../components/TestMetadataCardInput'; async function sendLabOrderToHealthGorilla(medplum: MedplumClient, labOrder: ServiceRequest): Promise<void> { return medplum.executeBot( { system: 'https://www.medplum.com/integrations/bot-identifier', value: 'health-gorilla-labs/send-to-health-gorilla', }, labOrder, ContentType.FHIR_JSON ); } export function HomePage(): JSX.Element { const medplum = useMedplum(); const [patient, setPatient] = useState<Patient | undefined>(); const [requester, setRequester] = useState<Practitioner | undefined>(); const labOrderReturn = useHealthGorillaLabOrder({ patient, requester, }); const { state, searchAvailableTests, setTests, setDiagnoses, updateBillingInformation, setSpecimenCollectedDateTime, setOrderNotes, createOrderBundle, } = labOrderReturn; const [transactionResponse, setTransactionResponse] = useState<Bundle>(); const [labOrder, setLabOrder] = useState<ServiceRequest>(); const [createError, setCreateError] = useState<{ generic?: unknown; validation?: LabOrderInputErrors } | undefined>(); const handleCreateOrderBundle = async (): Promise<void> => { try { const { transactionResponse, serviceRequest } = await createOrderBundle(); setCreateError(undefined); setLabOrder(serviceRequest); setTransactionResponse(transactionResponse); } catch (err) { console.error(err); setLabOrder(undefined); setTransactionResponse(undefined); if (err instanceof LabOrderValidationError && err.errors) { setCreateError({ validation: err.errors }); } else { setCreateError({ generic: err }); } } }; function displayGenericError(error: unknown): React.ReactNode { if (error instanceof Error) { return error.toString(); } return String(error); } return ( <HealthGorillaLabOrderProvider {...labOrderReturn}> <Container size="md"> <Panel> <Stack gap="md"> <Input.Wrapper label="Requester" required error={createError?.validation?.requester?.message}> <ResourceInput<Practitioner> resourceType="Practitioner" name="Requester" onChange={setRequester} searchCriteria={{ identifier: `${NPI_SYSTEM}|` }} /> </Input.Wrapper> <Input.Wrapper label="Patient" required error={createError?.validation?.patient?.message}> <ResourceInput<Patient> resourceType="Patient" name="patient" onChange={setPatient} /> </Input.Wrapper> <PerformingLabInput patient={patient} error={createError?.validation?.performingLab} /> <div> <AsyncAutocomplete<TestCoding> required error={createError?.validation?.selectedTests?.message} label="Selected tests" disabled={!state.performingLab} maxValues={10} loadOptions={searchAvailableTests} toOption={TestCodingToOption} onChange={setTests} /> {state.selectedTests.length > 0 && ( <Group mt="md" gap="md" align="flex-start" wrap="wrap"> {state.selectedTests.map((test) => ( <TestMetadataCardInput key={test.code} test={test} metadata={state.testMetadata[test.code]} error={createError?.validation?.testMetadata?.[test.code]} /> ))} </Group> )} </div> <div> <ValueSetAutocomplete label="Diagnoses" binding="http://hl7.org/fhir/sid/icd-10-cm" name="diagnoses" maxValues={10} onChange={(items) => { const codeableConcepts = items.map((item) => ({ coding: [item] })) as DiagnosisCodeableConcept[]; setDiagnoses(codeableConcepts); }} /> </div> <Group align="flex-start" gap={48}> <div> <Radio.Group value={state.billingInformation.billTo} error={createError?.validation?.billingInformation?.billTo?.message} onChange={(newBillTo) => { updateBillingInformation({ billTo: newBillTo as BillingInformation['billTo'] }); }} label="Bill to" withAsterisk > <Stack gap={4}> <Radio value="patient" label="Patient" /> <Radio value="insurance" label="Insurance" /> <Radio value="self" label="Our Account" /> </Stack> </Radio.Group> </div> {patient && ( <CoverageInput patient={patient} error={createError?.validation?.billingInformation?.patientCoverage} /> )} </Group> <TextInput label="Order notes" onChange={(e) => setOrderNotes(e.currentTarget.value)} /> <DateTimeInput label="Specimen collection time" name="" onChange={(isoDateTimeString) => { setSpecimenCollectedDateTime(isoDateTimeString ? new Date(isoDateTimeString) : undefined); }} /> <div> <Group> <Button onClick={handleCreateOrderBundle} disabled={false}> Create order bundle </Button> <Button onClick={() => { if (labOrder) { sendLabOrderToHealthGorilla(medplum, labOrder) .then((response) => { console.log('Sent to HG response', response); }) .catch((error) => console.error('Error sending to HG', error)); } }} disabled={!labOrder} > Submit Order to Health Gorilla </Button> </Group> </div> <div> {labOrder && ( <> <Text fw={500} c="green"> Created Lab Order: </Text> <Code block>{JSON.stringify(labOrder, null, 2)}</Code> </> )} {!labOrder && transactionResponse && ( <> <Text fw={500} c="red"> Bundle transaction response: </Text> <Code block>{JSON.stringify(transactionResponse, null, 2)}</Code> </> )} {Boolean(createError?.generic) && ( <> <Text>Create bundle error:</Text> {displayGenericError(createError?.generic)} </> )} </div> <div> <Text>State:</Text> <Code block>{JSON.stringify(state, null, 2)}</Code> </div> <div> <Text>Patient:</Text> <Code block>{JSON.stringify(patient, null, 2)}</Code> </div> </Stack> </Panel> </Container> </HealthGorillaLabOrderProvider> ); } function TestCodingToOption(element: TestCoding): AsyncAutocompleteOption<TestCoding> { return { value: toKey(element), label: getDisplay(element), resource: element, }; } function toKey(element: TestCoding): string { if (typeof element.code === 'string') { return element.code; } return JSON.stringify(element); } function getDisplay(item: TestCoding): string { if (typeof item.display === 'string') { return item.display; } return toKey(item); }

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