fix(admin-ui): Price list domain fixes (#5339)
**Fixes** - Fixes an issue where click the Delete button on a table item in the Price List overview would also navigate to the Price Lists page - Fixes an issue where Region prices were not showing which region they belong to in the Bulk Editor - Fixes an issue where the wrong widgets were being injected into the Price List domain **Improvements** - Align label for Expiry date field - Add form validation to require setting the prices of at least one product variant when creating or adding prices to a Price List. - Show a prompt warning if prices have not been set for all selected products in the create and add product flows.
This commit is contained in:
@@ -69,6 +69,7 @@ const PriceListDetailsSection = ({
|
||||
name,
|
||||
}
|
||||
),
|
||||
verificationText: name,
|
||||
confirmText,
|
||||
cancelText,
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ const PriceListEdit = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-y-2">
|
||||
{getWidgets("product.details.before").map((w, i) => {
|
||||
{getWidgets("price_list.details.before").map((w, i) => {
|
||||
return (
|
||||
<WidgetContainer
|
||||
key={i}
|
||||
@@ -60,7 +60,7 @@ const PriceListEdit = () => {
|
||||
})}
|
||||
<PriceListGeneralSection priceList={price_list} />
|
||||
<PriceListPricesSection priceList={price_list} />
|
||||
{getWidgets("product.details.after").map((w, i) => {
|
||||
{getWidgets("price_list.details.after").map((w, i) => {
|
||||
return (
|
||||
<WidgetContainer
|
||||
key={i}
|
||||
|
||||
@@ -12,9 +12,9 @@ import {
|
||||
import * as React from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import * as z from "zod"
|
||||
|
||||
import { useAdminCreatePriceListPrices } from "medusa-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { Form } from "../../../../components/helpers/form"
|
||||
import useNotification from "../../../../hooks/use-notification"
|
||||
import { useFeatureFlag } from "../../../../providers/feature-flag-provider"
|
||||
@@ -110,6 +110,7 @@ const AddProductsModal = ({
|
||||
trigger,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
setError,
|
||||
getValues,
|
||||
reset,
|
||||
formState: { isDirty },
|
||||
@@ -138,6 +139,7 @@ const AddProductsModal = ({
|
||||
const onCloseModal = React.useCallback(() => {
|
||||
onOpenChange(false)
|
||||
setTab(Tab.PRODUCTS)
|
||||
setSelectedIds([])
|
||||
setStatus({
|
||||
[Tab.PRODUCTS]: "not-started",
|
||||
[Tab.PRICES]: "not-started",
|
||||
@@ -205,6 +207,42 @@ const AddProductsModal = ({
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
const prices: PricePayload[] = []
|
||||
|
||||
const productPriceKeys = Object.keys(data.prices.products)
|
||||
const productIds = data.products.ids
|
||||
|
||||
if (!productPriceKeys.length || !data.prices.products) {
|
||||
setError("prices.products", {
|
||||
type: "manual",
|
||||
message: t(
|
||||
"price-list-add-products-modal-no-prices-error",
|
||||
"Please assign prices for at least one product."
|
||||
) as string,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const missingProducts = productIds.filter(
|
||||
(id) => !productPriceKeys.includes(id)
|
||||
)
|
||||
|
||||
if (missingProducts.length > 0) {
|
||||
const res = await prompt({
|
||||
title: t(
|
||||
"price-list-add-products-modal-missing-prices-title",
|
||||
"Incomplete price list"
|
||||
),
|
||||
description: t(
|
||||
"price-list-add-products-modal-missing-prices-description",
|
||||
"Prices have not been assigned to all of your chosen products. Would you like to continue?"
|
||||
),
|
||||
})
|
||||
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (const productId of Object.keys(data.prices.products)) {
|
||||
const product = data.prices.products[productId]
|
||||
|
||||
@@ -533,6 +571,7 @@ const AddProductsModal = ({
|
||||
</span>
|
||||
</ProgressTabs.Trigger>
|
||||
<ProgressTabs.Trigger
|
||||
disabled={selectedIds.length === 0}
|
||||
value={Tab.PRICES}
|
||||
className="w-full max-w-[200px]"
|
||||
status={status[Tab.PRICES]}
|
||||
|
||||
+4
-1
@@ -376,7 +376,10 @@ const PriceListDates = ({ form, layout }: PriceListDetailsFormProps) => {
|
||||
return (
|
||||
<Form.Item>
|
||||
<Form.Label>
|
||||
{t("price-list-details-form-ends-at-label", "End date")}
|
||||
{t(
|
||||
"price-list-details-form-ends-at-label",
|
||||
"Expiry date"
|
||||
)}
|
||||
</Form.Label>
|
||||
<Form.Control>
|
||||
<DatePicker
|
||||
|
||||
+25
-4
@@ -9,6 +9,7 @@ import { useDebounce } from "../../../../hooks/use-debounce"
|
||||
import { NestedForm } from "../../../../utils/nested-form"
|
||||
import { ProductFilter, ProductFilterMenu } from "../../components"
|
||||
import { PriceListPricesSchema } from "./types"
|
||||
import { Form } from "../../../../components/helpers/form"
|
||||
|
||||
type PriceListPricesFormProps = {
|
||||
form: NestedForm<PriceListPricesSchema>
|
||||
@@ -23,7 +24,12 @@ const PriceListPricesForm = ({
|
||||
productIds,
|
||||
priceListId,
|
||||
}: PriceListPricesFormProps) => {
|
||||
const { path, getValues } = form
|
||||
const {
|
||||
control,
|
||||
path,
|
||||
getValues,
|
||||
formState: { isDirty },
|
||||
} = form
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -87,9 +93,24 @@ const PriceListPricesForm = ({
|
||||
<div className="relative flex h-full w-full flex-col">
|
||||
<div>
|
||||
<div className="border-ui-border-base bg-ui-bg-base z-10 flex items-center justify-between border-b px-4 py-3">
|
||||
<Heading>
|
||||
{t("price-list-prices-form-heading", "Edit prices")}
|
||||
</Heading>
|
||||
<div className="flex items-center gap-x-3">
|
||||
<Heading>
|
||||
{t("price-list-prices-form-heading", "Edit prices")}
|
||||
</Heading>
|
||||
{isDirty && (
|
||||
<Form.Field
|
||||
control={control}
|
||||
name={path("products")}
|
||||
render={() => {
|
||||
return (
|
||||
<Form.Item>
|
||||
<Form.ErrorMessage />
|
||||
</Form.Item>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<ProductFilterMenu
|
||||
onClearFilters={() => setFilters({})}
|
||||
|
||||
+2
-1
@@ -1565,8 +1565,9 @@ const PriceListProductPricesForm = ({
|
||||
<span>
|
||||
{t(
|
||||
"price-list-product-prices-form-column-regions-price-label",
|
||||
"Price {{code}}",
|
||||
"Price {{name}} ({{code}})",
|
||||
{
|
||||
name: region.name,
|
||||
code: region.currency_code.toUpperCase(),
|
||||
}
|
||||
)}
|
||||
|
||||
@@ -132,6 +132,7 @@ const PriceListNew = () => {
|
||||
reset,
|
||||
getValues,
|
||||
setValue,
|
||||
setError,
|
||||
handleSubmit,
|
||||
formState: { isDirty },
|
||||
} = form
|
||||
@@ -163,6 +164,7 @@ const PriceListNew = () => {
|
||||
const onCloseModal = React.useCallback(() => {
|
||||
setOpen(false)
|
||||
setTab(Tab.DETAILS)
|
||||
setSelectedIds([])
|
||||
setStatus({
|
||||
[Tab.DETAILS]: "not-started",
|
||||
[Tab.PRODUCTS]: "not-started",
|
||||
@@ -228,6 +230,42 @@ const PriceListNew = () => {
|
||||
await handleSubmit(async (data) => {
|
||||
const prices: PricePayload[] = []
|
||||
|
||||
const productPriceKeys = Object.keys(data.prices.products)
|
||||
const productIds = data.products.ids
|
||||
|
||||
if (!productPriceKeys.length || !data.prices.products) {
|
||||
setError("prices.products", {
|
||||
type: "manual",
|
||||
message: t(
|
||||
"price-list-new-form-no-prices-error",
|
||||
"Please set prices for at least one product."
|
||||
) as string,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const missingProducts = productIds.filter(
|
||||
(id) => !productPriceKeys.includes(id)
|
||||
)
|
||||
|
||||
if (missingProducts.length > 0) {
|
||||
const res = await prompt({
|
||||
title: t(
|
||||
"price-list-new-form-missing-prices-title",
|
||||
"Incomplete price list"
|
||||
),
|
||||
description: t(
|
||||
"price-list-new-products-modal-missing-prices-description",
|
||||
"Prices have not been assigned to all of your chosen products. Would you like to proceed?"
|
||||
),
|
||||
})
|
||||
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop through all the products and variants
|
||||
* and create a payload for each price.
|
||||
@@ -348,6 +386,8 @@ const PriceListNew = () => {
|
||||
mutateAsync,
|
||||
notification,
|
||||
onCloseModal,
|
||||
setError,
|
||||
prompt,
|
||||
t,
|
||||
isTaxInclPricesEnabled,
|
||||
regions,
|
||||
|
||||
@@ -384,7 +384,9 @@ const PriceListTableRowActions = ({ row }: PriceListTableRowActionsProps) => {
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handleDelete = async () => {
|
||||
const handleDelete = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
|
||||
const response = await prompt({
|
||||
title: "Are you sure?",
|
||||
description: "This will permanently delete the price list",
|
||||
|
||||
Reference in New Issue
Block a user