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

493 lines
13 KiB
TypeScript
Raw Normal View History

2025-08-28 18:27:07 +02:00
import { useEffect } from "react";
import { z } from "zod/v4";
2025-08-04 17:45:44 +02:00
import {
2025-08-28 18:27:07 +02:00
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
2025-08-04 17:45:44 +02:00
} from "~/components/custom_ui/form";
import { NumberInput } from "~/components/custom_ui/InputNumber";
2025-08-04 17:45:44 +02:00
import Input from "~/components/custom_ui/input";
import { MultiSelect, UnpackOptions } from "~/components/custom_ui/multiselect";
import { Textarea } from "~/components/custom_ui/textarea";
2025-08-28 18:27:07 +02:00
import { Button } from "~/components/ui/button";
2025-08-04 17:45:44 +02:00
import {
2025-08-28 18:27:07 +02:00
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
2025-08-04 17:45:44 +02:00
} from "~/components/ui/select";
2025-08-28 18:27:07 +02:00
import { Switch } from "~/components/ui/switch";
import { useZodForm } from "~/lib/zodForm";
import type {
Prezziario,
PrezziarioIdprezziario,
} from "~/schemas/public/Prezziario";
import { api } from "~/utils/api";
2025-08-04 17:45:44 +02:00
type FormPrezzoProps = {
2025-08-28 18:27:07 +02:00
initialValues: Prezziario | null;
submitMutation: (values: Omit<Prezziario, "nome" | "descrizione">) => void;
2025-08-04 17:45:44 +02:00
};
export const FormPrezzo = ({
2025-08-28 18:27:07 +02:00
initialValues,
submitMutation,
2025-08-04 17:45:44 +02:00
}: FormPrezzoProps) => {
2025-08-28 18:27:07 +02:00
const { data: stringhe_condizioni } =
api.settings.getStringheCondizioni.useQuery();
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const stringhe_condizioni_options = [
"",
...(stringhe_condizioni
? stringhe_condizioni.map((v) => String(v.stinga_id))
: []),
];
const PrezzoSchema = z
.object({
2025-08-29 16:18:32 +02:00
desc_en: z.string(),
desc_it: z.string(),
2025-08-28 18:27:07 +02:00
idprezziario: z.custom<PrezziarioIdprezziario>(),
isAcconto: z.boolean(),
isActive: z.boolean(),
2025-08-29 16:18:32 +02:00
isConsulenza: z.boolean(),
isSaldo: z.boolean(),
isStabile: z.boolean(),
isTransitorio: z.boolean(),
2025-08-28 18:27:07 +02:00
nome_en: z.string(),
2025-08-29 16:18:32 +02:00
nome_it: z.string(),
prezzo_cent: z.number().min(0),
2025-08-28 18:27:07 +02:00
sconto: z.number().min(0).max(100),
2025-08-29 16:18:32 +02:00
testo_condizioni: z.string().nullable(),
2025-08-28 18:27:07 +02:00
})
.superRefine(({ testo_condizioni }, ctx) => {
if (testo_condizioni) {
if (
stringhe_condizioni_options.length > 0 &&
!stringhe_condizioni_options.includes(testo_condizioni)
) {
console.log("testo_condizioni", testo_condizioni);
console.log(
"stringhe_condizioni_options",
stringhe_condizioni_options,
);
2025-08-28 18:27:07 +02:00
ctx.addIssue({
code: "custom",
input: testo_condizioni,
2025-08-29 16:18:32 +02:00
message: "Testo condizioni non valido",
2025-08-28 18:27:07 +02:00
path: ["testo_condizioni"],
});
}
}
});
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
type PrezzoFormValues = z.infer<typeof PrezzoSchema>;
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
// This can come from your database or API.
const defaultValues: PrezzoFormValues = initialValues || {
2025-08-29 16:18:32 +02:00
desc_en: "",
desc_it: "",
2025-08-28 18:27:07 +02:00
idprezziario: "" as PrezziarioIdprezziario,
isAcconto: false,
isActive: false,
2025-08-29 16:18:32 +02:00
isConsulenza: false,
isSaldo: false,
isStabile: false,
isTransitorio: false,
2025-08-28 18:27:07 +02:00
nome_en: "",
2025-08-29 16:18:32 +02:00
nome_it: "",
prezzo_cent: 0,
2025-08-28 18:27:07 +02:00
sconto: 0,
2025-08-29 16:18:32 +02:00
testo_condizioni: "",
2025-08-28 18:27:07 +02:00
};
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
z.config(z.locales.it());
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const form = useZodForm(PrezzoSchema, {
defaultValues: defaultValues,
});
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
function onSubmit(fields: PrezzoFormValues) {
submitMutation({
...fields,
testo_condizioni: fields.testo_condizioni || null,
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const { watch } = form;
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
useEffect(() => {
const subscription = watch((value, { name, type }) =>
console.log(value, name, type),
);
return () => subscription.unsubscribe();
}, [watch]);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const scontoWch = watch(["sconto", "prezzo_cent"]);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return (
<>
<Form {...form}>
<form
className="space-y-4 px-0.5"
2025-08-29 16:18:32 +02:00
onSubmit={form.handleSubmit(onSubmit)}
2025-08-28 18:27:07 +02:00
>
<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}
disabled={initialValues != null}
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-[150px]"
{...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-[150px]"
{...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>
<FormMessage />
</div>
2025-08-04 17:45:44 +02:00
<MultiSelect
defaultValue={
field.value
? { label: field.value, value: field.value }
: {
label: "",
value: "",
}
}
elementId="select-testo-condizioni"
elementName="testo_condizioni"
isMulti={false}
onValueChange={async (value) => {
if (value.value === "") {
field.onChange(undefined);
} else {
field.onChange(
stringhe_condizioni_options[
Number.parseInt(value.value)
],
);
2025-08-28 18:27:07 +02:00
}
2025-08-04 17:45:44 +02:00
await form.trigger("testo_condizioni");
2025-08-28 18:27:07 +02:00
}}
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="isAcconto"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="isAcconto">Acconto</FormLabel>
<FormControl>
<Switch
checked={field.value}
className="data-[state=checked]:bg-neutral-700"
id="isAcconto"
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="isSaldo"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="isSaldo">Saldo</FormLabel>
<FormControl>
<Switch
checked={field.value}
className="data-[state=checked]:bg-neutral-700"
id="isSaldo"
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="isConsulenza"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="isConsulenza">Consulenza</FormLabel>
<FormControl>
<Switch
checked={field.value}
className="data-[state=checked]:bg-neutral-700"
id="isConsulenza"
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="isTransitorio"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="isTransitorio">Transitorio</FormLabel>
<FormControl>
<Switch
checked={field.value}
className="data-[state=checked]:bg-neutral-700"
id="isTransitorio"
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="isStabile"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="isStabile">Stabile</FormLabel>
<FormControl>
<Switch
checked={field.value}
className="data-[state=checked]:bg-neutral-700"
id="isStabile"
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
</div>
</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
checked={field.value}
className="data-[state=checked]:bg-neutral-700"
id="isActive"
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="prezzo_cent"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="prezzo">Prezzo</FormLabel>
<FormMessage />
</div>
<FormControl>
<NumberInput
suffix=" €"
{...field}
decimalScale={2}
decimalSeparator=","
fixedDecimalScale
id="prezzo"
min={0}
onValueChange={(v) => {
field.onChange((v || 0) * 100);
}}
value={Number(field.value) / 100}
/>
</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-[180px]">
<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>
)}
/>
2025-08-04 17:45:44 +02:00
{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>
2025-08-28 18:27:07 +02:00
<div className="flex flex-row gap-4">
<Button type="submit">
{initialValues ? "Aggiorna" : "Crea Prezzo"}
</Button>
</div>
</form>
</Form>
</>
);
2025-08-04 17:45:44 +02:00
};