Skip to main content
Glama
BatchPage.tsx6.42 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { Anchor, Button, Group, JsonInput, Tabs, Text, Title, useMantineTheme } from '@mantine/core'; import type { FileWithPath } from '@mantine/dropzone'; import { Dropzone } from '@mantine/dropzone'; import { notifications } from '@mantine/notifications'; import { convertToTransactionBundle, normalizeErrorString } from '@medplum/core'; import type { Bundle } from '@medplum/fhirtypes'; import { Document, Form, useMedplum } from '@medplum/react'; import { IconCheck, IconUpload, IconX } from '@tabler/icons-react'; import type { JSX } from 'react'; import { useCallback, useState } from 'react'; const DEFAULT_VALUE = `{"resourceType": "Bundle"}`; interface ShowNotificationProps { readonly id: string; readonly title: string; readonly message: string; readonly color?: string; readonly icon?: JSX.Element | null; readonly withCloseButton?: boolean; readonly method?: 'show' | 'update'; readonly loading?: boolean; } function showNotification({ id, title, message, color, icon, withCloseButton = false, method = 'show', loading, }: ShowNotificationProps): void { notifications[method]({ id, loading, title, message, color, icon, withCloseButton, autoClose: false, }); } export function BatchPage(): JSX.Element { const theme = useMantineTheme(); const medplum = useMedplum(); const [output, setOutput] = useState<Record<string, Bundle>>({}); const submitBatch = useCallback( async (str: string, fileName: string) => { const id = 'batch-upload'; showNotification({ id, title: 'Batch Upload in Progress', message: 'Your batch data is being uploaded. This may take a moment...', method: 'show', loading: true, }); try { setOutput({}); let bundle = JSON.parse(str) as Bundle; if (bundle.type !== 'batch' && bundle.type !== 'transaction') { bundle = convertToTransactionBundle(bundle); } const result = await medplum.executeBatch(bundle); setOutput((prev) => ({ ...prev, [fileName]: result, })); showNotification({ id, title: 'Batch Upload Successful', message: 'Your batch data was successfully uploaded.', color: 'green', method: 'update', icon: <IconCheck size="1rem" />, withCloseButton: true, }); } catch (err) { showNotification({ id, title: 'Batch Upload Failed', color: 'red', message: normalizeErrorString(err), method: 'update', icon: <IconX size="1rem" />, withCloseButton: true, }); } }, [medplum] ); const handleFiles = useCallback( async (files: FileWithPath[]) => { for (const file of files) { const reader = new FileReader(); reader.onload = (e) => submitBatch(e.target?.result as string, file.name as string); reader.readAsText(file); } }, [submitBatch] ); const handleJson = useCallback( async (formData: Record<string, string>) => { await submitBatch(formData.input, 'JSON Data'); }, [submitBatch] ); return ( <Document> <Title>Batch Create</Title> <Text> Use this page to create, read, or update multiple resources. For more details, see{' '} <Anchor href="https://www.hl7.org/fhir/http.html#transaction">FHIR Batch and Transaction</Anchor>. </Text> {Object.keys(output).length === 0 && ( <> <h3>Input</h3> <Tabs defaultValue="upload"> <Tabs.List> <Tabs.Tab value="upload">File Upload</Tabs.Tab> <Tabs.Tab value="json">JSON</Tabs.Tab> </Tabs.List> <Tabs.Panel value="upload" pt="xs"> <Dropzone onDrop={handleFiles} accept={['application/json']}> <Group justify="center" gap="xl" style={{ minHeight: 220, pointerEvents: 'none' }}> <Dropzone.Accept> <IconUpload size={50} stroke={1.5} color={theme.colors[theme.primaryColor][5]} /> </Dropzone.Accept> <Dropzone.Reject> <IconX size={50} stroke={1.5} color={theme.colors.red[5]} /> </Dropzone.Reject> <Dropzone.Idle> <IconUpload size={50} stroke={1.5} /> </Dropzone.Idle> <div> <Text size="xl" inline> Drag files here or click to select files </Text> <Text size="sm" color="dimmed" inline mt={7}> Attach as many files as you like </Text> </div> </Group> </Dropzone> </Tabs.Panel> <Tabs.Panel value="json" pt="xs"> <Form onSubmit={handleJson}> <JsonInput data-testid="batch-input" name="input" autosize minRows={20} defaultValue={DEFAULT_VALUE} deserialize={JSON.parse} /> <Group justify="flex-end" mt="xl" wrap="nowrap"> <Button type="submit">Submit</Button> </Group> </Form> </Tabs.Panel> </Tabs> </> )} {Object.keys(output).length > 0 && ( <> <h3>Output</h3> <Tabs defaultValue={Object.keys(output)[0]}> <Tabs.List> {Object.keys(output).map((name) => ( <Tabs.Tab key={name} value={name}> {name} </Tabs.Tab> ))} </Tabs.List> {Object.keys(output).map((name) => ( <Tabs.Panel key={name} value={name}> <pre style={{ border: '1px solid #888' }}>{JSON.stringify(output[name], undefined, 2)}</pre> </Tabs.Panel> ))} </Tabs> <Group justify="flex-end" mt="xl" wrap="nowrap"> <Button onClick={() => setOutput({})}>Start over</Button> </Group> </> )} </Document> ); }

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