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

318 lines
9.1 KiB
TypeScript

import { format } from "date-fns";
import { it } from "date-fns/locale";
import { CalendarIcon } from "lucide-react";
import toast from "react-hot-toast";
import { z } from "zod/v4";
import {
CredenzaBody,
CredenzaClose,
CredenzaFooter,
} from "~/components/custom_ui/credenza";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "~/components/custom_ui/form";
import { MultiSelect } from "~/components/custom_ui/multiselect";
import { Button } from "~/components/ui/button";
import { Calendar } from "~/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "~/components/ui/popover";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "~/components/ui/select";
import { Switch } from "~/components/ui/switch";
import { cn, formatCurrency } from "~/lib/utils";
import { useZodForm } from "~/lib/zodForm";
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
import type { Prezziario } from "~/schemas/public/Prezziario";
import type { ServizioServizioId } from "~/schemas/public/Servizio";
import type { UsersId } from "~/schemas/public/Users";
import { zPrezziarioId } from "~/server/utils/zod_types";
import { api } from "~/utils/api";
type FormProps = {
prezziario: Prezziario[];
servizioId: ServizioServizioId;
userId: UsersId;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
};
export const FormNewOrder = ({
prezziario,
servizioId,
userId,
setOpen,
}: FormProps) => {
const schema = z.object({
packid: zPrezziarioId,
isActive: z.boolean(),
type: z.enum(OrderTypeEnum),
created_at: z.date(),
amount_cent: z.number(),
sconto: z.number().min(0).max(100),
});
type FormValues = z.infer<typeof schema>;
z.config(z.locales.it());
const form = useZodForm(schema, {
defaultValues: {
sconto: 0,
amount_cent: 0,
isActive: false,
},
});
const utils = api.useUtils();
const { mutate: createOrder } = api.servizio.createOrdine.useMutation({
onError: (error) => {
toast.error(`Errore nella creazione dell'ordine: ${error.message}`);
},
onSuccess: async () => {
toast.success("Ordine creato con successo");
await utils.servizio.invalidate();
setOpen(false);
form.reset();
},
});
const { watch } = form;
const scontoWch = watch(["sconto", "amount_cent"]);
function onSubmit(fields: FormValues) {
createOrder({
data: { ...fields, servizio_id: servizioId, userid: userId },
});
}
return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<CredenzaBody className="max-h-[80vh] w-full pb-5">
<div className="space-y-8 px-0.5">
<FormField
control={form.control}
name="packid"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-pack">Servizio</FormLabel>
<FormMessage />
</div>
<FormControl>
<MultiSelect
defaultValue={undefined}
elementId="select-pack"
elementName="packid"
isMulti={false}
onValueChange={(value) => {
const selected = zPrezziarioId.safeParse(value.value);
if (selected.success) {
field.onChange(selected.data);
const selectedPrezzo = prezziario.find(
(p) => p.idprezziario === selected.data,
);
if (selectedPrezzo) {
form.setValue(
"amount_cent",
selectedPrezzo.prezzo_cent,
);
}
} else {
console.error(
"Invalid packid selected",
value.value,
);
}
}}
options={prezziario.map((p) => ({
label: `${p.idprezziario} - ${p.nome_it} (${formatCurrency(p.prezzo_cent / 100)})`,
value: p.idprezziario,
}))}
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="titolo">Tipologia</FormLabel>
<FormMessage />
</div>
<FormControl>
<Select
defaultValue={field.value || ""}
onValueChange={field.onChange}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleziona tipologia" />
</SelectTrigger>
</FormControl>
<SelectContent id="select-color">
{Object.values(OrderTypeEnum).map((option) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="created_at"
render={({ field }) => (
<FormItem className="flex w-full flex-col">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="created_at">Data Ordine</FormLabel>
<FormMessage />
</div>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
className={cn(
"h-10 w-full bg-card pl-3 text-left font-medium",
!field.value && "text-muted-foreground",
"rounded-lg border border-neutral-300 shadow-xs outline-hidden file:border-0 file:bg-transparent file:font-medium file:text-sm disabled:cursor-not-allowed disabled:opacity-50 dark:focus:border-transparent",
)}
id="created_at"
variant={"outline"}
>
{field.value ? (
format(field.value, "PPP", {
locale: it,
})
) : (
<span>Scegli data</span>
)}
<CalendarIcon className="ml-auto size-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent align="start" className="w-auto p-0">
<Calendar
autoFocus
captionLayout="dropdown"
defaultMonth={new Date()}
endMonth={new Date(2050, 12)}
locale={it}
mode="single"
onSelect={field.onChange}
selected={field.value || undefined}
/>
</PopoverContent>
</Popover>
</FormItem>
)}
/>
<FormField
control={form.control}
name="isActive"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="isActive">Pagato</FormLabel>
<FormControl>
<Switch
className="data-[state=checked]:bg-neutral-700"
defaultChecked={field.value}
id="isActive"
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
</div>
<FormDescription>
Imposta se il servizio è pagato
</FormDescription>
</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>
Prezzo scontato:{" "}
{formatCurrency(
((1 - scontoWch[0] / 100) * scontoWch[1]) / 100,
)}
</span>
) : (
<span>Prezzo: {formatCurrency(scontoWch[1] / 100)}</span>
)}
</div>
</CredenzaBody>
<CredenzaFooter>
<Button type="submit" variant="success">
Salva
</Button>
<CredenzaClose asChild>
<Button aria-label="Close Credenza" variant="outline">
Chiudi
</Button>
</CredenzaClose>
</CredenzaFooter>
</form>
</Form>
</>
);
};