import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; import { Button, ButtonSet, InlineLoading } from '@carbon/react'; import { I18nextProvider, useTranslation } from 'react-i18next'; import { useSession, type Visit } from '@openmrs/esm-framework'; import { FormFactoryProvider } from './provider/form-factory-provider'; import { init, teardown } from './lifecycle'; import { isEmpty, useFormJson } from '.'; import { formEngineAppName } from './globals'; import { reportError } from './utils/error-utils'; import { getDateWithinVisitWindow } from './utils/common-utils'; import { useFormCollapse } from './hooks/useFormCollapse'; import { useFormWorkspaceSize } from './hooks/useFormWorkspaceSize'; import { usePageObserver } from './components/sidebar/usePageObserver'; import { usePatientData } from './hooks/usePatientData'; import type { FormField, FormSchema, SessionMode, PreFilledQuestions } from './types'; import FormProcessorFactory from './components/processor-factory/form-processor-factory.component'; import Loader from './components/loaders/loader.component'; import MarkdownWrapper from './components/inputs/markdown/markdown-wrapper.component'; import PatientBanner from './components/patient-banner/patient-banner.component'; import Sidebar from './components/sidebar/sidebar.component'; import styles from './form-engine.scss'; interface FormEngineProps { patientUUID: string; formUUID?: string; formJson?: FormSchema; encounterUUID?: string; visit?: Visit; formSessionIntent?: string; mode?: SessionMode; onSubmit?: (data: any) => void; onCancel?: () => void; handleClose?: () => void; handleConfirmQuestionDeletion?: (question: Readonly) => Promise; markFormAsDirty?: (isDirty: boolean) => void; hideControls?: boolean; hidePatientBanner?: boolean; preFilledQuestions?: PreFilledQuestions; } const FormEngine = ({ formJson, patientUUID, formUUID, encounterUUID, visit, formSessionIntent, mode, onSubmit, onCancel, handleClose, handleConfirmQuestionDeletion, markFormAsDirty, hideControls = false, hidePatientBanner = false, preFilledQuestions, }: FormEngineProps) => { const { t } = useTranslation(); const session = useSession(); const ref = useRef(null); const rawSessionDate = useRef(new Date()); // Recompute when the visit bounds arrive or change; the visit prop may not be // fully loaded when the form mounts. const sessionDate = useMemo(() => { return getDateWithinVisitWindow(rawSessionDate.current, visit); }, [visit?.startDatetime, visit?.stopDatetime]); const workspaceSize = useFormWorkspaceSize(ref); const { patient, isLoadingPatient } = usePatientData(patientUUID); const [isLoadingDependencies, setIsLoadingDependencies] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [isFormDirty, setIsFormDirty] = useState(false); const sessionMode = !isEmpty(mode) ? mode : !isEmpty(encounterUUID) ? 'edit' : 'enter'; const { isFormExpanded, hideFormCollapseToggle } = useFormCollapse(sessionMode); const { hasMultiplePages } = usePageObserver(); const { formJson: refinedFormJson, isLoading: isLoadingFormJson, formError, } = useFormJson(formUUID, formJson, encounterUUID, formSessionIntent, preFilledQuestions); const showPatientBanner = useMemo(() => { if (hidePatientBanner) { return false; } return patient && workspaceSize === 'ultra-wide' && mode !== 'embedded-view'; }, [patient, mode, workspaceSize, hidePatientBanner]); const isFormWorkspaceTooNarrow = useMemo(() => ['narrow'].includes(workspaceSize), [workspaceSize]); const showBottomButtonSet = useMemo(() => { if (mode === 'embedded-view' || isLoadingDependencies || hasMultiplePages === null) { return false; } return isFormWorkspaceTooNarrow || !hasMultiplePages; }, [mode, isFormWorkspaceTooNarrow, isLoadingDependencies, hasMultiplePages]); const showSidebar = useMemo(() => { if (mode === 'embedded-view' || isLoadingDependencies || hasMultiplePages === null) { return false; } return !isFormWorkspaceTooNarrow && hasMultiplePages; }, [isFormWorkspaceTooNarrow, isLoadingDependencies, hasMultiplePages]); useEffect(() => { reportError(formError, t('errorLoadingFormSchema', 'Error loading form schema')); }, [formError]); useEffect(() => { init(); return () => { teardown(); }; }, []); useEffect(() => { markFormAsDirty?.(isFormDirty); }, [isFormDirty]); const handleSubmit = useCallback((e: React.FormEvent) => { e.preventDefault(); setIsSubmitting(true); }, []); return (
{isLoadingPatient || isLoadingFormJson ? ( ) : ( {}, handleClose: () => {}, }} hideFormCollapseToggle={hideFormCollapseToggle} setIsFormDirty={setIsFormDirty}>
{isLoadingDependencies && (
)}
{showSidebar && ( )}
{showPatientBanner && } {refinedFormJson.markdown && (
)}
{showBottomButtonSet && !hideControls && ( )}
)} ); }; function I18FormEngine(props: FormEngineProps) { return ( ); } export default I18FormEngine;