RJBULK-APP/components/forms/fuel-purchase-form.tsx

125 lines
4.6 KiB
TypeScript
Raw Permalink Normal View History

"use client";
import { useActionState, useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Select } from "@/components/ui/select";
import { createFuelPurchase, updateFuelPurchase, type ActionResult } from "@/actions/fuel-purchases";
import { toast } from "sonner";
import type { FuelPurchase, Supplier } from "@/lib/types";
import { formatCurrency, formatNumber } from "@/lib/utils";
const initial: ActionResult = { ok: false, error: "" };
function todayISO() {
return new Date().toISOString().slice(0, 10);
}
export function FuelPurchaseForm({ suppliers, purchase }: { suppliers: Supplier[]; purchase?: FuelPurchase }) {
const router = useRouter();
const isEdit = !!purchase;
const bound = isEdit ? updateFuelPurchase.bind(null, purchase!.id) : createFuelPurchase;
const [state, action, pending] = useActionState(bound, initial);
const ref = useRef<HTMLFormElement>(null);
const [supplier, setSupplier] = useState(purchase?.supplier || "");
const [qty, setQty] = useState<number | "">(purchase?.purchase_qty ?? "");
const [price, setPrice] = useState<number | "">(purchase?.purchase_price ?? "");
useEffect(() => {
if (state.ok) {
toast.success(isEdit ? "Purchase updated" : "Purchase created");
if (!isEdit) {
ref.current?.reset();
setQty("");
setPrice("");
} else {
router.push("/fuel-purchases");
}
} else if (state.error) { toast.error(state.error); }
}, [state, isEdit, router]);
const total = qty && price ? Number(qty) * Number(price) : 0;
return (
<Card className="max-w-2xl">
<CardHeader>
<CardTitle>{isEdit ? "Edit fuel purchase" : "Add fuel purchase"}</CardTitle>
</CardHeader>
<form ref={ref} action={action}>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="supplier">Supplier *</Label>
<Select
id="supplier"
name="supplier"
value={supplier}
onChange={(e) => setSupplier(e.target.value)}
required
options={suppliers.map((s) => ({ value: s.id, label: s.name }))}
placeholder="Select a supplier"
/>
</div>
<div className="grid gap-4 sm:grid-cols-3">
<div className="space-y-2">
<Label htmlFor="purchase_qty">Qty (litres) *</Label>
<Input
id="purchase_qty"
name="purchase_qty"
type="number"
step="0.01"
min="0.01"
value={qty}
onChange={(e) => setQty(e.target.value === "" ? "" : Number(e.target.value))}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="purchase_price">Price / L *</Label>
<Input
id="purchase_price"
name="purchase_price"
type="number"
step="0.01"
min="0.01"
value={price}
onChange={(e) => setPrice(e.target.value === "" ? "" : Number(e.target.value))}
required
/>
</div>
<div className="space-y-2">
<Label>Total</Label>
<div className="h-9 rounded-md border bg-muted/50 px-3 py-1.5 text-sm font-medium">
{formatCurrency(total)}
</div>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="purchase_date">Purchase date *</Label>
<Input
id="purchase_date"
name="purchase_date"
type="date"
defaultValue={(purchase?.purchase_date || "").slice(0, 10) || todayISO()}
required
/>
</div>
{qty && price && (
<p className="text-xs text-muted-foreground">
{formatNumber(Number(qty))} L × {formatCurrency(Number(price))}/L = {formatCurrency(total)}
</p>
)}
</CardContent>
<CardFooter className="flex justify-between">
<Button type="button" variant="ghost" onClick={() => router.back()}>Cancel</Button>
<Button type="submit" disabled={pending}>
{pending ? "Saving…" : isEdit ? "Update purchase" : "Create purchase"}
</Button>
</CardFooter>
</form>
</Card>
);
}