Skip to content

Commit

Permalink
Revert "(feat) O3-4400 - render queue entry status in patient banner" (
Browse files Browse the repository at this point in the history
…#1505)

* Revert "(feat) O3-4400 - render queue entry status in patient banner (#1497)"

This reverts commit 06f8743.

* Update yarn.lock

* Update yarn.lock
  • Loading branch information
NethmiRodrigo authored Feb 27, 2025
1 parent e62d4f9 commit 1178d66
Show file tree
Hide file tree
Showing 16 changed files with 453 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const AddressComponent: React.FC = () => {
style={{ margin: '0', minWidth: '100%' }}
kind="error"
lowContrast={true}
title={t('errorFetchingOrderedFields', 'Error occurred fetching ordered fields for address hierarchy')}
title={t('errorFetchingOrderedFields', 'Error occured fetching ordered fields for address hierarchy')}
/>
</AddressComponentContainer>
);
Expand Down
13 changes: 5 additions & 8 deletions packages/esm-service-queues-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ export const editQueueEntryStatusModal = getAsyncLifecycle(
},
);

export const patientInfoBannerSlot = getAsyncLifecycle(() => import('./patient-info/patient-info.component'), {
featureName: 'patient info slot',
moduleName,
});

export const removeQueueEntry = getAsyncLifecycle(
() => import('./remove-queue-entry-dialog/remove-queue-entry.component'),
{
Expand Down Expand Up @@ -164,14 +169,6 @@ export const activeVisitsRowActions = getAsyncLifecycle(
},
);

export const patientBannerQueueEntryStatus = getAsyncLifecycle(
() => import('./patient-info/patient-banner-queue-entry-status.extension'),
{
featureName: 'patient-info-queue-entry-status',
moduleName,
},
);

export function startupApp() {
registerBreadcrumbs([]);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React from 'react';
import dayjs from 'dayjs';
import { useTranslation } from 'react-i18next';
import { InlineLoading } from '@carbon/react';
import { formatDatetime, parseDate, type Visit } from '@openmrs/esm-framework';
import { useAppointments } from './appointments.resource';
import styles from './appointment-details.scss';
import { usePastVisits } from '../past-visit/past-visit.resource';
import { type Appointment } from '../types';

interface AppointmentDetailsProps {
patientUuid: string;
}

interface PastAppointmentDetailsProps {
pastVisit: Visit;
isLoading: boolean;
}

interface UpcomingAppointmentDetailsProps {
upcomingAppointment: Appointment;
isLoading: boolean;
}

const PastAppointmentDetails: React.FC<PastAppointmentDetailsProps> = ({ pastVisit, isLoading }) => {
const { t } = useTranslation();

if (isLoading) {
return (
<div className={styles.widgetCard}>
<InlineLoading description={t('loading', 'Loading...')} role="progressbar" />
</div>
);
}

if (pastVisit) {
return (
<div className={styles.widgetCard}>
<React.Fragment>
<p className={styles.title}>{t('lastEncounter', 'Last encounter')}</p>
<p className={styles.subtitle}>
{formatDatetime(parseDate(pastVisit?.startDatetime))} ·{' '}
{pastVisit?.visitType ? pastVisit?.visitType?.display : '--'} ·{' '}
{pastVisit?.location ? pastVisit.location?.display : '--'}
</p>
</React.Fragment>
</div>
);
}
return (
<p className={styles.content}>{t('noLastEncounter', 'There is no last encounter to display for this patient')}</p>
);
};

const UpcomingAppointmentDetails: React.FC<UpcomingAppointmentDetailsProps> = ({ upcomingAppointment, isLoading }) => {
const { t } = useTranslation();

if (isLoading) {
return (
<div className={styles.widgetCard}>
<InlineLoading description={t('loading', 'Loading...')} role="progressbar" />
</div>
);
}

if (upcomingAppointment) {
return (
<div className={styles.widgetCard}>
<React.Fragment>
<p className={styles.title}>{t('returnDate', 'Return Date')}</p>
<p className={styles.subtitle}>
{formatDatetime(parseDate(upcomingAppointment?.startDateTime))} ·{' '}
{upcomingAppointment?.service ? upcomingAppointment?.service?.name : '--'} ·{' '}
{upcomingAppointment?.location ? upcomingAppointment?.location?.name : '--'}{' '}
</p>
</React.Fragment>
</div>
);
}

return <p className={styles.content}>{t('noReturnDate', 'There is no return date to display for this patient')}</p>;
};

const AppointmentDetails: React.FC<AppointmentDetailsProps> = ({ patientUuid }) => {
const { t } = useTranslation();
const startDate = dayjs(new Date().toISOString()).subtract(6, 'month').toISOString();
const { upcomingAppointment, isLoading } = useAppointments(patientUuid, startDate);
const { visits, isLoading: loading } = usePastVisits(patientUuid);

return (
<>
<PastAppointmentDetails pastVisit={visits} isLoading={loading} />
<UpcomingAppointmentDetails upcomingAppointment={upcomingAppointment} isLoading={isLoading} />
</>
);
};

export default AppointmentDetails;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@use '@carbon/colors';
@use '@carbon/layout';
@use '@carbon/type';
@use '@openmrs/esm-styleguide/src/vars' as *;

.widgetCard {
border: 1px solid colors.$blue-20;
border-left: none;
border-right: none;
background-color: colors.$blue-10;
padding: layout.$spacing-05;
}

.title {
@include type.type-style('heading-compact-01');
color: $text-02;
margin-bottom: layout.$spacing-02;
}

.subtitle {
color: $text-02;
@include type.type-style('body-compact-01');
}

.content {
@include type.type-style('heading-compact-01');
background-color: colors.$blue-10;
padding: layout.$spacing-05;
border: 1px solid colors.$blue-20;
}

.tile {
text-align: center;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { screen } from '@testing-library/react';
import { mockPatient, renderWithSwr } from 'tools';
import { useAppointments } from './appointments.resource';
import { usePastVisits } from './../past-visit/past-visit.resource';
import AppointmentDetails from './appointment-details.component';

const testProps = {
patientUuid: mockPatient.id,
};

const mockUseAppointments = jest.mocked(useAppointments);
const mockUsePastVisits = jest.mocked(usePastVisits);

jest.mock('./../past-visit/past-visit.resource', () => ({
usePastVisits: jest.fn(),
}));

jest.mock('./appointments.resource', () => ({
useAppointments: jest.fn(),
}));

describe('RecentandUpcomingAppointments', () => {
it('renders no data if past and upcoming visit is empty', async () => {
mockUseAppointments.mockReturnValueOnce({
upcomingAppointment: null,
error: null,
isLoading: false,
isValidating: false,
});
mockUsePastVisits.mockReturnValueOnce({ visits: null, error: null, isLoading: false, isValidating: false });
renderAppointments();

expect(screen.getByText(/there is no last encounter to display for this patient/i)).toBeInTheDocument();
expect(screen.getByText(/there is no return date to display for this patient/i)).toBeInTheDocument();
});
});

const renderAppointments = () => {
renderWithSwr(<AppointmentDetails {...testProps} />);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import dayjs from 'dayjs';
import useSWR from 'swr';
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
import { type AppointmentsFetchResponse } from '../types';

export const appointmentsSearchUrl = `${restBaseUrl}/appointments/search`;

export function useAppointments(patientUuid: string, startDate: string) {
const abortController = new AbortController();

const fetcher = () =>
openmrsFetch(appointmentsSearchUrl, {
method: 'POST',
signal: abortController.signal,
headers: {
'Content-Type': 'application/json',
},
body: {
patientUuid: patientUuid,
startDate: startDate,
},
});

const { data, error, isLoading, isValidating } = useSWR<AppointmentsFetchResponse, Error>(
appointmentsSearchUrl,
fetcher,
);

const appointments = data?.data?.length
? data.data.sort((a, b) => (b.startDateTime > a.startDateTime ? 1 : -1))
: null;

const upcomingAppointment = appointments?.find((appointment) =>
dayjs((appointment.startDateTime / 1000) * 1000).isAfter(dayjs()),
);

return {
upcomingAppointment: upcomingAppointment ? upcomingAppointment : null,
error,
isLoading,
isValidating,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { openmrsFetch, restBaseUrl, useConfig, type Patient } from '@openmrs/esm-framework';
import useSWRImmutable from 'swr/immutable';
import { type ConfigObject } from '../../config-schema';
import { type Attribute } from '../../types';

const customRepresentation =
'custom:(uuid,display,identifiers:(identifier,uuid,preferred,location:(uuid,name),identifierType:(uuid,name,format,formatDescription,validator)),person:(uuid,display,gender,birthdate,dead,age,deathDate,birthdateEstimated,causeOfDeath,preferredName:(uuid,preferred,givenName,middleName,familyName),attributes,preferredAddress:(uuid,preferred,address1,address2,cityVillage,longitude,stateProvince,latitude,country,postalCode,countyDistrict,address3,address4,address5,address6,address7)))';

/**
* React hook that takes a patientUuid and returns patient attributes {@link Attribute}
* @param patientUuid Unique patient identifier
* @returns An object containing patient attributes, an `isLoading` boolean and an `error` object
*/
export const usePatientAttributes = (patientUuid: string) => {
const { data, error, isLoading } = useSWRImmutable<{ data: Patient }>(
`${restBaseUrl}/patient/${patientUuid}?v=${customRepresentation}`,
openmrsFetch,
);

return {
isLoading,
attributes: data?.data.person.attributes ?? [],
error: error,
};
};
/**
* React hook that takes patientUuid {@link string} and return contact details
* derived from patient-attributes using configured attributeTypes
* @param patientUuid Unique patient identifier {@type string}
* @returns Object containing `contactAttribute` {@link Attribute} loading status
*/
export const usePatientContactAttributes = (patientUuid: string) => {
const { contactAttributeType } = useConfig<ConfigObject>();
const { attributes, isLoading } = usePatientAttributes(patientUuid);
const contactAttributes = attributes.filter(
({ attributeType }) => contactAttributeType?.some((uuid) => attributeType.uuid === uuid),
);
return {
contactAttributes: contactAttributes ?? [],
isLoading,
};
};

This file was deleted.

This file was deleted.

Loading

0 comments on commit 1178d66

Please sign in to comment.