Browse Source

add customer from the vehicle list

Bernt Christian Egeland 7 months ago
parent
commit
51e8f375c9

+ 6 - 1
src/features/customers/Components/CustomerForm.tsx

@@ -29,9 +29,10 @@ interface CustomerFormProps {
     company?: string | null;
     notes?: string | null;
   };
+  onCreated?: (customer: { id: string; name: string; company: string | null }) => void;
 }
 
-export function CustomerForm({ open, onOpenChange, customer }: CustomerFormProps) {
+export function CustomerForm({ open, onOpenChange, customer, onCreated }: CustomerFormProps) {
   const router = useRouter();
   const modal = useGlassModal();
   const [loading, setLoading] = useState(false);
@@ -57,6 +58,10 @@ export function CustomerForm({ open, onOpenChange, customer }: CustomerFormProps
     if (result.success) {
       toast.success(customer ? "Customer updated" : "Customer created");
       onOpenChange(false);
+      if (!customer && result.data && onCreated) {
+        const created = result.data as { id: string; name: string; company: string | null };
+        onCreated({ id: created.id, name: created.name, company: created.company ?? null });
+      }
       router.refresh();
     } else {
       modal.open("error", "Error", result.error || "Failed to save customer");

+ 82 - 57
src/features/vehicles/Components/VehicleForm.tsx

@@ -35,8 +35,9 @@ import {
 import { toast } from "sonner";
 import { useGlassModal } from "@/components/glass-modal";
 import { createVehicle, updateVehicle } from "../Actions/vehicleActions";
-import { Camera, Check, ChevronsUpDown, Loader2, X } from "lucide-react";
+import { Camera, Check, ChevronsUpDown, Loader2, Plus, X } from "lucide-react";
 import { compressImage } from "@/lib/compress-image";
+import { CustomerForm } from "@/features/customers/Components/CustomerForm";
 import type { CreateVehicleInput } from "../Schema/vehicleSchema";
 
 interface VehicleFormProps {
@@ -70,6 +71,8 @@ export function VehicleForm({ open, onOpenChange, vehicle, customers }: VehicleF
     vehicle?.customerId || "none"
   );
   const [customerOpen, setCustomerOpen] = useState(false);
+  const [showCustomerForm, setShowCustomerForm] = useState(false);
+  const [localCustomers, setLocalCustomers] = useState(customers || []);
   const fileRef = useRef<HTMLInputElement>(null);
 
   // Sync state when vehicle prop changes (e.g. opening edit for a different vehicle)
@@ -81,10 +84,10 @@ export function VehicleForm({ open, onOpenChange, vehicle, customers }: VehicleF
 
   const selectedCustomerLabel = useMemo(() => {
     if (!selectedCustomerId || selectedCustomerId === "none") return "No customer";
-    const c = customers?.find((c) => c.id === selectedCustomerId);
+    const c = localCustomers.find((c) => c.id === selectedCustomerId);
     if (!c) return "No customer";
     return c.name + (c.company ? ` (${c.company})` : "");
-  }, [selectedCustomerId, customers]);
+  }, [selectedCustomerId, localCustomers]);
 
   const handleImageSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
     const file = e.target.files?.[0];
@@ -174,6 +177,7 @@ export function VehicleForm({ open, onOpenChange, vehicle, customers }: VehicleF
   };
 
   return (
+  <>
     <Dialog open={open} onOpenChange={onOpenChange}>
       <DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-lg">
         <DialogHeader>
@@ -229,60 +233,71 @@ export function VehicleForm({ open, onOpenChange, vehicle, customers }: VehicleF
           </div>
 
           {/* Customer selector */}
-          {customers && customers.length > 0 && (
-            <div className="space-y-2">
-              <Label>Customer</Label>
-              <Popover open={customerOpen} onOpenChange={setCustomerOpen} modal={true}>
-                <PopoverTrigger asChild>
-                  <Button
-                    variant="outline"
-                    role="combobox"
-                    aria-expanded={customerOpen}
-                    className="w-full justify-between font-normal"
-                  >
-                    <span className="truncate">{selectedCustomerLabel}</span>
-                    <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
-                  </Button>
-                </PopoverTrigger>
-                <PopoverContent className="w-[--radix-popover-trigger-width] p-0" align="start">
-                  <Command>
-                    <CommandInput placeholder="Search customers..." />
-                    <CommandList className="max-h-60 overflow-y-auto">
-                      <CommandEmpty>No customer found.</CommandEmpty>
-                      <CommandGroup>
-                        <CommandItem
-                          value="no-customer"
-                          onSelect={() => {
-                            setSelectedCustomerId("none");
-                            setCustomerOpen(false);
-                          }}
-                        >
-                          <Check className={`mr-2 h-4 w-4 ${selectedCustomerId === "none" ? "opacity-100" : "opacity-0"}`} />
-                          No customer
-                        </CommandItem>
-                        {customers.map((c) => {
-                          const label = c.name + (c.company ? ` (${c.company})` : "");
-                          return (
-                            <CommandItem
-                              key={c.id}
-                              value={label}
-                              onSelect={() => {
-                                setSelectedCustomerId(c.id);
-                                setCustomerOpen(false);
-                              }}
-                            >
-                              <Check className={`mr-2 h-4 w-4 ${selectedCustomerId === c.id ? "opacity-100" : "opacity-0"}`} />
-                              {label}
-                            </CommandItem>
-                          );
-                        })}
-                      </CommandGroup>
-                    </CommandList>
-                  </Command>
-                </PopoverContent>
-              </Popover>
-            </div>
-          )}
+          <div className="space-y-2">
+            <Label>Customer</Label>
+            <Popover open={customerOpen} onOpenChange={setCustomerOpen} modal={true}>
+              <PopoverTrigger asChild>
+                <Button
+                  variant="outline"
+                  role="combobox"
+                  aria-expanded={customerOpen}
+                  className="w-full justify-between font-normal"
+                >
+                  <span className="truncate">{selectedCustomerLabel}</span>
+                  <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
+                </Button>
+              </PopoverTrigger>
+              <PopoverContent className="w-[--radix-popover-trigger-width] p-0" align="start">
+                <Command>
+                  <CommandInput placeholder="Search customers..." />
+                  <CommandList className="max-h-60 overflow-y-auto">
+                    <CommandEmpty>No customer found.</CommandEmpty>
+                    <CommandGroup>
+                      <CommandItem
+                        value="no-customer"
+                        onSelect={() => {
+                          setSelectedCustomerId("none");
+                          setCustomerOpen(false);
+                        }}
+                      >
+                        <Check className={`mr-2 h-4 w-4 ${selectedCustomerId === "none" ? "opacity-100" : "opacity-0"}`} />
+                        No customer
+                      </CommandItem>
+                      {localCustomers.map((c) => {
+                        const label = c.name + (c.company ? ` (${c.company})` : "");
+                        return (
+                          <CommandItem
+                            key={c.id}
+                            value={label}
+                            onSelect={() => {
+                              setSelectedCustomerId(c.id);
+                              setCustomerOpen(false);
+                            }}
+                          >
+                            <Check className={`mr-2 h-4 w-4 ${selectedCustomerId === c.id ? "opacity-100" : "opacity-0"}`} />
+                            {label}
+                          </CommandItem>
+                        );
+                      })}
+                    </CommandGroup>
+                  </CommandList>
+                  <div className="border-t p-1">
+                    <button
+                      type="button"
+                      className="flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent"
+                      onClick={() => {
+                        setCustomerOpen(false);
+                        setShowCustomerForm(true);
+                      }}
+                    >
+                      <Plus className="h-4 w-4" />
+                      Add Customer
+                    </button>
+                  </div>
+                </Command>
+              </PopoverContent>
+            </Popover>
+          </div>
 
           <div className="grid grid-cols-2 gap-4">
             <div className="space-y-2">
@@ -424,5 +439,15 @@ export function VehicleForm({ open, onOpenChange, vehicle, customers }: VehicleF
         </form>
       </DialogContent>
     </Dialog>
+
+    <CustomerForm
+      open={showCustomerForm}
+      onOpenChange={setShowCustomerForm}
+      onCreated={(created) => {
+        setLocalCustomers((prev) => [...prev, created]);
+        setSelectedCustomerId(created.id);
+      }}
+    />
+  </>
   );
 }