feat(dashboard): allow custom shopping prices for claims/exchanges (#8912)
**What** - add editing custom shipping price for claims and exchanges --- CLOSES CC-369
This commit is contained in:
+135
-23
@@ -47,6 +47,7 @@ import {
|
||||
useRemoveClaimInboundItem,
|
||||
useUpdateClaimInboundItem,
|
||||
useUpdateClaimInboundShipping,
|
||||
useUpdateClaimOutboundShipping,
|
||||
} from "../../../../../hooks/api/claims"
|
||||
import { useUpdateReturn } from "../../../../../hooks/api/returns.tsx"
|
||||
import { sdk } from "../../../../../lib/client"
|
||||
@@ -78,10 +79,17 @@ export const ClaimCreateForm = ({
|
||||
* STATE
|
||||
*/
|
||||
const { setIsOpen } = useStackedModal()
|
||||
const [isShippingPriceEdit, setIsShippingPriceEdit] = useState(false)
|
||||
const [customShippingAmount, setCustomShippingAmount] = useState<
|
||||
number | string
|
||||
>(0)
|
||||
const [isShippingInboundPriceEdit, setIsShippingInboundPriceEdit] =
|
||||
useState(false)
|
||||
const [isShippingOutboundPriceEdit, setIsShippingOutboundPriceEdit] =
|
||||
useState(false)
|
||||
|
||||
const [customInboundShippingAmount, setCustomInboundShippingAmount] =
|
||||
useState<number | string>(0)
|
||||
|
||||
const [customOutboundShippingAmount, setCustomOutboundShippingAmount] =
|
||||
useState<number | string>(0)
|
||||
|
||||
const [inventoryMap, setInventoryMap] = useState<
|
||||
Record<string, InventoryLevelDTO[]>
|
||||
>({})
|
||||
@@ -113,6 +121,11 @@ export const ClaimCreateForm = ({
|
||||
isPending: isUpdatingInboundShipping,
|
||||
} = useUpdateClaimInboundShipping(claim.id, order.id)
|
||||
|
||||
const {
|
||||
mutateAsync: updateOutboundShipping,
|
||||
isPending: isUpdatingOutboundShipping,
|
||||
} = useUpdateClaimOutboundShipping(claim.id, order.id)
|
||||
|
||||
const {
|
||||
mutateAsync: deleteInboundShipping,
|
||||
isPending: isDeletingInboundShipping,
|
||||
@@ -133,6 +146,7 @@ export const ClaimCreateForm = ({
|
||||
isAddingInboundShipping ||
|
||||
isUpdatingInboundShipping ||
|
||||
isDeletingInboundShipping ||
|
||||
isUpdatingOutboundShipping ||
|
||||
isAddingInboundItem ||
|
||||
isRemovingInboundItem ||
|
||||
isUpdatingInboundItem ||
|
||||
@@ -235,10 +249,28 @@ export const ClaimCreateForm = ({
|
||||
)
|
||||
)
|
||||
|
||||
const inboundShipping = preview.shipping_methods.find((s) => {
|
||||
return !!s.actions?.find(
|
||||
(a) => a.action === "SHIPPING_ADD" && !!a.return_id
|
||||
)
|
||||
})
|
||||
|
||||
const outboundShipping = preview.shipping_methods.find((s) => {
|
||||
return !!s.actions?.find((a) => a.action === "SHIPPING_ADD" && !a.return_id)
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (inboundShipping) {
|
||||
setCustomInboundShippingAmount(inboundShipping.total)
|
||||
}
|
||||
}, [inboundShipping])
|
||||
|
||||
useEffect(() => {
|
||||
if (outboundShipping) {
|
||||
setCustomOutboundShippingAmount(outboundShipping.total)
|
||||
}
|
||||
}, [outboundShipping])
|
||||
|
||||
const {
|
||||
fields: inboundItems,
|
||||
append,
|
||||
@@ -291,7 +323,7 @@ export const ClaimCreateForm = ({
|
||||
}, [previewItems])
|
||||
|
||||
useEffect(() => {
|
||||
const method = preview.shipping_methods.find(
|
||||
let method = preview.shipping_methods.find(
|
||||
(s) =>
|
||||
!!s.actions?.find((a) => a.action === "SHIPPING_ADD" && !!a.return_id)
|
||||
)
|
||||
@@ -299,14 +331,27 @@ export const ClaimCreateForm = ({
|
||||
if (method) {
|
||||
form.setValue("inbound_option_id", method.shipping_option_id)
|
||||
}
|
||||
|
||||
method = preview.shipping_methods.find(
|
||||
(s) =>
|
||||
!!s.actions?.find((a) => a.action === "SHIPPING_ADD" && !a.return_id)
|
||||
)
|
||||
|
||||
if (method) {
|
||||
form.setValue("outbound_option_id", method.shipping_option_id)
|
||||
}
|
||||
}, [preview.shipping_methods])
|
||||
|
||||
useEffect(() => {
|
||||
form.setValue("location_id", orderReturn?.location_id)
|
||||
}, [orderReturn])
|
||||
|
||||
const showInboundItemsPlaceholder = !inboundItems.length
|
||||
const shippingOptionId = form.watch("inbound_option_id")
|
||||
const showInboundItemsPlaceholder = !inboundPreviewItems.length
|
||||
const showOutboundItemsPlaceholder = !outboundPreviewItems.length
|
||||
|
||||
const inboundShippingOptionId = form.watch("inbound_option_id")
|
||||
const outboundShippingOptionId = form.watch("outbound_option_id")
|
||||
|
||||
const prompt = usePrompt()
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (data) => {
|
||||
@@ -393,10 +438,16 @@ export const ClaimCreateForm = ({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isShippingPriceEdit) {
|
||||
document.getElementById("js-shipping-input")?.focus()
|
||||
if (isShippingInboundPriceEdit) {
|
||||
document.getElementById("js-shipping-inbound-input")?.focus()
|
||||
}
|
||||
}, [isShippingPriceEdit])
|
||||
}, [isShippingInboundPriceEdit])
|
||||
|
||||
useEffect(() => {
|
||||
if (isShippingOutboundPriceEdit) {
|
||||
document.getElementById("js-shipping-outbound-input")?.focus()
|
||||
}
|
||||
}, [isShippingOutboundPriceEdit])
|
||||
|
||||
const showLevelsWarning = useMemo(() => {
|
||||
if (!locationId) {
|
||||
@@ -753,22 +804,22 @@ export const ClaimCreateForm = ({
|
||||
</span>
|
||||
|
||||
<span className="txt-small text-ui-fg-subtle flex items-center">
|
||||
{!isShippingPriceEdit && (
|
||||
{!isShippingInboundPriceEdit && (
|
||||
<IconButton
|
||||
onClick={() => setIsShippingPriceEdit(true)}
|
||||
onClick={() => setIsShippingInboundPriceEdit(true)}
|
||||
variant="transparent"
|
||||
className="text-ui-fg-muted"
|
||||
disabled={
|
||||
showInboundItemsPlaceholder || !shippingOptionId
|
||||
showInboundItemsPlaceholder || !inboundShippingOptionId
|
||||
}
|
||||
>
|
||||
<PencilSquare />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
{isShippingPriceEdit ? (
|
||||
{isShippingInboundPriceEdit ? (
|
||||
<CurrencyInput
|
||||
id="js-shipping-input"
|
||||
id="js-shipping-inbound-input"
|
||||
onBlur={() => {
|
||||
let actionId
|
||||
|
||||
@@ -786,9 +837,9 @@ export const ClaimCreateForm = ({
|
||||
})
|
||||
|
||||
const customPrice =
|
||||
customShippingAmount === ""
|
||||
customInboundShippingAmount === ""
|
||||
? null
|
||||
: parseFloat(customShippingAmount)
|
||||
: parseFloat(customInboundShippingAmount)
|
||||
|
||||
if (actionId) {
|
||||
updateInboundShipping(
|
||||
@@ -803,15 +854,15 @@ export const ClaimCreateForm = ({
|
||||
}
|
||||
)
|
||||
}
|
||||
setIsShippingPriceEdit(false)
|
||||
setIsShippingInboundPriceEdit(false)
|
||||
}}
|
||||
symbol={
|
||||
currencies[order.currency_code.toUpperCase()]
|
||||
.symbol_native
|
||||
}
|
||||
code={order.currency_code}
|
||||
onValueChange={setCustomShippingAmount}
|
||||
value={customShippingAmount}
|
||||
onValueChange={setCustomInboundShippingAmount}
|
||||
value={customInboundShippingAmount}
|
||||
disabled={showInboundItemsPlaceholder}
|
||||
/>
|
||||
) : (
|
||||
@@ -826,9 +877,70 @@ export const ClaimCreateForm = ({
|
||||
</span>
|
||||
|
||||
<span className="txt-small text-ui-fg-subtle flex items-center">
|
||||
{getStylizedAmount(
|
||||
outboundShipping?.amount ?? 0,
|
||||
order.currency_code
|
||||
{!isShippingOutboundPriceEdit && (
|
||||
<IconButton
|
||||
onClick={() => setIsShippingOutboundPriceEdit(true)}
|
||||
variant="transparent"
|
||||
className="text-ui-fg-muted"
|
||||
disabled={
|
||||
showOutboundItemsPlaceholder ||
|
||||
!outboundShippingOptionId
|
||||
}
|
||||
>
|
||||
<PencilSquare />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
{isShippingOutboundPriceEdit ? (
|
||||
<CurrencyInput
|
||||
id="js-shipping-outbound-input"
|
||||
onBlur={() => {
|
||||
let actionId
|
||||
|
||||
preview.shipping_methods.forEach((s) => {
|
||||
if (s.actions) {
|
||||
for (const a of s.actions) {
|
||||
if (a.action === "SHIPPING_ADD" && !a.return_id) {
|
||||
actionId = a.id
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const customPrice =
|
||||
customOutboundShippingAmount === ""
|
||||
? null
|
||||
: parseFloat(customOutboundShippingAmount)
|
||||
|
||||
if (actionId) {
|
||||
updateOutboundShipping(
|
||||
{
|
||||
actionId,
|
||||
custom_price: customPrice,
|
||||
},
|
||||
{
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
setIsShippingOutboundPriceEdit(false)
|
||||
}}
|
||||
symbol={
|
||||
currencies[order.currency_code.toUpperCase()]
|
||||
.symbol_native
|
||||
}
|
||||
code={order.currency_code}
|
||||
onValueChange={setCustomOutboundShippingAmount}
|
||||
value={customOutboundShippingAmount}
|
||||
disabled={showOutboundItemsPlaceholder}
|
||||
/>
|
||||
) : (
|
||||
getStylizedAmount(
|
||||
outboundShipping?.amount ?? 0,
|
||||
order.currency_code
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
+1
@@ -17,6 +17,7 @@ export const ClaimCreateSchema = z.object({
|
||||
),
|
||||
location_id: z.string().optional(),
|
||||
inbound_option_id: z.string().nullish(),
|
||||
outbound_option_id: z.string().nullish(),
|
||||
send_notification: z.boolean().optional(),
|
||||
})
|
||||
|
||||
|
||||
+123
-23
@@ -28,6 +28,7 @@ import {
|
||||
useCancelExchangeRequest,
|
||||
useExchangeConfirmRequest,
|
||||
useUpdateExchangeInboundShipping,
|
||||
useUpdateExchangeOutboundShipping,
|
||||
} from "../../../../../hooks/api/exchanges"
|
||||
import { currencies } from "../../../../../lib/data/currencies"
|
||||
import { ExchangeInboundSection } from "./exchange-inbound-section.tsx"
|
||||
@@ -54,10 +55,14 @@ export const ExchangeCreateForm = ({
|
||||
/**
|
||||
* STATE
|
||||
*/
|
||||
const [isShippingPriceEdit, setIsShippingPriceEdit] = useState(false)
|
||||
const [customShippingAmount, setCustomShippingAmount] = useState<
|
||||
number | string
|
||||
>(0)
|
||||
const [isInboundShippingPriceEdit, setIsInboundShippingPriceEdit] =
|
||||
useState(false)
|
||||
const [isOutboundShippingPriceEdit, setIsOutboundShippingPriceEdit] =
|
||||
useState(false)
|
||||
const [customInboundShippingAmount, setCustomInboundShippingAmount] =
|
||||
useState<number | string>(0)
|
||||
const [customOutboundShippingAmount, setCustomOutboundShippingAmount] =
|
||||
useState<number | string>(0)
|
||||
|
||||
/**
|
||||
* MUTATIONS
|
||||
@@ -70,11 +75,19 @@ export const ExchangeCreateForm = ({
|
||||
|
||||
const {
|
||||
mutateAsync: updateInboundShipping,
|
||||
isPending: isUpdatingInboundShipping,
|
||||
isPending: isUpdatingOutboundShipping,
|
||||
} = useUpdateExchangeInboundShipping(exchange.id, order.id)
|
||||
|
||||
const {
|
||||
mutateAsync: updateOutboundShipping,
|
||||
isPending: isUpdatingInboundShipping,
|
||||
} = useUpdateExchangeOutboundShipping(exchange.id, order.id)
|
||||
|
||||
const isRequestLoading =
|
||||
isConfirming || isCanceling || isUpdatingInboundShipping
|
||||
isConfirming ||
|
||||
isCanceling ||
|
||||
isUpdatingInboundShipping ||
|
||||
isUpdatingOutboundShipping
|
||||
|
||||
/**
|
||||
* Only consider items that belong to this exchange.
|
||||
@@ -144,11 +157,31 @@ export const ExchangeCreateForm = ({
|
||||
resolver: zodResolver(ExchangeCreateSchema),
|
||||
})
|
||||
|
||||
const inboundShipping = preview.shipping_methods.find((s) => {
|
||||
return !!s.actions?.find(
|
||||
(a) => a.action === "SHIPPING_ADD" && !!a.return_id
|
||||
)
|
||||
})
|
||||
|
||||
const outboundShipping = preview.shipping_methods.find((s) => {
|
||||
return !!s.actions?.find((a) => a.action === "SHIPPING_ADD" && !a.return_id)
|
||||
})
|
||||
|
||||
const shippingOptionId = form.watch("inbound_option_id")
|
||||
useEffect(() => {
|
||||
if (inboundShipping) {
|
||||
setCustomInboundShippingAmount(inboundShipping.total)
|
||||
}
|
||||
}, [inboundShipping])
|
||||
|
||||
useEffect(() => {
|
||||
if (outboundShipping) {
|
||||
setCustomOutboundShippingAmount(outboundShipping.total)
|
||||
}
|
||||
}, [outboundShipping])
|
||||
|
||||
const inboundShippingOptionId = form.watch("inbound_option_id")
|
||||
const outboundShippingOptionId = form.watch("outbound_option_id")
|
||||
|
||||
const prompt = usePrompt()
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (data) => {
|
||||
@@ -176,10 +209,16 @@ export const ExchangeCreateForm = ({
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (isShippingPriceEdit) {
|
||||
document.getElementById("js-shipping-input")?.focus()
|
||||
if (isInboundShippingPriceEdit) {
|
||||
document.getElementById("js-inbound-shipping-input")?.focus()
|
||||
}
|
||||
}, [isShippingPriceEdit])
|
||||
}, [isInboundShippingPriceEdit])
|
||||
|
||||
useEffect(() => {
|
||||
if (isOutboundShippingPriceEdit) {
|
||||
document.getElementById("js-outbound-shipping-input")?.focus()
|
||||
}
|
||||
}, [isOutboundShippingPriceEdit])
|
||||
|
||||
useEffect(() => {
|
||||
/**
|
||||
@@ -284,22 +323,22 @@ export const ExchangeCreateForm = ({
|
||||
</span>
|
||||
|
||||
<span className="txt-small text-ui-fg-subtle flex items-center">
|
||||
{!isShippingPriceEdit && (
|
||||
{!isInboundShippingPriceEdit && (
|
||||
<IconButton
|
||||
onClick={() => setIsShippingPriceEdit(true)}
|
||||
onClick={() => setIsInboundShippingPriceEdit(true)}
|
||||
variant="transparent"
|
||||
className="text-ui-fg-muted"
|
||||
disabled={
|
||||
!inboundPreviewItems?.length || !shippingOptionId
|
||||
!inboundPreviewItems?.length || !inboundShippingOptionId
|
||||
}
|
||||
>
|
||||
<PencilSquare />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
{isShippingPriceEdit ? (
|
||||
{isInboundShippingPriceEdit ? (
|
||||
<CurrencyInput
|
||||
id="js-shipping-input"
|
||||
id="js-inbound-shipping-input"
|
||||
onBlur={() => {
|
||||
let actionId
|
||||
|
||||
@@ -317,9 +356,9 @@ export const ExchangeCreateForm = ({
|
||||
})
|
||||
|
||||
const customPrice =
|
||||
customShippingAmount === ""
|
||||
customInboundShippingAmount === ""
|
||||
? null
|
||||
: parseFloat(customShippingAmount)
|
||||
: parseFloat(customInboundShippingAmount)
|
||||
|
||||
if (actionId) {
|
||||
updateInboundShipping(
|
||||
@@ -334,15 +373,15 @@ export const ExchangeCreateForm = ({
|
||||
}
|
||||
)
|
||||
}
|
||||
setIsShippingPriceEdit(false)
|
||||
setIsInboundShippingPriceEdit(false)
|
||||
}}
|
||||
symbol={
|
||||
currencies[order.currency_code.toUpperCase()]
|
||||
.symbol_native
|
||||
}
|
||||
code={order.currency_code}
|
||||
onValueChange={setCustomShippingAmount}
|
||||
value={customShippingAmount}
|
||||
onValueChange={setCustomInboundShippingAmount}
|
||||
value={customInboundShippingAmount}
|
||||
disabled={!inboundPreviewItems?.length}
|
||||
/>
|
||||
) : (
|
||||
@@ -357,9 +396,70 @@ export const ExchangeCreateForm = ({
|
||||
</span>
|
||||
|
||||
<span className="txt-small text-ui-fg-subtle flex items-center">
|
||||
{getStylizedAmount(
|
||||
outboundShipping?.amount ?? 0,
|
||||
order.currency_code
|
||||
{!isOutboundShippingPriceEdit && (
|
||||
<IconButton
|
||||
onClick={() => setIsOutboundShippingPriceEdit(true)}
|
||||
variant="transparent"
|
||||
className="text-ui-fg-muted"
|
||||
disabled={
|
||||
!outboundPreviewItems?.length ||
|
||||
!outboundShippingOptionId
|
||||
}
|
||||
>
|
||||
<PencilSquare />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
{isOutboundShippingPriceEdit ? (
|
||||
<CurrencyInput
|
||||
id="js-outbound-shipping-input"
|
||||
onBlur={() => {
|
||||
let actionId
|
||||
|
||||
preview.shipping_methods.forEach((s) => {
|
||||
if (s.actions) {
|
||||
for (const a of s.actions) {
|
||||
if (a.action === "SHIPPING_ADD" && !a.return_id) {
|
||||
actionId = a.id
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const customPrice =
|
||||
customOutboundShippingAmount === ""
|
||||
? null
|
||||
: parseFloat(customOutboundShippingAmount)
|
||||
|
||||
if (actionId) {
|
||||
updateOutboundShipping(
|
||||
{
|
||||
actionId,
|
||||
custom_price: customPrice,
|
||||
},
|
||||
{
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
setIsOutboundShippingPriceEdit(false)
|
||||
}}
|
||||
symbol={
|
||||
currencies[order.currency_code.toUpperCase()]
|
||||
.symbol_native
|
||||
}
|
||||
code={order.currency_code}
|
||||
onValueChange={setCustomOutboundShippingAmount}
|
||||
value={customOutboundShippingAmount}
|
||||
disabled={!outboundPreviewItems?.length}
|
||||
/>
|
||||
) : (
|
||||
getStylizedAmount(
|
||||
outboundShipping?.amount ?? 0,
|
||||
order.currency_code
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
+2
-1
@@ -11,12 +11,13 @@ export const ExchangeCreateSchema = z.object({
|
||||
),
|
||||
outbound_items: z.array(
|
||||
z.object({
|
||||
item_id: z.string(), // TODO: variant id?
|
||||
item_id: z.string(),
|
||||
quantity: z.number(),
|
||||
})
|
||||
),
|
||||
location_id: z.string().optional(),
|
||||
inbound_option_id: z.string().nullish(),
|
||||
outbound_option_id: z.string().nullish(),
|
||||
send_notification: z.boolean().optional(),
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user