fix(dashboard): RMA allow shipping unset (#12021)

**What**
- allow unsetting shipping in RMA flows + bug cleanup around shipping flows in claims / exchanges
- additional fixes:
  - product type create form 
  - product type delete
  - delete confirmation messages
  - fix a few inventory strings + material field

---

CLOSES CMRC-945
CLOSES CMRC-953
CLOSES CMRC-967
CLOSES CMRC-963
CLOSES CMRC-954


Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Frane Polić
2025-03-28 10:25:44 +01:00
committed by GitHub
parent 29f6c2a8dd
commit 350b6b9a71
15 changed files with 147 additions and 89 deletions

View File

@@ -3034,6 +3034,9 @@
"manageLocations": {
"type": "string"
},
"manageLocationQuantity": {
"type": "string"
},
"deleteWarning": {
"type": "string"
},
@@ -3252,6 +3255,7 @@
"locationLevels",
"associatedVariants",
"manageLocations",
"manageLocationQuantity",
"deleteWarning",
"editItemDetails",
"create",

View File

@@ -811,6 +811,7 @@
"locationLevels": "Locations",
"associatedVariants": "Associated variants",
"manageLocations": "Manage locations",
"manageLocationQuantity": "Manage location quantity",
"deleteWarning": "You are about to delete an inventory item. This action cannot be undone.",
"editItemDetails": "Edit item details",
"create": {
@@ -1501,7 +1502,7 @@
"successToast": "Location {{name}} was successfully updated."
},
"delete": {
"confirmation": "You are about to delete the stock location {{name}}. This action cannot be undone."
"confirmation": "You are about to delete the stock location \"{{name}}\". This action cannot be undone."
},
"fulfillmentProviders": {
"header": "Fulfillment Providers",
@@ -1520,7 +1521,7 @@
"header": "Shipping"
},
"disable": {
"confirmation": "Are you sure that you want to disable {{name}}? This will delete all associated service zones and shipping options, and cannot be undone.",
"confirmation": "Are you sure that you want to disable \"{{name}}\"? This will delete all associated service zones and shipping options, and cannot be undone.",
"pickup": "Pickup was successfully disabled.",
"shipping": "Shipping was successfully disabled."
},
@@ -1577,7 +1578,7 @@
"action": "Create option"
},
"delete": {
"confirmation": "You are about to delete the shipping option {{name}}. This action cannot be undone.",
"confirmation": "You are about to delete the shipping option \"{{name}}\". This action cannot be undone.",
"successToast": "Shipping option {{name}} was successfully deleted."
},
"edit": {
@@ -1666,7 +1667,7 @@
"successToast": "Service zone {{name}} was successfully updated."
},
"delete": {
"confirmation": "You are about to delete the service zone {{name}}. This action cannot be undone.",
"confirmation": "You are about to delete the service zone \"{{name}}\". This action cannot be undone.",
"successToast": "Service zone {{name}} was successfully deleted."
},
"manageAreas": {
@@ -1831,7 +1832,7 @@
"successToast": "Tax rate was successfully updated."
},
"delete": {
"confirmation": "You are about to delete the tax rate {{name}}. This action cannot be undone.",
"confirmation": "You are about to delete the tax rate \"{{name}}\". This action cannot be undone.",
"successToast": "Tax rate was successfully deleted."
}
},
@@ -2089,11 +2090,11 @@
},
"value_type": {
"fixed": {
"title": "Promotion Value",
"title": "Fixed amount",
"description": "The amount to be discounted. eg. 100"
},
"percentage": {
"title": "Promotion Value",
"title": "Percentage",
"description": "The percentage to discount off the amount. eg. 8%"
}
}
@@ -2206,8 +2207,8 @@
"domain": "Price Lists",
"subtitle": "Create sales or override prices for specific conditions.",
"delete": {
"confirmation": "You are about to delete the price list {{title}}. This action cannot be undone.",
"successToast": "Price list {{title}} was successfully deleted."
"confirmation": "You are about to delete the price list \"{{title}}\". This action cannot be undone.",
"successToast": "Price list \"{{title}}\" was successfully deleted."
},
"create": {
"header": "Create Price List",
@@ -2643,8 +2644,8 @@
"successToast": "Return reason {{label}} was successfully updated."
},
"delete": {
"confirmation": "You are about to delete the return reason {{label}}. This action cannot be undone.",
"successToast": "Return reason {{label}} was successfully deleted."
"confirmation": "You are about to delete the return reason \"{{label}}\". This action cannot be undone.",
"successToast": "Return reason \"{{label}}\" was successfully deleted."
},
"fields": {
"value": {
@@ -2764,8 +2765,8 @@
"successToast": "Product type {{value}} was successfully updated."
},
"delete": {
"confirmation": "You are about to delete the product type {{value}}. This action cannot be undone.",
"successToast": "Product type {{value}} was successfully deleted."
"confirmation": "You are about to delete the product type \"{{value}}\". This action cannot be undone.",
"successToast": "Product type \"{{value}}\" was successfully deleted."
},
"fields": {
"value": "Value"

View File

@@ -41,7 +41,7 @@ export const AdjustInventoryDrawer = () => {
return (
<RouteDrawer>
<RouteDrawer.Header>
<Heading>{t("inventory.manageLocations")}</Heading>
<Heading>{t("inventory.manageLocationQuantity")}</Heading>
</RouteDrawer.Header>
{ready && (
<AdjustInventoryForm

View File

@@ -24,6 +24,7 @@ const EditInventoryItemAttributesSchema = z.object({
length: z.number().positive().optional(),
weight: z.number().positive().optional(),
mid_code: z.string().optional(),
material: z.string().optional(),
hs_code: z.string().optional(),
origin_country: z.string().optional(),
})
@@ -35,6 +36,7 @@ const getDefaultValues = (item: InventoryTypes.InventoryItemDTO) => {
length: item.length ?? undefined,
weight: item.weight ?? undefined,
mid_code: item.mid_code ?? undefined,
material: item.material ?? undefined,
hs_code: item.hs_code ?? undefined,
origin_country: item.origin_country ?? undefined,
}
@@ -216,6 +218,21 @@ export const EditInventoryItemAttributesForm = ({
)
}}
/>
<Form.Field
control={form.control}
name="material"
render={({ field }) => {
return (
<Form.Item>
<Form.Label optional>{t("fields.material")}</Form.Label>
<Form.Control>
<Input {...field} />
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}

View File

@@ -39,6 +39,7 @@ export const InventoryItemAttributeSection = ({
<SectionRow title={t("fields.length")} value={inventoryItem.length} />
<SectionRow title={t("fields.weight")} value={inventoryItem.weight} />
<SectionRow title={t("fields.midCode")} value={inventoryItem.mid_code} />
<SectionRow title={t("fields.material")} value={inventoryItem.material} />
<SectionRow title={t("fields.hsCode")} value={inventoryItem.hs_code} />
<SectionRow
title={t("fields.countryOfOrigin")}

View File

@@ -425,13 +425,15 @@ export const ClaimCreateForm = ({
await updateReturn({ location_id: selectedLocationId })
}
const onShippingOptionChange = async (selectedOptionId: string) => {
const onShippingOptionChange = async (
selectedOptionId: string | undefined
) => {
const inboundShippingMethods = preview.shipping_methods.filter((s) => {
const action = s.actions?.find(
(a) => a.action === "SHIPPING_ADD" && !!a.return_id
)
return action && !action?.return_id
return action && !!action?.return_id
})
const promises = inboundShippingMethods
@@ -448,14 +450,16 @@ export const ClaimCreateForm = ({
await Promise.all(promises)
await addInboundShipping(
{ shipping_option_id: selectedOptionId },
{
onError: (error) => {
toast.error(error.message)
},
}
)
if (selectedOptionId) {
await addInboundShipping(
{ shipping_option_id: selectedOptionId },
{
onError: (error) => {
toast.error(error.message)
},
}
)
}
}
useEffect(() => {
@@ -740,10 +744,11 @@ export const ClaimCreateForm = ({
<Form.Item>
<Form.Control>
<Combobox
allowClear
value={value ?? undefined}
onChange={(val) => {
onChange(val)
val && onShippingOptionChange(val)
onShippingOptionChange(val)
}}
{...field}
options={inboundShippingOptions.map((so) => ({

View File

@@ -197,7 +197,9 @@ export const ClaimOutboundSection = ({
setIsOpen("outbound-items", false)
}
const onShippingOptionChange = async (selectedOptionId: string) => {
const onShippingOptionChange = async (
selectedOptionId: string | undefined
) => {
const outboundShippingMethods = preview.shipping_methods.filter((s) => {
const action = s.actions?.find(
(a) => a.action === "SHIPPING_ADD" && !a.return_id
@@ -220,14 +222,16 @@ export const ClaimOutboundSection = ({
await Promise.all(promises)
await addOutboundShipping(
{ shipping_option_id: selectedOptionId },
{
onError: (error) => {
toast.error(error.message)
},
}
)
if (selectedOptionId) {
await addOutboundShipping(
{ shipping_option_id: selectedOptionId },
{
onError: (error) => {
toast.error(error.message)
},
}
)
}
}
const showLevelsWarning = useMemo(() => {
@@ -403,10 +407,11 @@ export const ClaimOutboundSection = ({
<Form.Item>
<Form.Control>
<Combobox
allowClear
value={value ?? undefined}
onChange={(val) => {
onChange(val)
val && onShippingOptionChange(val)
onShippingOptionChange(val)
}}
{...field}
options={outboundShippingOptions.map((so) => ({

View File

@@ -199,7 +199,7 @@ export const ExchangeInboundSection = ({
inboundShippingMethod.shipping_option_id
)
} else {
form.setValue("inbound_option_id", null)
form.setValue("inbound_option_id", "")
}
}, [preview.shipping_methods])
@@ -246,7 +246,9 @@ export const ExchangeInboundSection = ({
await updateReturn({ location_id: selectedLocationId })
}
const onShippingOptionChange = async (selectedOptionId: string) => {
const onShippingOptionChange = async (
selectedOptionId: string | undefined
) => {
const inboundShippingMethods = preview.shipping_methods.filter((s) =>
s.actions?.find((a) => a.action === "SHIPPING_ADD" && !!a.return_id)
)
@@ -265,14 +267,16 @@ export const ExchangeInboundSection = ({
await Promise.all(promises)
await addInboundShipping(
{ shipping_option_id: selectedOptionId },
{
onError: (error) => {
toast.error(error.message)
},
}
)
if (selectedOptionId) {
await addInboundShipping(
{ shipping_option_id: selectedOptionId },
{
onError: (error) => {
toast.error(error.message)
},
}
)
}
}
const showLevelsWarning = useMemo(() => {
@@ -505,10 +509,11 @@ export const ExchangeInboundSection = ({
<Form.Item>
<Form.Control>
<Combobox
allowClear
value={value ?? undefined}
onChange={(val) => {
onChange(val)
val && onShippingOptionChange(val)
onShippingOptionChange(val)
}}
{...field}
options={inboundShippingOptions.map((so) => ({

View File

@@ -30,7 +30,6 @@ import { ItemPlaceholder } from "../../../order-create-claim/components/claim-cr
import { AddExchangeOutboundItemsTable } from "../add-exchange-outbound-items-table"
import { ExchangeOutboundItem } from "./exchange-outbound-item"
import { CreateExchangeSchemaType } from "./schema"
import { log } from "console"
type ExchangeOutboundSectionProps = {
order: AdminOrder
@@ -206,11 +205,13 @@ export const ExchangeOutboundSection = ({
if (outboundShipping) {
form.setValue("outbound_option_id", outboundShipping.shipping_option_id)
} else {
form.setValue("outbound_option_id", null)
form.setValue("outbound_option_id", "")
}
}, [preview.shipping_methods])
const onShippingOptionChange = async (selectedOptionId: string) => {
const onShippingOptionChange = async (
selectedOptionId: string | undefined
) => {
const outboundShippingMethods = preview.shipping_methods.filter(
(s) =>
!!s.actions?.find((a) => a.action === "SHIPPING_ADD" && !a.return_id)
@@ -230,14 +231,16 @@ export const ExchangeOutboundSection = ({
await Promise.all(promises)
await addOutboundShipping(
{ shipping_option_id: selectedOptionId },
{
onError: (error) => {
toast.error(error.message)
},
}
)
if (selectedOptionId) {
await addOutboundShipping(
{ shipping_option_id: selectedOptionId },
{
onError: (error) => {
toast.error(error.message)
},
}
)
}
}
const showLevelsWarning = useMemo(() => {
@@ -412,11 +415,12 @@ export const ExchangeOutboundSection = ({
<Form.Item>
<Form.Control>
<Combobox
allowClear
noResultsPlaceholder={<OutboundShippingPlaceholder />}
value={value ?? undefined}
onChange={(val) => {
onChange(val)
val && onShippingOptionChange(val)
onShippingOptionChange(val)
}}
{...field}
options={outboundShippingOptions.map((so) => ({

View File

@@ -292,7 +292,9 @@ export const ReturnCreateForm = ({
await updateReturnRequest({ location_id: selectedLocationId })
}
const onShippingOptionChange = async (selectedOptionId: string) => {
const onShippingOptionChange = async (
selectedOptionId: string | undefined
) => {
const promises = preview.shipping_methods
.map((s) => s.actions?.find((a) => a.action === "SHIPPING_ADD")?.id)
.filter(Boolean)
@@ -300,7 +302,9 @@ export const ReturnCreateForm = ({
await Promise.all(promises)
await addReturnShipping({ shipping_option_id: selectedOptionId })
if (selectedOptionId) {
await addReturnShipping({ shipping_option_id: selectedOptionId })
}
}
useEffect(() => {
@@ -562,7 +566,6 @@ export const ReturnCreateForm = ({
</Form.Hint>
</div>
{/* TODO: WHAT IF THE RETURN OPTION HAS COMPUTED PRICE*/}
<Form.Field
control={form.control}
name="option_id"
@@ -571,6 +574,7 @@ export const ReturnCreateForm = ({
<Form.Item>
<Form.Control>
<Combobox
allowClear
value={value}
onChange={(v) => {
onChange(v)

View File

@@ -1,17 +1,20 @@
import { useNavigate } from "react-router-dom"
import { toast, usePrompt } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { useDeleteProductType } from "../../../../hooks/api/product-types"
export const useDeleteProductTypeAction = (id: string) => {
export const useDeleteProductTypeAction = (id: string, value: string) => {
const { t } = useTranslation()
const prompt = usePrompt()
const navigate = useNavigate()
const { mutateAsync } = useDeleteProductType(id)
const handleDelete = async () => {
const result = await prompt({
title: t("general.areYouSure"),
description: t("productTypes.delete.confirmation"),
description: t("productTypes.delete.confirmation", { value }),
confirmText: t("actions.delete"),
cancelText: t("actions.cancel"),
})
@@ -22,7 +25,8 @@ export const useDeleteProductTypeAction = (id: string) => {
await mutateAsync(undefined, {
onSuccess: () => {
toast.success(t("productTypes.delete.successToast"))
navigate("/settings/product-types", { replace: true })
toast.success(t("productTypes.delete.successToast", { value }))
},
onError: (e) => {
toast.error(e.message)

View File

@@ -34,7 +34,7 @@ export const CreateProductTypeForm = () => {
onSuccess: ({ product_type }) => {
toast.success(
t("productTypes.create.successToast", {
value: product_type.value,
value: product_type.value.trim(),
})
)
@@ -49,28 +49,8 @@ export const CreateProductTypeForm = () => {
return (
<RouteFocusModal.Form form={form}>
<KeyboundForm
onSubmit={handleSubmit}
className="flex flex-col overflow-hidden"
>
<RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
size="small"
variant="primary"
type="submit"
isLoading={isPending}
>
{t("actions.create")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex flex-col items-center overflow-y-auto p-16">
<KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
<RouteFocusModal.Body className="flex flex-col items-center overflow-auto p-16">
<div className="flex w-full max-w-[720px] flex-col gap-y-8">
<div>
<Heading>{t("productTypes.create.header")}</Heading>
@@ -97,6 +77,23 @@ export const CreateProductTypeForm = () => {
</div>
</div>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
size="small"
variant="primary"
type="submit"
isLoading={isPending}
>
{t("actions.create")}
</Button>
</div>
</RouteFocusModal.Footer>
</KeyboundForm>
</RouteFocusModal.Form>
)

View File

@@ -13,7 +13,10 @@ export const ProductTypeGeneralSection = ({
productType,
}: ProductTypeGeneralSectionProps) => {
const { t } = useTranslation()
const handleDelete = useDeleteProductTypeAction(productType.id)
const handleDelete = useDeleteProductTypeAction(
productType.id,
productType.value
)
return (
<Container className="flex items-center justify-between">

View File

@@ -12,7 +12,10 @@ export const ProductTypeRowActions = ({
productType,
}: ProductTypeRowActionsProps) => {
const { t } = useTranslation()
const handleDelete = useDeleteProductTypeAction(productType.id)
const handleDelete = useDeleteProductTypeAction(
productType.id,
productType.value
)
return (
<ActionMenu