fix(dashboard): Add FE validation for conditional prices (#10523)

* fix(dashboard): Add FE validation for conditional prices

* fix(dashboard): Add FE validation for conditional prices

* lint

* only set error on one field

* fix which field shows error
This commit is contained in:
Kasper Fabricius Kristensen
2024-12-10 11:57:10 +01:00
committed by GitHub
parent 69f4c4f4e0
commit de81187929
5 changed files with 174 additions and 79 deletions
@@ -9,6 +9,7 @@ import {
import {
Badge,
Button,
clx,
CurrencyInput,
Heading,
IconButton,
@@ -110,14 +111,28 @@ export const ConditionalPriceForm = ({
remove(index)
}
const handleOnSubmit = conditionalPriceForm.handleSubmit((values) => {
setFormValue(name, values.prices, {
shouldDirty: true,
shouldValidate: true,
shouldTouch: true,
})
onCloseConditionalPricesModal()
})
const handleOnSubmit = conditionalPriceForm.handleSubmit(
(values) => {
setFormValue(name, values.prices, {
shouldDirty: true,
shouldValidate: true,
shouldTouch: true,
})
onCloseConditionalPricesModal()
},
(e) => {
const indexesWithErrors = Object.keys(e.prices || {})
setValue((prev) => {
const values = new Set(prev)
indexesWithErrors.forEach((index) => {
values.add(getRuleValue(Number(index)))
})
return Array.from(values)
})
}
)
// Intercept the Cmd + Enter key to only save the inner form.
const handleOnKeyDown = (event: React.KeyboardEvent<HTMLFormElement>) => {
@@ -255,7 +270,9 @@ const ConditionalPriceItem = ({
return (
<Accordion.Item
value={getRuleValue(index)}
className="bg-ui-bg-component shadow-elevation-card-rest rounded-lg"
className={clx(
"bg-ui-bg-component shadow-elevation-card-rest rounded-lg"
)}
>
<Accordion.Trigger asChild>
<div className="group/trigger flex w-full cursor-pointer items-start justify-between gap-x-2 p-3">
@@ -73,18 +73,120 @@ export type UpdateConditionalPrice = z.infer<
typeof UpdateConditionalPriceSchema
>
export const CondtionalPriceRuleSchema = z.object({
prices: z.array(ConditionalPriceSchema),
})
function refineDuplicates(
data: {
prices: {
amount: string | number
gte?: string | number | null | undefined
lte?: string | number | null | undefined
lt?: number | null | undefined
gt?: number | null | undefined
eq?: number | null | undefined
}[]
},
ctx: z.RefinementCtx
) {
const prices = data.prices
for (let i = 0; i < prices.length; i++) {
for (let j = i + 1; j < prices.length; j++) {
const price1 = prices[i]
const price2 = prices[j]
if (price1.amount === "" || price2.amount === "") {
continue
}
const price1Amount = castNumber(price1.amount)
const price2Amount = castNumber(price2.amount)
if (price1Amount === price2Amount) {
addDuplicateAmountError(ctx, j)
}
// Then check conditions separately
const conditions = [
{ value: price1.gte, type: "gte" },
{ value: price1.lte, type: "lte" },
{ value: price1.eq, type: "eq" },
{ value: price1.lt, type: "lt" },
{ value: price1.gt, type: "gt" },
] as const
conditions.forEach((condition1) => {
if (!condition1.value && condition1.value !== 0) {
return
}
const conditions2 = [
{ value: price2.gte, type: "gte" },
{ value: price2.lte, type: "lte" },
{ value: price2.eq, type: "eq" },
{ value: price2.lt, type: "lt" },
{ value: price2.gt, type: "gt" },
] as const
conditions2.forEach((condition2) => {
if (!condition2.value && condition2.value !== 0) {
return
}
const condition1Value = castNumber(
condition1.value as string | number
)
const condition2Value = castNumber(
condition2.value as string | number
)
if (condition1Value === condition2Value) {
addOverlappingConditionError(ctx, j, condition2.type)
}
})
})
}
}
}
export const CondtionalPriceRuleSchema = z
.object({
prices: z.array(ConditionalPriceSchema),
})
.superRefine(refineDuplicates)
export type CondtionalPriceRuleSchemaType = z.infer<
typeof CondtionalPriceRuleSchema
>
export const UpdateConditionalPriceRuleSchema = z.object({
prices: z.array(UpdateConditionalPriceSchema),
})
export const UpdateConditionalPriceRuleSchema = z
.object({
prices: z.array(UpdateConditionalPriceSchema),
})
.superRefine(refineDuplicates)
export type UpdateConditionalPriceRuleSchemaType = z.infer<
typeof UpdateConditionalPriceRuleSchema
>
const addDuplicateAmountError = (ctx: z.RefinementCtx, index: number) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t(
"stockLocations.shippingOptions.conditionalPrices.errors.duplicateAmount"
),
path: ["prices", index, "amount"],
})
}
const addOverlappingConditionError = (
ctx: z.RefinementCtx,
index: number,
type: "gte" | "lte" | "eq" | "lt" | "gt"
) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t(
"stockLocations.shippingOptions.conditionalPrices.errors.overlappingConditions"
),
path: ["prices", index, type],
})
}