Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) O3-2163: Add support for editing end visit data/time on current visits #1712

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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