Skip to content

Commit

Permalink
(feat) O3-3333: Include telephone number in excel sheet export (#1277)
Browse files Browse the repository at this point in the history
* Telephone Number should be included in Download

* Update index.ts

* Update index.ts

* update- Telephone Number should be included in Download

* Rename

* config to hide telephone numbers by default. return empty when no telephone record is found.

* comment added

* update- from the comments on the PR

* Fixup

---------

Co-authored-by: Dennis Kigen <kigen.work@gmail.com>
  • Loading branch information
lucyjemutai and denniskigen authored Aug 16, 2024
1 parent 0e32ba8 commit 5ae28d3
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
} from '@openmrs/esm-framework';
import { Download } from '@carbon/react/icons';
import { EmptyState } from '../../empty-state/empty-state.component';
import { downloadAppointmentsAsExcel } from '../../helpers/excel';
import { exportAppointmentsToSpreadsheet } from '../../helpers/excel';
import { useTodaysVisits } from '../../hooks/useTodaysVisits';
import { type Appointment } from '../../types';
import { type ConfigObject } from '../../config-schema';
Expand Down Expand Up @@ -154,7 +154,7 @@ const AppointmentsTable: React.FC<AppointmentsTableProps> = ({ appointments, isL
noToday: true,
})
: null;
downloadAppointmentsAsExcel(appointments, `${tableHeading}_appointments_${date}`);
exportAppointmentsToSpreadsheet(appointments, `${tableHeading}_appointments_${date}`);
}}>
{t('download', 'Download')}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { render, screen } from '@testing-library/react';
import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
import { type ConfigObject, configSchema } from '../../config-schema';
import type { Appointment, AppointmentKind, AppointmentStatus } from '../../types';
import { downloadAppointmentsAsExcel } from '../../helpers/excel';
import { exportAppointmentsToSpreadsheet } from '../../helpers/excel';
import { getByTextWithMarkup } from 'tools';
import AppointmentsTable from './appointments-table.component';

Expand Down Expand Up @@ -61,7 +61,7 @@ const mockAppointments = [
},
] as unknown as Array<Appointment>;

const mockDownloadAppointmentsAsExcel = jest.mocked(downloadAppointmentsAsExcel);
const mockExportAppointmentsToSpreadsheet = jest.mocked(exportAppointmentsToSpreadsheet);
const mockUseConfig = jest.mocked(useConfig<ConfigObject>);

jest.mock('../../helpers/excel');
Expand Down Expand Up @@ -122,7 +122,7 @@ describe('AppointmentsTable', () => {
const downloadButton = screen.getByRole('button', { name: /download/i });
await user.click(downloadButton);
expect(downloadButton).toBeInTheDocument();
expect(mockDownloadAppointmentsAsExcel).toHaveBeenCalledWith(mockAppointments, expect.anything());
expect(mockExportAppointmentsToSpreadsheet).toHaveBeenCalledWith(mockAppointments, expect.anything());
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import { Download } from '@carbon/react/icons';
import { ConfigurableLink, useConfig, usePagination } from '@openmrs/esm-framework';
import { useUnscheduledAppointments } from '../../hooks/useUnscheduledAppointments';
import { downloadUnscheduledAppointments } from '../../helpers/excel';
import { exportUnscheduledAppointmentsToSpreadsheet } from '../../helpers/excel';
import { EmptyState } from '../../empty-state/empty-state.component';
import { getPageSizes, useSearchResults } from '../utils';
import { type ConfigObject } from '../../config-schema';
Expand Down Expand Up @@ -99,7 +99,7 @@ const UnscheduledAppointments: React.FC = () => {
size="lg"
kind="tertiary"
renderIcon={Download}
onClick={() => downloadUnscheduledAppointments(unscheduledAppointments)}>
onClick={() => exportUnscheduledAppointmentsToSpreadsheet(unscheduledAppointments)}>
{t('download', 'Download')}
</Button>
</TableToolbarContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
import { type ConfigObject, configSchema } from '../../config-schema';
import { getByTextWithMarkup } from 'tools';
import { useUnscheduledAppointments } from '../../hooks/useUnscheduledAppointments';
import { downloadUnscheduledAppointments } from '../../helpers/excel';
import { exportUnscheduledAppointmentsToSpreadsheet } from '../../helpers/excel';
import UnscheduledAppointments from './unscheduled-appointments.component';

const mockDownloadAppointmentsAsExcel = jest.mocked(downloadUnscheduledAppointments);
const mockExportUnscheduledAppointmentsToSpreadsheet = jest.mocked(exportUnscheduledAppointmentsToSpreadsheet);
const mockUseUnscheduledAppointments = jest.mocked(useUnscheduledAppointments);
const mockUseConfig = jest.mocked(useConfig<ConfigObject>);

Expand Down Expand Up @@ -113,7 +113,7 @@ describe('UnscheduledAppointments', () => {
const downloadButton = await screen.findByText('Download');
expect(downloadButton).toBeInTheDocument();
await user.click(downloadButton);
expect(mockDownloadAppointmentsAsExcel).toHaveBeenCalledWith(mockUnscheduledAppointments);
expect(mockExportUnscheduledAppointmentsToSpreadsheet).toHaveBeenCalledWith(mockUnscheduledAppointments);
});

it('renders a message if there are no unscheduled appointments', async () => {
Expand Down
6 changes: 6 additions & 0 deletions packages/esm-appointments-app/src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { Type, restBaseUrl, validators } from '@openmrs/esm-framework';
import { spaHomePage } from './constants';

export const configSchema = {
includePhoneNumberInExcelSpreadsheet: {
_type: Type.Boolean,
_description: 'Whether to include phone numbers in the exported Excel spreadsheet',
_default: false,
},
allowAllDayAppointments: {
_type: Type.Boolean,
_description: 'Whether to allow scheduling of all-day appointments (vs appointments with start time and end time)',
Expand Down Expand Up @@ -146,4 +151,5 @@ export interface ConfigObject {
showUnscheduledAppointmentsTab: boolean;
useBahmniAppointmentsUI: boolean;
useFullViewPrivilege: boolean;
includePhoneNumberInExcelSpreadsheet: boolean;
}
46 changes: 31 additions & 15 deletions packages/esm-appointments-app/src/helpers/excel.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,49 @@
import { formatDate } from '@openmrs/esm-framework';
import * as XLSX from 'xlsx';
import { fetchCurrentPatient, formatDate, getConfig } from '@openmrs/esm-framework';
import { type Appointment } from '../types';
import { type ConfigObject } from '../config-schema';
import { moduleName } from '../constants';

/**
* Downloads the provided appointments as an Excel file.
* @param {Array<Appointment>} appointments - The list of appointments to download.
* Exports the provided appointments as an Excel spreadsheet.
* @param {Array<Appointment>} appointments - The list of appointments to export.
* @param {string} [fileName] - The name of the downloaded file
*/
export function downloadAppointmentsAsExcel(appointments: Array<Appointment>, fileName = 'Appointments') {
const appointmentsJSON = appointments?.map((appointment: Appointment) => ({
'Patient name': appointment.patient.name,
Gender: appointment.patient.gender === 'F' ? 'Female' : 'Male',
Age: appointment.patient.age,
Identifier: appointment.patient.identifier ?? '--',
'Appointment type': appointment.service?.name,
Date: formatDate(new Date(appointment.startDateTime), { mode: 'wide' }),
}));
export async function exportAppointmentsToSpreadsheet(appointments: Array<Appointment>, fileName = 'Appointments') {
const config = await getConfig<ConfigObject>(moduleName);
const includePhoneNumbers = config.includePhoneNumberInExcelSpreadsheet ?? false;

const appointmentsJSON = await Promise.all(
appointments.map(async (appointment: Appointment) => {
const patientInfo = await fetchCurrentPatient(appointment.patient.uuid);
const phoneNumber =
includePhoneNumbers && patientInfo?.telecom
? patientInfo.telecom.map((telecomObj) => telecomObj?.value).join(', ')
: '';

return {
'Patient name': appointment.patient.name,
Gender: appointment.patient.gender === 'F' ? 'Female' : 'Male',
Age: appointment.patient.age,
Identifier: appointment.patient.identifier ?? '--',
'Appointment type': appointment.service?.name,
Date: formatDate(new Date(appointment.startDateTime), { mode: 'wide' }),
...(includePhoneNumbers ? { 'Telephone number': phoneNumber } : {}),
};
}),
);

const worksheet = createWorksheet(appointmentsJSON);
const workbook = createWorkbook(worksheet, 'Appointment list');
XLSX.writeFile(workbook, `${fileName}.xlsx`, { compression: true });
}

/**
Downloads unscheduled appointments as an Excel file.
@param {Array<Object>} unscheduledAppointments - The list of unscheduled appointments to download.
Exports unscheduled appointments as an Excel spreadsheet.
@param {Array<Object>} unscheduledAppointments - The list of unscheduled appointments to export.
@param {string} fileName - The name of the file to download. Defaults to 'Unscheduled appointments {current date and time}'.
*/
export function downloadUnscheduledAppointments(
export function exportUnscheduledAppointmentsToSpreadsheet(
unscheduledAppointments: Array<any>,
fileName = `Unscheduled appointments ${formatDate(new Date(), { year: true, time: true })}`,
) {
Expand Down
1 change: 0 additions & 1 deletion packages/esm-appointments-app/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export enum AppointmentKind {
WALKIN = 'WalkIn',
VIRTUAL = 'Virtual',
}

// TODO: remove interface elements that aren't actually present on the Appointment object returned from the Appointment API
export interface Appointment {
appointmentKind: AppointmentKind;
Expand Down

0 comments on commit 5ae28d3

Please sign in to comment.