Procházet zdrojové kódy

Add quick-create menu in header with + shortcut (#116)

* quick add link

* test
Bernt Christian Egeland před 6 měsíci
rodič
revize
18f95fbc60

+ 9 - 0
messages/de/navigation.json

@@ -57,6 +57,15 @@
   "search": "Suchen...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Neuer Arbeitsauftrag",
+  "quickCreate": {
+    "title": "Schnell hinzufügen",
+    "workOrder": "Arbeitsauftrag",
+    "customer": "Kunde",
+    "vehicle": "Fahrzeug",
+    "quote": "Angebot",
+    "part": "Ersatzteil",
+    "inspection": "Inspektion"
+  },
   "purchaseWhiteLabel": "White-Label erwerben",
   "licenseExpired": "Ihre Lizenz ist abgelaufen. Funktionen können eingeschränkt sein.",
   "licenseExpiresTomorrow": "Ihre Lizenz läuft morgen ab.",

+ 9 - 0
messages/en/navigation.json

@@ -57,6 +57,15 @@
   "search": "Search...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "New Work Order",
+  "quickCreate": {
+    "title": "Quick Add",
+    "workOrder": "Work Order",
+    "customer": "Customer",
+    "vehicle": "Vehicle",
+    "quote": "Quote",
+    "part": "Part",
+    "inspection": "Inspection"
+  },
   "purchaseWhiteLabel": "Purchase White-Label",
   "licenseExpired": "Your license has expired. Features may be limited.",
   "licenseExpiresTomorrow": "Your license expires tomorrow.",

+ 9 - 0
messages/es/navigation.json

@@ -57,6 +57,15 @@
   "search": "Buscar...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Nueva orden de trabajo",
+  "quickCreate": {
+    "title": "Agregar rápido",
+    "workOrder": "Orden de trabajo",
+    "customer": "Cliente",
+    "vehicle": "Vehículo",
+    "quote": "Presupuesto",
+    "part": "Pieza",
+    "inspection": "Inspección"
+  },
   "purchaseWhiteLabel": "Adquirir marca blanca",
   "licenseExpired": "Su licencia ha expirado. Las funciones pueden estar limitadas.",
   "licenseExpiresTomorrow": "Su licencia expira mañana.",

+ 9 - 0
messages/fr/navigation.json

@@ -57,6 +57,15 @@
   "search": "Rechercher…",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Nouvel ordre de travail",
+  "quickCreate": {
+    "title": "Ajout rapide",
+    "workOrder": "Ordre de travail",
+    "customer": "Client",
+    "vehicle": "Véhicule",
+    "quote": "Devis",
+    "part": "Pièce",
+    "inspection": "Inspection"
+  },
   "purchaseWhiteLabel": "Acheter la marque blanche",
   "licenseExpired": "Votre licence a expiré. Les fonctionnalités peuvent être limitées.",
   "licenseExpiresTomorrow": "Votre licence expire demain.",

+ 9 - 0
messages/it/navigation.json

@@ -57,6 +57,15 @@
   "search": "Cerca...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Nuovo ordine di lavoro",
+  "quickCreate": {
+    "title": "Aggiungi rapido",
+    "workOrder": "Ordine di lavoro",
+    "customer": "Cliente",
+    "vehicle": "Veicolo",
+    "quote": "Preventivo",
+    "part": "Ricambio",
+    "inspection": "Ispezione"
+  },
   "purchaseWhiteLabel": "Acquista White-Label",
   "licenseExpired": "La tua licenza è scaduta. Le funzionalità potrebbero essere limitate.",
   "licenseExpiresTomorrow": "La tua licenza scade domani.",

+ 9 - 0
messages/nb/navigation.json

@@ -57,6 +57,15 @@
   "search": "Søk...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Ny arbeidsordre",
+  "quickCreate": {
+    "title": "Hurtiglegg til",
+    "workOrder": "Arbeidsordre",
+    "customer": "Kunde",
+    "vehicle": "Kjøretøy",
+    "quote": "Tilbud",
+    "part": "Del",
+    "inspection": "Inspeksjon"
+  },
   "purchaseWhiteLabel": "Kjøp White-Label",
   "licenseExpired": "Lisensen din har utløpt. Funksjoner kan være begrenset.",
   "licenseExpiresTomorrow": "Lisensen din utløper i morgen.",

+ 9 - 0
messages/nl/navigation.json

@@ -57,6 +57,15 @@
   "search": "Zoeken...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Nieuwe werkorder",
+  "quickCreate": {
+    "title": "Snel toevoegen",
+    "workOrder": "Werkorder",
+    "customer": "Klant",
+    "vehicle": "Voertuig",
+    "quote": "Offerte",
+    "part": "Onderdeel",
+    "inspection": "Keuring"
+  },
   "purchaseWhiteLabel": "White-label aanschaffen",
   "licenseExpired": "Uw licentie is verlopen. Functies kunnen beperkt zijn.",
   "licenseExpiresTomorrow": "Uw licentie verloopt morgen.",

+ 9 - 0
messages/pl/navigation.json

@@ -57,6 +57,15 @@
   "search": "Szukaj...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Nowe zlecenie",
+  "quickCreate": {
+    "title": "Szybkie dodanie",
+    "workOrder": "Zlecenie",
+    "customer": "Klient",
+    "vehicle": "Pojazd",
+    "quote": "Wycena",
+    "part": "Część",
+    "inspection": "Przegląd"
+  },
   "purchaseWhiteLabel": "Kup White-Label",
   "licenseExpired": "Twoja licencja wygasła. Funkcje mogą być ograniczone.",
   "licenseExpiresTomorrow": "Twoja licencja wygasa jutro.",

+ 9 - 0
messages/pt-BR/navigation.json

@@ -57,6 +57,15 @@
   "search": "Buscar...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Nova Ordem de Serviço",
+  "quickCreate": {
+    "title": "Adicionar rápido",
+    "workOrder": "Ordem de Serviço",
+    "customer": "Cliente",
+    "vehicle": "Veículo",
+    "quote": "Orçamento",
+    "part": "Peça",
+    "inspection": "Inspeção"
+  },
   "purchaseWhiteLabel": "Adquirir White-Label",
   "licenseExpired": "Sua licença expirou. Os recursos podem estar limitados.",
   "licenseExpiresTomorrow": "Sua licença expira amanhã.",

+ 9 - 0
messages/ru/navigation.json

@@ -57,6 +57,15 @@
   "search": "Поиск...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Новый заказ-наряд",
+  "quickCreate": {
+    "title": "Быстрое добавление",
+    "workOrder": "Заказ-наряд",
+    "customer": "Клиент",
+    "vehicle": "Транспорт",
+    "quote": "Предложение",
+    "part": "Запчасть",
+    "inspection": "Осмотр"
+  },
   "purchaseWhiteLabel": "Купить White-Label",
   "licenseExpired": "Ваша лицензия истекла. Функции могут быть ограничены.",
   "licenseExpiresTomorrow": "Ваша лицензия истекает завтра.",

+ 9 - 0
messages/tr/navigation.json

@@ -57,6 +57,15 @@
   "search": "Ara...",
   "shortcut": "Ctrl+K",
   "newWorkOrder": "Yeni İş Emri",
+  "quickCreate": {
+    "title": "Hızlı Ekle",
+    "workOrder": "İş Emri",
+    "customer": "Müşteri",
+    "vehicle": "Araç",
+    "quote": "Teklif",
+    "part": "Parça",
+    "inspection": "Muayene"
+  },
   "purchaseWhiteLabel": "Beyaz Etiket Satın Al",
   "licenseExpired": "Lisansınız sona erdi. Özellikler sınırlı olabilir.",
   "licenseExpiresTomorrow": "Lisansınız yarın sona eriyor.",

+ 11 - 1
src/app/(authenticated)/customers/customers-client.tsx

@@ -1,6 +1,6 @@
 "use client";
 
-import { useState, useCallback, useTransition } from "react";
+import { useState, useCallback, useTransition, useEffect } from "react";
 import { useRouter, usePathname, useSearchParams } from "next/navigation";
 import { useTranslations } from "next-intl";
 import { Button } from "@/components/ui/button";
@@ -79,6 +79,16 @@ export function CustomersClient({
   const modal = useGlassModal();
   const confirm = useConfirm();
 
+  useEffect(() => {
+    if (searchParams.get("create") === "true") {
+      setShowForm(true);
+      const params = new URLSearchParams(searchParams.toString());
+      params.delete("create");
+      const cleanUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname;
+      window.history.replaceState(null, "", cleanUrl);
+    }
+  }, [searchParams, pathname]);
+
   const navigate = useCallback(
     (params: Record<string, string | number | undefined>) => {
       const newParams = new URLSearchParams(searchParams.toString());

+ 2 - 10
src/app/(authenticated)/inspections/[id]/page.tsx

@@ -3,6 +3,7 @@ import { InspectionPageClient, type InspectionData } from "@/features/inspection
 import { PageHeader } from "@/components/page-header";
 import { getAuthContext } from "@/lib/get-auth-context";
 import { getFeatures } from "@/lib/features";
+import { redirect } from "next/navigation";
 
 export default async function InspectionDetailPage({
   params,
@@ -17,16 +18,7 @@ export default async function InspectionDetailPage({
   ]);
 
   if (!result.success || !result.data) {
-    return (
-      <>
-        <PageHeader />
-        <div className="flex h-[50vh] items-center justify-center">
-          <p className="text-muted-foreground">
-            {result.error || "Inspection not found"}
-          </p>
-        </div>
-      </>
-    );
+    redirect("/inspections");
   }
 
   const features = authContext?.organizationId

+ 11 - 1
src/app/(authenticated)/inventory/inventory-client.tsx

@@ -1,6 +1,6 @@
 "use client";
 
-import { useState, useCallback, useTransition } from "react";
+import { useState, useCallback, useTransition, useEffect } from "react";
 import { BarcodeScannerDialog } from '@/components/barcode-scanner-dialog';
 import { BarcodeScanActionDialog } from '@/features/inventory/Components/BarcodeScanActionDialog';
 import { useHardwareScanner } from '@/hooks/use-hardware-scanner';
@@ -137,6 +137,16 @@ export function InventoryClient({
   } | null>(null);
   const [scannedBarcode, setScannedBarcode] = useState('');
   const [showScanActions, setShowScanActions] = useState(false);
+
+  useEffect(() => {
+    if (searchParams.get("create") === "true") {
+      setShowForm(true);
+      const params = new URLSearchParams(searchParams.toString());
+      params.delete("create");
+      const cleanUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname;
+      window.history.replaceState(null, "", cleanUrl);
+    }
+  }, [searchParams, pathname]);
   const modal = useGlassModal();
   const confirm = useConfirm();
 

+ 12 - 2
src/app/(authenticated)/quotes/quotes-client.tsx

@@ -1,6 +1,6 @@
 'use client'
 
-import { useState, useCallback, useTransition } from 'react'
+import { useState, useCallback, useTransition, useEffect } from 'react'
 import { useRouter, usePathname, useSearchParams } from 'next/navigation'
 import { useTranslations } from 'next-intl'
 import { useFormatDate } from '@/lib/use-format-date'
@@ -96,6 +96,16 @@ export function QuotesClient({
   const [newCustomerId, setNewCustomerId] = useState('')
   const [creating, setCreating] = useState(false)
 
+  useEffect(() => {
+    if (searchParams.get('create') === 'true') {
+      setShowNewDialog(true)
+      const params = new URLSearchParams(searchParams.toString())
+      params.delete('create')
+      const cleanUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname
+      window.history.replaceState(null, '', cleanUrl)
+    }
+  }, [searchParams, pathname])
+
   const navigate = useCallback(
     (params: Record<string, string | number | undefined>) => {
       const newParams = new URLSearchParams(searchParams.toString())
@@ -265,7 +275,7 @@ export function QuotesClient({
 
       {/* New Quote Dialog */}
       <Dialog open={showNewDialog} onOpenChange={setShowNewDialog}>
-        <DialogContent className="sm:max-w-md">
+        <DialogContent className="sm:max-w-md" aria-describedby={undefined}>
           <DialogHeader>
             <DialogTitle>{t('form.newQuote')}</DialogTitle>
           </DialogHeader>

+ 10 - 0
src/app/(authenticated)/vehicles/vehicles-client.tsx

@@ -112,6 +112,16 @@ export function VehiclesClient({
   const modal = useGlassModal()
   const confirm = useConfirm()
 
+  useEffect(() => {
+    if (searchParams.get('create') === 'true') {
+      setShowForm(true)
+      const params = new URLSearchParams(searchParams.toString())
+      params.delete('create')
+      const cleanUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname
+      window.history.replaceState(null, '', cleanUrl)
+    }
+  }, [searchParams, pathname])
+
   const toggleView = (v: 'table' | 'grid' | 'grid6') => {
     setView(v)
     document.cookie = `${VIEW_COOKIE}=${v};path=/;max-age=${60 * 60 * 24 * 365}`

+ 2 - 2
src/components/page-header.tsx

@@ -18,7 +18,7 @@ import { SidebarTrigger } from '@/components/ui/sidebar'
 import { AlertTriangle, Search, X, Zap } from 'lucide-react'
 import { useShowWhiteLabelCta } from '@/components/white-label-cta-context'
 import { useLicenseExpiry } from '@/components/license-expiry-context'
-import { NewWorkOrderButton } from '@/components/new-work-order-button'
+import { QuickCreateMenu } from '@/components/quick-create-menu'
 
 function SearchTrigger() {
   const t = useTranslations('navigation')
@@ -162,7 +162,7 @@ export function PageHeader() {
       </Breadcrumb>
       <div className="ml-auto flex items-center gap-2">
         <SearchTrigger />
-        <NewWorkOrderButton />
+        <QuickCreateMenu />
         {showWhiteLabelCta && (
           <Button asChild variant="outline" size="sm" className="hidden sm:inline-flex">
             <Link href="/settings/license">

+ 166 - 0
src/components/quick-create-menu.tsx

@@ -0,0 +1,166 @@
+"use client";
+
+import { useState } from "react";
+import { useRouter } from "next/navigation";
+import { useTranslations } from "next-intl";
+import { Button } from "@/components/ui/button";
+import {
+  DropdownMenu,
+  DropdownMenuContent,
+  DropdownMenuItem,
+  DropdownMenuSeparator,
+  DropdownMenuLabel,
+  DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import {
+  Plus,
+  Loader2,
+  Wrench,
+  Users,
+  Car,
+  FileText,
+  Package,
+  ClipboardCheck,
+} from "lucide-react";
+import { VehiclePickerDialog } from "@/components/vehicle-picker-dialog";
+import { NewInspectionDialog } from "@/features/inspections/Components/NewInspectionDialog";
+import { getVehicles } from "@/features/vehicles/Actions/vehicleActions";
+import { getCustomersList } from "@/features/customers/Actions/customerActions";
+import { getTemplates } from "@/features/inspections/Actions/templateActions";
+
+interface VehicleOption {
+  id: string;
+  make: string;
+  model: string;
+  year: number;
+  licensePlate: string | null;
+  customer: { id: string; name: string; company: string | null } | null;
+}
+
+interface CustomerOption {
+  id: string;
+  name: string;
+  company: string | null;
+}
+
+interface TemplateOption {
+  id: string;
+  name: string;
+  isDefault: boolean;
+}
+
+export function QuickCreateMenu() {
+  const t = useTranslations("navigation");
+  const router = useRouter();
+  const [vehicleDialogOpen, setVehicleDialogOpen] = useState(false);
+  const [inspectionDialogOpen, setInspectionDialogOpen] = useState(false);
+  const [loading, setLoading] = useState(false);
+  const [vehicles, setVehicles] = useState<VehicleOption[]>([]);
+  const [customers, setCustomers] = useState<CustomerOption[]>([]);
+  const [templates, setTemplates] = useState<TemplateOption[]>([]);
+
+  const handleNewWorkOrder = async () => {
+    setLoading(true);
+    try {
+      const [vehiclesResult, customersResult] = await Promise.all([
+        getVehicles(),
+        getCustomersList(),
+      ]);
+      setVehicles(
+        vehiclesResult.success && vehiclesResult.data
+          ? vehiclesResult.data.map((v) => ({
+              id: v.id,
+              make: v.make,
+              model: v.model,
+              year: v.year,
+              licensePlate: v.licensePlate,
+              customer: v.customer,
+            }))
+          : []
+      );
+      setCustomers(
+        customersResult.success && customersResult.data
+          ? customersResult.data
+          : []
+      );
+      setVehicleDialogOpen(true);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleNewInspection = async () => {
+    setLoading(true);
+    try {
+      const result = await getTemplates();
+      setTemplates(
+        result.success && result.data
+          ? result.data.map((t) => ({
+              id: t.id,
+              name: t.name,
+              isDefault: t.isDefault,
+            }))
+          : []
+      );
+      setInspectionDialogOpen(true);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  return (
+    <>
+      <DropdownMenu>
+        <DropdownMenuTrigger asChild>
+          <Button size="sm" disabled={loading}>
+            {loading ? (
+              <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
+            ) : (
+              <Plus className="mr-1 h-3.5 w-3.5" />
+            )}
+            {t("quickCreate.title")}
+          </Button>
+        </DropdownMenuTrigger>
+        <DropdownMenuContent align="end" className="w-48">
+          <DropdownMenuLabel>{t("quickCreate.title")}</DropdownMenuLabel>
+          <DropdownMenuSeparator />
+          <DropdownMenuItem onSelect={handleNewWorkOrder}>
+            <Wrench className="h-4 w-4" />
+            {t("quickCreate.workOrder")}
+          </DropdownMenuItem>
+          <DropdownMenuItem onSelect={() => router.push("/customers?create=true")}>
+            <Users className="h-4 w-4" />
+            {t("quickCreate.customer")}
+          </DropdownMenuItem>
+          <DropdownMenuItem onSelect={() => router.push("/vehicles?create=true")}>
+            <Car className="h-4 w-4" />
+            {t("quickCreate.vehicle")}
+          </DropdownMenuItem>
+          <DropdownMenuItem onSelect={() => router.push("/quotes?create=true")}>
+            <FileText className="h-4 w-4" />
+            {t("quickCreate.quote")}
+          </DropdownMenuItem>
+          <DropdownMenuItem onSelect={() => router.push("/inventory?create=true")}>
+            <Package className="h-4 w-4" />
+            {t("quickCreate.part")}
+          </DropdownMenuItem>
+          <DropdownMenuItem onSelect={handleNewInspection}>
+            <ClipboardCheck className="h-4 w-4" />
+            {t("quickCreate.inspection")}
+          </DropdownMenuItem>
+        </DropdownMenuContent>
+      </DropdownMenu>
+      <VehiclePickerDialog
+        open={vehicleDialogOpen}
+        onOpenChange={setVehicleDialogOpen}
+        vehicles={vehicles}
+        customers={customers}
+      />
+      <NewInspectionDialog
+        open={inspectionDialogOpen}
+        onOpenChange={setInspectionDialogOpen}
+        templates={templates}
+      />
+    </>
+  );
+}

+ 1 - 1
src/components/vehicle-picker-dialog.tsx

@@ -171,7 +171,7 @@ export function VehiclePickerDialog({
 
   return (
     <Dialog open={open} onOpenChange={handleOpenChange}>
-      <DialogContent className="max-w-md">
+      <DialogContent className="max-w-md" aria-describedby={undefined}>
         {pickerStep === "select" ? (
           <>
             <DialogHeader>

+ 94 - 78
src/features/inspections/Components/NewInspectionDialog.tsx

@@ -1,38 +1,47 @@
-"use client";
+'use client'
 
-import { useState, useEffect, useTransition } from "react";
-import { useRouter } from "next/navigation";
+import { useState, useEffect, useTransition } from 'react'
+import { useRouter } from 'next/navigation'
+import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
 import {
-  Dialog, DialogContent, DialogHeader, DialogTitle,
-} from "@/components/ui/dialog";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { Label } from "@/components/ui/label";
+  Select,
+  SelectContent,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from '@/components/ui/select'
+import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
 import {
-  Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
-} from "@/components/ui/select";
-import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
-import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
-import { Check, ChevronsUpDown, Loader2, Settings } from "lucide-react";
-import { cn } from "@/lib/utils";
-import Link from "next/link";
-import { toast } from "sonner";
-import { useServiceType } from "@/components/service-type-context";
-import { createInspection } from "../Actions/inspectionActions";
-import { getVehicles } from "@/features/vehicles/Actions/vehicleActions";
+  Command,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+} from '@/components/ui/command'
+import { Check, ChevronsUpDown, Loader2, Settings } from 'lucide-react'
+import { cn } from '@/lib/utils'
+import Link from 'next/link'
+import { toast } from 'sonner'
+import { useServiceType } from '@/components/service-type-context'
+import { createInspection } from '../Actions/inspectionActions'
+import { getVehicles } from '@/features/vehicles/Actions/vehicleActions'
 
 interface TemplateOption {
-  id: string;
-  name: string;
-  isDefault: boolean;
+  id: string
+  name: string
+  isDefault: boolean
 }
 
 interface VehicleOption {
-  id: string;
-  make: string;
-  model: string;
-  year: number;
-  licensePlate: string | null;
+  id: string
+  make: string
+  model: string
+  year: number
+  licensePlate: string | null
 }
 
 export function NewInspectionDialog({
@@ -41,71 +50,73 @@ export function NewInspectionDialog({
   templates,
   preselectedVehicleId,
 }: {
-  open: boolean;
-  onOpenChange: (open: boolean) => void;
-  templates: TemplateOption[];
-  preselectedVehicleId?: string;
+  open: boolean
+  onOpenChange: (open: boolean) => void
+  templates: TemplateOption[]
+  preselectedVehicleId?: string
 }) {
-  const router = useRouter();
-  const serviceType = useServiceType();
-  const [isPending, startTransition] = useTransition();
-  const [vehicles, setVehicles] = useState<VehicleOption[]>([]);
-  const [loadingVehicles, setLoadingVehicles] = useState(false);
+  const router = useRouter()
+  const serviceType = useServiceType()
+  const [isPending, startTransition] = useTransition()
+  const [vehicles, setVehicles] = useState<VehicleOption[]>([])
+  const [loadingVehicles, setLoadingVehicles] = useState(false)
 
-  const defaultTemplate = templates.find((t) => t.isDefault);
-  const [vehicleId, setVehicleId] = useState(preselectedVehicleId || "");
-  const [templateId, setTemplateId] = useState(defaultTemplate?.id || "");
-  const [mileage, setMileage] = useState("");
-  const [vehiclePopoverOpen, setVehiclePopoverOpen] = useState(false);
+  const defaultTemplate = templates.find((t) => t.isDefault)
+  const [vehicleId, setVehicleId] = useState(preselectedVehicleId || '')
+  const [templateId, setTemplateId] = useState(defaultTemplate?.id || '')
+  const [mileage, setMileage] = useState('')
+  const [vehiclePopoverOpen, setVehiclePopoverOpen] = useState(false)
 
   useEffect(() => {
     if (open && vehicles.length === 0) {
-      setLoadingVehicles(true);
+      setLoadingVehicles(true)
       getVehicles().then((result) => {
         if (result.success && result.data) {
-          setVehicles(result.data.map((v) => ({
-            id: v.id,
-            make: v.make,
-            model: v.model,
-            year: v.year,
-            licensePlate: v.licensePlate,
-          })));
+          setVehicles(
+            result.data.map((v) => ({
+              id: v.id,
+              make: v.make,
+              model: v.model,
+              year: v.year,
+              licensePlate: v.licensePlate,
+            }))
+          )
         }
-        setLoadingVehicles(false);
-      });
+        setLoadingVehicles(false)
+      })
     }
-  }, [open, vehicles.length]);
+  }, [open, vehicles.length])
 
   useEffect(() => {
     if (open) {
-      setVehicleId(preselectedVehicleId || "");
-      setTemplateId(defaultTemplate?.id || "");
-      setMileage("");
+      setVehicleId(preselectedVehicleId || '')
+      setTemplateId(defaultTemplate?.id || '')
+      setMileage('')
     }
-  }, [open, preselectedVehicleId, defaultTemplate?.id]);
+  }, [open, preselectedVehicleId, defaultTemplate?.id])
 
   const handleSubmit = (e: React.FormEvent) => {
-    e.preventDefault();
+    e.preventDefault()
     startTransition(async () => {
       const result = await createInspection({
         vehicleId,
         templateId,
         mileage: mileage ? parseInt(mileage, 10) : undefined,
-      });
+      })
 
       if (result.success && result.data) {
-        toast.success("Inspection created");
-        onOpenChange(false);
-        router.push(`/inspections/${result.data.id}`);
+        toast.success('Inspection created')
+        onOpenChange(false)
+        router.push(`/inspections/${result.data.id}`)
       } else {
-        toast.error(result.error || "Failed to create inspection");
+        toast.error(result.error || 'Failed to create inspection')
       }
-    });
-  };
+    })
+  }
 
   return (
     <Dialog open={open} onOpenChange={onOpenChange}>
-      <DialogContent className="sm:max-w-md">
+      <DialogContent className="sm:max-w-md" aria-describedby={undefined}>
         <DialogHeader>
           <DialogTitle>New Inspection</DialogTitle>
         </DialogHeader>
@@ -128,12 +139,12 @@ export function NewInspectionDialog({
                   >
                     {vehicleId
                       ? (() => {
-                          const v = vehicles.find((v) => v.id === vehicleId);
+                          const v = vehicles.find((v) => v.id === vehicleId)
                           return v
-                            ? `${v.year} ${v.make} ${v.model}${v.licensePlate ? ` (${v.licensePlate})` : ""}`
-                            : "Select vehicle";
+                            ? `${v.year} ${v.make} ${v.model}${v.licensePlate ? ` (${v.licensePlate})` : ''}`
+                            : 'Select vehicle'
                         })()
-                      : "Select vehicle"}
+                      : 'Select vehicle'}
                     <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
                   </Button>
                 </PopoverTrigger>
@@ -144,20 +155,25 @@ export function NewInspectionDialog({
                       <CommandEmpty>No vehicles found.</CommandEmpty>
                       <CommandGroup>
                         {vehicles.map((v) => {
-                          const label = `${v.year} ${v.make} ${v.model}${v.licensePlate ? ` (${v.licensePlate})` : ""}`;
+                          const label = `${v.year} ${v.make} ${v.model}${v.licensePlate ? ` (${v.licensePlate})` : ''}`
                           return (
                             <CommandItem
                               key={v.id}
                               value={label}
                               onSelect={() => {
-                                setVehicleId(v.id);
-                                setVehiclePopoverOpen(false);
+                                setVehicleId(v.id)
+                                setVehiclePopoverOpen(false)
                               }}
                             >
-                              <Check className={cn("mr-2 h-4 w-4", vehicleId === v.id ? "opacity-100" : "opacity-0")} />
+                              <Check
+                                className={cn(
+                                  'mr-2 h-4 w-4',
+                                  vehicleId === v.id ? 'opacity-100' : 'opacity-0'
+                                )}
+                              />
                               {label}
                             </CommandItem>
-                          );
+                          )
                         })}
                       </CommandGroup>
                     </CommandList>
@@ -171,7 +187,7 @@ export function NewInspectionDialog({
             <div className="flex items-center justify-between">
               <Label>Template</Label>
               <Link
-                href="/settings/templates?tab=inspection"
+                href="/settings/templates?tab=inspections"
                 className="inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground"
                 onClick={() => onOpenChange(false)}
               >
@@ -187,7 +203,7 @@ export function NewInspectionDialog({
                 {templates.map((t) => (
                   <SelectItem key={t.id} value={t.id}>
                     {t.name}
-                    {t.isDefault ? " (Default)" : ""}
+                    {t.isDefault ? ' (Default)' : ''}
                   </SelectItem>
                 ))}
               </SelectContent>
@@ -200,7 +216,7 @@ export function NewInspectionDialog({
               type="number"
               value={mileage}
               onChange={(e) => setMileage(e.target.value)}
-              placeholder={serviceType === 'marine' ? "Current engine hours" : "Current mileage"}
+              placeholder={serviceType === 'marine' ? 'Current engine hours' : 'Current mileage'}
               min={0}
             />
           </div>
@@ -217,5 +233,5 @@ export function NewInspectionDialog({
         </form>
       </DialogContent>
     </Dialog>
-  );
+  )
 }