Skip to content

Commit

Permalink
Merge branch 'main' into O3-4045
Browse files Browse the repository at this point in the history
  • Loading branch information
denniskigen authored Oct 2, 2024
2 parents 879a09d + d4f6f05 commit aa3ca0c
Show file tree
Hide file tree
Showing 29 changed files with 260 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,12 @@ const PatientInfo: React.FC<PatientInfoProps> = ({ patient }) => {

const name = patient ? getPatientName(patient) : '';
const patientUuid = `${patient?.id}`;
const { currentVisit } = useVisit(patientUuid);
const patientNameIsTooLong = !isTablet && name.trim().length > 25;
const { currentVisit } = useVisit(patientUuid);
const { queueEntry } = useVisitQueueEntry(patientUuid, currentVisit?.uuid);

const visitType = queueEntry?.visitType ?? '';
const priority = queueEntry?.priority ?? '';

const getServiceString = useCallback(() => {
if (queueEntry?.status && queueEntry.service) {
return `${t(queueEntry.status)} - ${t(queueEntry.service)}`;
Expand Down Expand Up @@ -93,7 +92,12 @@ const PatientInfo: React.FC<PatientInfoProps> = ({ patient }) => {
) : (
<span className={styles.patientName}>{name} </span>
)}
<span className={styles.patientInfo}>{`${age(patient?.birthDate)}, ${getGender(patient?.gender)}`}</span>

<span className={styles.patientInfo}>
{patient?.birthDate ? `${age(patient.birthDate)}, ` : ''}
{patient?.gender ? getGender(patient.gender) : ''}
</span>

{queueEntry && (
<>
<div className={styles.navDivider} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
}

.navLogo {
margin-right: layout.$spacing-06;
margin-right: layout.$spacing-05;
margin-left: layout.$spacing-04;
}

Expand All @@ -34,7 +34,7 @@
display: flex;
align-items: center;
color: $ui-02;
padding: 0 layout.$spacing-03;
padding: 0 layout.$spacing-05;

:global(.cds--popover--bottom .cds--popover-content) {
text-align: center;
Expand Down Expand Up @@ -65,7 +65,7 @@
.patientInfo {
color: $color-gray-30;
@include type.type-style('body-compact-01');
margin: 0 layout.$spacing-03 0;
margin: 0 layout.$spacing-05 0;
}
}

Expand All @@ -80,9 +80,9 @@
}

.navDivider {
background-color: colors.$teal-20;
background-color: var(--brand-03);
width: 0.05rem;
height: 2.25rem;
height: 1.5rem;
}

.tag {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { type ComponentProps, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Button } from '@carbon/react';
import { EditIcon, showModal, useLayoutType } from '@openmrs/esm-framework';
import styles from './edit-queue-entry.scss';
import { type MappedVisitQueueEntry } from './queue.resource';
import styles from './edit-queue-entry.scss';

interface EditQueueEntryProps {
queueEntry: MappedVisitQueueEntry;
Expand All @@ -23,7 +23,7 @@ export const EditQueueEntry: React.FC<EditQueueEntryProps> = ({ queueEntry }) =>
<Button
className={styles.editStatusBtn}
onClick={launchEditPriorityModal}
size={isTablet ? 'sm' : 'md'}
size={isTablet ? 'sm' : 'lg'}
iconDescription={t('movePatientToNextService', 'Move patient to next service')}
renderIcon={(props: ComponentProps<typeof EditIcon>) => (
<EditIcon className={styles.editStatusIcon} size={16} {...props} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,18 @@ const VisitDateTimeField: React.FC<VisitDateTimeFieldProps> = ({
dateFormat="d/m/Y"
datePickerType="single"
id={dateFieldName}
minDate={minDateObj}
maxDate={maxDateObj}
minDate={minDateObj}
onChange={([date]) => onChange(date)}
value={value ? dayjs(value).format('DD/MM/YYYY') : null}
>
<DatePickerInput
id={`${dateFieldName}Input`}
invalid={Boolean(errors[dateFieldName])}
invalidText={errors[dateFieldName]?.message}
labelText={t('date', 'Date')}
placeholder="dd/mm/yyyy"
style={{ width: '100%' }}
invalid={!!errors[dateFieldName]}
invalidText={errors[dateFieldName]?.message}
/>
</DatePicker>
</ResponsiveWrapper>
Expand All @@ -78,26 +78,26 @@ const VisitDateTimeField: React.FC<VisitDateTimeFieldProps> = ({
render={({ field: { onBlur, onChange, value } }) => (
<TimePicker
id={timeFieldName}
invalid={Boolean(errors[timeFieldName])}
invalidText={errors[timeFieldName]?.message}
labelText={t('time', 'Time')}
onBlur={onBlur}
onChange={(event) => onChange(event.target.value as amPm)}
pattern="^(1[0-2]|0?[1-9]):([0-5]?[0-9])$"
style={{ marginLeft: '0.125rem', flex: 'none' }}
value={value}
onBlur={onBlur}
invalid={!!errors[timeFieldName]}
invalidText={errors[timeFieldName]?.message}
>
<Controller
name={timeFormatFieldName}
control={control}
render={({ field: { onChange, value } }) => (
<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}
aria-label={t('timeFormat ', 'Time Format')}
invalid={!!errors[timeFormatFieldName]}
invalidText={errors[timeFormatFieldName]?.message}
>
<SelectItem value="AM" text="AM" />
<SelectItem value="PM" text="PM" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,58 +121,71 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
});

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

const visitFormSchema = useMemo(() => {
const createVisitAttributeSchema = (required: boolean) =>
required
? z.string({
required_error: t('fieldRequired', 'This field is required'),
})
: z.string().optional();

const visitAttributes = (config.visitAttributeTypes ?? [])?.reduce(
(acc, { uuid, required }) => ({
...acc,
[uuid]: required
? z
.string({
required_error: t('fieldRequired', 'This field is required'),
})
.refine((value) => !!value, t('fieldRequired', 'This field is required'))
: z.string().optional(),
[uuid]: createVisitAttributeSchema(required),
}),
{},
);

return z.object({
visitStartDate: z.date().refine(
(value) => {
const today = dayjs();
const startDate = dayjs(value);
return displayVisitStopDateTimeFields ? true : startDate.isSameOrBefore(today, 'day');
},
t('invalidVisitStartDate', 'Start date needs to be on or before {{firstEncounterDatetime}}', {
firstEncounterDatetime: formatDatetime(new Date()),
interpolation: {
escapeValue: false,
// Validates that the start time is not in the future
const validateStartTime = (data: z.infer<typeof visitFormSchema>) => {
const [visitStartHours, visitStartMinutes] = convertTime12to24(data.visitStartTime, data.visitStartTimeFormat);
const visitStartDatetime = new Date(data.visitStartDate).setHours(visitStartHours, visitStartMinutes);
return new Date(visitStartDatetime) <= new Date();
};

return z
.object({
visitStartDate: z.date().refine(
(value) => {
const today = dayjs();
const startDate = dayjs(value);
return displayVisitStopDateTimeFields ? true : startDate.isSameOrBefore(today, 'day');
},
t('invalidVisitStartDate', 'Start date needs to be on or before {{firstEncounterDatetime}}', {
firstEncounterDatetime: formatDatetime(new Date()),
interpolation: {
escapeValue: false,
},
}),
),
visitStartTime: z
.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(),
programType: z.string().optional(),
visitType: z.string().refine((value) => !!value, t('visitTypeRequired', 'Visit type is required')),
visitLocation: z.object({
display: z.string(),
uuid: z.string(),
}),
),
visitStartTime: z
.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(),
programType: z.string().optional(),
visitType: z.string().refine((value) => !!value, t('visitTypeRequired', 'Visit type is required')),
visitLocation: z.object({
display: z.string(),
uuid: z.string(),
}),
visitAttributes: z.object(visitAttributes),
});
visitAttributes: z.object(visitAttributes),
})
.refine((data) => validateStartTime(data), {
message: t('futureStartTime', 'Visit start time cannot be in the future'),
path: ['visitStartTime'],
});
}, [t, config, displayVisitStopDateTimeFields]);

const defaultValues = useMemo(() => {
Expand Down Expand Up @@ -386,7 +399,7 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
);

const onSubmit = useCallback(
(data: VisitFormData, event) => {
(data: VisitFormData) => {
if (visitToEdit && !validateVisitStartStopDatetime()) {
return;
}
Expand Down Expand Up @@ -464,45 +477,41 @@ const StartVisitForm: React.FC<StartVisitFormProps> = ({
.pipe(first())
.subscribe({
next: (response) => {
if (response.status === 201) {
if (config.showServiceQueueFields && queueLocation && service && priority) {
// retrieve values from the queue extension
setVisitUuid(response.data.uuid);

saveQueueEntry(
response.data.uuid,
service,
patientUuid,
priority,
status,
sortWeight,
queueLocation,
visitQueueNumberAttributeUuid,
abortController,
).then(
({ status }) => {
if (status === 201) {
mutateCurrentVisit();
mutateVisits();
mutateInfiniteVisits();
mutateQueueEntry();
showSnackbar({
kind: 'success',
title: t('visitStarted', 'Visit started'),
subtitle: t('queueAddedSuccessfully', `Patient added to the queue successfully.`),
});
}
},
(error) => {
showSnackbar({
title: t('queueEntryError', 'Error adding patient to the queue'),
kind: 'error',
isLowContrast: false,
subtitle: error?.message,
});
},
);
}
if (config.showServiceQueueFields && queueLocation && service && priority) {
// retrieve values from the queue extension
setVisitUuid(response.data.uuid);

saveQueueEntry(
response.data.uuid,
service,
patientUuid,
priority,
status,
sortWeight,
queueLocation,
visitQueueNumberAttributeUuid,
abortController,
).then(
({ status }) => {
mutateCurrentVisit();
mutateVisits();
mutateInfiniteVisits();
mutateQueueEntry();
showSnackbar({
kind: 'success',
title: t('visitStarted', 'Visit started'),
subtitle: t('queueAddedSuccessfully', `Patient added to the queue successfully.`),
});
},
(error) => {
showSnackbar({
title: t('queueEntryError', 'Error adding patient to the queue'),
kind: 'error',
isLowContrast: false,
subtitle: error?.message,
});
},
);

if (config.showUpcomingAppointments && upcomingAppointment) {
updateAppointmentStatus('CheckedIn', upcomingAppointment.uuid, abortController).then(
Expand Down
Loading

0 comments on commit aa3ca0c

Please sign in to comment.