Feat/show order as allocated (#3497)

**What**
- Add check to see if any items is unfulfilled when validating reservations for allocation indicator
- Add reservations refresh to onSuccess when creating a fulfillment to automatically update indicators on fulfillment



Fixes CORE-1248

Co-authored-by: Sebastian Rindom <7554214+srindom@users.noreply.github.com>
This commit is contained in:
Philip KorsholmandSebastian Rindom authored and GitHub committed 2023-03-16 15:49:00 +00:00
1 parent e6e5291527
commit 2e12f13565
6 files changed
+54 -41

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/admin-ui": patch
---
fix(admin-ui): fix(admin-ui): ensure that the allocation indicator is correctly displayed
@@ -26,26 +26,19 @@ const EditAllocationDrawer = ({
close, close,
reservation, reservation,
item, item,
sales_channel_id,
totalReservedQuantity, totalReservedQuantity,
}: { }: {
close: () => void close: () => void
reservation?: ReservationItemDTO reservation?: ReservationItemDTO
item: LineItem item: LineItem
totalReservedQuantity: number totalReservedQuantity: number
sales_channel_id?: string
}) => { }) => {
const form = useForm<EditAllocationLineItemForm>() const form = useForm<EditAllocationLineItemForm>()
const { control, setValue, handleSubmit } = form const { control, setValue, handleSubmit } = form
// if not sales channel is present fetch all locations const { stock_locations, isLoading: isLoadingStockLocations } =
const stockLocationsFilter: { sales_channel_id?: string } = {} useAdminStockLocations()
if (sales_channel_id) {
stockLocationsFilter.sales_channel_id = sales_channel_id
}
const { stock_locations } = useAdminStockLocations(stockLocationsFilter)
const { variant, isLoading } = useAdminVariantsInventory( const { variant, isLoading } = useAdminVariantsInventory(
item.variant_id as string item.variant_id as string
@@ -59,14 +52,14 @@ const EditAllocationDrawer = ({
) )
const locationOptions = useMemo(() => { const locationOptions = useMemo(() => {
if (!stock_locations) { if (!stock_locations || isLoadingStockLocations) {
return [] return []
} }
return stock_locations.map((sl) => ({ return stock_locations.map((sl) => ({
value: sl.id, value: sl.id,
label: sl.name, label: sl.name,
})) }))
}, [stock_locations]) }, [isLoadingStockLocations, stock_locations])
const notification = useNotification() const notification = useNotification()
const handleDelete = () => { const handleDelete = () => {
@@ -36,12 +36,14 @@ type CreateFulfillmentModalProps = {
email?: string email?: string
orderToFulfill: Order | ClaimOrder | Swap orderToFulfill: Order | ClaimOrder | Swap
orderId: string orderId: string
onComplete?: () => void
} }
const CreateFulfillmentModal: React.FC<CreateFulfillmentModalProps> = ({ const CreateFulfillmentModal: React.FC<CreateFulfillmentModalProps> = ({
handleCancel, handleCancel,
orderToFulfill, orderToFulfill,
orderId, orderId,
onComplete,
}) => { }) => {
const { isFeatureEnabled } = useFeatureFlag() const { isFeatureEnabled } = useFeatureFlag()
const isLocationFulfillmentEnabled = const isLocationFulfillmentEnabled =
@@ -187,6 +189,7 @@ const CreateFulfillmentModal: React.FC<CreateFulfillmentModalProps> = ({
onSuccess: () => { onSuccess: () => {
notification("Success", successText, "success") notification("Success", successText, "success")
handleCancel() handleCancel()
onComplete && onComplete()
}, },
onError: (err) => notification("Error", getErrorMessage(err), "error"), onError: (err) => notification("Error", getErrorMessage(err), "error"),
}) })
@@ -11,19 +11,15 @@ import { OrderEditContext } from "../../edit/context"
import OrderLine from "../order-line" import OrderLine from "../order-line"
import StatusIndicator from "../../../../components/fundamentals/status-indicator" import StatusIndicator from "../../../../components/fundamentals/status-indicator"
import { sum } from "lodash" import { sum } from "lodash"
import { useAdminReservations } from "medusa-react"
import { useFeatureFlag } from "../../../../providers/feature-flag-provider" import { useFeatureFlag } from "../../../../providers/feature-flag-provider"
import useToggleState from "../../../../hooks/use-toggle-state" import useToggleState from "../../../../hooks/use-toggle-state"
type SummaryCardProps = { type SummaryCardProps = {
order: Order order: Order
reservations: ReservationItemDTO[]
} }
const SummaryCard: React.FC<SummaryCardProps> = ({ const SummaryCard: React.FC<SummaryCardProps> = ({ order, reservations }) => {
order,
}: {
order: Order
}) => {
const { const {
state: allocationModalIsOpen, state: allocationModalIsOpen,
open: showAllocationModal, open: showAllocationModal,
@@ -34,23 +30,8 @@ const SummaryCard: React.FC<SummaryCardProps> = ({
const { isFeatureEnabled } = useFeatureFlag() const { isFeatureEnabled } = useFeatureFlag()
const inventoryEnabled = isFeatureEnabled("inventoryService") const inventoryEnabled = isFeatureEnabled("inventoryService")
const { reservations, isLoading, refetch } = useAdminReservations(
{
line_item_id: order.items.map((item) => item.id),
},
{
enabled: inventoryEnabled,
}
)
React.useEffect(() => {
if (inventoryEnabled) {
refetch()
}
}, [inventoryEnabled, refetch])
const reservationItemsMap = useMemo(() => { const reservationItemsMap = useMemo(() => {
if (!reservations?.length || !inventoryEnabled || isLoading) { if (!reservations?.length || !inventoryEnabled) {
return {} return {}
} }
@@ -66,15 +47,17 @@ const SummaryCard: React.FC<SummaryCardProps> = ({
}, },
{} {}
) )
}, [reservations, inventoryEnabled, isLoading]) }, [reservations, inventoryEnabled])
const allItemsReserved = useMemo(() => { const allItemsReserved = useMemo(() => {
return order.items.every((item) => { return order.items.every((item) => {
const reservations = reservationItemsMap[item.id] const reservations = reservationItemsMap[item.id]
if (!reservations) {
return false return (
} item.quantity === item.fulfilled_quantity ||
return sum(reservations.map((r) => r.quantity)) === item.quantity (reservations &&
sum(reservations.map((r) => r.quantity)) === item.quantity)
)
}) })
}, [reservationItemsMap, order]) }, [reservationItemsMap, order])
@@ -14,6 +14,7 @@ import {
useAdminCapturePayment, useAdminCapturePayment,
useAdminOrder, useAdminOrder,
useAdminRegion, useAdminRegion,
useAdminReservations,
useAdminUpdateOrder, useAdminUpdateOrder,
} from "medusa-react" } from "medusa-react"
import { useNavigate, useParams } from "react-router-dom" import { useNavigate, useParams } from "react-router-dom"
@@ -55,8 +56,9 @@ import useClipboard from "../../../hooks/use-clipboard"
import { useHotkeys } from "react-hotkeys-hook" import { useHotkeys } from "react-hotkeys-hook"
import useImperativeDialog from "../../../hooks/use-imperative-dialog" import useImperativeDialog from "../../../hooks/use-imperative-dialog"
import useNotification from "../../../hooks/use-notification" import useNotification from "../../../hooks/use-notification"
import { useState } from "react" import { useEffect, useState } from "react"
import useToggleState from "../../../hooks/use-toggle-state" import useToggleState from "../../../hooks/use-toggle-state"
import { useFeatureFlag } from "../../../providers/feature-flag-provider"
type OrderDetailFulfillment = { type OrderDetailFulfillment = {
title: string title: string
@@ -145,6 +147,23 @@ const OrderDetails = () => {
const { region } = useAdminRegion(order?.region_id!, { const { region } = useAdminRegion(order?.region_id!, {
enabled: !!order?.region_id, enabled: !!order?.region_id,
}) })
const { isFeatureEnabled } = useFeatureFlag()
const inventoryEnabled = isFeatureEnabled("inventoryService")
const { reservations, refetch: refetchReservations } = useAdminReservations(
{
line_item_id: order?.items.map((item) => item.id),
},
{
enabled: inventoryEnabled,
}
)
useEffect(() => {
if (inventoryEnabled) {
refetchReservations()
}
}, [inventoryEnabled, refetchReservations])
const navigate = useNavigate() const navigate = useNavigate()
const notification = useNotification() const notification = useNotification()
@@ -316,7 +335,7 @@ const OrderDetails = () => {
</div> </div>
</BodyCard> </BodyCard>
<SummaryCard order={order} /> <SummaryCard order={order} reservations={reservations || []} />
<BodyCard <BodyCard
className={"mb-4 h-auto min-h-0 w-full"} className={"mb-4 h-auto min-h-0 w-full"}
@@ -513,6 +532,7 @@ const OrderDetails = () => {
orderToFulfill={order as any} orderToFulfill={order as any}
handleCancel={() => setShowFulfillment(false)} handleCancel={() => setShowFulfillment(false)}
orderId={order.id} orderId={order.id}
onComplete={refetchReservations}
/> />
)} )}
{showRefund && ( {showRefund && (
@@ -94,13 +94,17 @@ const ReservationIndicator = ({
const locationMap = new Map(stock_locations?.map((l) => [l.id, l.name]) || []) const locationMap = new Map(stock_locations?.map((l) => [l.id, l.name]) || [])
const reservationsSum = sum(reservations?.map((r) => r.quantity) || []) const reservationsSum = sum(reservations?.map((r) => r.quantity) || [])
const awaitingAllocation = lineItem.quantity - reservationsSum
const allocatableSum = lineItem.quantity - (lineItem?.fulfilled_quantity || 0)
const awaitingAllocation = allocatableSum - reservationsSum
return ( return (
<div className={awaitingAllocation ? "text-rose-50" : "text-grey-40"}> <div className={awaitingAllocation ? "text-rose-50" : "text-grey-40"}>
<Tooltip <Tooltip
content={ content={
<div className="inter-small-regular flex flex-col items-center px-1 pt-1 pb-2"> <div className="inter-small-regular flex flex-col items-center px-1 pt-1 pb-2">
{reservationsSum || awaitingAllocation ? (
<div className="gap-y-base grid grid-cols-1 divide-y"> <div className="gap-y-base grid grid-cols-1 divide-y">
{!!awaitingAllocation && ( {!!awaitingAllocation && (
<span className="flex w-full items-center"> <span className="flex w-full items-center">
@@ -118,6 +122,11 @@ const ReservationIndicator = ({
/> />
))} ))}
</div> </div>
) : (
<span className="flex w-full items-center">
This item has been fulfilled.
</span>
)}
</div> </div>
} }
side="bottom" side="bottom"