diff --git a/client/packages/coldchain/src/Equipment/DetailView/UpdateStatusButton.tsx b/client/packages/coldchain/src/Equipment/DetailView/UpdateStatusButton.tsx index ae1d60d732..958dc0a829 100644 --- a/client/packages/coldchain/src/Equipment/DetailView/UpdateStatusButton.tsx +++ b/client/packages/coldchain/src/Equipment/DetailView/UpdateStatusButton.tsx @@ -145,7 +145,7 @@ export const UpdateStatusButtonComponent = ({ nextButton={ currentTab === Tabs.Status ? ( diff --git a/client/packages/coldchain/src/Equipment/ImportAsset/EquipmentImportModal.tsx b/client/packages/coldchain/src/Equipment/ImportAsset/EquipmentImportModal.tsx index 66d03df807..d9e65b8357 100644 --- a/client/packages/coldchain/src/Equipment/ImportAsset/EquipmentImportModal.tsx +++ b/client/packages/coldchain/src/Equipment/ImportAsset/EquipmentImportModal.tsx @@ -255,7 +255,7 @@ export const EquipmentImportModal: FC = ({ diff --git a/client/packages/common/src/hooks/useDialog/useDialog.stories.tsx b/client/packages/common/src/hooks/useDialog/useDialog.stories.tsx index 08d4ca63f5..5e8aabcf15 100644 --- a/client/packages/common/src/hooks/useDialog/useDialog.stories.tsx +++ b/client/packages/common/src/hooks/useDialog/useDialog.stories.tsx @@ -29,7 +29,7 @@ const Template: StoryFn = () => { } - nextButton={} + nextButton={} okButton={} >
This is an example dialog. There's nothing much to see
diff --git a/client/packages/common/src/intl/locales/en/common.json b/client/packages/common/src/intl/locales/en/common.json index 4b741783f7..4dafb56594 100644 --- a/client/packages/common/src/intl/locales/en/common.json +++ b/client/packages/common/src/intl/locales/en/common.json @@ -67,11 +67,13 @@ "button.new-return": "New Return", "button.new-shipment": "New Shipment", "button.new-stock": "New Stock", + "button.next": "Next", "button.next-step": "Next step", "button.ok": "OK", "button.ok-and-next": "OK & Next", "button.open-the-menu": "Open the menu", "button.order-more": "Order more", + "button.previous": "Previous", "button.print": "Print", "button.print-qr": "Print QR code", "button.reduce-lines-to-zero": "Reduce lines to 0", @@ -142,6 +144,7 @@ "customers": "Customers", "dashboard": "Dashboard", "description.already-issued": "Quantity already issued in shipments.", + "description.available-stock": "The customer's initial stock on hand + incoming stock +/- inventory adjustments - outgoing stock", "description.average-monthly-consumption": "Average Monthly Consumption", "description.breach-type": "Ongoing breach type", "description.bundle-ratio": "The number of units of the bundled item for every unit of the base item", @@ -157,7 +160,6 @@ "description.fc-sell-price": "Foreign currency sell price per pack", "description.forecast-quantity": "Forecast Quantity to Reach Target", "description.initial-stock-on-hand": "Stock on hand on the first day of the program period", - "description.available-stock": "The customer's initial stock on hand + incoming stock +/- inventory adjustments - outgoing stock", "description.invoice-number": "Shipment number", "description.last-reading-datetime": "Date and time of the last reading", "description.max-min-temperature": "Maximum or minimum temperature reading", @@ -225,6 +227,7 @@ "error.date_minDate": "Date value is too low", "error.dose-ages-out-of-order": "Each dose needs a higher minimum age than the previous dose", "error.duplicate-asset-number": "duplicate asset number in csv", + "error.duplicate-item-variant-name": "Item variant with the same name already exists for this item", "error.duplicated-field": "{{field}} already exists in the import file", "error.encounter-not-created": "Unable to create encounter", "error.encounter-not-found": "Encounter not found.", @@ -241,7 +244,6 @@ "error.failed-to-save-item-variant": "Failed to save item variant", "error.failed-to-save-service-charges": "Failed to save service charges", "error.failed-to-save-vaccination": "Failed to save vaccination!", - "error.duplicate-item-variant-name": "Item variant with the same name already exists for this item", "error.failed-to-save-vaccine-course": "Failed to save vaccine course", "error.fetch-notifications": "Unable to display cold chain notifications", "error.field-must-be-specified": "{{field}} must be specified", @@ -738,11 +740,11 @@ "label.invoice-number": "Number", "label.is-vaccine": "Vaccine", "label.issue": "Issue", + "label.item-variant": "Item Variant", "label.item_few": "Items", "label.item_many": "Items", "label.item_one": "Item", "label.item_other": "Items", - "label.item-variant": "Item Variant", "label.items-expiring-before": "Items expiring before", "label.items-no-stock_few": "Items with no stock", "label.items-no-stock_many": "Items with no stock", @@ -1366,8 +1368,8 @@ "messages.new-sensor": "A new sensor has been added as a result of importing these logs. Would you like to assign a location to the new sensor now?", "messages.no": "No", "messages.no-available-periods": "No available periods found for schedule", - "messages.no-bundled-items": "No bundled items configured", "messages.no-batch-selected": "You have not selected a batch for this vaccination. No stock transaction will be created. Are you sure you want to continue?", + "messages.no-bundled-items": "No bundled items configured", "messages.no-contact-traces": "This patient has no contact traces", "messages.no-data-available": "No data available", "messages.no-data-found": "No data found", @@ -1634,8 +1636,8 @@ "text.months": "Months", "title.adjustment-details": "Adjustment Details", "title.amc": "AMC", - "title.bundled-on": "Bundled on", "title.bundle-with": "Bundle with", + "title.bundled-on": "Bundled on", "title.categories": "Categories", "title.confirm-delete-encounter": "Delete encounter", "title.details": "Details", diff --git a/client/packages/common/src/types/schema.ts b/client/packages/common/src/types/schema.ts index 50ff60f2e2..b13b063d9f 100644 --- a/client/packages/common/src/types/schema.ts +++ b/client/packages/common/src/types/schema.ts @@ -94,6 +94,10 @@ export enum ActivityLogNodeType { AssetLogReasonDeleted = 'ASSET_LOG_REASON_DELETED', AssetPropertyCreated = 'ASSET_PROPERTY_CREATED', AssetUpdated = 'ASSET_UPDATED', + DemographicIndicatorCreated = 'DEMOGRAPHIC_INDICATOR_CREATED', + DemographicIndicatorUpdated = 'DEMOGRAPHIC_INDICATOR_UPDATED', + DemographicProjectionCreated = 'DEMOGRAPHIC_PROJECTION_CREATED', + DemographicProjectionUpdated = 'DEMOGRAPHIC_PROJECTION_UPDATED', InventoryAdjustment = 'INVENTORY_ADJUSTMENT', InvoiceCreated = 'INVOICE_CREATED', InvoiceDeleted = 'INVOICE_DELETED', @@ -2969,21 +2973,9 @@ export type InsertResponseRequisitionLineErrorInterface = { }; export type InsertResponseRequisitionLineInput = { - additionInUnits?: InputMaybe; - averageMonthlyConsumption?: InputMaybe; - comment?: InputMaybe; - daysOutOfStock?: InputMaybe; - expiringUnits?: InputMaybe; id: Scalars['String']['input']; - incomingUnits?: InputMaybe; itemId: Scalars['String']['input']; - lossInUnits?: InputMaybe; - optionId?: InputMaybe; - outgoingUnits?: InputMaybe; - requestedQuantity?: InputMaybe; requisitionId: Scalars['String']['input']; - stockOnHand?: InputMaybe; - supplyQuantity?: InputMaybe; }; export type InsertResponseRequisitionLineResponse = InsertResponseRequisitionLineError | RequisitionLineNode; @@ -6510,6 +6502,7 @@ export type RequisitionLineNode = { remainingQuantityToSupply: Scalars['Float']['output']; /** Quantity requested */ requestedQuantity: Scalars['Float']['output']; + requisitionNumber: Scalars['Int']['output']; /** * Calculated quantity * When months_of_stock < requisition.min_months_of_stock, calculated = average_monthly_consumption * requisition.max_months_of_stock - months_of_stock diff --git a/client/packages/common/src/ui/components/buttons/ButtonShowcase.stories.tsx b/client/packages/common/src/ui/components/buttons/ButtonShowcase.stories.tsx index f16816fc02..f3fb0700b1 100644 --- a/client/packages/common/src/ui/components/buttons/ButtonShowcase.stories.tsx +++ b/client/packages/common/src/ui/components/buttons/ButtonShowcase.stories.tsx @@ -115,7 +115,10 @@ const Template: StoryFn<{ color: 'primary' | 'secondary' }> = ({ color }) => { - + diff --git a/client/packages/common/src/ui/components/buttons/standard/DialogButton.tsx b/client/packages/common/src/ui/components/buttons/standard/DialogButton.tsx index d6dbbd76a5..b8559a57d5 100644 --- a/client/packages/common/src/ui/components/buttons/standard/DialogButton.tsx +++ b/client/packages/common/src/ui/components/buttons/standard/DialogButton.tsx @@ -15,6 +15,8 @@ import { ButtonWithIcon } from './ButtonWithIcon'; type DialogButtonVariant = | 'cancel' | 'back' + | 'previous' + | 'next-and-ok' | 'next' | 'ok' | 'save' @@ -63,12 +65,18 @@ const getButtonProps = ( labelKey: 'button.ok', variant: 'contained', }; - case 'next': + case 'next-and-ok': return { icon: , labelKey: 'button.ok-and-next', variant: 'contained', }; + case 'next': + return { + icon: , + labelKey: 'button.next', + variant: 'contained', + }; case 'save': return { icon: , @@ -99,6 +107,12 @@ const getButtonProps = ( labelKey: 'button.close', variant: 'outlined', }; + case 'previous': + return { + icon: , + labelKey: 'button.previous', + variant: 'outlined', + }; } }; diff --git a/client/packages/common/src/ui/components/navigation/ListOptions/ListOptions.tsx b/client/packages/common/src/ui/components/navigation/ListOptions/ListOptions.tsx new file mode 100644 index 0000000000..8f7665cc87 --- /dev/null +++ b/client/packages/common/src/ui/components/navigation/ListOptions/ListOptions.tsx @@ -0,0 +1,87 @@ +import { CheckIcon, ChevronDownIcon } from '@common/icons'; +import { + List, + ListItemIcon, + ListItem, + ListItemText, + Divider, + Box, +} from '@mui/material'; +import React from 'react'; + +export type ListOptionValues = { + id: string; + value: string; +}; + +interface ListProps { + onClick: (id: string) => void; + options: ListOptionValues[]; + currentId?: string; + enteredLineIds?: string[]; +} + +export const ListOptions = ({ + onClick, + options, + currentId, + enteredLineIds, +}: ListProps) => { + const startIcon = ( + + ); + + const endIcon = ( + + ); + + return ( + + {options?.map((option, _) => ( + + onClick(option.id)} + > + + + {startIcon} + + + + + + {endIcon} + + + + + + ))} + + ); +}; diff --git a/client/packages/common/src/ui/components/navigation/ListOptions/index.ts b/client/packages/common/src/ui/components/navigation/ListOptions/index.ts new file mode 100644 index 0000000000..225a6a5b73 --- /dev/null +++ b/client/packages/common/src/ui/components/navigation/ListOptions/index.ts @@ -0,0 +1 @@ +export * from './ListOptions'; diff --git a/client/packages/common/src/ui/components/navigation/index.ts b/client/packages/common/src/ui/components/navigation/index.ts index 4e8b74981d..1c37703ad9 100644 --- a/client/packages/common/src/ui/components/navigation/index.ts +++ b/client/packages/common/src/ui/components/navigation/index.ts @@ -3,3 +3,4 @@ export * from './AppNavSection'; export * from './Breadcrumbs'; export * from './ExternalNavLink'; export * from './Tabs'; +export * from './ListOptions'; diff --git a/client/packages/inventory/src/Stocktake/DetailView/modal/StocktakeLineEdit/StocktakeLineEditModal.tsx b/client/packages/inventory/src/Stocktake/DetailView/modal/StocktakeLineEdit/StocktakeLineEditModal.tsx index ce4116bfc5..3b3e434d36 100644 --- a/client/packages/inventory/src/Stocktake/DetailView/modal/StocktakeLineEdit/StocktakeLineEditModal.tsx +++ b/client/packages/inventory/src/Stocktake/DetailView/modal/StocktakeLineEdit/StocktakeLineEditModal.tsx @@ -38,7 +38,7 @@ export const StocktakeLineEditModal: FC< cancelButton={} nextButton={ diff --git a/client/packages/invoices/src/InboundShipment/DetailView/modals/InboundLineEdit/InboundLineEdit.tsx b/client/packages/invoices/src/InboundShipment/DetailView/modals/InboundLineEdit/InboundLineEdit.tsx index f6283441fb..45b49b807b 100644 --- a/client/packages/invoices/src/InboundShipment/DetailView/modals/InboundLineEdit/InboundLineEdit.tsx +++ b/client/packages/invoices/src/InboundShipment/DetailView/modals/InboundLineEdit/InboundLineEdit.tsx @@ -155,7 +155,7 @@ export const InboundLineEdit: FC = ({ cancelButton={} nextButton={ { await saveLines(); diff --git a/client/packages/invoices/src/OutboundShipment/DetailView/OutboundLineEdit/OutboundLineEdit.tsx b/client/packages/invoices/src/OutboundShipment/DetailView/OutboundLineEdit/OutboundLineEdit.tsx index 0313bb54a8..e5a4a87be5 100644 --- a/client/packages/invoices/src/OutboundShipment/DetailView/OutboundLineEdit/OutboundLineEdit.tsx +++ b/client/packages/invoices/src/OutboundShipment/DetailView/OutboundLineEdit/OutboundLineEdit.tsx @@ -222,7 +222,7 @@ export const OutboundLineEdit: React.FC = ({ nextButton={ } diff --git a/client/packages/invoices/src/Prescriptions/DetailView/PrescriptionLineEdit/PrescriptionLineEdit.tsx b/client/packages/invoices/src/Prescriptions/DetailView/PrescriptionLineEdit/PrescriptionLineEdit.tsx index 9bcaeb3d30..ba0903e560 100644 --- a/client/packages/invoices/src/Prescriptions/DetailView/PrescriptionLineEdit/PrescriptionLineEdit.tsx +++ b/client/packages/invoices/src/Prescriptions/DetailView/PrescriptionLineEdit/PrescriptionLineEdit.tsx @@ -196,7 +196,7 @@ export const PrescriptionLineEdit: React.FC = ({ nextButton={ } diff --git a/client/packages/invoices/src/Returns/modals/CustomerReturn/CustomerReturn.tsx b/client/packages/invoices/src/Returns/modals/CustomerReturn/CustomerReturn.tsx index 2f881b1351..ac6d342ba8 100644 --- a/client/packages/invoices/src/Returns/modals/CustomerReturn/CustomerReturn.tsx +++ b/client/packages/invoices/src/Returns/modals/CustomerReturn/CustomerReturn.tsx @@ -120,7 +120,7 @@ export const CustomerReturnEditModal = ({ const NextStepButton = ( @@ -129,7 +129,7 @@ export const CustomerReturnEditModal = ({ const OkAndNextButton = ( @@ -127,7 +127,7 @@ export const SupplierReturnEditModal = ({ const OkAndNextButton = ( { await save(); setPreviousItemLineId(null); diff --git a/client/packages/requisitions/src/RequisitionService.tsx b/client/packages/requisitions/src/RequisitionService.tsx index 181aa9c1db..8d776808b6 100644 --- a/client/packages/requisitions/src/RequisitionService.tsx +++ b/client/packages/requisitions/src/RequisitionService.tsx @@ -7,6 +7,7 @@ import { import { ListView as ResponseRequisitionListView, DetailView as ResponseRequisitionDetailView, + ResponseLineEditPage, } from './ResponseRequisition'; import { RouteBuilder, Routes, Route } from '@openmsupply-client/common'; import { AppRoute } from '@openmsupply-client/config'; @@ -21,6 +22,13 @@ const customerRequisitionRoute = RouteBuilder.create( .addPart(':requisitionNumber') .build(); +const customerRequisitionLineRoute = RouteBuilder.create( + AppRoute.CustomerRequisition +) + .addPart(':requisitionNumber') + .addPart(':itemId') + .build(); + const internalOrdersRoute = RouteBuilder.create(AppRoute.InternalOrder).build(); const internalOrderRoute = RouteBuilder.create(AppRoute.InternalOrder) .addPart(':requisitionNumber') @@ -43,6 +51,10 @@ export const RequisitionService: FC = () => { path={customerRequisitionRoute} element={} /> + } + /> } diff --git a/client/packages/requisitions/src/ResponseRequisition/DetailView/AppBarButtons/AppBarButtons.tsx b/client/packages/requisitions/src/ResponseRequisition/DetailView/AppBarButtons/AppBarButtons.tsx index 88a2c97e61..16d0d2ddc1 100644 --- a/client/packages/requisitions/src/ResponseRequisition/DetailView/AppBarButtons/AppBarButtons.tsx +++ b/client/packages/requisitions/src/ResponseRequisition/DetailView/AppBarButtons/AppBarButtons.tsx @@ -1,8 +1,10 @@ import React from 'react'; import { AppBarButtonsPortal, + ButtonWithIcon, Grid, LoadingButton, + PlusCircleIcon, PrinterIcon, ReportContext, useDetailPanel, @@ -17,11 +19,24 @@ import { SupplyRequestedQuantityButton } from './SupplyRequestedQuantityButton'; import { useResponse } from '../../api'; import { JsonData } from '@openmsupply-client/programs'; -export const AppBarButtonsComponent = () => { +interface AppBarButtonProps { + isDisabled: boolean; + hasLinkedRequisition: boolean; + isProgram: boolean; + onAddItem: (newState: boolean) => void; +} + +export const AppBarButtonsComponent = ({ + isDisabled, + hasLinkedRequisition, + isProgram, + onAddItem, +}: AppBarButtonProps) => { + const t = useTranslation(); const { OpenButton } = useDetailPanel(); const { data } = useResponse.document.get(); const { print, isPrinting } = usePrintReport(); - const t = useTranslation(); + const disableAddButton = isDisabled || isProgram || hasLinkedRequisition; const printReport = ( report: ReportRowFragment, @@ -34,6 +49,13 @@ export const AppBarButtonsComponent = () => { return ( + } + onClick={() => onAddItem(true)} + /> + void; onRowClick: null | ((line: ResponseLineFragment) => void); + disableAddLine: boolean; } -export const ContentArea = ({ onRowClick }: ContentAreaProps) => { +export const ContentArea = ({ + onRowClick, + onAddItem, + disableAddLine, +}: ContentAreaProps) => { + const t = useTranslation(); const { columns, lines } = useResponse.line.list(); return ( @@ -15,7 +26,12 @@ export const ContentArea = ({ onRowClick }: ContentAreaProps) => { onRowClick={onRowClick} columns={columns} data={lines} - noDataElement={} + noDataElement={ + + } /> ); }; diff --git a/client/packages/requisitions/src/ResponseRequisition/DetailView/DetailView.tsx b/client/packages/requisitions/src/ResponseRequisition/DetailView/DetailView.tsx index 23ab4b0ac5..6323205c57 100644 --- a/client/packages/requisitions/src/ResponseRequisition/DetailView/DetailView.tsx +++ b/client/packages/requisitions/src/ResponseRequisition/DetailView/DetailView.tsx @@ -10,37 +10,55 @@ import { useEditModal, createQueryParamsStore, DetailTabs, + BasicModal, + Box, + FnUtils, } from '@openmsupply-client/common'; import { AppRoute } from '@openmsupply-client/config'; -import { ActivityLogList } from '@openmsupply-client/system'; +import { + ActivityLogList, + ItemRowFragment, + StockItemSearchInput, +} from '@openmsupply-client/system'; import { Toolbar } from './Toolbar/Toolbar'; import { Footer } from './Footer'; import { AppBarButtons } from './AppBarButtons'; import { SidePanel } from './SidePanel'; import { ContentArea } from './ContentArea'; import { useResponse, ResponseLineFragment } from '../api'; -import { ResponseLineEdit } from './ResponseLineEdit'; export const DetailView: FC = () => { - const isDisabled = useResponse.utils.isDisabled(); - const { onOpen, onClose, entity, isOpen } = - useEditModal(); - const { data, isLoading } = useResponse.document.get(); - const navigate = useNavigate(); const t = useTranslation(); + const navigate = useNavigate(); + const { data, isLoading } = useResponse.document.get(); + const isDisabled = useResponse.utils.isDisabled(); + const { onOpen, isOpen, onClose } = useEditModal(); + const { mutateAsync } = useResponse.line.insert(); - const onRowClick = useCallback( - (line: ResponseLineFragment) => { - onOpen(line); - }, - [onOpen] - ); + const onRowClick = useCallback((line: ResponseLineFragment) => { + navigate( + RouteBuilder.create(AppRoute.Distribution) + .addPart(AppRoute.CustomerRequisition) + .addPart(String(line.requisitionNumber)) + .addPart(String(line.item.id)) + .build(), + { replace: true } + ); + }, []); if (isLoading) return ; const tabs = [ { - Component: , + Component: ( + onOpen(null)} + onRowClick={!isDisabled ? onRowClick : null} + disableAddLine={ + isDisabled || !!data?.linkedRequisition || !!data?.programName + } + /> + ), value: 'Details', }, { @@ -56,14 +74,45 @@ export const DetailView: FC = () => { initialSortBy: { key: 'itemName' }, })} > - + onOpen(null)} + />