Skip to main content
Glama
QuestionnaireForm.tsx5.28 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { Group, Stack, Text, Title } from '@mantine/core'; import { createReference, getExtension, getReferenceString } from '@medplum/core'; import type { Encounter, Questionnaire, QuestionnaireResponse, Reference } from '@medplum/fhirtypes'; import { QUESTIONNAIRE_SIGNATURE_REQUIRED_URL, QUESTIONNAIRE_SIGNATURE_RESPONSE_URL, useMedplum, useQuestionnaireForm, } from '@medplum/react-hooks'; import type { JSX } from 'react'; import { useCallback, useMemo, useRef, useState } from 'react'; import { Form } from '../Form/Form'; import { SubmitButton } from '../Form/SubmitButton'; import { SignatureInput } from '../SignatureInput/SignatureInput'; import { QuestionnaireFormItemArray } from './QuestionnaireFormItemArray'; import { QuestionnaireFormStepper } from './QuestionnaireFormStepper'; export interface QuestionnaireFormProps { readonly questionnaire: Questionnaire | Reference<Questionnaire>; readonly questionnaireResponse?: QuestionnaireResponse | Reference<QuestionnaireResponse>; readonly subject?: Reference; readonly encounter?: Reference<Encounter>; readonly source?: QuestionnaireResponse['source']; readonly disablePagination?: boolean; readonly excludeButtons?: boolean; readonly submitButtonText?: string; readonly onChange?: (response: QuestionnaireResponse) => void; readonly onSubmit?: (response: QuestionnaireResponse) => void; } export function QuestionnaireForm(props: QuestionnaireFormProps): JSX.Element | null { const medplum = useMedplum(); const [signatureRequiredSubmitted, setSignatureRequiredSubmitted] = useState(false); const propsRef = useRef(props); propsRef.current = props; const onFormChange = useCallback((response: QuestionnaireResponse) => { setSignatureRequiredSubmitted(false); propsRef.current.onChange?.(response); }, []); const formState = useQuestionnaireForm({ questionnaire: props.questionnaire, defaultValue: props.questionnaireResponse, subject: props.subject, encounter: props.encounter, source: props.source, disablePagination: props.disablePagination, onChange: onFormChange, }); const formStateRef = useRef(formState); formStateRef.current = formState; const isSignatureRequired = useMemo(() => { if (formState.loading) { return false; } return !!getExtension(formState.questionnaire, QUESTIONNAIRE_SIGNATURE_REQUIRED_URL); }, [formState]); const hasSignature = useMemo(() => { if (formState.loading) { return false; } return !!formState.questionnaireResponse.extension?.find((ext) => ext.url === QUESTIONNAIRE_SIGNATURE_RESPONSE_URL); }, [formState]); const handleSubmit = useCallback(() => { const formState = formStateRef.current; if (formState.loading) { return; } const onSubmit = propsRef.current.onSubmit; if (!onSubmit) { return; } if (isSignatureRequired && !hasSignature) { setSignatureRequiredSubmitted(true); return; } const questionnaire = formState.questionnaire; const response = formState.questionnaireResponse; const subject = propsRef.current.subject; let source = propsRef.current.source; if (!source) { const profile = medplum.getProfile(); if (profile) { source = createReference(profile); } } onSubmit({ ...response, questionnaire: questionnaire.url ?? getReferenceString(questionnaire), subject, source, authored: new Date().toISOString(), status: 'completed', }); }, [medplum, isSignatureRequired, hasSignature]); if (formState.loading) { return null; } return ( <Form testid="questionnaire-form" onSubmit={handleSubmit}> {formState.questionnaire.title && <Title>{formState.questionnaire.title}</Title>} {formState.pagination ? ( <QuestionnaireFormStepper formState={formState} submitButtonText={props.submitButtonText} excludeButtons={props.excludeButtons} > <QuestionnaireFormItemArray formState={formState} context={[]} items={formState.items} responseItems={formState.responseItems} /> </QuestionnaireFormStepper> ) : ( <> <QuestionnaireFormItemArray formState={formState} context={[]} items={formState.items} responseItems={formState.responseItems} /> {isSignatureRequired && ( <Stack mt="md" gap={0}> <Text size="sm" fw={500}> Signature </Text> <SignatureInput onChange={formState.onChangeSignature} /> {!hasSignature && signatureRequiredSubmitted && ( <Text c="red" size="sm"> Signature is required. </Text> )} </Stack> )} {!props.excludeButtons && ( <Group justify="flex-end" mt="xl" gap="xs"> <SubmitButton>{props.submitButtonText ?? 'Submit'}</SubmitButton> </Group> )} </> )} </Form> ); }

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