diff --git a/packages/esm-patient-common-lib/src/orders/types/order.ts b/packages/esm-patient-common-lib/src/orders/types/order.ts index 8a56bddc16..edda893ad7 100644 --- a/packages/esm-patient-common-lib/src/orders/types/order.ts +++ b/packages/esm-patient-common-lib/src/orders/types/order.ts @@ -70,6 +70,7 @@ export interface OrderBasketItem { previousOrder?: string; orderType?: string; orderNumber?: string; + scheduledDate?: Date; } export type OrderUrgency = 'ROUTINE' | 'STAT' | 'ON_SCHEDULED_DATE'; @@ -96,6 +97,7 @@ export interface OrderPost { instructions?: string; accessionNumber?: string; orderType?: string; + scheduledDate?: string; } export interface DrugOrderPost extends OrderPost { diff --git a/packages/esm-patient-orders-app/src/components/orders-details-table.component.tsx b/packages/esm-patient-orders-app/src/components/orders-details-table.component.tsx index 5e5edac6eb..54ed9d2f53 100644 --- a/packages/esm-patient-orders-app/src/components/orders-details-table.component.tsx +++ b/packages/esm-patient-orders-app/src/components/orders-details-table.component.tsx @@ -70,12 +70,10 @@ interface OrderDetailsProps { } interface OrderBasketItemActionsProps { - items: Array; openOrderBasket: () => void; openOrderForm: (additionalProps?: { order: MutableOrderBasketItem }) => void; orderItem: Order; responsiveSize: string; - setOrderItems: (orderType: string, items: Array) => void; } interface OrderHeaderProps { @@ -114,7 +112,6 @@ const OrderDetailsTable: React.FC = ({ patientUuid, showAddBu const patient = usePatient(patientUuid); const { excludePatientIdentifierCodeTypes } = useConfig(); const [isPrinting, setIsPrinting] = useState(false); - const { orders, setOrders } = useOrderBasket(); const { data: orderTypes } = useOrderTypes(); const [selectedOrderTypeUuid, setSelectedOrderTypeUuid] = useState(null); const [selectedFromDate, setSelectedFromDate] = useState(null); @@ -512,11 +509,9 @@ const OrderDetailsTable: React.FC = ({ patientUuid, showAddBu {isOmrsOrder(matchingOrder) ? ( openOrderForm(matchingOrder)} orderItem={matchingOrder} - setOrderItems={setOrders} responsiveSize={responsiveSize} /> ) : ( @@ -604,14 +599,13 @@ const OrderDetailsTable: React.FC = ({ 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(orderItem.orderType.uuid); + const alreadyInBasket = orders.some((x) => x.uuid === orderItem.uuid); const handleModifyClick = useCallback(() => { if (orderItem.type === 'drugorder') { @@ -619,7 +613,7 @@ function OrderBasketItemActions({ .then((res) => { const medicationOrder = res.data; const medicationItem = buildMedicationOrder(medicationOrder, 'REVISE'); - setOrderItems(medicationsOrderBasket, [...items, medicationItem]); + setOrders([...orders, medicationItem]); openOrderForm({ order: medicationItem }); }) .catch((e) => { @@ -627,14 +621,14 @@ function OrderBasketItemActions({ }); } 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 }); @@ -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 ( diff --git a/packages/esm-patient-orders-app/src/order-basket/general-order-type/general-order-form/general-order-form.component.tsx b/packages/esm-patient-orders-app/src/order-basket/general-order-type/general-order-form/general-order-form.component.tsx index a56ca2c007..85c03495a2 100644 --- a/packages/esm-patient-orders-app/src/order-basket/general-order-type/general-order-form/general-order-form.component.tsx +++ b/packages/esm-patient-orders-app/src/order-basket/general-order-type/general-order-form/general-order-form.component.tsx @@ -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, @@ -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'; @@ -57,20 +58,26 @@ 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], ); @@ -78,6 +85,8 @@ export function OrderForm({ control, handleSubmit, formState: { errors, defaultValues, isDirty }, + setValue, + watch, } = useForm({ mode: 'all', resolver: zodResolver(OrderFormSchema), @@ -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()); }, []); @@ -133,6 +144,16 @@ export function OrderForm({ } }; + const handleUpdateUrgency = (fieldOnChange: ControllerRenderProps['onChange']) => { + return (e: ChangeEvent) => { + const value = e.target.value as OrderUrgency; + if (value !== 'ON_SCHEDULED_DATE') { + setValue('scheduledDate', null); + } + fieldOnChange(e); + }; + }; + useEffect(() => { promptBeforeClosing(() => isDirty); }, [isDirty, promptBeforeClosing]); @@ -189,6 +210,7 @@ export function OrderForm({ + {priorityOptions.map((option) => ( + + ))} + + )} + /> + + + + {isScheduledDateRequired && ( ( - + minDate={new Date()} + size={responsiveSize} + /> )} /> - {orderReasons.length > 0 && ( - - - - ( - item?.display} - onBlur={onBlur} - onChange={({ selectedItem }) => onChange(selectedItem?.uuid || '')} - selectedItem={''} - shouldFilterItem={filterItemsByName} - size={responsiveSize} - titleText={t('orderReason', 'Order reason')} - /> - )} - /> - - - - )} + )} + {orderReasons.length > 0 && ( ( -