89 lines
4.1 KiB
TypeScript
89 lines
4.1 KiB
TypeScript
import { useEffect } from "react";
|
|
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
|
|
import { Plus } from "lucide-react";
|
|
import { useForm } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { z } from "zod";
|
|
import { useCreateCustomer, useUpdateCustomer, useGetCustomerById, trpc } from "shared-react";
|
|
import { Button, Input, BackLink, buttonVariants } from "#/components/ui";
|
|
|
|
const formSchema = z.object({
|
|
mobile: z.string().min(1, "Mobile is required"),
|
|
name: z.string().nullable().optional(),
|
|
added_on: z.string().min(1, "Date is required"),
|
|
});
|
|
|
|
type FormValues = z.infer<typeof formSchema>;
|
|
|
|
export const Route = createFileRoute("/customers/add")({
|
|
component: AddCustomerPage,
|
|
validateSearch: (search: Record<string, unknown>) => ({ id: search.id ? Number(search.id) : undefined }),
|
|
staticData: { title: "Add Customer", subtitle: "Register a new customer" },
|
|
});
|
|
|
|
function AddCustomerPage() {
|
|
const navigate = useNavigate();
|
|
const { id: editId } = Route.useSearch();
|
|
const createMutation = useCreateCustomer();
|
|
const updateMutation = useUpdateCustomer();
|
|
const { data: existingCustomer } = useGetCustomerById(editId ?? 0);
|
|
const utils = trpc.useUtils();
|
|
const isEditing = typeof editId === "number" && editId > 0;
|
|
|
|
const { register, handleSubmit, reset, formState: { errors } } = useForm<FormValues>({
|
|
resolver: zodResolver(formSchema),
|
|
defaultValues: { mobile: "", name: null, added_on: new Date().toISOString().slice(0, 10) },
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (isEditing && existingCustomer) {
|
|
reset({ mobile: existingCustomer.mobile, name: existingCustomer.name, added_on: existingCustomer.added_on });
|
|
}
|
|
}, [isEditing, existingCustomer, reset]);
|
|
|
|
function onSubmit(values: FormValues) {
|
|
if (isEditing) {
|
|
updateMutation.mutate(
|
|
{ id: editId!, ...values },
|
|
{ onSuccess: () => { utils.customers.list.invalidate(); utils.customers.byId.invalidate({ id: editId! }); navigate({ to: "/customers" }); } },
|
|
);
|
|
} else {
|
|
createMutation.mutate(values, { onSuccess: () => { utils.customers.list.invalidate(); navigate({ to: "/customers" }); } });
|
|
}
|
|
}
|
|
|
|
const mutation = isEditing ? updateMutation : createMutation;
|
|
|
|
return (
|
|
<div>
|
|
<BackLink to="/customers" label="Customers" />
|
|
<form onSubmit={handleSubmit(onSubmit)} className="bg-white rounded-lg shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_1px_2px_rgba(0,0,0,0.04)] p-8 max-w-xl">
|
|
<h2 className="text-xl font-semibold mb-1">{isEditing ? "Edit Customer" : "New Customer"}</h2>
|
|
<p className="text-sm text-slate-600 mb-7">{isEditing ? "Update customer details." : "Register a new customer."}</p>
|
|
<div className="space-y-5">
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-900 mb-1.5">Mobile <span className="text-red-600">*</span></label>
|
|
<Input {...register("mobile")} variant={errors.mobile ? "error" : "default"} placeholder="9876543210" />
|
|
{errors.mobile && <p className="text-sm text-red-600 mt-1">{errors.mobile.message}</p>}
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-900 mb-1.5">Name</label>
|
|
<Input {...register("name", { setValueAs: (v: string) => v || null })} placeholder="John Doe" />
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-900 mb-1.5">Registered Date <span className="text-red-600">*</span></label>
|
|
<Input type="date" {...register("added_on")} variant={errors.added_on ? "error" : "default"} />
|
|
{errors.added_on && <p className="text-sm text-red-600 mt-1">{errors.added_on.message}</p>}
|
|
</div>
|
|
</div>
|
|
<div className="flex justify-end gap-3 pt-5 mt-5 border-t border-slate-200">
|
|
<Link to="/customers" className={buttonVariants({ variant: "outline" })}>Cancel</Link>
|
|
<Button type="submit" disabled={mutation.isPending}>
|
|
<Plus className="w-[15px] h-[15px]" />{mutation.isPending ? "Saving..." : isEditing ? "Update" : "Save"}
|
|
</Button>
|
|
</div>
|
|
{mutation.error && <p className="text-sm text-red-600 mt-4">Failed to {isEditing ? "update" : "create"} customer.</p>}
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|