Bernt Christian Egeland 7 месяцев назад
Родитель
Сommit
35d7ab9681

+ 7 - 1
src/app/(authenticated)/calendar/page.tsx

@@ -1,5 +1,6 @@
 import { getCalendarEvents } from "@/features/calendar/Actions/calendarActions";
 import { getVehicles } from "@/features/vehicles/Actions/vehicleActions";
+import { getCustomersList } from "@/features/customers/Actions/customerActions";
 import { getSettings } from "@/features/settings/Actions/settingsActions";
 import { SETTING_KEYS } from "@/features/settings/Schema/settingsSchema";
 import { PageHeader } from "@/components/page-header";
@@ -17,13 +18,14 @@ export default async function CalendarPage() {
   const start = new Date(now.getFullYear(), now.getMonth(), 1);
   const end = new Date(now.getFullYear(), now.getMonth() + 1, 0);
 
-  const [eventsResult, settingsResult, vehiclesResult] = await Promise.all([
+  const [eventsResult, settingsResult, vehiclesResult, customersResult] = await Promise.all([
     getCalendarEvents({
       start: toLocalDateStr(start),
       end: toLocalDateStr(end),
     }),
     getSettings([SETTING_KEYS.CURRENCY_CODE]),
     getVehicles(),
+    getCustomersList(),
   ]);
 
   const events = eventsResult.success && eventsResult.data ? eventsResult.data : [];
@@ -39,6 +41,9 @@ export default async function CalendarPage() {
         customer: v.customer,
       }))
     : [];
+  const customers = customersResult.success && customersResult.data
+    ? customersResult.data
+    : [];
 
   return (
     <>
@@ -51,6 +56,7 @@ export default async function CalendarPage() {
           initialDay={now.getDate()}
           todayStr={toLocalDateStr(now)}
           vehicles={vehicles}
+          customers={customers}
           currencyCode={currencyCode}
         />
       </div>

+ 7 - 1
src/app/(authenticated)/work-orders/page.tsx

@@ -2,6 +2,7 @@ import { getWorkOrders } from "@/features/vehicles/Actions/serviceActions";
 import { getSettings } from "@/features/settings/Actions/settingsActions";
 import { SETTING_KEYS } from "@/features/settings/Schema/settingsSchema";
 import { getVehicles } from "@/features/vehicles/Actions/vehicleActions";
+import { getCustomersList } from "@/features/customers/Actions/customerActions";
 import { WorkOrdersClient } from "./work-orders-client";
 import { PageHeader } from "@/components/page-header";
 
@@ -16,7 +17,7 @@ export default async function WorkOrdersPage({
   }>;
 }) {
   const params = await searchParams;
-  const [result, settingsResult, vehiclesResult] = await Promise.all([
+  const [result, settingsResult, vehiclesResult, customersResult] = await Promise.all([
     getWorkOrders({
       page: params.page ? parseInt(params.page) : 1,
       pageSize: params.pageSize ? parseInt(params.pageSize) : 20,
@@ -25,6 +26,7 @@ export default async function WorkOrdersPage({
     }),
     getSettings([SETTING_KEYS.CURRENCY_CODE]),
     getVehicles(),
+    getCustomersList(),
   ]);
 
   if (!result.success || !result.data) {
@@ -52,6 +54,9 @@ export default async function WorkOrdersPage({
         customer: v.customer,
       }))
     : [];
+  const customers = customersResult.success && customersResult.data
+    ? customersResult.data
+    : [];
 
   return (
     <>
@@ -60,6 +65,7 @@ export default async function WorkOrdersPage({
         <WorkOrdersClient
           data={result.data}
           vehicles={vehicles}
+          customers={customers}
           currencyCode={currencyCode}
           search={params.search || ""}
           statusFilter={params.status || "all"}

+ 17 - 74
src/app/(authenticated)/work-orders/work-orders-client.tsx

@@ -1,6 +1,6 @@
 "use client";
 
-import { useState, useCallback, useTransition, useMemo } from "react";
+import { useState, useCallback, useTransition } from "react";
 import { useRouter, usePathname, useSearchParams } from "next/navigation";
 import { toast } from "sonner";
 import { format } from "date-fns";
@@ -15,12 +15,6 @@ import {
   TableHeader,
   TableRow,
 } from "@/components/ui/table";
-import {
-  Dialog,
-  DialogContent,
-  DialogHeader,
-  DialogTitle,
-} from "@/components/ui/dialog";
 import {
   DropdownMenu,
   DropdownMenuContent,
@@ -31,13 +25,13 @@ import { DataTablePagination } from "@/components/data-table-pagination";
 import { statusColors } from "@/lib/table-utils";
 import { updateServiceStatus } from "@/features/vehicles/Actions/serviceActions";
 import {
-  Car,
   ChevronDown,
   Loader2,
   Plus,
   Search,
 } from "lucide-react";
 import { formatCurrency } from "@/lib/format";
+import { VehiclePickerDialog } from "@/components/vehicle-picker-dialog";
 
 interface WorkOrder {
   id: string;
@@ -68,6 +62,12 @@ interface VehicleOption {
   customer: { id: string; name: string; company: string | null } | null;
 }
 
+interface CustomerOption {
+  id: string;
+  name: string;
+  company: string | null;
+}
+
 interface PaginatedData {
   records: WorkOrder[];
   total: number;
@@ -111,12 +111,14 @@ const statusTransitions: Record<string, { label: string; target: string }[]> = {
 export function WorkOrdersClient({
   data,
   vehicles = [],
+  customers = [],
   currencyCode = "USD",
   search,
   statusFilter,
 }: {
   data: PaginatedData;
   vehicles?: VehicleOption[];
+  customers?: CustomerOption[];
   currencyCode?: string;
   search: string;
   statusFilter: string;
@@ -128,20 +130,6 @@ export function WorkOrdersClient({
   const [searchInput, setSearchInput] = useState(search);
   const [navigatingId, setNavigatingId] = useState<string | null>(null);
   const [showPicker, setShowPicker] = useState(false);
-  const [vehicleSearch, setVehicleSearch] = useState("");
-
-  const filteredVehicles = useMemo(() => {
-    if (!vehicleSearch) return vehicles;
-    const q = vehicleSearch.toLowerCase();
-    return vehicles.filter(
-      (v) =>
-        v.make.toLowerCase().includes(q) ||
-        v.model.toLowerCase().includes(q) ||
-        v.licensePlate?.toLowerCase().includes(q) ||
-        v.customer?.name.toLowerCase().includes(q) ||
-        String(v.year).includes(q)
-    );
-  }, [vehicles, vehicleSearch]);
 
   const navigate = useCallback(
     (params: Record<string, string | number | undefined>) => {
@@ -335,58 +323,13 @@ export function WorkOrdersClient({
         onNavigate={navigate}
       />
 
-      {/* Vehicle picker dialog */}
-      <Dialog open={showPicker} onOpenChange={setShowPicker}>
-        <DialogContent className="max-w-md">
-          <DialogHeader>
-            <DialogTitle>Select Vehicle</DialogTitle>
-          </DialogHeader>
-          <div className="space-y-3">
-            <div className="relative">
-              <Search className="absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
-              <Input
-                placeholder="Search vehicles..."
-                value={vehicleSearch}
-                onChange={(e) => setVehicleSearch(e.target.value)}
-                className="pl-9"
-                autoFocus
-              />
-            </div>
-            <div className="max-h-[300px] overflow-y-auto">
-              {filteredVehicles.length === 0 ? (
-                <p className="py-6 text-center text-sm text-muted-foreground">
-                  {vehicles.length === 0 ? "No vehicles found. Add a vehicle first." : "No vehicles match your search."}
-                </p>
-              ) : (
-                <div className="space-y-1">
-                  {filteredVehicles.map((v) => (
-                    <button
-                      key={v.id}
-                      type="button"
-                      className="flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left transition-colors hover:bg-muted"
-                      onClick={() => {
-                        setShowPicker(false);
-                        setVehicleSearch("");
-                        router.push(`/vehicles/${v.id}/service/new`);
-                      }}
-                    >
-                      <Car className="h-4 w-4 shrink-0 text-muted-foreground" />
-                      <div className="min-w-0 flex-1">
-                        <p className="text-sm font-medium">
-                          {v.year} {v.make} {v.model}
-                        </p>
-                        <p className="text-xs text-muted-foreground">
-                          {[v.licensePlate, v.customer?.name].filter(Boolean).join(" · ") || "No plate"}
-                        </p>
-                      </div>
-                    </button>
-                  ))}
-                </div>
-              )}
-            </div>
-          </div>
-        </DialogContent>
-      </Dialog>
+      <VehiclePickerDialog
+        open={showPicker}
+        onOpenChange={setShowPicker}
+        vehicles={vehicles}
+        customers={customers}
+        title="Select Vehicle"
+      />
     </div>
   );
 }

+ 382 - 0
src/components/vehicle-picker-dialog.tsx

@@ -0,0 +1,382 @@
+"use client";
+
+import { useState, useMemo } 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 {
+  Select,
+  SelectContent,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from "@/components/ui/select";
+import {
+  Dialog,
+  DialogContent,
+  DialogHeader,
+  DialogTitle,
+} from "@/components/ui/dialog";
+import {
+  Loader2,
+  Plus,
+  Car,
+  Search,
+  ArrowLeft,
+  UserPlus,
+} from "lucide-react";
+import { createVehicle } from "@/features/vehicles/Actions/vehicleActions";
+import { createCustomer } from "@/features/customers/Actions/customerActions";
+import { toast } from "sonner";
+
+interface Vehicle {
+  id: string;
+  make: string;
+  model: string;
+  year: number;
+  licensePlate: string | null;
+  customer: { id: string; name: string; company: string | null } | null;
+}
+
+interface Customer {
+  id: string;
+  name: string;
+  company: string | null;
+}
+
+interface VehiclePickerDialogProps {
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+  vehicles: Vehicle[];
+  customers: Customer[];
+  title?: string;
+}
+
+export function VehiclePickerDialog({
+  open,
+  onOpenChange,
+  vehicles,
+  customers,
+  title = "Select Vehicle for Work Order",
+}: VehiclePickerDialogProps) {
+  const router = useRouter();
+
+  const [vehicleSearch, setVehicleSearch] = useState("");
+  const [pickerStep, setPickerStep] = useState<"select" | "create">("select");
+
+  const [newMake, setNewMake] = useState("");
+  const [newModel, setNewModel] = useState("");
+  const [newYear, setNewYear] = useState("");
+  const [newPlate, setNewPlate] = useState("");
+  const [selectedCustomerId, setSelectedCustomerId] = useState("");
+  const [showNewCustomer, setShowNewCustomer] = useState(false);
+  const [newCustomerName, setNewCustomerName] = useState("");
+  const [newCustomerPhone, setNewCustomerPhone] = useState("");
+  const [creating, setCreating] = useState(false);
+
+  const filteredVehicles = useMemo(() => {
+    if (!vehicleSearch.trim()) return vehicles;
+    const q = vehicleSearch.toLowerCase();
+    return vehicles.filter(
+      (v) =>
+        `${v.year} ${v.make} ${v.model}`.toLowerCase().includes(q) ||
+        v.licensePlate?.toLowerCase().includes(q) ||
+        v.customer?.name.toLowerCase().includes(q)
+    );
+  }, [vehicles, vehicleSearch]);
+
+  const resetState = () => {
+    setPickerStep("select");
+    setVehicleSearch("");
+    setNewMake("");
+    setNewModel("");
+    setNewYear("");
+    setNewPlate("");
+    setSelectedCustomerId("");
+    setShowNewCustomer(false);
+    setNewCustomerName("");
+    setNewCustomerPhone("");
+    setCreating(false);
+  };
+
+  const handleSelect = (vehicleId: string) => {
+    onOpenChange(false);
+    resetState();
+    router.push(`/vehicles/${vehicleId}/service/new`);
+  };
+
+  const handleOpenChange = (nextOpen: boolean) => {
+    onOpenChange(nextOpen);
+    if (!nextOpen) resetState();
+  };
+
+  const handleQuickCreate = async () => {
+    if (!newMake.trim() || !newModel.trim() || !newYear.trim()) return;
+    const yearNum = parseInt(newYear, 10);
+    if (isNaN(yearNum) || yearNum < 1900 || yearNum > new Date().getFullYear() + 2) {
+      toast.error("Please enter a valid year");
+      return;
+    }
+    if (showNewCustomer && !newCustomerName.trim()) {
+      toast.error("Customer name is required");
+      return;
+    }
+
+    setCreating(true);
+    try {
+      let customerId = selectedCustomerId || undefined;
+
+      if (showNewCustomer && newCustomerName.trim()) {
+        const customerResult = await createCustomer({
+          name: newCustomerName.trim(),
+          phone: newCustomerPhone.trim() || undefined,
+        });
+        if (!customerResult.success || !customerResult.data) {
+          toast.error(customerResult.error || "Failed to create customer");
+          setCreating(false);
+          return;
+        }
+        customerId = customerResult.data.id;
+      }
+
+      const vehicleResult = await createVehicle({
+        make: newMake.trim(),
+        model: newModel.trim(),
+        year: yearNum,
+        licensePlate: newPlate.trim() || undefined,
+        customerId,
+      });
+      if (!vehicleResult.success || !vehicleResult.data) {
+        toast.error(vehicleResult.error || "Failed to create vehicle");
+        setCreating(false);
+        return;
+      }
+
+      handleSelect(vehicleResult.data.id);
+    } catch {
+      toast.error("Something went wrong");
+      setCreating(false);
+    }
+  };
+
+  return (
+    <Dialog open={open} onOpenChange={handleOpenChange}>
+      <DialogContent className="max-w-md">
+        {pickerStep === "select" ? (
+          <>
+            <DialogHeader>
+              <DialogTitle>{title}</DialogTitle>
+            </DialogHeader>
+            <div className="space-y-3">
+              <div className="relative">
+                <Search className="absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
+                <Input
+                  placeholder="Search vehicles..."
+                  value={vehicleSearch}
+                  onChange={(e) => setVehicleSearch(e.target.value)}
+                  className="pl-9"
+                  autoFocus
+                />
+              </div>
+              <div className="max-h-[300px] overflow-y-auto">
+                {filteredVehicles.length === 0 ? (
+                  <div className="py-6 text-center space-y-3">
+                    <p className="text-sm text-muted-foreground">
+                      {vehicles.length === 0
+                        ? "No vehicles found."
+                        : "No vehicles match your search."}
+                    </p>
+                    <Button
+                      variant="outline"
+                      onClick={() => setPickerStep("create")}
+                    >
+                      <Plus className="h-4 w-4 mr-1" />
+                      Add New Vehicle
+                    </Button>
+                  </div>
+                ) : (
+                  <div className="space-y-1">
+                    {filteredVehicles.map((v) => (
+                      <button
+                        key={v.id}
+                        type="button"
+                        className="flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left transition-colors hover:bg-muted"
+                        onClick={() => handleSelect(v.id)}
+                      >
+                        <Car className="h-4 w-4 shrink-0 text-muted-foreground" />
+                        <div className="min-w-0 flex-1">
+                          <p className="text-sm font-medium">
+                            {v.year} {v.make} {v.model}
+                          </p>
+                          <p className="text-xs text-muted-foreground">
+                            {[v.licensePlate, v.customer?.name]
+                              .filter(Boolean)
+                              .join(" · ") || "No plate"}
+                          </p>
+                        </div>
+                      </button>
+                    ))}
+                  </div>
+                )}
+              </div>
+              <Button
+                variant="ghost"
+                className="w-full"
+                onClick={() => setPickerStep("create")}
+              >
+                <Plus className="h-4 w-4 mr-1" />
+                Add New Vehicle
+              </Button>
+            </div>
+          </>
+        ) : (
+          <>
+            <DialogHeader>
+              <div className="flex items-center gap-2">
+                <Button
+                  variant="ghost"
+                  size="icon"
+                  className="h-7 w-7 shrink-0"
+                  onClick={() => setPickerStep("select")}
+                >
+                  <ArrowLeft className="h-4 w-4" />
+                </Button>
+                <DialogTitle>Add New Vehicle</DialogTitle>
+              </div>
+            </DialogHeader>
+            <div className="space-y-4">
+              <div className="grid grid-cols-2 gap-3">
+                <div className="space-y-1.5">
+                  <Label htmlFor="new-make">Make *</Label>
+                  <Input
+                    id="new-make"
+                    placeholder="e.g. Toyota"
+                    value={newMake}
+                    onChange={(e) => setNewMake(e.target.value)}
+                    autoFocus
+                  />
+                </div>
+                <div className="space-y-1.5">
+                  <Label htmlFor="new-model">Model *</Label>
+                  <Input
+                    id="new-model"
+                    placeholder="e.g. Camry"
+                    value={newModel}
+                    onChange={(e) => setNewModel(e.target.value)}
+                  />
+                </div>
+              </div>
+              <div className="grid grid-cols-2 gap-3">
+                <div className="space-y-1.5">
+                  <Label htmlFor="new-year">Year *</Label>
+                  <Input
+                    id="new-year"
+                    type="number"
+                    placeholder={`e.g. ${new Date().getFullYear()}`}
+                    value={newYear}
+                    onChange={(e) => setNewYear(e.target.value)}
+                  />
+                </div>
+                <div className="space-y-1.5">
+                  <Label htmlFor="new-plate">License Plate</Label>
+                  <Input
+                    id="new-plate"
+                    placeholder="e.g. ABC-1234"
+                    value={newPlate}
+                    onChange={(e) => setNewPlate(e.target.value)}
+                  />
+                </div>
+              </div>
+              <div className="space-y-1.5">
+                <Label>Customer</Label>
+                {!showNewCustomer ? (
+                  <Select
+                    value={selectedCustomerId}
+                    onValueChange={(val) => {
+                      if (val === "__new__") {
+                        setShowNewCustomer(true);
+                        setSelectedCustomerId("");
+                      } else {
+                        setSelectedCustomerId(val);
+                      }
+                    }}
+                  >
+                    <SelectTrigger className="w-full">
+                      <SelectValue placeholder="Select a customer (optional)" />
+                    </SelectTrigger>
+                    <SelectContent>
+                      <SelectItem value="__new__">
+                        <span className="flex items-center gap-1.5">
+                          <UserPlus className="h-3.5 w-3.5" />
+                          Create new customer
+                        </span>
+                      </SelectItem>
+                      {customers.map((c) => (
+                        <SelectItem key={c.id} value={c.id}>
+                          {c.name}{c.company ? ` (${c.company})` : ""}
+                        </SelectItem>
+                      ))}
+                    </SelectContent>
+                  </Select>
+                ) : (
+                  <div className="space-y-3 rounded-md border p-3">
+                    <div className="flex items-center justify-between">
+                      <p className="text-sm font-medium">New Customer</p>
+                      <Button
+                        variant="ghost"
+                        size="sm"
+                        className="h-7 text-xs"
+                        onClick={() => {
+                          setShowNewCustomer(false);
+                          setNewCustomerName("");
+                          setNewCustomerPhone("");
+                        }}
+                      >
+                        Cancel
+                      </Button>
+                    </div>
+                    <div className="grid grid-cols-2 gap-3">
+                      <div className="space-y-1.5">
+                        <Label htmlFor="new-customer-name">Name *</Label>
+                        <Input
+                          id="new-customer-name"
+                          placeholder="Customer name"
+                          value={newCustomerName}
+                          onChange={(e) => setNewCustomerName(e.target.value)}
+                        />
+                      </div>
+                      <div className="space-y-1.5">
+                        <Label htmlFor="new-customer-phone">Phone</Label>
+                        <Input
+                          id="new-customer-phone"
+                          placeholder="Phone number"
+                          value={newCustomerPhone}
+                          onChange={(e) => setNewCustomerPhone(e.target.value)}
+                        />
+                      </div>
+                    </div>
+                  </div>
+                )}
+              </div>
+              <Button
+                className="w-full"
+                disabled={creating || !newMake.trim() || !newModel.trim() || !newYear.trim() || (showNewCustomer && !newCustomerName.trim())}
+                onClick={handleQuickCreate}
+              >
+                {creating ? (
+                  <>
+                    <Loader2 className="h-4 w-4 mr-1 animate-spin" />
+                    Creating...
+                  </>
+                ) : (
+                  "Create & Continue"
+                )}
+              </Button>
+            </div>
+          </>
+        )}
+      </DialogContent>
+    </Dialog>
+  );
+}

+ 16 - 82
src/features/calendar/Components/CalendarClient.tsx

@@ -1,24 +1,14 @@
 "use client";
 
 import { useState, useCallback, useMemo } from "react";
-import { useRouter } from "next/navigation";
 import { Button } from "@/components/ui/button";
 import { Card, CardContent } from "@/components/ui/card";
-import { Input } from "@/components/ui/input";
-import {
-  Dialog,
-  DialogContent,
-  DialogHeader,
-  DialogTitle,
-} from "@/components/ui/dialog";
 import { Checkbox } from "@/components/ui/checkbox";
 import {
   ChevronLeft,
   ChevronRight,
   Loader2,
   Plus,
-  Car,
-  Search,
   Wrench,
   FileText,
   AlertTriangle,
@@ -28,6 +18,7 @@ import { CalendarEventList } from "./CalendarEventList";
 import { toLocalDateStr } from "./calendar-utils";
 import { getCalendarEvents } from "../Actions/calendarActions";
 import type { CalendarEvent } from "../Actions/calendarActions";
+import { VehiclePickerDialog } from "@/components/vehicle-picker-dialog";
 
 interface Vehicle {
   id: string;
@@ -38,6 +29,12 @@ interface Vehicle {
   customer: { id: string; name: string; company: string | null } | null;
 }
 
+interface Customer {
+  id: string;
+  name: string;
+  company: string | null;
+}
+
 interface CalendarClientProps {
   initialEvents: CalendarEvent[];
   initialMonth: number;
@@ -45,6 +42,7 @@ interface CalendarClientProps {
   initialDay: number;
   todayStr: string; // YYYY-MM-DD computed on server to avoid hydration mismatch
   vehicles: Vehicle[];
+  customers: Customer[];
   currencyCode: string;
 }
 
@@ -89,10 +87,9 @@ export default function CalendarClient({
   initialDay,
   todayStr,
   vehicles,
+  customers,
   currencyCode,
 }: CalendarClientProps) {
-  const router = useRouter();
-
   const [month, setMonth] = useState(initialMonth);
   const [year, setYear] = useState(initialYear);
   const [events, setEvents] = useState<CalendarEvent[]>(initialEvents);
@@ -108,7 +105,6 @@ export default function CalendarClient({
 
   // Vehicle picker
   const [showPicker, setShowPicker] = useState(false);
-  const [vehicleSearch, setVehicleSearch] = useState("");
 
   const selectedDateStr = toLocalDateStr(selectedDate);
 
@@ -138,17 +134,6 @@ export default function CalendarClient({
 
   const days = getMonthDays(year, month);
 
-  const filteredVehicles = useMemo(() => {
-    if (!vehicleSearch.trim()) return vehicles;
-    const q = vehicleSearch.toLowerCase();
-    return vehicles.filter(
-      (v) =>
-        `${v.year} ${v.make} ${v.model}`.toLowerCase().includes(q) ||
-        v.licensePlate?.toLowerCase().includes(q) ||
-        v.customer?.name.toLowerCase().includes(q)
-    );
-  }, [vehicles, vehicleSearch]);
-
   const fetchEvents = useCallback(async (y: number, m: number) => {
     setLoading(true);
     const start = new Date(y, m, 1);
@@ -192,12 +177,6 @@ export default function CalendarClient({
     }
   };
 
-  const handleCreateWorkOrder = (vehicleId: string) => {
-    setShowPicker(false);
-    setVehicleSearch("");
-    router.push(`/vehicles/${vehicleId}/service/new`);
-  };
-
   return (
     <div className="space-y-4">
       {/* Summary stats */}
@@ -361,58 +340,13 @@ export default function CalendarClient({
         </div>
       </div>
 
-      {/* Vehicle picker dialog */}
-      <Dialog open={showPicker} onOpenChange={setShowPicker}>
-        <DialogContent className="max-w-md">
-          <DialogHeader>
-            <DialogTitle>Select Vehicle for Work Order</DialogTitle>
-          </DialogHeader>
-          <div className="space-y-3">
-            <div className="relative">
-              <Search className="absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
-              <Input
-                placeholder="Search vehicles..."
-                value={vehicleSearch}
-                onChange={(e) => setVehicleSearch(e.target.value)}
-                className="pl-9"
-                autoFocus
-              />
-            </div>
-            <div className="max-h-[300px] overflow-y-auto">
-              {filteredVehicles.length === 0 ? (
-                <p className="py-6 text-center text-sm text-muted-foreground">
-                  {vehicles.length === 0
-                    ? "No vehicles found. Add a vehicle first."
-                    : "No vehicles match your search."}
-                </p>
-              ) : (
-                <div className="space-y-1">
-                  {filteredVehicles.map((v) => (
-                    <button
-                      key={v.id}
-                      type="button"
-                      className="flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left transition-colors hover:bg-muted"
-                      onClick={() => handleCreateWorkOrder(v.id)}
-                    >
-                      <Car className="h-4 w-4 shrink-0 text-muted-foreground" />
-                      <div className="min-w-0 flex-1">
-                        <p className="text-sm font-medium">
-                          {v.year} {v.make} {v.model}
-                        </p>
-                        <p className="text-xs text-muted-foreground">
-                          {[v.licensePlate, v.customer?.name]
-                            .filter(Boolean)
-                            .join(" · ") || "No plate"}
-                        </p>
-                      </div>
-                    </button>
-                  ))}
-                </div>
-              )}
-            </div>
-          </div>
-        </DialogContent>
-      </Dialog>
+      <VehiclePickerDialog
+        open={showPicker}
+        onOpenChange={setShowPicker}
+        vehicles={vehicles}
+        customers={customers}
+        title="Select Vehicle for Work Order"
+      />
     </div>
   );
 }

+ 1 - 1
src/features/calendar/Components/calendar-utils.ts

@@ -7,7 +7,7 @@ export function toLocalDateStr(d: Date): string {
 }
 
 export function formatDateHeader(d: Date): string {
-  return d.toLocaleDateString(undefined, {
+  return d.toLocaleDateString("en-US", {
     weekday: "long",
     month: "long",
     day: "numeric",