220 lines
5.2 KiB
TypeScript
220 lines
5.2 KiB
TypeScript
"use client";
|
|
|
|
import { z } from "zod";
|
|
import { useForm } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { toast } from "@/hooks/useToast";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle
|
|
} from "@/components/ui/dialog";
|
|
import {
|
|
Form,
|
|
FormControl,
|
|
FormField,
|
|
FormItem,
|
|
FormLabel,
|
|
FormMessage
|
|
} from "@/components/ui/form";
|
|
import { Input } from "@/components/ui/input";
|
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
import { Purchase } from "../data/schema";
|
|
import { usePurchase } from "@/hooks/usePurchase";
|
|
|
|
const formSchema = z.object({
|
|
user_id: z.number({ invalid_type_error: "User ID must be a number." }),
|
|
used_coupon_id: z.number().nullable().optional(),
|
|
total: z
|
|
.number({ invalid_type_error: "Total must be a number." })
|
|
.min(0, { message: "Total must be at least 0." }),
|
|
date_purchased: z.coerce.date({
|
|
errorMap: () => ({ message: "Invalid date." })
|
|
}),
|
|
isEdit: z.boolean()
|
|
});
|
|
|
|
type PurchaseForm = z.infer<typeof formSchema>;
|
|
|
|
interface Props {
|
|
currentRow?: Purchase;
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
}
|
|
|
|
export function PurchasesActionDialog({
|
|
currentRow,
|
|
open,
|
|
onOpenChange
|
|
}: Props) {
|
|
const { createPurchase, updatePurchase } = usePurchase(currentRow?.id);
|
|
const isEdit = !!currentRow;
|
|
|
|
const form = useForm<PurchaseForm>({
|
|
resolver: zodResolver(formSchema),
|
|
defaultValues: isEdit
|
|
? {
|
|
...currentRow,
|
|
date_purchased: new Date(currentRow.date_purchased),
|
|
isEdit
|
|
}
|
|
: {
|
|
user_id: 0,
|
|
used_coupon_id: null,
|
|
total: 0,
|
|
date_purchased: new Date(),
|
|
isEdit
|
|
}
|
|
});
|
|
|
|
const onSubmit = (values: PurchaseForm) => {
|
|
try {
|
|
const payload = {
|
|
...values,
|
|
date_purchased: values.date_purchased.toISOString()
|
|
};
|
|
|
|
if (isEdit) {
|
|
updatePurchase.mutate({ ...currentRow, ...payload });
|
|
} else {
|
|
createPurchase.mutate({...payload, used_coupon_id: payload.used_coupon_id ?? null });
|
|
}
|
|
|
|
toast({
|
|
title: isEdit ? "Purchase updated" : "Purchase created",
|
|
description: `User #${values.user_id} • ${values.total.toFixed(2)}€`
|
|
});
|
|
form.reset();
|
|
onOpenChange(false);
|
|
} catch (err) {
|
|
toast({
|
|
title: "An error occurred",
|
|
description: (err as Error).message,
|
|
variant: "destructive"
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog
|
|
open={open}
|
|
onOpenChange={(state) => {
|
|
form.reset();
|
|
onOpenChange(state);
|
|
}}>
|
|
<DialogContent className="sm:max-w-lg">
|
|
<DialogHeader className="text-left">
|
|
<DialogTitle>
|
|
{isEdit ? "Edit Purchase" : "Add New Purchase"}
|
|
</DialogTitle>
|
|
<DialogDescription>
|
|
{isEdit
|
|
? "Update the purchase details below."
|
|
: "Record a new purchase."}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<ScrollArea className="-mr-4 h-[26.25rem] w-full py-1 pr-4">
|
|
<Form {...form}>
|
|
<form
|
|
id="purchase-form"
|
|
onSubmit={form.handleSubmit(onSubmit)}
|
|
className="space-y-4 p-0.5">
|
|
<FormField
|
|
control={form.control}
|
|
name="user_id"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>User ID</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="number"
|
|
placeholder="e.g., 12"
|
|
{...field}
|
|
onChange={(e) => field.onChange(Number(e.target.value))}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<FormField
|
|
control={form.control}
|
|
name="used_coupon_id"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Coupon ID (optional)</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="number"
|
|
placeholder="e.g., 5"
|
|
value={field.value ?? ""}
|
|
onChange={(e) => {
|
|
const value = e.target.value;
|
|
field.onChange(value === "" ? null : Number(value));
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<FormField
|
|
control={form.control}
|
|
name="total"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Total (€)</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="number"
|
|
step="0.01"
|
|
placeholder="e.g., 12.50"
|
|
{...field}
|
|
onChange={(e) => field.onChange(Number(e.target.value))}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<FormField
|
|
control={form.control}
|
|
name="date_purchased"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Purchase Date</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="datetime-local"
|
|
value={field.value.toISOString().slice(0, 16)}
|
|
onChange={(e) =>
|
|
field.onChange(new Date(e.target.value))
|
|
}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</form>
|
|
</Form>
|
|
</ScrollArea>
|
|
|
|
<DialogFooter>
|
|
<Button type="submit" form="purchase-form">
|
|
Save changes
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|