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:
Kasper Fabricius Kristensen
2023-10-12 08:34:46 +00:00
committed by GitHub
parent a91fca69ac
commit e04927a3cb
12 changed files with 140 additions and 21 deletions
@@ -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]}
@@ -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
@@ -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({})}
@@ -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",