Skip to main content
Glama
TaskQuestionnaireForm.tsx5.73 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { Box, Group, Skeleton, Stack } from '@mantine/core'; import { createReference, normalizeOperationOutcome } from '@medplum/core'; import type { OperationOutcome, Questionnaire, QuestionnaireResponse, Reference, Task } from '@medplum/fhirtypes'; import { OperationOutcomeAlert, QuestionnaireForm, QuestionnaireResponseDisplay, useMedplum, useMedplumProfile, } from '@medplum/react'; import { useEffect, useState } from 'react'; import type { JSX } from 'react'; import { showErrorNotification } from '../../../utils/notifications'; interface TaskQuestionnaireFormProps { task: Task; onChangeResponse?: (response: QuestionnaireResponse) => void; } export const TaskQuestionnaireForm = ({ task, onChangeResponse }: TaskQuestionnaireFormProps): JSX.Element => { const medplum = useMedplum(); const author = useMedplumProfile(); const [questionnaire, setQuestionnaire] = useState<Questionnaire | undefined>(undefined); const [questionnaireResponse, setQuestionnaireResponse] = useState<QuestionnaireResponse | undefined>(undefined); const [loading, setLoading] = useState(false); const [outcome, setOutcome] = useState<OperationOutcome | undefined>(undefined); useEffect(() => { const updateQuestionnaireResponse = async (): Promise<void> => { if (questionnaireResponse && task.status === 'completed' && questionnaireResponse?.status !== 'completed') { const updatedResponse: QuestionnaireResponse = { ...questionnaireResponse, status: 'completed', }; await medplum.updateResource(updatedResponse); } }; updateQuestionnaireResponse().catch(showErrorNotification); }, [task, questionnaireResponse, medplum]); const onChange = (response: QuestionnaireResponse): void => { const baseResponse = questionnaireResponse || response; const updatedResponse: QuestionnaireResponse = { ...baseResponse, item: response.item, status: 'in-progress', authored: new Date().toISOString(), source: author && createReference(author), encounter: task.encounter, }; onChangeResponse?.(updatedResponse); }; useEffect(() => { const fetchResources = async (): Promise<void> => { const questionnaireReference = task.input?.[0]?.valueReference as Reference<Questionnaire>; const questionnaireResponseReference = task.output?.[0]?.valueReference as Reference<QuestionnaireResponse>; if (questionnaireResponseReference) { const response = await medplum.readReference(questionnaireResponseReference); setQuestionnaireResponse(response as QuestionnaireResponse); } if (questionnaireReference) { const questionnaireResponse = await medplum.readReference(questionnaireReference); setQuestionnaire(questionnaireResponse as Questionnaire); } }; setOutcome(undefined); setLoading(true); fetchResources() .catch((err) => { setOutcome(normalizeOperationOutcome(err)); }) .finally(() => setLoading(false)); }, [medplum, task]); if (loading) { return <QuestionnaireSkeleton />; } return ( <Box p={0}> {task.status !== 'completed' && questionnaire && ( <QuestionnaireForm questionnaire={questionnaire} questionnaireResponse={questionnaireResponse} excludeButtons={true} onChange={onChange} /> )} {task.status === 'completed' && questionnaireResponse && ( <QuestionnaireResponseDisplay questionnaireResponse={questionnaireResponse} /> )} {outcome && <OperationOutcomeAlert outcome={outcome} />} </Box> ); }; const QuestionnaireSkeleton = (): JSX.Element => ( <Stack gap="lg"> {/* Form title/header */} <Stack gap="xs"> <Skeleton height={24} width="60%" /> <Skeleton height={16} width="85%" /> </Stack> {/* Question group 1 */} <Stack gap="sm"> <Skeleton height={18} width="45%" /> <Group gap="md"> <Skeleton height={20} width={20} radius="xl" /> <Skeleton height={16} width="15%" /> <Skeleton height={20} width={20} radius="xl" /> <Skeleton height={16} width="12%" /> </Group> </Stack> {/* Question group 2 */} <Stack gap="sm"> <Skeleton height={18} width="38%" /> <Skeleton height={36} width="100%" radius="md" /> </Stack> {/* Question group 3 */} <Stack gap="sm"> <Skeleton height={18} width="52%" /> <Stack gap="xs"> <Group gap="sm"> <Skeleton height={16} width={16} radius="sm" /> <Skeleton height={14} width="25%" /> </Group> <Group gap="sm"> <Skeleton height={16} width={16} radius="sm" /> <Skeleton height={14} width="30%" /> </Group> <Group gap="sm"> <Skeleton height={16} width={16} radius="sm" /> <Skeleton height={14} width="22%" /> </Group> </Stack> </Stack> {/* Text area question */} <Stack gap="sm"> <Skeleton height={18} width="42%" /> <Skeleton height={80} width="100%" radius="md" /> </Stack> {/* Another radio group */} <Stack gap="sm"> <Skeleton height={18} width="48%" /> <Group gap="md"> <Skeleton height={20} width={20} radius="xl" /> <Skeleton height={16} width="18%" /> <Skeleton height={20} width={20} radius="xl" /> <Skeleton height={16} width="20%" /> <Skeleton height={20} width={20} radius="xl" /> <Skeleton height={16} width="16%" /> </Group> </Stack> </Stack> );

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