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