import React, { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useForm, FormProvider } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Workspace2 } from '@openmrs/esm-framework'; import type { PatientWorkspace2DefinitionProps } from '@openmrs/esm-patient-common-lib'; import ProceduresForm from './procedures.form.component'; import type { Procedure } from '../../types'; export type ProceduresFormProps = { procedure?: Procedure; formContext: 'creating' | 'editing'; }; const createSchema = (t: (key: string, fallback: string) => string) => z .object({ procedureCoded: z.string().min(1, t('procedureRequired', 'A procedure is required')), procedureType: z.string().min(1, t('procedureTypeRequired', 'Procedure type is required')), bodySite: z.string().min(1, t('bodySiteRequired', 'Body site is required')), startDateTime: z.date().optional().nullable(), endDateTime: z.date().optional().nullable(), status: z.string().min(1, t('statusRequired', 'Status is required')), notes: z.string().optional(), estimatedStartDate: z.string().optional(), duration: z .number() .int() .positive(t('durationPositive', 'Duration must be a positive number')) .nullable() .optional(), durationUnit: z.string().optional().nullable(), }) .refine((data) => Boolean(data.startDateTime) || Boolean(data.estimatedStartDate), { message: t('startDateRequired', 'Start date is required'), path: ['startDateTime'], }) .refine( (data) => { if (!data.endDateTime) return true; if (data.startDateTime) { return data.endDateTime >= data.startDateTime; } if (data.estimatedStartDate) { const [year, month] = data.estimatedStartDate.split('-').map(Number); const start = new Date(year, Number.isFinite(month) ? month - 1 : 0, 1); return data.endDateTime >= start; } return true; }, { message: t('endDateAfterStart', 'End date must be on or after start date'), path: ['endDateTime'] }, ) .refine( (data) => { const hasDuration = data.duration != null; return !hasDuration || Boolean(data.durationUnit); }, { message: t('durationUnitRequired', 'Duration unit is required when a duration is provided'), path: ['durationUnit'], }, ); export type ProceduresFormSchema = z.infer>; const ProceduresFormWorkspace: React.FC> = ({ closeWorkspace, groupProps: { patientUuid }, workspaceProps: { procedure, formContext }, }) => { const { t } = useTranslation(); const schema = useMemo(() => createSchema(t), [t]); const methods = useForm({ mode: 'all', resolver: zodResolver(schema), defaultValues: { procedureCoded: procedure?.procedureCoded?.uuid ?? '', procedureType: procedure?.procedureType?.uuid ?? '', bodySite: procedure?.bodySite?.uuid ?? '', startDateTime: procedure?.startDateTime ? new Date(procedure.startDateTime) : null, endDateTime: procedure?.endDateTime ? new Date(procedure.endDateTime) : null, status: procedure?.status?.uuid ?? '', notes: procedure?.notes ?? '', estimatedStartDate: procedure?.estimatedStartDate ?? '', duration: typeof procedure?.duration === 'number' ? procedure.duration : null, durationUnit: procedure?.durationUnit?.uuid ?? '', }, }); const closeWorkspaceHandler = useCallback(() => { closeWorkspace({ discardUnsavedChanges: true }); }, [closeWorkspace]); return ( ); }; export default ProceduresFormWorkspace;