Skip to content

Commit

Permalink
(fix) O3-4294: Fix incorrect visit date validation in edit mode in th…
Browse files Browse the repository at this point in the history
…e visit form (#2165)

* (fix) visit-form: Fix incorrect visit date validation

Removes a condition that was incorrectly bypassing start date validation when `displayVisitStopDateTimeFields` is true. Start dates should always be validated
to be on or before the current date, regardless of whether stop date fields
are displayed.

* Rename variable

* `maxVisitStartDatetime` should default to the current date
  • Loading branch information
denniskigen authored Jan 10, 2025
1 parent de7b6bd commit 3381e4e
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ const VisitDateTimeField: React.FC<VisitDateTimeFieldProps> = ({
<TimePickerSelect
aria-label={t('timeFormat ', 'Time Format')}
id={`${timeFormatFieldName}Input`}
invalid={Boolean(errors[timeFormatFieldName])}
invalidText={errors[timeFormatFieldName]?.message}
onChange={(event) => onChange(event.target.value as amPm)}
value={value}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,21 +271,6 @@ describe('Visit form', () => {
await user.click(screen.getByLabelText(/Outpatient visit/i));
});

it('displays an error message when the visit start date is in the future', async () => {
const user = userEvent.setup();

renderVisitForm();

const dateInput = screen.getByRole('textbox', { name: /date/i });
const futureDate = dayjs().add(1, 'month').format('DD/MM/YYYY');

await user.clear(dateInput);
await user.type(dateInput, futureDate);
await user.tab();

expect(screen.getByText(/start date needs to be on or before/i)).toBeInTheDocument();
});

// TODO: Figure out why this test is failing
xit('displays an error message when the visit start time is in the future', async () => {
const user = userEvent.setup();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import classNames from 'classnames';
import dayjs from 'dayjs';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
dayjs.extend(isSameOrBefore);
import { Controller, FormProvider, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import {
Button,
ButtonSet,
Expand All @@ -12,17 +21,17 @@ import {
Stack,
Switch,
} from '@carbon/react';
import { zodResolver } from '@hookform/resolvers/zod';
import {
type AssignedExtension,
Extension,
ExtensionSlot,
formatDatetime,
type NewVisitPayload,
saveVisit,
showSnackbar,
toDateObjectStrict,
toOmrsIsoString,
type AssignedExtension,
type NewVisitPayload,
type Visit,
updateVisit,
useConfig,
useConnectivity,
Expand All @@ -31,7 +40,6 @@ import {
usePatient,
useSession,
useVisit,
type Visit,
} from '@openmrs/esm-framework';
import {
convertTime12to24,
Expand All @@ -40,24 +48,11 @@ import {
time12HourFormatRegex,
useActivePatientEnrollment,
} from '@openmrs/esm-patient-common-lib';
import classNames from 'classnames';
import dayjs from 'dayjs';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Controller, FormProvider, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { type ChartConfig } from '../../config-schema';

import { useDefaultVisitLocation } from '../hooks/useDefaultVisitLocation';
import { useEmrConfiguration } from '../hooks/useEmrConfiguration';
import { useVisitAttributeTypes } from '../hooks/useVisitAttributeType';
import { useInfiniteVisits, useVisits } from '../visits-widget/visit.resource';
import BaseVisitType from './base-visit-type.component';
import LocationSelector from './location-selector.component';
import { MemoizedRecommendedVisitType } from './recommended-visit-type.component';
import VisitAttributeTypeFields from './visit-attribute-type.component';
import VisitDateTimeField from './visit-date-time.component';
import { useVisitAttributeTypes } from '../hooks/useVisitAttributeType';
import {
createVisitAttribute,
deleteVisitAttribute,
Expand All @@ -67,10 +62,13 @@ import {
type VisitFormCallbacks,
type VisitFormData,
} from './visit-form.resource';
import { MemoizedRecommendedVisitType } from './recommended-visit-type.component';
import BaseVisitType from './base-visit-type.component';
import LocationSelector from './location-selector.component';
import VisitAttributeTypeFields from './visit-attribute-type.component';
import VisitDateTimeField from './visit-date-time.component';
import styles from './visit-form.scss';

dayjs.extend(isSameOrBefore);

interface StartVisitFormProps extends DefaultPatientWorkspaceProps {
/**
* A unique string identifying where the visit form is opened from.
Expand Down Expand Up @@ -121,6 +119,7 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
const [extraVisitInfo, setExtraVisitInfo] = useState(null);

const [visitFormCallbacks, setVisitFormCallbacks] = useVisitFormCallbacks();

const displayVisitStopDateTimeFields = useMemo(
() => Boolean(visitToEdit?.uuid || showVisitEndDateTimeFields),
[visitToEdit?.uuid, showVisitEndDateTimeFields],
Expand Down Expand Up @@ -157,7 +156,8 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
(value) => {
const today = dayjs();
const startDate = dayjs(value);
return displayVisitStopDateTimeFields ? true : startDate.isSameOrBefore(today, 'day');

return startDate.isSameOrBefore(today, 'day');
},
t('invalidVisitStartDate', 'Start date needs to be on or before {{firstEncounterDatetime}}', {
firstEncounterDatetime: formatDatetime(new Date()),
Expand Down Expand Up @@ -253,15 +253,18 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
}, [isDirty, promptBeforeClosing]);

let [maxVisitStartDatetime, minVisitStopDatetime] = useMemo(() => {
const now = Date.now();

if (!visitToEdit?.encounters?.length) {
return [null, null];
return [now, null];
}

const allEncountersDateTime = visitToEdit?.encounters?.map(({ encounterDatetime }) =>
const allEncounterDatetimes = visitToEdit?.encounters?.map(({ encounterDatetime }) =>
Date.parse(encounterDatetime),
);
const maxVisitStartDatetime = Math.min(...allEncountersDateTime);
const minVisitStopDatetime = Math.max(...allEncountersDateTime);

const maxVisitStartDatetime = Math.min(...allEncounterDatetimes);
const minVisitStopDatetime = Math.max(...allEncounterDatetimes);
return [maxVisitStartDatetime, minVisitStopDatetime];
}, [visitToEdit]);

Expand Down

0 comments on commit 3381e4e

Please sign in to comment.