feat(dashboard) admin 3.0 create discounts form (#6590)

This commit is contained in:
Frane Polić
2024-03-11 08:06:27 +01:00
committed by GitHub
parent 2d00625729
commit 39e73b2d2f
5 changed files with 991 additions and 245 deletions

View File

@@ -237,12 +237,15 @@
"discounts": {
"domain": "Discounts",
"startDate": "Start date",
"createDiscountTitle": "Create discount",
"validDuration": "Duration of the discount",
"redemptionsLimit": "Redemptions limit",
"endDate": "End date",
"type": "Discount type",
"percentageDiscount": "Percentage discount",
"freeShipping": "Free shipping",
"fixedDiscount": "Fixed discount",
"fixedAmount": "Fixed amount",
"validRegions": "Valid regions",
"deleteWarning": "You are about to delete the discount {{code}}. This action cannot be undone.",
"editDiscountDetails": "Edit discount details",
@@ -260,6 +263,13 @@
"chooseValidRegions": "Choose valid regions",
"noConditions": "No conditions are defined for this discount.",
"editConditions": "Edit conditions",
"conditionsHint": "Create conditions to apply on the discount",
"isTemplateDiscount": "Is this a template discount?",
"percentageDescription" : "Discount applied in %",
"fixedDescription" : "Amount discount",
"shippingDescription" : "Override delivery amount",
"selectRegionFirst": "Select region first",
"templateHint": "Template discounts allow you to define a set of rules that can be used across a group of discounts. This is useful in campaigns that should generate unique codes for each user, but where the rules for all unique codes should be the same.",
"conditions": {
"including": {
"products_one": "Discount applies to <0/> product.",

View File

@@ -1,8 +1,24 @@
import { Heading, Input, Text } from "@medusajs/ui"
import { useEffect, useMemo } from "react"
import {
Checkbox,
DatePicker,
Heading,
Input,
Select,
Switch,
Text,
Textarea,
RadioGroup,
CurrencyInput,
} from "@medusajs/ui"
import { Trans, useTranslation } from "react-i18next"
import { useAdminRegions } from "medusa-react"
import { Form } from "../../../../../components/common/form"
import { CreateDiscountFormReturn } from "./create-discount-form.tsx"
import { CreateDiscountFormReturn } from "./create-discount-form"
import { Combobox } from "../../../../../components/common/combobox"
import { getCurrencySymbol } from "../../../../../lib/currencies"
import { DiscountRuleType } from "./types"
type CreateDiscountPropsProps = {
form: CreateDiscountFormReturn
@@ -11,56 +27,638 @@ type CreateDiscountPropsProps = {
export const CreateDiscountDetails = ({ form }: CreateDiscountPropsProps) => {
const { t } = useTranslation()
const { regions } = useAdminRegions()
const watchType = form.watch("type")
const watchRegion = form.watch("regions")
const isFixedDiscount = watchType === DiscountRuleType.FIXED
const isFreeShipping = watchType === DiscountRuleType.FREE_SHIPPING
const activeRegion = useMemo(() => {
if (!watchRegion || !regions?.length) {
return
}
return regions.find((r) => r.id === watchRegion[0])
}, [regions, watchRegion])
useEffect(() => {
form.setValue("regions", [])
form.setValue("value", undefined)
}, [watchType])
return (
<div className="flex size-full flex-col items-center overflow-auto p-16">
<div className="flex w-full max-w-[736px] flex-col justify-center px-2 pb-2">
<div className="flex flex-col gap-y-1">
<Heading>{t("discount.createDiscountTitle")}</Heading>
<Text size="small" className="text-ui-fg-subtle">
{t("discounts.createDiscountHint")}
</Text>
<Heading>{t("discounts.createDiscountTitle")}</Heading>
</div>
<div className="flex flex-col gap-y-8 divide-y [&>div]:pt-8">
<div id="general" className="flex flex-col gap-y-8">
<div className="flex flex-col gap-y-2">
<div className="grid grid-cols-2 gap-x-4">
<Form.Field
control={form.control}
name="code"
render={({ field }) => {
return (
<Form.Item>
<Form.Label>{t("fields.code")}</Form.Label>
<Form.Control>
<Input {...field} />
</Form.Control>
</Form.Item>
)
}}
/>
{/* DETAILS */}
<div className="flex flex-col gap-y-8">
<Form.Field
control={form.control}
name="type"
render={({ field }) => {
return (
<Form.Item>
<Form.Label>{t("discounts.type")}</Form.Label>
<Form.Control>
<RadioGroup
className="flex justify-between gap-4"
{...field}
onValueChange={field.onChange}
>
<RadioGroup.ChoiceBox
className="flex-1"
value={DiscountRuleType.PERCENTAGE}
label={t("fields.percentage")}
description={t("discounts.percentageDescription")}
/>
<RadioGroup.ChoiceBox
className="flex-1"
value={DiscountRuleType.FIXED}
label={t("discounts.fixedAmount")}
description={t("discounts.fixedDescription")}
/>
<RadioGroup.ChoiceBox
className="flex-1"
value={DiscountRuleType.FREE_SHIPPING}
label={t("discounts.freeShipping")}
description={t("discounts.shippingDescription")}
/>
</RadioGroup>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<div className="grid grid-cols-2 gap-x-4">
<Form.Field
control={form.control}
name="regions"
render={({ field: { onChange, value, ref, ...field } }) => {
return (
<Form.Item>
<Form.Label>
{t("discounts.chooseValidRegions")}
</Form.Label>
<Form.Control>
{isFixedDiscount ? (
<Select
value={value[0]}
onValueChange={(v) => {
if (v) {
onChange([v])
}
}}
{...field}
>
<Select.Trigger ref={ref}>
<Select.Value />
</Select.Trigger>
<Select.Content>
{(regions || []).map((r) => (
<Select.Item key={r.id} value={r.id}>
{r.name}
</Select.Item>
))}
</Select.Content>
</Select>
) : (
<Combobox
options={(regions || []).map((r) => ({
label: r.name,
value: r.id,
}))}
value={value}
onChange={onChange}
{...field}
/>
)}
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
</div>
<div className="grid grid-cols-2 gap-x-4">
<Form.Field
control={form.control}
name="code"
render={({ field }) => {
return (
<Form.Item>
<Form.Label>{t("fields.code")}</Form.Label>
<Form.Control>
<Input {...field} />
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
{!isFreeShipping && (
<Form.Field
control={form.control}
name="value"
render={({ field }) => {
render={({ field: { onChange, ...field } }) => {
return (
<Form.Item>
<Form.Label>{t("fields.percentage")}</Form.Label>
<Form.Label
tooltip={
isFixedDiscount &&
!activeRegion &&
t("discounts.selectRegionFirst")
}
>
{isFixedDiscount
? t("fields.amount")
: t("fields.percentage")}
</Form.Label>
<Form.Control>
<Input {...field} />
{isFixedDiscount ? (
activeRegion ? (
<CurrencyInput
min={0}
onValueChange={onChange}
code={activeRegion.currency_code}
symbol={getCurrencySymbol(
activeRegion.currency_code
)}
{...field}
/>
) : (
<Input key="placeholder" disabled />
)
) : (
<Input
key="amount"
type="number"
min={0}
max={100}
{...field}
value={field.value || ""}
onChange={(e) => {
const value = e.target.value
if (value === "") {
onChange(null)
} else {
onChange(parseFloat(value))
}
}}
/>
)}
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
</div>
)}
</div>
<Text size="small" leading="compact" className="text-ui-fg-subtle">
<Trans
i18nKey="discounts.titleHint"
t={t}
components={[<br key="break" />]}
/>
</Text>
<Form.Field
control={form.control}
name="description"
render={({ field }) => {
return (
<Form.Item>
<Form.Label optional>{t("fields.description")}</Form.Label>
<Form.Control>
<Textarea {...field} />
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="is_dynamic"
render={({ field: { value, onChange, ...field } }) => {
return (
<Form.Item>
<div className="flex gap-2">
<Form.Control>
<Checkbox
checked={value}
onCheckedChange={(s) => onChange(s === true)}
{...field}
/>
</Form.Control>
<Form.Label
className="cursor-pointer"
tooltip={t("discounts.templateHint")}
>
{t("discounts.isTemplateDiscount")}
</Form.Label>
</div>
{/*<Form.ErrorMessage />*/}
</Form.Item>
)
}}
/>
</div>
{/* CONFIGURATIONS */}
<div className="flex flex-col gap-y-8">
<div className="flex flex-col gap-y-1">
<Text
size="large"
leading="compact"
className="text-ui-fg-base"
weight="plus"
>
{t("fields.configurations")}
</Text>
<Text
size="small"
leading="compact"
className="text-ui-fg-subtle"
>
<Trans
i18nKey="discounts.titleHint"
t={t}
i18nKey="discounts.codeHint"
components={[<br key="break" />]}
/>
</Text>
</div>
<div className="flex flex-col gap-y-4">
<Form.Field
control={form.control}
name="start_date_enabled"
render={({ field: { value, onChange, ...field } }) => (
<Form.Item>
<div className="flex items-center justify-between">
<Form.Label tooltip="todo">
{t("discounts.hasStartDate")}
</Form.Label>
<Form.Control>
<Switch
{...field}
checked={!!value}
onCheckedChange={onChange}
/>
</Form.Control>
</div>
<Form.Hint className="!mt-1">
{t("discounts.startDateHint")}
</Form.Hint>
<Form.ErrorMessage />
</Form.Item>
)}
/>
{form.watch("start_date_enabled") && (
<Form.Field
control={form.control}
name="start_date"
render={({
field: { value, onChange, ref: _ref, ...field },
}) => {
return (
<Form.Item>
<div className="grid grid-cols-2 gap-x-4">
<Form.Control>
<DatePicker
showTimePicker
value={value ?? undefined}
onChange={(v) => {
onChange(v ?? null)
}}
{...field}
/>
</Form.Control>
</div>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
)}
</div>
<div className="flex flex-col gap-y-4">
<Form.Field
control={form.control}
name="end_date_enabled"
render={({ field: { value, onChange, ...field } }) => {
return (
<Form.Item>
<div className="flex items-center justify-between">
<Form.Label tooltip="todo">
{t("discounts.hasEndDate")}
</Form.Label>
<Form.Control>
<Switch
{...field}
checked={!!value}
onCheckedChange={onChange}
/>
</Form.Control>
</div>
<Form.Hint className="!mt-1">
{t("discounts.endDateHint")}
</Form.Hint>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
{form.watch("end_date_enabled") && (
<Form.Field
control={form.control}
name="end_date"
render={({
field: { value, onChange, ref: _ref, ...field },
}) => {
return (
<Form.Item>
<div className="grid grid-cols-2 gap-x-4">
<Form.Control>
<DatePicker
showTimePicker
value={value ?? undefined}
onChange={(v) => {
onChange(v ?? null)
}}
{...field}
/>
</Form.Control>
</div>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
)}
</div>
<div className="flex flex-col gap-y-4">
<Form.Field
control={form.control}
name="usage_limit_enabled"
render={({ field }) => {
return (
<Form.Item>
<div className="flex items-center justify-between">
<Form.Label tooltip="todo">
{t("discounts.hasUsageLimit")}
</Form.Label>
<Form.Control>
<Switch
checked={!!field.value}
onCheckedChange={field.onChange}
/>
</Form.Control>
</div>
<Form.Hint className="!mt-1">
{t("discounts.usageLimitHint")}
</Form.Hint>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
{form.watch("usage_limit_enabled") && (
<Form.Field
control={form.control}
name="usage_limit"
render={({ field }) => {
return (
<Form.Item>
<Form.Control>
<div className="grid grid-cols-2 gap-x-4">
<Input
{...field}
type="number"
min={0}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</div>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
)}
</div>
<div className="flex flex-col gap-y-4">
<Form.Field
control={form.control}
name="valid_duration_enabled"
render={({ field }) => {
return (
<Form.Item>
<div className="flex items-center justify-between">
<Form.Label tooltip="todo">
{t("discounts.hasDurationLimit")}
</Form.Label>
<Form.Control>
<Switch
checked={!!field.value}
onCheckedChange={field.onChange}
/>
</Form.Control>
</div>
<Form.Hint className="!mt-1">
{t("discounts.durationHint")}
</Form.Hint>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
{form.watch("valid_duration_enabled") && (
<div className="flex items-center justify-between gap-3">
<Form.Field
control={form.control}
name="years"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.years")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="months"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.months")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="days"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.days")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="hours"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.hours")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="minutes"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.minutes")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
</div>
)}
</div>
</div>
{/* CONDITIONS */}
<div className="flex flex-col gap-y-8">
<div className="flex flex-col gap-y-1">
<Text
size="large"
leading="compact"
className="text-ui-fg-base"
weight="plus"
>
{t("fields.conditions")}
</Text>
<Text
size="small"
leading="compact"
className="text-ui-fg-subtle"
>
<Trans
t={t}
i18nKey="discounts.conditionsHint"
components={[<br key="break" />]}
/>
</Text>

View File

@@ -2,19 +2,104 @@ import * as zod from "zod"
import { zodResolver } from "@hookform/resolvers/zod"
import { UseFormReturn, useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import { formatISODuration } from "date-fns"
import { Button } from "@medusajs/ui"
import { useAdminCreateDiscount } from "medusa-react"
import { useAdminCreateDiscount, useAdminRegions } from "medusa-react"
import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
import { CreateDiscountDetails } from "./create-discount-details.tsx"
import { CreateDiscountDetails } from "./create-discount-details"
import { DiscountRuleType } from "./types"
import { getDbAmount } from "../../../../../lib/money-amount-helpers"
const CreateDiscountSchema = zod.object({
code: zod.string(),
})
/**
* There is some duplication here but we cannot achieve
* expected behaviour from the form with only union + discriminateUnion
*/
const CreateDiscountSchema = zod.discriminatedUnion("type", [
zod.object({
code: zod.string(),
regions: zod.array(zod.string()).min(1),
is_dynamic: zod.boolean(),
start_date: zod.date().optional(),
end_date: zod.date().optional(),
usage_limit: zod.number().optional(),
description: zod.string().optional(),
start_date_enabled: zod.boolean().optional(),
end_date_enabled: zod.boolean().optional(),
usage_limit_enabled: zod.boolean().optional(),
valid_duration_enabled: zod.boolean().optional(),
years: zod.number().optional(),
months: zod.number().optional(),
days: zod.number().optional(),
hours: zod.number().optional(),
minutes: zod.number().optional(),
value: zod.number().min(0).max(100),
type: zod.literal(DiscountRuleType.PERCENTAGE),
}),
zod.object({
code: zod.string(),
regions: zod.array(zod.string()).min(1),
is_dynamic: zod.boolean(),
start_date: zod.date().optional(),
end_date: zod.date().optional(),
usage_limit: zod.number().optional(),
description: zod.string().optional(),
start_date_enabled: zod.boolean().optional(),
end_date_enabled: zod.boolean().optional(),
usage_limit_enabled: zod.boolean().optional(),
valid_duration_enabled: zod.boolean().optional(),
years: zod.number().optional(),
months: zod.number().optional(),
days: zod.number().optional(),
hours: zod.number().optional(),
minutes: zod.number().optional(),
value: zod.union([zod.string(), zod.number()]).refine((value) => {
if (value === "") {
return false
}
const num = Number(value)
if (isNaN(num)) {
return false
}
return num >= 0
}, "Amount must be a positive number"),
type: zod.literal(DiscountRuleType.FIXED),
}),
zod.object({
code: zod.string(),
regions: zod.array(zod.string()).min(1),
is_dynamic: zod.boolean(),
start_date: zod.date().optional(),
end_date: zod.date().optional(),
usage_limit: zod.number().optional(),
description: zod.string().optional(),
start_date_enabled: zod.boolean().optional(),
end_date_enabled: zod.boolean().optional(),
usage_limit_enabled: zod.boolean().optional(),
valid_duration_enabled: zod.boolean().optional(),
years: zod.number().optional(),
months: zod.number().optional(),
days: zod.number().optional(),
hours: zod.number().optional(),
minutes: zod.number().optional(),
value: zod.undefined(),
type: zod.literal(DiscountRuleType.FREE_SHIPPING),
}),
])
type Schema = zod.infer<typeof CreateDiscountSchema>
export type CreateDiscountFormReturn = UseFormReturn<Schema>
@@ -25,17 +110,65 @@ export const CreateDiscountForm = () => {
const form = useForm<Schema>({
defaultValues: {
code: "",
regions: [],
is_dynamic: false,
type: DiscountRuleType.PERCENTAGE,
},
resolver: zodResolver(CreateDiscountSchema),
})
const { mutateAsync, isLoading } = useAdminCreateDiscount()
const { regions } = useAdminRegions()
const handleSubmit = form.handleSubmit(async (values: Schema) => {
const getValue = () => {
if (values.type === DiscountRuleType.FREE_SHIPPING) {
return 0
}
if (values.type === DiscountRuleType.PERCENTAGE) {
return values.value
}
const amount =
typeof values.value === "string" ? Number(values.value) : values.value
const region = regions!.find((r) => r.id === values.regions[0])
return getDbAmount(amount, region!.currency_code)
}
const duration = {
years: values.years,
months: values.months,
days: values.days,
hours: values.hours,
minutes: values.minutes,
}
const isDurationEmpty = Object.values(duration).every((v) => !v)
const handleSubmit = form.handleSubmit(async (values) => {
await mutateAsync(
{
code: values.code,
regions: values.regions,
is_dynamic: values.is_dynamic,
starts_at: values.start_date_enabled ? values.start_date : undefined,
ends_at: values.end_date_enabled ? values.end_date : undefined,
is_disabled: false,
usage_limit: values.usage_limit_enabled
? values.usage_limit
: undefined,
valid_duration:
values.valid_duration_enabled && !isDurationEmpty
? formatISODuration(duration)
: undefined,
rule: {
value: getValue(),
type: values.type,
description: values.description,
allocation: "total" as any,
},
},
{
onSuccess: ({ discount }) => {

View File

@@ -0,0 +1,5 @@
export enum DiscountRuleType {
FIXED = "fixed",
PERCENTAGE = "percentage",
FREE_SHIPPING = "free_shipping",
}

View File

@@ -79,16 +79,18 @@ export const EditDiscountConfigurationForm = ({
const { mutateAsync, isLoading } = useAdminUpdateDiscount(discount.id)
const handleSubmit = form.handleSubmit(async (data) => {
const duration = pick(data, ["years", "months", "days", "hours", "minutes"])
const isDurationEmpty = Object.values(duration).every((v) => !v)
await mutateAsync(
{
starts_at: data.start_date,
ends_at: data.end_date_enabled ? data.end_date : null,
usage_limit: data.enable_usage_limit ? data.usage_limit : null,
valid_duration: data.enable_duration
? formatISODuration(
pick(data, ["years", "months", "days", "hours", "minutes"])
)
: null,
valid_duration:
data.enable_duration && !isDurationEmpty
? formatISODuration(duration)
: null,
},
{
onSuccess: () => {
@@ -189,40 +191,34 @@ export const EditDiscountConfigurationForm = ({
)
}}
/>
<Form.Field
control={form.control}
name="end_date"
render={({
field: { value, onChange, ref: _ref, ...field },
}) => {
return (
<Form.Item>
<div className="flex items-center justify-between">
<Form.Control>
<DatePicker
showTimePicker
value={value ?? undefined}
onChange={(v) => {
onChange(v ?? null)
}}
{...field}
/**
* TODO: FIX bug in the picker when a placeholder is provided it resets selected value to undefined
*/
// placeholder="DD/MM/YYYY HH:MM"
/*
* Disable input here. If set on Field it wont properly set the value.
*/
disabled={!form.watch("end_date_enabled")}
/>
</Form.Control>
</div>
{form.watch("end_date_enabled") && (
<Form.Field
control={form.control}
name="end_date"
render={({
field: { value, onChange, ref: _ref, ...field },
}) => {
return (
<Form.Item>
<div className="flex items-center justify-between">
<Form.Control>
<DatePicker
showTimePicker
value={value ?? undefined}
onChange={(v) => {
onChange(v ?? null)
}}
{...field}
/>
</Form.Control>
</div>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
)}
</div>
<div className="flex flex-col gap-y-4">
@@ -254,34 +250,35 @@ export const EditDiscountConfigurationForm = ({
}}
/>
<Form.Field
control={form.control}
name="usage_limit"
render={({ field }) => {
return (
<Form.Item>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_usage_limit")}
onChange={(e) => {
const value = e.target.value
{form.watch("enable_usage_limit") && (
<Form.Field
control={form.control}
name="usage_limit"
render={({ field }) => {
return (
<Form.Item>
<Form.Control>
<Input
{...field}
type="number"
min={0}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
)}
</div>
<div className="flex flex-col gap-y-4">
@@ -312,155 +309,158 @@ export const EditDiscountConfigurationForm = ({
)
}}
/>
<div className="flex items-center justify-between gap-3">
<Form.Field
control={form.control}
name="years"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.years")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_duration")}
onChange={(e) => {
const value = e.target.value
{form.watch("enable_duration") && (
<div className="flex items-center justify-between gap-3">
<Form.Field
control={form.control}
name="years"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.years")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="months"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.months")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_duration")}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="months"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.months")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_duration")}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="days"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.days")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_duration")}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="days"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.days")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_duration")}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
</div>
<div className="flex items-center gap-3">
<Form.Field
control={form.control}
name="hours"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.hours")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_duration")}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
</div>
)}
{form.watch("enable_duration") && (
<div className="flex items-center gap-3">
<Form.Field
control={form.control}
name="hours"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.hours")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_duration")}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="minutes"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.minutes")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_duration")}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
console.log(Number(value))
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
</div>
if (value === "") {
field.onChange(null)
} else {
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
<Form.Field
control={form.control}
name="minutes"
render={({ field }) => {
return (
<Form.Item className="flex-1">
<Form.Label>{t("fields.minutes")}</Form.Label>
<Form.Control>
<Input
{...field}
type="number"
min={0}
disabled={!form.watch("enable_duration")}
onChange={(e) => {
const value = e.target.value
if (value === "") {
field.onChange(null)
} else {
console.log(Number(value))
field.onChange(Number(value))
}
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
</div>
)}
</div>
</div>
</RouteDrawer.Body>