Skip to content

Commit

Permalink
Merge pull request #3 from FelixKiprotich350/main
Browse files Browse the repository at this point in the history
(feat) intial UI design and population of prescribed medical supplies which are non drugs
  • Loading branch information
makombe authored Oct 31, 2024
2 parents 24545c0 + eded378 commit 1168846
Show file tree
Hide file tree
Showing 53 changed files with 8,191 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { render } from '@testing-library/react';
import React from 'react';
import ActionButtons from './action-buttons.component';
import { type MedicationRequest, MedicationRequestStatus, NonDrugMedicationDispense } from '../types';
import { toDateObjectStrict, useConfig } from '@openmrs/esm-framework';
import { date } from 'zod';

const mockedUseConfig = useConfig as jest.Mock;
const mockPatientUuid = '558494fe-5850-4b34-a3bf-06550334ba4a';
const mockEncounterUuid = '7aee7123-9e50-4f72-a636-895d77a63e98';

describe('Action Buttons Component tests', () => {
beforeEach(() => {
mockedUseConfig.mockReturnValue({
medicationRequestExpirationPeriodInDays: 90,
actionButtons: {
pauseButton: {
enabled: true,
},
closeButton: {
enabled: true,
},
},
dispenseBehavior: {
allowModifyingPrescription: false,
restrictTotalQuantityDispensed: false,
},
});
});

test('component should render dispense button if active medication', () => {
// status = active, and validity period start set to current datetime
const medicationRequest: NonDrugMedicationDispense = {
uuid: 'd4f69a68-1171-4e47-8693-478df18daf40',
patient: 'd4f69a68-1171-4e47-8693-478df18daf40',
encounter: 'd4f69a68-1171-4e47-8693-478df18daf40',
dispensingUnit: { uuid: 'd4f69a68-1171-4e47-8693-478df18daf40', display: 'Test' },
quantity: 2,
display: 'test ',
instrucions: 'test',
status: '',
medicalSupplyOrder: 'd4f69a68-1171-4e47-8693-478df18daf40',
concept: '',
dateDispensed: new Date(),
statusReason: '',
location: '',
encounters: '',
dispenser: '',
};

const { getByText, container } = render(
<ActionButtons
patientUuid={mockPatientUuid}
encounterUuid={mockEncounterUuid}
medicationDispense={medicationRequest}
/>,
);
expect(getByText('Dispense')).toBeInTheDocument();
});

// status = active, but validity period start time years in the past
test('component should not render dispense button if expired medication', () => {
// status = active, and validity period start set to current datetime
const medicationRequest: NonDrugMedicationDispense = {
uuid: 'd4f69a68-1171-4e47-8693-478df18daf40',
patient: 'd4f69a68-1171-4e47-8693-478df18daf40',
encounter: 'd4f69a68-1171-4e47-8693-478df18daf40',
dispensingUnit: { uuid: 'd4f69a68-1171-4e47-8693-478df18daf40', display: 'Test' },
quantity: 2,
display: 'test ',
instrucions: 'test',
status: '',
medicalSupplyOrder: 'd4f69a68-1171-4e47-8693-478df18daf40',
concept: '',
dateDispensed: new Date(),
statusReason: '',
location: '',
encounters: '',
dispenser: '',
};

const { queryByText, container } = render(
<ActionButtons
patientUuid={mockPatientUuid}
encounterUuid={mockEncounterUuid}
medicationDispense={medicationRequest}
/>,
);
// expect(queryByText('Dispense')).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import { Button } from '@carbon/react';
import { useTranslation } from 'react-i18next';
import { useConfig, useSession } from '@openmrs/esm-framework';
import {
MedicationDispenseStatus,
type MedicationRequestBundle,
MedicationRequestStatus,
NonDrugMedicationDispense,
} from '../types';
import { launchOverlay } from '../hooks/useOverlay';
import {
computeMedicationRequestStatus,
computeQuantityRemaining,
getMostRecentMedicationDispenseStatus,
} from '../utils';
import { type PharmacyConfig } from '../config-schema';
import { initiateMedicationDispenseBody, useProviders } from '../medication-dispense/medication-dispense.resource';
import DispenseForm from '../forms/dispense-form.component';
import PauseDispenseForm from '../forms/pause-dispense-form.component';
import CloseDispenseForm from '../forms/close-dispense-form.component';
import styles from './action-buttons.scss';

interface ActionButtonsProps {
patientUuid: string;
encounterUuid: string;
medicationDispense: NonDrugMedicationDispense;
}

const ActionButtons: React.FC<ActionButtonsProps> = ({ patientUuid, encounterUuid, medicationDispense }) => {
const { t } = useTranslation();
const config = useConfig<PharmacyConfig>();
const session = useSession();
const providers = useProviders(config.dispenserProviderRoles);

return (
<div className={styles.actionBtns}>
{medicationDispense.uuid ? (
<Button
kind="primary"
onClick={() =>
launchOverlay(
t('dispensePrescription', 'Dispense prescription'),
<DispenseForm
patientUuid={patientUuid}
encounterUuid={encounterUuid}
medicationDispense={medicationDispense}
mode="enter"
/>,
)
}>
{t('dispense', 'Dispense')}
</Button>
) : null}
{medicationDispense.uuid ? (
<Button
kind="secondary"
onClick={() =>
launchOverlay(
t('pausePrescription', 'Pause prescription'),
<PauseDispenseForm patientUuid={patientUuid} encounterUuid={encounterUuid} mode="enter" />,
)
}>
{t('pause', 'Pause')}
</Button>
) : null}
{medicationDispense.uuid ? (
<Button
kind="danger"
onClick={() =>
launchOverlay(
t('closePrescription', 'Close prescription'),
<CloseDispenseForm patientUuid={patientUuid} encounterUuid={encounterUuid} mode="enter" />,
)
}>
{t('close', 'Close')}
</Button>
) : null}
</div>
);
};

export default ActionButtons;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.actionBtns {
float: right;
margin-left: 1rem;
margin-top: 2rem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { render } from '@testing-library/react';
import { type MedicationReferenceOrCodeableConcept } from '../types';
import MedicationCard from './medication-card.component';

describe('Medication Card Component tests', () => {
test('component should render medication card without edit action button', () => {
const medication: MedicationReferenceOrCodeableConcept = {
medicationReference: {
display: 'Some Medication',
reference: '',
type: '',
},
};

const { getByText, container } = render(<MedicationCard medication={medication} />);
expect(getByText('Some Medication')).toBeInTheDocument();
expect(container.querySelector('svg')).not.toBeInTheDocument();
});

test('component should render medication card with edit action button', () => {
const medication: MedicationReferenceOrCodeableConcept = {
medicationReference: {
display: 'Some Medication',
reference: '',
type: '',
},
};

const action = () => 0;

const { getByText, container } = render(<MedicationCard medication={medication} editAction={action} />);
expect(getByText('Some Medication')).toBeInTheDocument();
expect(container.querySelector('svg')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { ReactSVGElement } from 'react';
import { Tile } from '@carbon/react';
import { Edit } from '@carbon/react/icons';
import { type MedicationReferenceOrCodeableConcept } from '../types/index';
import styles from './medication-card.scss';
import { getMedicationDisplay } from '../utils';

const MedicationCard: React.FC<{
medication: MedicationReferenceOrCodeableConcept;
editAction?: Function;
}> = ({ medication, editAction }) => {
return (
<Tile className={styles.medicationTile}>
<p className={styles.medicationName}>
<strong>{getMedicationDisplay(medication)}</strong>
</p>
{editAction && <Edit onClick={editAction as React.MouseEventHandler<ReactSVGElement>} />}
</Tile>
);
};

export default MedicationCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@use '@carbon/styles/scss/spacing';
@use '@carbon/styles/scss/type';
@import '~@openmrs/esm-styleguide/src/vars';

.medicationTile {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
margin: 2px 0 8px;
padding: 0 8px 0 8px;
background-color: #fff;
border-left: 4px solid var(--brand-03);
color: $text-02;
margin-bottom: 1rem !important;
}

.medicationName {
font-size: 15px !important;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
@use '@carbon/styles/scss/spacing';
@use '@carbon/styles/scss/type';
@import '~@openmrs/esm-styleguide/src/vars';

.medicationDispenseReviewContainer {
display: flex;
flex-direction: column;
padding-bottom: 10px;

:global(.cds--css-grid) {
padding-left: 0 !important;
padding-right: 0 !important;
}
}

.dispenseDetailsContainer {
display: flex;
flex-direction: row;
flex: 1;
gap: spacing.$spacing-05;
}

.substitutionReason {
width: 100%;
}

.substitutionType {
width: 100%;
}

.productiveHeading02 {
color: $color-gray-70;
@include type.type-style('productive-heading-02');
}

:global(.omrs-breakpoint-lt-desktop) .formWrapper {
background-color: $openmrs-background-grey;
}

:global(.omrs-breakpoint-gt-tablet) .formWrapper {
background-color: $ui-02;
}

.formGroup {
display: flex;
margin-bottom: spacing.$spacing-02;
padding: spacing.$spacing-05;
}

:global(.omrs-breakpoint-lt-desktop) .formGroup > span {
flex: 1;
}

:global(.omrs-breakpoint-lt-desktop) .formGroup > div {
flex: 3;
}

.formGroup span {
@extend .productiveHeading02;
}

.patientInfo {
position: sticky;
z-index: 1000;
background-color: $ui-02;
top: 3rem;
overflow-y: auto;
}

:global(.omrs-breakpoint-lt-desktop) .formGroup {
flex-direction: row;
}

:global(.omrs-breakpoint-gt-tablet) .formGroup {
flex-direction: column;
}

.buttonGroup {
display: flex;
position: sticky;
bottom: 0;
width: 100%;
}

:global(.omrs-breakpoint-lt-desktop) .buttonGroup {
padding: spacing.$spacing-05 spacing.$spacing-06;
background-color: $ui-02;
}

.buttonGroup button {
max-width: none;
width: 50%;
height: spacing.$spacing-10;
align-items: flex-start;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import {
NonDrugDispensingUnit,
NonDrugMedicationDispense,
type DosageInstruction,
type MedicationDispense,
type MedicationRequest,
type Quantity,
} from '../types';
import styles from './medication-event.scss';

import { useTranslation } from 'react-i18next';

// can render MedicationRequest or MedicationDispense
const MedicationEvent: React.FC<{ medicationDispense: NonDrugMedicationDispense }> = ({ medicationDispense }) => {
const { t } = useTranslation();

return (
<div>
<p className={styles.medicationName}>
<strong>{medicationDispense.display}</strong>
</p>

<p className={styles.bodyLong01}>
<span className={styles.label01}>{t('quantity', 'Quantity').toUpperCase()}</span>{' '}
<span className={styles.quantity}> : {String(medicationDispense.quantity)} </span>
</p>
<p className={styles.bodyLong01}>
<span className={styles.label01}>{t('dispenseUnit', 'Dispense Unit').toUpperCase()}</span>{' '}
<span className={styles.quantity}> : {String(medicationDispense.dispensingUnit?.display)}</span>
</p>
</div>
);
};

export default MedicationEvent;
Loading

0 comments on commit 1168846

Please sign in to comment.