Skip to content

Commit

Permalink
(feat) O3-2163: Add support for editing end visit data/time on curren…
Browse files Browse the repository at this point in the history
…t visits (#1712)
  • Loading branch information
usamaidrsk authored Nov 4, 2024
1 parent 11a4f2f commit d17d51d
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 42 deletions.
18 changes: 12 additions & 6 deletions e2e/specs/edit-existing-visit.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from '@playwright/test';
import { type Visit } from '@openmrs/esm-framework';
import { type Patient, generateRandomPatient, deletePatient, startVisit } from '../commands';
import { deletePatient, generateRandomPatient, type Patient, startVisit } from '../commands';
import { test } from '../core';
import { ChartPage, VisitsPage } from '../pages';

Expand All @@ -27,18 +27,24 @@ test('Edit an existing visit', async ({ page }) => {

await test.step('Then I should see the `Edit Visit` form launch in the workspace', async () => {
await expect(chartPage.page.getByText(/visit start date and time/i)).toBeVisible();
const datePickerInput = chartPage.page.getByPlaceholder(/dd\/mm\/yyyy/i);
await expect(datePickerInput).toBeVisible();
const dateValue = await datePickerInput.inputValue();

const startDateInput = chartPage.page.locator('input#visitStartDateInput');
const startTimeInput = chartPage.page.locator('input#visitStartTime');

await expect(startDateInput).toBeVisible();
const dateValue = await startDateInput.inputValue();
expect(dateValue).not.toBe('');
expect(dateValue).toMatch(/^\d{2}\/\d{2}\/\d{4}$/);

await expect(chartPage.page.getByPlaceholder(/hh\:mm/i)).toBeVisible();
await expect(startTimeInput).toBeVisible();
const timeValue = await startTimeInput.inputValue();
expect(timeValue).toMatch(/^(1[0-2]|0?[1-9]):([0-5][0-9])$/);

await expect(chartPage.page.getByRole('combobox', { name: /select a location/i })).toBeVisible();
await expect(chartPage.page.getByRole('combobox', { name: /select a location/i })).toHaveValue('Outpatient Clinic');
await expect(chartPage.page.getByText(/visit type/i)).toBeVisible();
await expect(chartPage.page.getByLabel(/facility visit/i)).toBeChecked();
await expect(chartPage.page.getByRole('search', { name: /search for a visit type/i })).toBeVisible();
await expect(chartPage.page.getByRole('search', { name: /visit type/i })).toBeVisible();
await expect(chartPage.page.getByLabel(/facility visit/i)).toBeVisible();
await expect(chartPage.page.getByLabel(/home visit/i)).toBeVisible();
await expect(chartPage.page.getByLabel(/opd visit/i)).toBeVisible();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
import {
ExtensionSlot,
formatDatetime,
type NewVisitPayload,
openmrsFetch,
restBaseUrl,
saveVisit,
Expand All @@ -39,15 +40,14 @@ import {
useSession,
useVisit,
useVisitTypes,
type NewVisitPayload,
type Visit,
} from '@openmrs/esm-framework';
import {
convertTime12to24,
createOfflineVisitForPatient,
type DefaultPatientWorkspaceProps,
time12HourFormatRegex,
useActivePatientEnrollment,
type DefaultPatientWorkspaceProps,
} from '@openmrs/esm-patient-common-lib';
import { type ChartConfig } from '../../config-schema';
import { type VisitFormData } from './visit-form.resource';
Expand Down Expand Up @@ -121,8 +121,8 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
});

const displayVisitStopDateTimeFields = useMemo(
() => Boolean(visitToEdit?.stopDatetime || showVisitEndDateTimeFields),
[visitToEdit?.stopDatetime, showVisitEndDateTimeFields],
() => Boolean(visitToEdit?.uuid || showVisitEndDateTimeFields),
[visitToEdit?.uuid, showVisitEndDateTimeFields],
);

const visitFormSchema = useMemo(() => {
Expand All @@ -148,6 +148,8 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
return new Date(visitStartDatetime) <= new Date();
};

const hadPreviousStopDateTime = Boolean(visitToEdit?.stopDatetime);

return z
.object({
visitStartDate: z.date().refine(
Expand All @@ -167,13 +169,18 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
.string()
.refine((value) => value.match(time12HourFormatRegex), t('invalidTimeFormat', 'Invalid time format')),
visitStartTimeFormat: z.enum(['PM', 'AM']),
visitStopDate: displayVisitStopDateTimeFields ? z.date() : z.date().optional(),
visitStopTime: displayVisitStopDateTimeFields
? z
.string()
.refine((value) => value.match(time12HourFormatRegex), t('invalidTimeFormat', 'Invalid time format'))
: z.string().optional(),
visitStopTimeFormat: displayVisitStopDateTimeFields ? z.enum(['PM', 'AM']) : z.enum(['PM', 'AM']).optional(),
visitStopDate: displayVisitStopDateTimeFields && hadPreviousStopDateTime ? z.date() : z.date().optional(),
visitStopTime:
displayVisitStopDateTimeFields && hadPreviousStopDateTime
? z.string().refine((value) => value.match(time12HourFormatRegex), t('invalidTimeFormat'))
: z
.string()
.refine((value) => !value || value.match(time12HourFormatRegex), t('invalidTimeFormat'))
.optional(),
visitStopTimeFormat:
displayVisitStopDateTimeFields && hadPreviousStopDateTime
? z.enum(['PM', 'AM'])
: z.enum(['PM', 'AM']).optional(),
programType: z.string().optional(),
visitType: z.string().refine((value) => !!value, t('visitTypeRequired', 'Visit type is required')),
visitLocation: z.object({
Expand All @@ -186,7 +193,7 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
message: t('futureStartTime', 'Visit start time cannot be in the future'),
path: ['visitStartTime'],
});
}, [t, config, displayVisitStopDateTimeFields]);
}, [config.visitAttributeTypes, visitToEdit?.stopDatetime, t, displayVisitStopDateTimeFields]);

const defaultValues = useMemo(() => {
const visitStartDate = visitToEdit?.startDatetime ? new Date(visitToEdit?.startDatetime) : new Date();
Expand Down Expand Up @@ -283,31 +290,33 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
const visitStopTime = getValues('visitStopTime');
const visitStopTimeFormat = getValues('visitStopTimeFormat');

const [visitStopHours, visitStopMinutes] = convertTime12to24(visitStopTime, visitStopTimeFormat);

const visitStopDatetime = visitStopDate.setHours(visitStopHours, visitStopMinutes);

if (minVisitStopDatetime && visitStopDatetime <= minVisitStopDatetime) {
validSubmission = false;
setError('visitStopDate', {
message: t(
'visitStopDateMustBeAfterMostRecentEncounter',
'Stop date needs to be on or after {{lastEncounterDatetime}}',
{
lastEncounterDatetime: new Date(minVisitStopDatetime).toLocaleString(),
interpolation: {
escapeValue: false,
if (visitStopDate && visitStopTime && visitStopTimeFormat) {
const [visitStopHours, visitStopMinutes] = convertTime12to24(visitStopTime, visitStopTimeFormat);

const visitStopDatetime = visitStopDate.setHours(visitStopHours, visitStopMinutes);

if (minVisitStopDatetime && visitStopDatetime <= minVisitStopDatetime) {
validSubmission = false;
setError('visitStopDate', {
message: t(
'visitStopDateMustBeAfterMostRecentEncounter',
'Stop date needs to be on or after {{lastEncounterDatetime}}',
{
lastEncounterDatetime: new Date(minVisitStopDatetime).toLocaleString(),
interpolation: {
escapeValue: false,
},
},
},
),
});
}
),
});
}

if (visitStartDatetime >= visitStopDatetime) {
validSubmission = false;
setError('visitStopDate', {
message: t('invalidVisitStopDate', 'Visit stop date time cannot be on or before visit start date time'),
});
if (visitStartDatetime >= visitStopDatetime) {
validSubmission = false;
setError('visitStopDate', {
message: t('invalidVisitStopDate', 'Visit stop date time cannot be on or before visit start date time'),
});
}
}

return validSubmission;
Expand Down Expand Up @@ -442,7 +451,7 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
delete payload.patient;
}

if (displayVisitStopDateTimeFields) {
if (displayVisitStopDateTimeFields && visitStopDate && visitStopTime && visitStopTimeFormat) {
const [visitStopHours, visitStopMinutes] = convertTime12to24(visitStopTime, visitStopTimeFormat);

payload.stopDatetime = toDateObjectStrict(
Expand Down

0 comments on commit d17d51d

Please sign in to comment.