infoalloggi-monorepo/apps/infoalloggi/src/forms/FormPrezzo.tsx

497 lines
14 KiB
TypeScript

import { z } from "zod/v4";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "~/components/custom_ui/form";
import { NumberInput } from "~/components/custom_ui/InputNumber";
import { MultiSelect, UnpackOptions } from "~/components/custom_ui/multiselect";
import { Button } from "~/components/ui/button";
import Input from "~/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "~/components/ui/select";
import { Switch } from "~/components/ui/switch";
import { Textarea } from "~/components/ui/textarea";
import { FieldResetButton } from "~/forms/comps";
import { useZodForm } from "~/lib/zodForm";
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
import type {
Prezziario,
PrezziarioIdprezziario,
} from "~/schemas/public/Prezziario";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import { api } from "~/utils/api";
type FormPrezzoProps = {
initialValues: Prezziario | null;
submitMutation: (values: Omit<Prezziario, "nome" | "descrizione">) => void;
};
export const FormPrezzo = ({
initialValues,
submitMutation,
}: FormPrezzoProps) => {
const { data: stringhe_condizioni } =
api.strings.getStringheCondizioni.useQuery();
const stringhe_condizioni_options = stringhe_condizioni
? stringhe_condizioni.map((v) => String(v.stinga_id))
: [];
const PrezzoSchema = z
.object({
desc_en: z.string(),
desc_it: z.string(),
idprezziario: z.custom<PrezziarioIdprezziario>(),
isActive: z.boolean(),
nome_en: z.string(),
nome_it: z.string(),
prezzo_cent: z.number().min(0),
sconto: z.number().min(0).max(100),
testo_condizioni: z.string().nullable(),
order_type: z.enum(OrderTypeEnum).nullable(),
service_type: z.enum(TipologiaPosizioneEnum).nullable(),
service_variant: z.number().nullable(),
})
.superRefine(({ testo_condizioni }, ctx) => {
if (testo_condizioni) {
if (
stringhe_condizioni_options.length > 0 &&
!stringhe_condizioni_options.includes(testo_condizioni)
) {
ctx.addIssue({
code: "custom",
input: testo_condizioni,
message: "Testo condizioni non valido",
path: ["testo_condizioni"],
});
}
}
});
type PrezzoFormValues = z.infer<typeof PrezzoSchema>;
// This can come from your database or API.
const defaultValues: PrezzoFormValues = initialValues || {
desc_en: "",
desc_it: "",
idprezziario: "" as PrezziarioIdprezziario,
isActive: false,
nome_en: "",
nome_it: "",
prezzo_cent: 0,
sconto: 0,
testo_condizioni: "",
order_type: null,
service_type: null,
service_variant: null,
};
z.config(z.locales.it());
const form = useZodForm(PrezzoSchema, {
defaultValues: defaultValues,
});
function onSubmit(fields: PrezzoFormValues) {
submitMutation({
...fields,
testo_condizioni: fields.testo_condizioni || null,
});
}
const { watch } = form;
const scontoWch = watch(["sconto", "prezzo_cent"]);
return (
<>
<Form {...form}>
<form
className="space-y-4 px-0.5"
onSubmit={form.handleSubmit(onSubmit)}
>
<div className="flex flex-col gap-4 md:flex-row">
<div className="flex w-full flex-col gap-4 md:w-2/3">
<FormField
control={form.control}
name="idprezziario"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="idprezziario">Codice</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input placeholder="" {...field} id="idprezziario" />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="nome_it"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="nome_it">Nome IT</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input placeholder="" {...field} id="nome_it" />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="desc_it"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel
className="font-medium text-base"
htmlFor="desc_it"
>
Descrizione IT
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Textarea
className="min-h-37.5"
{...field}
id="desc_it"
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="nome_en"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="nome_en">Nome EN</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input placeholder="" {...field} id="nome_en" />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="desc_en"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel
className="font-medium text-base"
htmlFor="desc_en"
>
Descrizione EN
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Textarea
className="min-h-37.5"
{...field}
id="desc_en"
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="testo_condizioni"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel
className="font-medium text-base"
htmlFor="select-testo-condizioni"
>
ID Testo Condizioni
</FormLabel>
{field.value !== null && (
<FieldResetButton
onClick={() =>
form.setValue("testo_condizioni", null)
}
/>
)}
<FormMessage />
</div>
<MultiSelect
defaultValue={
field.value
? { label: field.value, value: field.value }
: {
label: "",
value: "",
}
}
elementId="select-testo-condizioni"
elementName="testo_condizioni"
isMulti={false}
key={`${field.value}-select-testo`}
onValueChange={async (value) => {
if (value.value !== "") {
field.onChange(
stringhe_condizioni_options[
Number.parseInt(value.value)
],
);
}
await form.trigger("testo_condizioni");
}}
options={UnpackOptions({
options: stringhe_condizioni_options,
})}
placeholder="Seleziona..."
/>
</FormItem>
)}
/>
</div>
<div className="flex w-full flex-col gap-4 md:w-1/3">
<FormField
control={form.control}
name="order_type"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-order_type">
Tipologia
</FormLabel>
{field.value !== null && (
<FieldResetButton
onClick={() => form.setValue("order_type", null)}
/>
)}
<FormMessage />
</div>
<FormControl>
<Select
defaultValue={field.value || ""}
key={`${field.value}-select-order_type`}
onValueChange={field.onChange}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleziona tipologia" />
</SelectTrigger>
</FormControl>
<SelectContent id="select-order_type">
{Object.values(OrderTypeEnum).map((option) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="service_type"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-service_type">
Categoria
</FormLabel>
{field.value !== null && (
<FieldResetButton
onClick={() => form.setValue("service_type", null)}
/>
)}
<FormMessage />
</div>
<FormControl>
<Select
defaultValue={field.value || ""}
key={`${field.value}-select-service_type`}
onValueChange={field.onChange}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleziona categoria" />
</SelectTrigger>
</FormControl>
<SelectContent id="select-service_type">
{Object.values(TipologiaPosizioneEnum).map(
(option) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
),
)}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="service_variant"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="service_variant">
Variante Categoria
</FormLabel>
{field.value !== null && (
<FieldResetButton
onClick={() => form.setValue("service_variant", null)}
/>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="service_variant"
onChange={(e) => {
const v = e.target.value.trim();
if (v !== null && v !== "") {
field.onChange(parseInt(v, 10));
} else {
field.onChange(null);
}
}}
value={field.value !== null ? String(field.value) : ""}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="isActive"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="isActive">Attivo</FormLabel>
<FormControl>
<Switch
className="data-[state=checked]:bg-neutral-700"
defaultChecked={field.value}
id="isActive"
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="prezzo_cent"
render={({ field }) => {
const { value, onChange, ...rest } = field;
return (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="prezzo">Prezzo</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<NumberInput
decimalScale={2}
{...rest}
decimalSeparator=","
defaultValue={Number(value) / 100}
fixedDecimalScale
id="prezzo"
min={0}
onValueChange={(v) => {
onChange((Number(v) || 0) * 100);
}}
suffix=" €"
/>
</FormControl>
</FormItem>
);
}}
/>
<FormField
control={form.control}
name="sconto"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="sconto">Sconto</FormLabel>
<FormControl>
<Select
defaultValue={String(field.value)}
name="sconto"
onValueChange={(value) => {
field.onChange(Number.parseInt(value));
}}
>
<SelectTrigger className="w-45">
<SelectValue placeholder="Sconto" />
</SelectTrigger>
<SelectContent>
{[
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60,
65, 70, 75, 80, 85, 90, 95, 100,
].map((v) => {
return (
<SelectItem key={v} value={String(v)}>
{v}%
</SelectItem>
);
})}
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
{scontoWch && scontoWch[0] !== 0 && (
<span className="text-gray-500 text-sm">
Prezzo scontato:{" "}
{(((1 - scontoWch[0] / 100) * scontoWch[1]) / 100).toFixed(2)}
</span>
)}
</div>
</div>
<div className="flex flex-row gap-4">
<Button type="submit">
{initialValues ? "Aggiorna" : "Crea Prezzo"}
</Button>
</div>
</form>
</Form>
</>
);
};