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

(refactor) follow up changes on the case management: discontinuity of case #520

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
38ba77e
(refactor) updating the uuids
its-kios09 Dec 11, 2024
e926ff3
(feat) added end relationship functionality
its-kios09 Dec 17, 2024
3b62701
(feat) added a ability to end relationship on case management
its-kios09 Dec 17, 2024
087818c
Merge branch 'main' into new-case-mgt
its-kios09 Dec 18, 2024
c307d8c
Merge branch 'main' into new-case-mgt
its-kios09 Dec 19, 2024
02936e8
Merge branch 'main' into new-case-mgt
its-kios09 Dec 19, 2024
84e2943
(refactor) updated PR per suggestion
its-kios09 Dec 19, 2024
e48dfcf
(feat) updated translation
its-kios09 Dec 19, 2024
b0fe70f
(feat) added the unit test
its-kios09 Dec 19, 2024
f01cd1c
Merge branch 'main' into new-case-mgt
its-kios09 Jan 7, 2025
cc40214
Merge branch 'main' into new-case-mgt
its-kios09 Jan 9, 2025
6af8746
Revert commits 38ba77e and e926ff3: Undo changes from Dec 16 and Dec 17
its-kios09 Jan 10, 2025
9e71a43
(refactor) updating the uuids
its-kios09 Dec 11, 2024
632b3f3
(feat) added end relationship functionality
its-kios09 Dec 17, 2024
53a27f5
(feat) added a ability to end relationship on case management
its-kios09 Dec 17, 2024
29e5d48
(refactor) updated PR per suggestion
its-kios09 Dec 19, 2024
8990d5a
(feat) updated translation
its-kios09 Dec 19, 2024
eb37f0a
(feat) added the unit test
its-kios09 Dec 19, 2024
c5e2a06
(refactor) updated the config schema uuids (#535)
its-kios09 Jan 9, 2025
86c22ca
Revert commits 38ba77e and e926ff3: Undo changes from Dec 16 and Dec 17
its-kios09 Jan 10, 2025
e576f5c
Revert "(feat) added end relationship functionality"
its-kios09 Jan 10, 2025
5f06452
(fix) duplicate type
its-kios09 Jan 10, 2025
6a65b2c
Merge branch 'new-case-mgt' of github.com:its-kios09/kenyaemr-esm-3.x…
its-kios09 Jan 10, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,116 +8,75 @@ import { launchWorkspace, showModal } from '@openmrs/esm-framework';
import { createMedicationDispenseProps } from './dispense.resource';

jest.mock('./test-order-action.resource');

const mockTestProps = {
order: { uuid: '123', patient: { uuid: '456' } } as Order,
patientUuid: 'patient-uuid-123',
encounterUuid: 'encounter-uuid-456',
medicationRequestBundle: {
request: {
id: 'med-request-789',
medicationReference: { reference: 'Medication/med-123' },
medicationCodeableConcept: { coding: [{ code: 'med-code-123' }] },
subject: { reference: 'Patient/patient-uuid-123' },
dispenseRequest: {
quantity: {
value: 30,
code: 'TAB',
unit: 'tablets',
system: 'http://terminology.hl7.org/CodeSystem/v3-orderableDrugForm',
jest.mock('./dispense.resource', () => ({
createMedicationDispenseProps: jest.fn(() => ({
whenHandedOver: '2024-12-17T11:52:35+00:00',
medicationRequestBundle: {
request: {
dispenseRequest: {
quantity: { value: 30, code: 'TAB', unit: 'tablets' },
},
},
dosageInstruction: [
{
text: 'Take 1 tablet daily',
timing: { code: 'daily' },
route: { coding: [{ code: 'PO' }] },
doseAndRate: [
{
doseQuantity: {
value: 1,
code: 'TAB',
unit: 'tablet',
},
},
],
additionalInstruction: [{ text: 'with food' }],
},
],
},
},
quantityRemaining: 30,
session: {
currentProvider: {
uuid: 'provider-uuid-123',
},
sessionLocation: {
uuid: 'location-uuid-456',
},
},
providers: [
{
uuid: 'provider-uuid-123',
display: 'Dr. Test Provider',
},
],
closeable: true,
} as Record<string, unknown>;
otherProp: 'test-value',
})),
}));

jest.mock('@openmrs/esm-framework', () => ({
launchWorkspace: jest.fn(),
showModal: jest.fn(),
}));

const testProps = {
order: { uuid: '123', patient: { uuid: '456' } } as Order,
modalName: 'test-modal',
actionText: 'Test Action',
};

describe('TestOrderAction', () => {
beforeEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
jest.useFakeTimers().setSystemTime(new Date('2024-12-17T11:52:35Z'));
});

test('should render loading when isLoading is true', () => {
afterEach(() => {
jest.useRealTimers();
});

test('should render loading state when isLoading is true', () => {
jest.spyOn(resource, 'useTestOrderBillStatus').mockReturnValueOnce({ isLoading: true, hasPendingPayment: false });
render(<TestOrderAction {...testProps} />);

expect(screen.getByText('Verifying bill status...')).toBeInTheDocument();
expect(screen.getByRole('button')).toBeDisabled();
});

test("should display `Unsettled bill for test` when there's a pending payment", () => {
test('should display "Unsettled bill" when hasPendingPayment is true', () => {
jest.spyOn(resource, 'useTestOrderBillStatus').mockReturnValueOnce({ isLoading: false, hasPendingPayment: true });
render(<TestOrderAction {...testProps} />);

expect(screen.getByText('Unsettled bill')).toBeInTheDocument();
expect(screen.getByRole('button')).toBeDisabled();
});

test("should display `Pick Lab Request` when there's no pending payment", async () => {
const user = userEvent.setup();
test('should render the button with correct action text', () => {
jest.spyOn(resource, 'useTestOrderBillStatus').mockReturnValueOnce({ isLoading: false, hasPendingPayment: false });
render(<TestOrderAction {...testProps} />);
const pickLabRequestMenuItem = screen.getByText('Pick Lab Request');
await user.click(pickLabRequestMenuItem);

expect(screen.queryByText('Unsettled bill.')).not.toBeInTheDocument();
expect(showModal).toBeCalledWith('pickup-lab-request-modal', {
closeModal: expect.any(Function),
order: { patient: { uuid: '456' }, uuid: '123' },
});
expect(screen.getByText('Test Action')).toBeInTheDocument();
expect(screen.getByRole('button')).toBeEnabled();
});

test('should not render the dispense form if closeable is false', () => {
jest.spyOn(resource, 'useTestOrderBillStatus').mockReturnValueOnce({ isLoading: false, hasPendingPayment: false });
render(<TestOrderAction {...testProps} closeable={false} />);
expect(screen.queryByText('Dispense')).not.toBeInTheDocument();
});
test('should not render button if order is in progress', () => {
const inProgressProps = {
...testProps,
order: { ...testProps.order, fulfillerStatus: 'IN_PROGRESS' } as Order,
};

test('should launch the dispense form when dispense order is part of props', async () => {
const user = userEvent.setup();
jest.spyOn(resource, 'useTestOrderBillStatus').mockReturnValueOnce({ isLoading: false, hasPendingPayment: false });
render(<TestOrderAction {...mockTestProps} />);
const dispenseButton = screen.getByRole('button', { name: 'Dispense' });
expect(dispenseButton).toBeInTheDocument();

await user.click(dispenseButton);
const dispenseFormProps = createMedicationDispenseProps(mockTestProps);
render(<TestOrderAction {...inProgressProps} />);

expect(launchWorkspace).toHaveBeenCalledWith('dispense-workspace', {
...dispenseFormProps,
});
expect(screen.queryByRole('button')).not.toBeInTheDocument();
});
});
24 changes: 6 additions & 18 deletions packages/esm-morgue-app/src/config-schema.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
import { Type } from '@openmrs/esm-framework';

export const configSchema = {
autopsyEncounterUuid: {
_type: Type.String,
_description: 'Encounter UUID for autopsy',
_default: '',
},
formsList: {
_type: Type.Object,
_description: 'List of form UUIDs',
_default: {
autopsyFormUuid: '',
},
},
morgueVisitTypeUuid: {
_type: Type.String,
_description: ' UUID for morgue visit',
Expand All @@ -21,7 +9,7 @@ export const configSchema = {
morgueDepartmentServiceTypeUuid: {
_type: Type.String,
_description: ' UUID for morgue department service type',
_default: 'd7bd4cc0-90b1-4f22-90f2-ab7fde936727',
_default: '5b9e6cd1-f836-4144-91e4-401c58dd62af',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are related to morgue functions

},
insurancepaymentModeUuid: {
_type: Type.String,
Expand All @@ -36,7 +24,7 @@ export const configSchema = {
tagNumberUuid: {
_type: Type.String,
_description: 'UUID for tag number concept',
_default: '13ba9c45-c540-4f10-b915-fa3d7baeb3d1',
_default: 'f2b35679-7ba9-4619-92cb-6872b0c6bf57',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this

},
morgueAdmissionEncounterType: {
_type: Type.String,
Expand All @@ -56,22 +44,22 @@ export const configSchema = {
obNumberUuid: {
_type: Type.String,
_description: 'UUID for ob number concept',
_default: 'c756d06a-22a5-4b66-933e-3d44667b72a0',
_default: '0dffecb3-2dc4-4d56-8cd4-56ba38579c69',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this

},
policeNameUuid: {
_type: Type.String,
_description: 'UUID for police name concept',
_default: '6d58d9b5-6f84-4e77-941e-f5cc86d18a60',
_default: 'd889f05b-0d9b-462f-ae8e-2e9be79fd954',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this

},
burialPermitNumberUuid: {
_type: Type.String,
_description: 'UUID for burial permit number concept',
_default: '29ef3df3-9845-49b0-96f2-5fb6d6240039',
_default: 'da524812-5600-4677-ba26-eb61eb925eef',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this

},
policeIDNumber: {
_type: Type.String,
_description: 'UUID for police id number concept',
_default: '8d488d02-d1d8-41a5-8219-61f4fc5dbeb0',
_default: '163084AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this

},
encounterProviderRoleUuid: {
_type: Type.UUID,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React, { useState } from 'react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { WatsonHealthStressBreathEditor } from '@carbon/react/icons';
import { Button } from '@carbon/react';
import styles from './case-management-header.scss';
import { launchWorkspace, navigate, useSession } from '@openmrs/esm-framework';
import { launchWorkspace, useSession } from '@openmrs/esm-framework';

const MetricsHeader = () => {
interface MetricsHeaderProps {
activeTabIndex: number;
}

const MetricsHeader: React.FC<MetricsHeaderProps> = ({ activeTabIndex }) => {
const { t } = useTranslation();
const { user } = useSession();
const metricsTitle = t(' ', 'Case Manager');
Expand All @@ -14,14 +18,18 @@ const MetricsHeader = () => {
workspaceTitle: 'Case Management Form',
});
};

const isDiscontinuationTab = activeTabIndex === 1;

return (
<div className={styles.metricsContainer}>
<div className={styles.actionBtn}>
<Button
kind="tertiary"
renderIcon={(props) => <WatsonHealthStressBreathEditor size={16} {...props} />}
iconDescription={t('addCase', 'Add case')}
onClick={handleAddCase}>
onClick={handleAddCase}
disabled={isDiscontinuationTab}>
{t('addCase', 'Add case')}
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Button, DatePicker, DatePickerInput, ModalFooter } from '@carbon/react';
import { useForm, Controller } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { mutate } from 'swr';
import { updateRelationship } from '../../relationships/relationship.resources';
import { showSnackbar } from '@openmrs/esm-framework';
import styles from './case-management-modal.scss';

const EndRelationshipSchema = z.object({
endDate: z.date({
required_error: 'End date is required',
its-kios09 marked this conversation as resolved.
Show resolved Hide resolved
invalid_type_error: 'Please select a valid date',
}),
});

type FormData = z.infer<typeof EndRelationshipSchema>;

interface EndRelationshipModalProps {
closeModal: () => void;
relationshipUuid: string;
}

const EndRelationshipModal: React.FC<EndRelationshipModalProps> = ({ closeModal, relationshipUuid }) => {
const { t } = useTranslation();

const {
handleSubmit,
control,
formState: { errors, isSubmitting },
} = useForm<FormData>({
resolver: zodResolver(EndRelationshipSchema),
defaultValues: { endDate: null },
});

const handleEndRelationship = async (data: FormData) => {
try {
await updateRelationship(relationshipUuid, { endDate: data.endDate });
mutate((key) => typeof key === 'string' && key.startsWith('/ws/rest/v1/relationship'), undefined, {
revalidate: true,
});
showSnackbar({
kind: 'success',
title: t('endRelationship', 'End relationship'),
subtitle: t('savedRlship', 'Relationship ended successfully'),
timeoutInMs: 3000,
isLowContrast: true,
});
closeModal();
} catch (error) {
showSnackbar({
kind: 'error',
title: t('relationshipError', 'Relationship Error'),
subtitle: t('RlshipErrorMessage', 'Request Failed'),
timeoutInMs: 2500,
isLowContrast: true,
});
}
};

return (
<form onSubmit={handleSubmit(handleEndRelationship)}>
<div className="cds--modal-header">
<h3 className="cds--modal-header__heading">{t('endRelationship', 'End Relationship')}</h3>
</div>
<div className="cds--modal-content">
<p>{t('rlshipConfirmationText', 'This will end the relationship. Are you sure you want to proceed?')}</p>

<div className={styles.centeredContainer}>
<Controller
name="endDate"
control={control}
render={({ field, fieldState }) => (
<DatePicker
datePickerType="single"
onChange={(e) => field.onChange(e[0])}
className={styles.formDatePicker}>
<DatePickerInput
placeholder="mm/dd/yyyy"
labelText={t('endDate', 'End Date')}
id="endDate-picker"
size="md"
className={styles.formDatePicker}
invalid={!!fieldState.error}
invalidText={fieldState.error?.message}
/>
</DatePicker>
)}
/>
</div>
</div>
<ModalFooter>
<Button kind="secondary" onClick={closeModal}>
{t('no', 'No')}
</Button>
<Button type="submit" kind="danger" disabled={isSubmitting}>
{t('yes', 'Yes')}
</Button>
</ModalFooter>
</form>
);
};

export default EndRelationshipModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@use '@carbon/colors';
@use '@carbon/layout';
@use '@carbon/type';

.formDatePicker input {
min-width: 50rem;
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid colors.$gray-50;
padding: layout.$spacing-04 layout.$spacing-05;
background-color: colors.$white;
}

.centeredContainer {
display: flex;
justify-content: center;
align-items: center;
margin: layout.$spacing-06 layout.$spacing-07;
}
Loading
Loading