health-petal/apps/pharmanager/src/routes/distributors/add.tsx
2026-05-23 14:37:24 +05:30

181 lines
5.5 KiB
TypeScript

import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { ArrowLeft, Plus } from "lucide-react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useCreateDistributor, trpc } from "shared-react";
const formSchema = z.object({
agency: z.string().min(1, "Agency name is required"),
contact: z.string().min(1, "Contact person is required"),
mobile: z
.string()
.length(10, "Valid 10-digit mobile number is required")
.regex(/^\d{10}$/, "Must be 10 digits"),
address: z.string().nullable().optional(),
});
type FormValues = z.infer<typeof formSchema>;
export const Route = createFileRoute("/distributors/add")({
component: AddDistributorPage,
staticData: {
title: "Add Distributor",
subtitle: "Register a new distributor agency",
},
});
function AddDistributorPage() {
const navigate = useNavigate();
const createMutation = useCreateDistributor();
const utils = trpc.useUtils();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
agency: "",
contact: "",
mobile: "",
address: null,
},
});
function onSubmit(values: FormValues) {
createMutation.mutate(
{
agency: values.agency,
contact: values.contact,
mobile: values.mobile,
address: values.address ?? null,
},
{
onSuccess: () => {
utils.distributor.list.invalidate();
navigate({ to: "/distributors" });
},
},
);
}
return (
<div>
<Link
to="/distributors"
className="inline-flex items-center gap-1.5 text-sm text-blue-600 hover:underline mb-5"
>
<ArrowLeft className="w-4 h-4" />
Back to Distributors
</Link>
<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-2xl"
>
<div className="grid grid-cols-2 gap-5">
<div className="col-span-full">
<label className="block text-sm font-medium text-slate-900 mb-1.5">
Agency Name{" "}
<span className="text-red-600 ml-0.5">*</span>
</label>
<input
type="text"
{...register("agency")}
placeholder="e.g. MediDistributors"
className={`w-full px-3.5 py-2.5 border rounded-md text-sm text-slate-900 bg-white transition-colors focus:outline-none focus:ring-[3px] focus:ring-blue-100 ${errors.agency ? "border-red-600" : "border-slate-200 focus:border-blue-600"}`}
/>
{errors.agency && (
<p className="text-sm text-red-600 mt-1">
{errors.agency.message}
</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-slate-900 mb-1.5">
Contact Person{" "}
<span className="text-red-600 ml-0.5">*</span>
</label>
<input
type="text"
{...register("contact")}
placeholder="e.g. Rahul Mehta"
className={`w-full px-3.5 py-2.5 border rounded-md text-sm text-slate-900 bg-white transition-colors focus:outline-none focus:ring-[3px] focus:ring-blue-100 ${errors.contact ? "border-red-600" : "border-slate-200 focus:border-blue-600"}`}
/>
{errors.contact && (
<p className="text-sm text-red-600 mt-1">
{errors.contact.message}
</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-slate-900 mb-1.5">
Contact Mobile{" "}
<span className="text-red-600 ml-0.5">*</span>
</label>
<input
type="tel"
{...register("mobile", {
onChange: (e) => {
e.target.value = e.target.value
.replace(/\D/g, "")
.slice(0, 10);
},
})}
placeholder="e.g. 9876500001"
className={`w-full px-3.5 py-2.5 border rounded-md text-sm text-slate-900 bg-white transition-colors focus:outline-none focus:ring-[3px] focus:ring-blue-100 ${errors.mobile ? "border-red-600" : "border-slate-200 focus:border-blue-600"}`}
/>
{errors.mobile && (
<p className="text-sm text-red-600 mt-1">
{errors.mobile.message}
</p>
)}
</div>
<div className="col-span-full">
<label className="block text-sm font-medium text-slate-900 mb-1.5">
Address
</label>
<textarea
{...register("address", {
setValueAs: (v: string) => v || null,
})}
rows={3}
placeholder="Full address of the distributor"
className="w-full px-3.5 py-2.5 border border-slate-200 rounded-md text-sm text-slate-900 bg-white resize-y min-h-[80px] transition-colors focus:outline-none focus:ring-[3px] focus:ring-blue-100 focus:border-blue-600"
/>
</div>
</div>
<div className="flex justify-end gap-3 pt-5 mt-2 border-t border-slate-200">
<Link
to="/distributors"
className="inline-flex items-center px-5 py-2.5 border border-slate-200 rounded-md text-sm font-medium text-slate-700 bg-white hover:bg-slate-50 transition-colors"
>
Cancel
</Link>
<button
type="submit"
disabled={createMutation.isPending}
className="inline-flex items-center gap-1.5 px-6 py-2.5 bg-blue-600 text-white rounded-md text-sm font-medium hover:bg-blue-700 disabled:opacity-50 transition-colors"
>
<Plus className="w-[15px] h-[15px]" />
{createMutation.isPending
? "Saving..."
: "Save Distributor"}
</button>
</div>
{createMutation.error && (
<p className="text-sm text-red-600 mt-4">
Failed to create distributor. Please try again.
</p>
)}
</form>
</div>
);
}