Skip to content

Commit

Permalink
Add sms-form to SGS O3 distro (#1)
Browse files Browse the repository at this point in the history
* Initial commit

* Fix tests

* Refine send sms components and add tests

* Fix version number and remove redundant imports

---------

Co-authored-by: Samuel Male <[email protected]>
  • Loading branch information
miirochristopher and samuelmale authored Oct 28, 2024
1 parent 5193fe0 commit 6b7bb67
Show file tree
Hide file tree
Showing 33 changed files with 28,555 additions and 4,472 deletions.
22,200 changes: 22,200 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"eslint-plugin-testing-library": "^6.2.2",
"husky": "^8.0.3",
"i18next": "^21.10.0",
"i18next-parser": "^6.6.0",
"i18next-parser": "^9.0.2",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-cli": "^29.7.0",
Expand Down
6 changes: 6 additions & 0 deletions packages/esm-patient-outcomes-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# esm-patient-outcomes-app

The patient reported outcomes widget. It provides a form for sending the Patient Reported Outcomes (PRO) Questionnaire to a patient through a link sent via SMS.

The form collects health outcomes directly reported by the patient who experienced them.

3 changes: 3 additions & 0 deletions packages/esm-patient-outcomes-app/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const rootConfig = require('../../jest.config.js');

module.exports = rootConfig;
59 changes: 59 additions & 0 deletions packages/esm-patient-outcomes-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "@i-tech-uw/esm-patient-outcomes-app",
"version": "1.0.0",
"license": "MPL-2.0",
"description": "Patient reported outcomes microfrontend for the OpenMRS SPA",
"browser": "dist/esm-patient-outcomes-app.js",
"main": "src/index.ts",
"source": true,
"scripts": {
"start": "openmrs develop",
"serve": "webpack serve --mode=development",
"debug": "npm run serve",
"build": "webpack --mode production --color",
"analyze": "webpack --mode=production --env analyze=true",
"lint": "cross-env eslint src --ext tsx,ts --fix --max-warnings=0",
"test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color",
"test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color",
"coverage": "yarn test --coverage",
"typescript": "tsc",
"extract-translations": "i18next 'src/**/*.component.tsx' 'src/**/*.modal.tsx' 'src/index.ts' --config ../../tools/i18next-parser.config.js"
},
"browserslist": [
"extends browserslist-config-openmrs"
],
"keywords": [
"openmrs"
],
"homepage": "https://github.com/I-TECH-UW/openmrs-esm-sgs#readme",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/I-TECH-UW/openmrs-esm-sgs.git"
},
"bugs": {
"url": "https://github.com/I-TECH-UW/openmrs-esm-sgs/issues"
},
"dependencies": {
"@carbon/react": "^1.12.0",
"@openmrs/esm-patient-common-lib": "*",
"lodash-es": "^4.17.21"
},
"peerDependencies": {
"@openmrs/esm-framework": "5.x",
"@openmrs/esm-patient-common-lib": "*",
"dayjs": "1.x",
"react": "18.x",
"react-i18next": "11.x",
"react-router-dom": "6.x",
"rxjs": "6.x",
"swr": "2.x"
},
"devDependencies": {
"@openmrs/esm-patient-common-lib": "*",
"webpack": "^5.88.2"
},
"author": "Christopher Miiro"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { usePatient, useVisit } from '@openmrs/esm-framework';
import { launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';

interface SendSmsActionOverflowMenuItemProps {
patientUuid: string;
}

const SendSmsActionOverflowMenuItem: React.FC<SendSmsActionOverflowMenuItemProps> = ({ patientUuid }) => {
const { t } = useTranslation();
const { currentVisit } = useVisit(patientUuid);
const { patient } = usePatient(patientUuid);
const handleClick = useCallback(() => launchPatientWorkspace('send-outcomes-form'), []);

const isDeceased = Boolean(patient?.deceasedDateTime);

return (
!currentVisit &&
!isDeceased && (
<li className="cds--overflow-menu-options__option">
<button
className="cds--overflow-menu-options__btn"
role="menuitem"
data-floating-menu-primary-focus
onClick={handleClick}
style={{
maxWidth: '100vw',
}}>
<span className="cds--overflow-menu-options__option-content">{t('sendSMS', 'Send PRO SMS')}</span>
</button>
</li>
)
);
};

export default SendSmsActionOverflowMenuItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';
import { screen, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
import { mockPatient } from 'tools';
import SendSmsActionOverflowMenuItem from './send-sms-action.component';

const mockUsePatient = jest.fn();
const mockUseVisit = jest.fn();

mockUseVisit.mockReturnValue({
currentVisit: null,
});

mockUsePatient.mockReturnValue({
error: null,
isLoading: false,
patient: null,
patientUuid: '',
});

jest.mock('@openmrs/esm-framework', () => ({
createGlobalStore: jest.fn(),
createUseStore: jest.fn(),
getGlobalStore: jest.fn(),
useVisit: jest.fn().mockImplementation((args) => mockUseVisit(args)),
usePatient: jest.fn().mockImplementation((args) => mockUsePatient(args)),
}));

jest.mock('@openmrs/esm-patient-common-lib', () => {
const originalModule = jest.requireActual('@openmrs/esm-patient-common-lib');
return {
...originalModule,
launchPatientWorkspace: jest.fn(),
};
});

describe('SendSmsActionOverflowMenuItem', () => {
it('should launch send outcomes sms form', async () => {
const user = userEvent.setup();

mockUsePatient.mockReturnValue({
error: null,
isLoading: false,
patient: mockPatient,
patientUuid: mockPatient.id,
});

render(<SendSmsActionOverflowMenuItem patientUuid={mockPatient.id} />);

const sendOutcomesFormButton = screen.getByRole('menuitem', { name: /Send PRO SMS/i });
expect(sendOutcomesFormButton).toBeInTheDocument();

await user.click(sendOutcomesFormButton);
expect(launchPatientWorkspace).toHaveBeenCalledTimes(1);
expect(launchPatientWorkspace).toHaveBeenCalledWith('send-outcomes-form');
});

it('should not show send outcomes sms button for deceased patient', () => {
mockUsePatient.mockReturnValue({
error: null,
isLoading: false,
patientUuid: mockPatient.id,
patient: {
...mockPatient,
deceasedDateTime: '2023-05-07T10:20:30Z',
},
});

render(<SendSmsActionOverflowMenuItem patientUuid={mockPatient.id} />);

const sendOutcomesFormButton = screen.queryByRole('menuitem', { name: /Send PRO SMS/i });
expect(sendOutcomesFormButton).not.toBeInTheDocument();
});
});
6 changes: 6 additions & 0 deletions packages/esm-patient-outcomes-app/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const spaRoot = window['getOpenmrsSpaBase']();
export const basePath = '/patient/:patientUuid/chart';
export const dashboardPath = `${basePath}/:view/*`;
export const spaBasePath = `${window.spaBase}${basePath}`;
export const moduleName = '@i-tech-uw/esm-patient-outcomes-app';
export const patientReportedOutcomesSlot = 'patient-reported-outcomes-slot';
21 changes: 21 additions & 0 deletions packages/esm-patient-outcomes-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getSyncLifecycle } from '@openmrs/esm-framework';
import * as PatientCommonLib from '@openmrs/esm-patient-common-lib';
import { moduleName } from './constants';
import sendSmsFormComponent from './smsform/send-sms-form.component';
import sendSmsActionButtonComponent from './actions/send-sms-action.component';

window['_openmrs_esm_patient_common_lib'] = PatientCommonLib;

export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');

export function startupApp() {}

export const sendSmsForm = getSyncLifecycle(sendSmsFormComponent, {
featureName: 'send-outcomes-form',
moduleName,
});

export const sendSmsActionButton = getSyncLifecycle(sendSmsActionButtonComponent, {
featureName: 'send-outcomes-button',
moduleName,
});
3 changes: 3 additions & 0 deletions packages/esm-patient-outcomes-app/src/root.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@use '@carbon/colors';
@use '@carbon/layout';
@use '@carbon/type';
30 changes: 30 additions & 0 deletions packages/esm-patient-outcomes-app/src/routes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "https://json.openmrs.org/routes.schema.json",
"backendDependencies": {
"webservices.rest": "^2.2.0"
},
"extensions": [
{
"name": "send-outcomes-button",
"component": "sendSmsActionButton",
"slot": "patient-actions-slot",
"online": true,
"offline": true
},
{
"name": "send-outcomes-form",
"component": "sendSmsForm",
"meta": {
"title": "Send PRO SMS"
},
"online": true,
"offline": true
}
],
"modals": [
{
"name": "print-identifier-sticker-modal",
"component": "printIdentifierStickerModal"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { saveQuestionnaire } from './send-sms-resource';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { type Observable } from 'rxjs';
import { type SmsFormData } from './types';
import { openmrsObservableFetch, restBaseUrl, type FetchResponse } from '@openmrs/esm-framework';

export function saveQuestionnaire(
payload: SmsFormData,
abortController: AbortController,
): Observable<FetchResponse<any>> {
return openmrsObservableFetch(`${restBaseUrl}/outcomes/sms`, {
signal: abortController.signal,
method: 'POST',
headers: {
'Content-type': 'application/json',
},
body: payload,
});
}

const saveQuestionnaireResource = {
saveQuestionnaire,
};

export default saveQuestionnaireResource;
9 changes: 9 additions & 0 deletions packages/esm-patient-outcomes-app/src/smsform/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type SmsFormData = {
to: string;
guid: string;
body: string;
source: string;
patientUuid: string;
};

export type UpdateSmsPayload = SmsFormData & {};
Loading

0 comments on commit 6b7bb67

Please sign in to comment.