Skip to content

Commit

Permalink
Merge branch 'main' into fix/O3-4329
Browse files Browse the repository at this point in the history
  • Loading branch information
Bharath-K-Shetty authored Jan 15, 2025
2 parents b3beb05 + 2ae8ff1 commit c336a20
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 175 deletions.
2 changes: 2 additions & 0 deletions packages/esm-patient-common-lib/src/orders/types/order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface OrderBasketItem {
previousOrder?: string;
orderType?: string;
orderNumber?: string;
scheduledDate?: Date;
}

export type OrderUrgency = 'ROUTINE' | 'STAT' | 'ON_SCHEDULED_DATE';
Expand All @@ -96,6 +97,7 @@ export interface OrderPost {
instructions?: string;
accessionNumber?: string;
orderType?: string;
scheduledDate?: string;
}

export interface DrugOrderPost extends OrderPost {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,10 @@ interface OrderDetailsProps {
}

interface OrderBasketItemActionsProps {
items: Array<MutableOrderBasketItem>;
openOrderBasket: () => void;
openOrderForm: (additionalProps?: { order: MutableOrderBasketItem }) => void;
orderItem: Order;
responsiveSize: string;
setOrderItems: (orderType: string, items: Array<MutableOrderBasketItem>) => void;
}

interface OrderHeaderProps {
Expand Down Expand Up @@ -114,7 +112,6 @@ const OrderDetailsTable: React.FC<OrderDetailsProps> = ({ patientUuid, showAddBu
const patient = usePatient(patientUuid);
const { excludePatientIdentifierCodeTypes } = useConfig();
const [isPrinting, setIsPrinting] = useState(false);
const { orders, setOrders } = useOrderBasket<MutableOrderBasketItem>();
const { data: orderTypes } = useOrderTypes();
const [selectedOrderTypeUuid, setSelectedOrderTypeUuid] = useState(null);
const [selectedFromDate, setSelectedFromDate] = useState(null);
Expand Down Expand Up @@ -512,11 +509,9 @@ const OrderDetailsTable: React.FC<OrderDetailsProps> = ({ patientUuid, showAddBu
<TableCell className="cds--table-column-menu">
{isOmrsOrder(matchingOrder) ? (
<OrderBasketItemActions
items={orders}
openOrderBasket={launchOrderBasket}
openOrderForm={() => openOrderForm(matchingOrder)}
orderItem={matchingOrder}
setOrderItems={setOrders}
responsiveSize={responsiveSize}
/>
) : (
Expand Down Expand Up @@ -604,37 +599,36 @@ const OrderDetailsTable: React.FC<OrderDetailsProps> = ({ patientUuid, showAddBu

function OrderBasketItemActions({
orderItem,
items,
setOrderItems,
openOrderBasket,
openOrderForm,
responsiveSize,
}: OrderBasketItemActionsProps) {
const { t } = useTranslation();
const alreadyInBasket = items.some((x) => x.uuid === orderItem.uuid);
const { orders, setOrders } = useOrderBasket<MutableOrderBasketItem>(orderItem.orderType.uuid);
const alreadyInBasket = orders.some((x) => x.uuid === orderItem.uuid);

const handleModifyClick = useCallback(() => {
if (orderItem.type === 'drugorder') {
getDrugOrderByUuid(orderItem.uuid)
.then((res) => {
const medicationOrder = res.data;
const medicationItem = buildMedicationOrder(medicationOrder, 'REVISE');
setOrderItems(medicationsOrderBasket, [...items, medicationItem]);
setOrders([...orders, medicationItem]);
openOrderForm({ order: medicationItem });
})
.catch((e) => {
console.error('Error modifying drug order: ', e);
});
} else if (orderItem.type === 'testorder') {
const labItem = buildLabOrder(orderItem, 'REVISE');
setOrderItems(orderItem.orderType.uuid, [...items, labItem]);
setOrders([...orders, labItem]);
openOrderForm({ order: labItem });
} else if (orderItem.type === 'order') {
const order = buildGeneralOrder(orderItem, 'REVISE');
setOrderItems(orderItem.orderType.uuid, [...items, order]);
setOrders([...orders, order]);
openOrderForm({ order });
}
}, [orderItem, openOrderForm, items, setOrderItems]);
}, [orderItem, openOrderForm, orders, setOrders]);

const handleAddResultsClick = useCallback(() => {
launchPatientWorkspace('test-results-form-workspace', { order: orderItem });
Expand All @@ -644,19 +638,19 @@ function OrderBasketItemActions({
if (orderItem.type === 'drugorder') {
getDrugOrderByUuid(orderItem.uuid).then((res) => {
let medicationOrder = res.data;
setOrderItems(medicationsOrderBasket, [...items, buildMedicationOrder(medicationOrder, 'DISCONTINUE')]);
setOrders([...orders, buildMedicationOrder(medicationOrder, 'DISCONTINUE')]);
openOrderBasket();
});
} else if (orderItem.type === 'testorder') {
const labItem = buildLabOrder(orderItem, 'DISCONTINUE');
setOrderItems(orderItem.orderType.uuid, [...items, labItem]);
setOrders([...orders, labItem]);
openOrderBasket();
} else {
const order = buildGeneralOrder(orderItem, 'DISCONTINUE');
setOrderItems(orderItem.orderType.uuid, [...items, order]);
setOrders([...orders, order]);
openOrderBasket();
}
}, [orderItem, setOrderItems, items, openOrderBasket]);
}, [orderItem, setOrders, orders, openOrderBasket]);

return (
<Layer className={styles.layer}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { type ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { Controller, type FieldErrors, useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import {
type OrderBasketItem,
type DefaultPatientWorkspaceProps,
launchPatientWorkspace,
useOrderBasket,
useOrderType,
priorityOptions,
type OrderUrgency,
} from '@openmrs/esm-patient-common-lib';
import { useLayoutType, useSession, useConfig, ExtensionSlot, OpenmrsDatePicker } from '@openmrs/esm-framework';
import {
Button,
ButtonSet,
Expand All @@ -17,15 +23,10 @@ import {
TextArea,
TextInput,
} from '@carbon/react';
import {
launchPatientWorkspace,
priorityOptions,
type DefaultPatientWorkspaceProps,
type OrderBasketItem,
useOrderBasket,
useOrderType,
} from '@openmrs/esm-patient-common-lib';
import { useLayoutType, useSession, ExtensionSlot, useConfig } from '@openmrs/esm-framework';
import { useTranslation } from 'react-i18next';
import { Controller, type ControllerRenderProps, type FieldErrors, useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { ordersEqual, prepOrderPostData } from '../resources';
import styles from './general-order-form.scss';
import { type ConfigObject } from '../../../config-schema';
Expand Down Expand Up @@ -57,27 +58,35 @@ export function OrderForm({

const OrderFormSchema = useMemo(
() =>
z.object({
instructions: z.string().nullish(),
urgency: z.string().refine((value) => value !== '', {
message: t('priorityRequired', 'Priority is required'),
z
.object({
instructions: z.string().nullish(),
urgency: z.string().refine((value) => value !== '', {
message: t('priorityRequired', 'Priority is required'),
}),
accessionNumber: z.string().nullish(),
concept: z.object(
{ display: z.string(), uuid: z.string() },
{
required_error: t('orderableConceptRequired', 'Orderable concept is required'),
invalid_type_error: t('orderableConceptRequired', 'Orderable concept is required'),
},
),
scheduledDate: z.date().nullish(),
})
.refine((data) => data.urgency !== 'ON_SCHEDULED_DATE' || data.scheduledDate, {
message: t('scheduledDateRequired', 'Scheduled date is required'),
path: ['scheduledDate'],
}),
accessionNumber: z.string().nullish(),
concept: z.object(
{ display: z.string(), uuid: z.string() },
{
required_error: t('orderableConceptRequired', 'Orderable concept is required'),
invalid_type_error: t('orderableConceptRequired', 'Orderable concept is required'),
},
),
}),
[t],
);

const {
control,
handleSubmit,
formState: { errors, defaultValues, isDirty },
setValue,
watch,
} = useForm<OrderBasketItem>({
mode: 'all',
resolver: zodResolver(OrderFormSchema),
Expand All @@ -86,6 +95,8 @@ export function OrderForm({
},
});

const isScheduledDateRequired = watch('urgency') === 'ON_SCHEDULED_DATE';

const filterItemsByName = useCallback((menu) => {
return menu?.item?.value?.toLowerCase().includes(menu?.inputValue?.toLowerCase());
}, []);
Expand Down Expand Up @@ -133,6 +144,16 @@ export function OrderForm({
}
};

const handleUpdateUrgency = (fieldOnChange: ControllerRenderProps['onChange']) => {
return (e: ChangeEvent<HTMLSelectElement>) => {
const value = e.target.value as OrderUrgency;
if (value !== 'ON_SCHEDULED_DATE') {
setValue('scheduledDate', null);
}
fieldOnChange(e);
};
};

useEffect(() => {
promptBeforeClosing(() => isDirty);
}, [isDirty, promptBeforeClosing]);
Expand Down Expand Up @@ -189,6 +210,7 @@ export function OrderForm({
<Select
id="priorityInput"
{...field}
onChange={handleUpdateUrgency(field.onChange)}
invalid={Boolean(fieldState?.error)}
invalidText={fieldState?.error?.message}
labelText={t('priority', 'Priority')}
Expand All @@ -202,6 +224,29 @@ export function OrderForm({
</InputWrapper>
</Column>
</Grid>
{isScheduledDateRequired && (
<Grid className={styles.gridRow}>
<Column lg={8} md={8} sm={4}>
<InputWrapper>
<Controller
name="scheduledDate"
control={control}
render={({ field, fieldState }) => (
<OpenmrsDatePicker
labelText={t('scheduledDate', 'Scheduled date')}
id="scheduledDate"
{...field}
invalid={Boolean(fieldState?.error?.message)}
invalidText={fieldState?.error?.message}
minDate={new Date()}
size={responsiveSize}
/>
)}
/>
</InputWrapper>
</Column>
</Grid>
)}
<Grid className={styles.gridRow}>
<Column lg={16} md={8} sm={4}>
<InputWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { type FetchResponse, openmrsFetch, type OpenmrsResource, restBaseUrl } from '@openmrs/esm-framework';
import {
type FetchResponse,
openmrsFetch,
type OpenmrsResource,
restBaseUrl,
toOmrsIsoString,
} from '@openmrs/esm-framework';
import {
type OrderBasketItem,
priorityOptions,
Expand Down Expand Up @@ -41,6 +47,7 @@ export function prepOrderPostData(
// orderReason: order.orderReason,
accessionNumber: order.accessionNumber,
urgency: order.urgency,
scheduledDate: order.scheduledDate ? toOmrsIsoString(order.scheduledDate) : null,
};
} else if (order.action === 'REVISE') {
return {
Expand All @@ -55,6 +62,7 @@ export function prepOrderPostData(
previousOrder: order.previousOrder,
accessionNumber: order.accessionNumber,
urgency: order.urgency,
scheduledDate: order.scheduledDate ? toOmrsIsoString(order.scheduledDate) : null,
};
} else if (order.action === 'DISCONTINUE') {
return {
Expand All @@ -68,6 +76,7 @@ export function prepOrderPostData(
previousOrder: order.previousOrder,
accessionNumber: order.accessionNumber,
urgency: order.urgency,
scheduledDate: order.scheduledDate ? toOmrsIsoString(order.scheduledDate) : null,
};
} else {
throw new Error(`Unknown order action: ${order.action}.`);
Expand Down
2 changes: 2 additions & 0 deletions packages/esm-patient-orders-app/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export function buildLabOrder(order: Order, action?: OrderAction) {
concept: order.concept,
orderType: order.orderType.uuid,
specimenSource: null,
scheduledDate: order.scheduledDate ? new Date(order.scheduledDate) : null,
};
}

Expand All @@ -106,5 +107,6 @@ export function buildGeneralOrder(order: Order, action?: OrderAction): OrderBask
concept: order.concept,
orderNumber: order.orderNumber,
orderType: order.orderType.uuid,
scheduledDate: order.scheduledDate ? new Date(order.scheduledDate) : null,
};
}
2 changes: 2 additions & 0 deletions packages/esm-patient-orders-app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
"saveLabResults": "Save lab results",
"saveOrder": "Save order",
"saving": "Saving",
"scheduledDate": "Scheduled date",
"scheduledDateRequired": "Scheduled date is required",
"searchAgain": "search again",
"searchFieldOrder": "Search for {{orderType}} order",
"searchOrderables": "Search orderables",
Expand Down
Loading

0 comments on commit c336a20

Please sign in to comment.