Parcourir la source

Add right-click row context menus and clickable customer/vehicle cells (#231)

* add right click context

* add clickable links for table content
Bernt Christian Egeland il y a 1 mois
Parent
commit
cb0a09082f
44 fichiers modifiés avec 890 ajouts et 110 suppressions
  1. 8 0
      messages/de/common.json
  2. 1 2
      messages/de/workOrders.json
  3. 8 0
      messages/en/common.json
  4. 1 2
      messages/en/workOrders.json
  5. 8 0
      messages/es/common.json
  6. 1 2
      messages/es/workOrders.json
  7. 8 0
      messages/fr/common.json
  8. 1 2
      messages/fr/workOrders.json
  9. 8 0
      messages/it/common.json
  10. 1 2
      messages/it/workOrders.json
  11. 8 0
      messages/lt/common.json
  12. 1 2
      messages/lt/workOrders.json
  13. 8 0
      messages/nb/common.json
  14. 1 2
      messages/nb/workOrders.json
  15. 8 0
      messages/nl/common.json
  16. 1 2
      messages/nl/workOrders.json
  17. 8 0
      messages/pl/common.json
  18. 1 2
      messages/pl/workOrders.json
  19. 8 0
      messages/pt-BR/common.json
  20. 1 2
      messages/pt-BR/workOrders.json
  21. 8 0
      messages/ru/common.json
  22. 1 2
      messages/ru/workOrders.json
  23. 8 0
      messages/tr/common.json
  24. 1 2
      messages/tr/workOrders.json
  25. 20 2
      src/app/(authenticated)/audit-log/audit-log-client.tsx
  26. 15 4
      src/app/(authenticated)/billing/billing-client.tsx
  27. 41 2
      src/app/(authenticated)/customers/customers-client.tsx
  28. 131 26
      src/app/(authenticated)/dashboard-client.tsx
  29. 28 4
      src/app/(authenticated)/inspections/inspections-client.tsx
  30. 36 1
      src/app/(authenticated)/inventory/inventory-client.tsx
  31. 25 1
      src/app/(authenticated)/labor-presets/labor-presets-client.tsx
  32. 31 6
      src/app/(authenticated)/observations/observations-client.tsx
  33. 47 4
      src/app/(authenticated)/quotes/quotes-client.tsx
  34. 34 1
      src/app/(authenticated)/vehicles/[id]/findings-table.tsx
  35. 28 1
      src/app/(authenticated)/vehicles/[id]/notes-table.tsx
  36. 23 1
      src/app/(authenticated)/vehicles/[id]/service-records-table.tsx
  37. 70 2
      src/app/(authenticated)/vehicles/vehicles-client.tsx
  38. 25 15
      src/app/(authenticated)/work-orders/work-orders-client.tsx
  39. 40 0
      src/components/table-cell-link.tsx
  40. 18 0
      src/components/table-context-menu-hint.tsx
  41. 61 3
      src/features/billing/Components/RecurringInvoicesClient.tsx
  42. 23 1
      src/features/inspections/Components/TemplateListClient.tsx
  43. 37 1
      src/features/status-reports/Components/StatusReportList.tsx
  44. 49 11
      src/features/vehicles/Components/RemindersPageClient.tsx

+ 8 - 0
messages/de/common.json

@@ -12,6 +12,14 @@
     "edit": "Bearbeiten",
     "add": "Hinzufügen"
   },
+  "contextMenu": {
+    "hint": "Tipp: Rechtsklick auf eine Zeile für Aktionen",
+    "open": "Öffnen",
+    "openVehicle": "Fahrzeug öffnen",
+    "openCustomer": "Kunde öffnen",
+    "edit": "Bearbeiten",
+    "delete": "Löschen"
+  },
   "form": {
     "email": "E-Mail",
     "password": "Passwort",

+ 1 - 2
messages/de/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Arbeitsauftrag öffnen",
       "openVehicle": "Fahrzeug öffnen",
-      "openCustomer": "Kunde öffnen",
-      "hint": "Tipp: Rechtsklick auf eine Zeile für Aktionen"
+      "openCustomer": "Kunde öffnen"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/en/common.json

@@ -12,6 +12,14 @@
     "edit": "Edit",
     "add": "Add"
   },
+  "contextMenu": {
+    "hint": "Tip: right-click a row for actions",
+    "open": "Open",
+    "openVehicle": "Open vehicle",
+    "openCustomer": "Open customer",
+    "edit": "Edit",
+    "delete": "Delete"
+  },
   "form": {
     "email": "Email",
     "password": "Password",

+ 1 - 2
messages/en/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Open work order",
       "openVehicle": "Open vehicle",
-      "openCustomer": "Open customer",
-      "hint": "Tip: right-click a row for actions"
+      "openCustomer": "Open customer"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/es/common.json

@@ -12,6 +12,14 @@
     "edit": "Editar",
     "add": "Agregar"
   },
+  "contextMenu": {
+    "hint": "Consejo: clic derecho en una fila para ver acciones",
+    "open": "Abrir",
+    "openVehicle": "Abrir vehículo",
+    "openCustomer": "Abrir cliente",
+    "edit": "Editar",
+    "delete": "Eliminar"
+  },
   "form": {
     "email": "Correo electronico",
     "password": "Contrasena",

+ 1 - 2
messages/es/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Abrir orden de trabajo",
       "openVehicle": "Abrir vehículo",
-      "openCustomer": "Abrir cliente",
-      "hint": "Consejo: clic derecho en una fila para ver acciones"
+      "openCustomer": "Abrir cliente"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/fr/common.json

@@ -12,6 +12,14 @@
     "edit": "Modifier",
     "add": "Ajouter"
   },
+  "contextMenu": {
+    "hint": "Astuce : clic droit sur une ligne pour les actions",
+    "open": "Ouvrir",
+    "openVehicle": "Ouvrir le véhicule",
+    "openCustomer": "Ouvrir le client",
+    "edit": "Modifier",
+    "delete": "Supprimer"
+  },
   "form": {
     "email": "Adresse e-mail",
     "password": "Mot de passe",

+ 1 - 2
messages/fr/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Ouvrir l'ordre de travail",
       "openVehicle": "Ouvrir le véhicule",
-      "openCustomer": "Ouvrir le client",
-      "hint": "Astuce : clic droit sur une ligne pour les actions"
+      "openCustomer": "Ouvrir le client"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/it/common.json

@@ -12,6 +12,14 @@
     "edit": "Modifica",
     "add": "Aggiungi"
   },
+  "contextMenu": {
+    "hint": "Suggerimento: clic destro su una riga per le azioni",
+    "open": "Apri",
+    "openVehicle": "Apri veicolo",
+    "openCustomer": "Apri cliente",
+    "edit": "Modifica",
+    "delete": "Elimina"
+  },
   "form": {
     "email": "Email",
     "password": "Password",

+ 1 - 2
messages/it/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Apri ordine di lavoro",
       "openVehicle": "Apri veicolo",
-      "openCustomer": "Apri cliente",
-      "hint": "Suggerimento: clic destro su una riga per le azioni"
+      "openCustomer": "Apri cliente"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/lt/common.json

@@ -12,6 +12,14 @@
     "edit": "Redaguoti",
     "add": "Pridėti"
   },
+  "contextMenu": {
+    "hint": "Patarimas: spustelėkite eilutę dešiniuoju mygtuku veiksmams",
+    "open": "Atidaryti",
+    "openVehicle": "Atidaryti transporto priemonę",
+    "openCustomer": "Atidaryti klientą",
+    "edit": "Redaguoti",
+    "delete": "Ištrinti"
+  },
   "form": {
     "email": "El. paštas",
     "password": "Slaptažodis",

+ 1 - 2
messages/lt/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Atidaryti darbo užsakymą",
       "openVehicle": "Atidaryti transporto priemonę",
-      "openCustomer": "Atidaryti klientą",
-      "hint": "Patarimas: spustelėkite eilutę dešiniuoju mygtuku veiksmams"
+      "openCustomer": "Atidaryti klientą"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/nb/common.json

@@ -12,6 +12,14 @@
     "edit": "Rediger",
     "add": "Legg til"
   },
+  "contextMenu": {
+    "hint": "Tips: høyreklikk på en rad for handlinger",
+    "open": "Åpne",
+    "openVehicle": "Åpne kjøretøy",
+    "openCustomer": "Åpne kunde",
+    "edit": "Rediger",
+    "delete": "Slett"
+  },
   "form": {
     "email": "E-post",
     "password": "Passord",

+ 1 - 2
messages/nb/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Åpne arbeidsordre",
       "openVehicle": "Åpne kjøretøy",
-      "openCustomer": "Åpne kunde",
-      "hint": "Tips: høyreklikk på en rad for handlinger"
+      "openCustomer": "Åpne kunde"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/nl/common.json

@@ -12,6 +12,14 @@
     "edit": "Bewerken",
     "add": "Toevoegen"
   },
+  "contextMenu": {
+    "hint": "Tip: rechtsklik op een rij voor acties",
+    "open": "Openen",
+    "openVehicle": "Voertuig openen",
+    "openCustomer": "Klant openen",
+    "edit": "Bewerken",
+    "delete": "Verwijderen"
+  },
   "form": {
     "email": "E-mail",
     "password": "Wachtwoord",

+ 1 - 2
messages/nl/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Werkorder openen",
       "openVehicle": "Voertuig openen",
-      "openCustomer": "Klant openen",
-      "hint": "Tip: rechtsklik op een rij voor acties"
+      "openCustomer": "Klant openen"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/pl/common.json

@@ -12,6 +12,14 @@
     "edit": "Edytuj",
     "add": "Dodaj"
   },
+  "contextMenu": {
+    "hint": "Wskazówka: kliknij wiersz prawym przyciskiem, aby zobaczyć akcje",
+    "open": "Otwórz",
+    "openVehicle": "Otwórz pojazd",
+    "openCustomer": "Otwórz klienta",
+    "edit": "Edytuj",
+    "delete": "Usuń"
+  },
   "form": {
     "email": "E-mail",
     "password": "Hasło",

+ 1 - 2
messages/pl/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Otwórz zlecenie",
       "openVehicle": "Otwórz pojazd",
-      "openCustomer": "Otwórz klienta",
-      "hint": "Wskazówka: kliknij wiersz prawym przyciskiem, aby zobaczyć akcje"
+      "openCustomer": "Otwórz klienta"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/pt-BR/common.json

@@ -12,6 +12,14 @@
     "edit": "Editar",
     "add": "Adicionar"
   },
+  "contextMenu": {
+    "hint": "Dica: clique com o botão direito em uma linha para ver ações",
+    "open": "Abrir",
+    "openVehicle": "Abrir veículo",
+    "openCustomer": "Abrir cliente",
+    "edit": "Editar",
+    "delete": "Excluir"
+  },
   "form": {
     "email": "E-mail",
     "password": "Senha",

+ 1 - 2
messages/pt-BR/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Abrir ordem de serviço",
       "openVehicle": "Abrir veículo",
-      "openCustomer": "Abrir cliente",
-      "hint": "Dica: clique com o botão direito em uma linha para ver ações"
+      "openCustomer": "Abrir cliente"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/ru/common.json

@@ -12,6 +12,14 @@
     "edit": "Редактировать",
     "add": "Добавить"
   },
+  "contextMenu": {
+    "hint": "Подсказка: правый клик по строке — действия",
+    "open": "Открыть",
+    "openVehicle": "Открыть автомобиль",
+    "openCustomer": "Открыть клиента",
+    "edit": "Редактировать",
+    "delete": "Удалить"
+  },
   "form": {
     "email": "Эл. почта",
     "password": "Пароль",

+ 1 - 2
messages/ru/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "Открыть заказ-наряд",
       "openVehicle": "Открыть автомобиль",
-      "openCustomer": "Открыть клиента",
-      "hint": "Подсказка: правый клик по строке — действия"
+      "openCustomer": "Открыть клиента"
     }
   },
   "vehiclePicker": {

+ 8 - 0
messages/tr/common.json

@@ -12,6 +12,14 @@
     "edit": "Düzenle",
     "add": "Ekle"
   },
+  "contextMenu": {
+    "hint": "İpucu: işlemler için bir satıra sağ tıklayın",
+    "open": "Aç",
+    "openVehicle": "Aracı aç",
+    "openCustomer": "Müşteriyi aç",
+    "edit": "Düzenle",
+    "delete": "Sil"
+  },
   "form": {
     "email": "E-posta",
     "password": "Şifre",

+ 1 - 2
messages/tr/workOrders.json

@@ -38,8 +38,7 @@
     "contextMenu": {
       "open": "İş emrini aç",
       "openVehicle": "Aracı aç",
-      "openCustomer": "Müşteriyi aç",
-      "hint": "İpucu: işlemler için bir satıra sağ tıklayın"
+      "openCustomer": "Müşteriyi aç"
     }
   },
   "vehiclePicker": {

+ 20 - 2
src/app/(authenticated)/audit-log/audit-log-client.tsx

@@ -27,10 +27,17 @@ import {
   DialogTitle,
   DialogDescription,
 } from "@/components/ui/dialog";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
 import { DataTablePagination } from "@/components/data-table-pagination";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
 import { useFormatDate } from "@/lib/use-format-date";
 import { cn } from "@/lib/utils";
-import { Search, Loader2 } from "lucide-react";
+import { ExternalLink, Search, Loader2 } from "lucide-react";
 
 type AuditLogData = {
   logs: {
@@ -176,6 +183,7 @@ export function AuditLogClient({
 
       {/* Table */}
       <div className="rounded-md border">
+        <TableContextMenuHint />
         <Table>
           <TableHeader>
             <TableRow>
@@ -195,7 +203,9 @@ export function AuditLogClient({
               </TableRow>
             ) : (
               data.logs.map((log) => (
-                <TableRow key={log.id} className="cursor-pointer" onClick={() => setSelectedLog(log)}>
+                <ContextMenu key={log.id} modal={false}>
+                <ContextMenuTrigger asChild>
+                <TableRow className="cursor-pointer" onClick={() => setSelectedLog(log)}>
                   <TableCell className="text-xs text-muted-foreground whitespace-nowrap" suppressHydrationWarning>
                     {formatDateTime(log.timestamp)}
                   </TableCell>
@@ -214,6 +224,14 @@ export function AuditLogClient({
                     {log.entityId?.substring(0, 8)}
                   </TableCell>
                 </TableRow>
+                </ContextMenuTrigger>
+                <ContextMenuContent className="min-w-52">
+                  <ContextMenuItem onClick={() => setSelectedLog(log)}>
+                    <ExternalLink className="mr-2 h-4 w-4" />
+                    {t("logDetails")}
+                  </ContextMenuItem>
+                </ContextMenuContent>
+                </ContextMenu>
               ))
             )}
           </TableBody>

+ 15 - 4
src/app/(authenticated)/billing/billing-client.tsx

@@ -20,6 +20,7 @@ import {
   TableRow,
 } from "@/components/ui/table";
 import { DataTablePagination } from "@/components/data-table-pagination";
+import { TableCellLink } from "@/components/table-cell-link";
 import {
   Loader2,
   Search,
@@ -378,12 +379,22 @@ export default function BillingClient({
                     </TableCell>
                     <TableCell className="truncate">{record.title}</TableCell>
                     <TableCell className="truncate">
-                      {record.vehicle
-                        ? `${record.vehicle.year} ${record.vehicle.make} ${record.vehicle.model}`
-                        : "\u2014"}
+                      {record.vehicle ? (
+                        <TableCellLink href={`/vehicles/${record.vehicle.id}`}>
+                          {record.vehicle.year} {record.vehicle.make} {record.vehicle.model}
+                        </TableCellLink>
+                      ) : (
+                        "\u2014"
+                      )}
                     </TableCell>
                     <TableCell className="truncate">
-                      {record.customer?.name || "\u2014"}
+                      {record.customer ? (
+                        <TableCellLink href={`/customers/${record.customer.id}`}>
+                          {record.customer.name}
+                        </TableCellLink>
+                      ) : (
+                        "\u2014"
+                      )}
                     </TableCell>
                     <TableCell>
                       {formatDate(new Date(record.serviceDate))}

+ 41 - 2
src/app/(authenticated)/customers/customers-client.tsx

@@ -21,7 +21,15 @@ import {
   DropdownMenuItem,
   DropdownMenuTrigger,
 } from "@/components/ui/dropdown-menu";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuSeparator,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
 import { DataTablePagination } from "@/components/data-table-pagination";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
 import { useGlassModal } from "@/components/glass-modal";
 import { useConfirm } from "@/components/confirm-dialog";
 import { CustomerForm } from "@/features/customers/Components/CustomerForm";
@@ -33,6 +41,7 @@ import {
   ArrowDown,
   ArrowUp,
   ArrowUpDown,
+  ExternalLink,
   Loader2,
   MoreVertical,
   Pencil,
@@ -78,6 +87,7 @@ export function CustomersClient({
 }) {
   const t = useTranslations("customers.list");
   const tc = useTranslations("common");
+  const tcm = useTranslations("common.contextMenu");
   const router = useRouter();
   const pathname = usePathname();
   const searchParams = useSearchParams();
@@ -249,7 +259,9 @@ export function CustomersClient({
       </div>
 
       {/* Table - only this scrolls */}
-      <div className="overflow-auto rounded-lg border max-h-[calc(100vh-220px)]">
+      <div className="rounded-lg border">
+        <TableContextMenuHint />
+        <div className="overflow-auto max-h-[calc(100vh-220px)]">
         <Table className="table-fixed">
           <TableHeader className="sticky top-0 z-10 bg-background">
             <TableRow>
@@ -308,8 +320,9 @@ export function CustomersClient({
               </TableRow>
             ) : (
               data.customers.map((c) => (
+                <ContextMenu key={c.id} modal={false}>
+                <ContextMenuTrigger asChild>
                 <TableRow
-                  key={c.id}
                   className={`cursor-pointer ${selected.has(c.id) ? "bg-muted/50" : ""}`}
                   onClick={() => router.push(`/customers/${c.id}`)}
                 >
@@ -371,10 +384,36 @@ export function CustomersClient({
                     </DropdownMenu>
                   </TableCell>
                 </TableRow>
+                </ContextMenuTrigger>
+                <ContextMenuContent className="min-w-52">
+                  <ContextMenuItem onClick={() => router.push(`/customers/${c.id}`)}>
+                    <ExternalLink className="mr-2 h-4 w-4" />
+                    {tcm("open")}
+                  </ContextMenuItem>
+                  <ContextMenuSeparator />
+                  <ContextMenuItem
+                    onClick={() => {
+                      setEditCustomer(c);
+                      setShowForm(true);
+                    }}
+                  >
+                    <Pencil className="mr-2 h-4 w-4" />
+                    {tc("buttons.edit")}
+                  </ContextMenuItem>
+                  <ContextMenuItem
+                    variant="destructive"
+                    onClick={() => handleDelete(c.id, c.name)}
+                  >
+                    <Trash2 className="mr-2 h-4 w-4" />
+                    {tc("buttons.delete")}
+                  </ContextMenuItem>
+                </ContextMenuContent>
+                </ContextMenu>
               ))
             )}
           </TableBody>
         </Table>
+        </div>
       </div>
 
       <DataTablePagination

+ 131 - 26
src/app/(authenticated)/dashboard-client.tsx

@@ -16,6 +16,13 @@ import {
   TableHeader,
   TableRow,
 } from "@/components/ui/table";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
+import { TableCellLink } from "@/components/table-cell-link";
 import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
 import { Switch } from "@/components/ui/switch";
 import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
@@ -24,10 +31,12 @@ import {
   AlertTriangle,
   ArrowRight,
   Bell,
+  Car,
   Check,
   ClipboardCheck,
   ClipboardList,
   Clock,
+  ExternalLink,
   Eye,
   EyeOff,
   FileText,
@@ -283,6 +292,7 @@ export function DashboardClient({
 }) {
   const formatCurrency = useFormatCurrency();
   const t = useTranslations("dashboard");
+  const tcm = useTranslations("common.contextMenu");
   const tAudit = useTranslations("audit");
   const distUnit = unitSystem === "metric" ? "km" : "mi";
   const router = useRouter();
@@ -1266,30 +1276,43 @@ export function DashboardClient({
                   ) : (
                     stats.recentServices.map((s) => {
                       const displayTotal = s.totalAmount > 0 ? s.totalAmount : s.cost;
+                      const recordHref = s.vehicle ? `/vehicles/${s.vehicle.id}/service/${s.id}` : `/sales/${s.id}`;
+                      const rowCustomer = s.customer ?? s.vehicle?.customer;
                       return (
+                        <ContextMenu key={s.id} modal={false}>
+                        <ContextMenuTrigger asChild>
                         <TableRow
-                          key={s.id}
                           className={`cursor-pointer transition-opacity ${navigatingId === s.id ? "opacity-50" : ""}`}
                           onClick={() => {
                             setNavigatingId(s.id);
-                            router.push(s.vehicle ? `/vehicles/${s.vehicle.id}/service/${s.id}` : `/sales/${s.id}`);
+                            router.push(recordHref);
                           }}
                         >
                           <TableCell className="font-mono text-xs">
                             {formatDate(new Date(s.startDateTime ?? s.serviceDate))}
                           </TableCell>
                           <TableCell>
-                            <div>
-                              {s.vehicle?.licensePlate && (
-                                <span className="font-mono text-sm">{s.vehicle.licensePlate}</span>
-                              )}
-                              <p className="text-xs text-muted-foreground">
-                                {s.vehicle ? `${s.vehicle.year} ${s.vehicle.make} ${s.vehicle.model}` : "-"}
-                              </p>
-                            </div>
+                            {s.vehicle ? (
+                              <TableCellLink href={`/vehicles/${s.vehicle.id}`} block>
+                                {s.vehicle.licensePlate && (
+                                  <span className="font-mono text-sm">{s.vehicle.licensePlate}</span>
+                                )}
+                                <p className="text-xs text-muted-foreground">
+                                  {s.vehicle.year} {s.vehicle.make} {s.vehicle.model}
+                                </p>
+                              </TableCellLink>
+                            ) : (
+                              <p className="text-xs text-muted-foreground">-</p>
+                            )}
                           </TableCell>
                           <TableCell className="hidden sm:table-cell text-muted-foreground">
-                            {(s.customer ?? s.vehicle?.customer)?.name || "-"}
+                            {rowCustomer ? (
+                              <TableCellLink href={`/customers/${rowCustomer.id}`}>
+                                {rowCustomer.name}
+                              </TableCellLink>
+                            ) : (
+                              "-"
+                            )}
                           </TableCell>
                           <TableCell className="font-medium">{s.title}</TableCell>
                           {stats.isAdmin && (
@@ -1303,6 +1326,31 @@ export function DashboardClient({
                             </TableCell>
                           )}
                         </TableRow>
+                        </ContextMenuTrigger>
+                        <ContextMenuContent className="min-w-52">
+                          <ContextMenuItem
+                            onClick={() => {
+                              setNavigatingId(s.id);
+                              router.push(recordHref);
+                            }}
+                          >
+                            <ExternalLink className="mr-2 h-4 w-4" />
+                            {tcm("open")}
+                          </ContextMenuItem>
+                          {s.vehicle && (
+                            <ContextMenuItem onClick={() => router.push(`/vehicles/${s.vehicle?.id}`)}>
+                              <Car className="mr-2 h-4 w-4" />
+                              {tcm("openVehicle")}
+                            </ContextMenuItem>
+                          )}
+                          {rowCustomer && (
+                            <ContextMenuItem onClick={() => router.push(`/customers/${rowCustomer.id}`)}>
+                              <Users className="mr-2 h-4 w-4" />
+                              {tcm("openCustomer")}
+                            </ContextMenuItem>
+                          )}
+                        </ContextMenuContent>
+                        </ContextMenu>
                       );
                     })
                   )}
@@ -1337,27 +1385,41 @@ export function DashboardClient({
                       </TableCell>
                     </TableRow>
                   ) : (
-                    stats.todaysServices.map((s) => (
+                    stats.todaysServices.map((s) => {
+                      const recordHref = s.vehicle ? `/vehicles/${s.vehicle.id}/service/${s.id}` : `/sales/${s.id}`;
+                      const rowCustomer = s.customer ?? s.vehicle?.customer;
+                      return (
+                      <ContextMenu key={s.id} modal={false}>
+                      <ContextMenuTrigger asChild>
                       <TableRow
-                        key={s.id}
                         className={`cursor-pointer transition-opacity ${navigatingId === s.id ? "opacity-50" : ""}`}
                         onClick={() => {
                           setNavigatingId(s.id);
-                          router.push(s.vehicle ? `/vehicles/${s.vehicle.id}/service/${s.id}` : `/sales/${s.id}`);
+                          router.push(recordHref);
                         }}
                       >
                         <TableCell>
-                          <div>
-                            {s.vehicle?.licensePlate && (
-                              <span className="font-mono text-sm font-medium">{s.vehicle.licensePlate}</span>
-                            )}
-                            <p className="text-xs text-muted-foreground">
-                              {s.vehicle ? `${s.vehicle.year} ${s.vehicle.make} ${s.vehicle.model}` : "-"}
-                            </p>
-                          </div>
+                          {s.vehicle ? (
+                            <TableCellLink href={`/vehicles/${s.vehicle.id}`} block>
+                              {s.vehicle.licensePlate && (
+                                <span className="font-mono text-sm font-medium">{s.vehicle.licensePlate}</span>
+                              )}
+                              <p className="text-xs text-muted-foreground">
+                                {s.vehicle.year} {s.vehicle.make} {s.vehicle.model}
+                              </p>
+                            </TableCellLink>
+                          ) : (
+                            <p className="text-xs text-muted-foreground">-</p>
+                          )}
                         </TableCell>
                         <TableCell className="hidden sm:table-cell text-muted-foreground">
-                          {(s.customer ?? s.vehicle?.customer)?.name || "-"}
+                          {rowCustomer ? (
+                            <TableCellLink href={`/customers/${rowCustomer.id}`}>
+                              {rowCustomer.name}
+                            </TableCellLink>
+                          ) : (
+                            "-"
+                          )}
                         </TableCell>
                         <TableCell className="font-medium">{s.title}</TableCell>
                         <TableCell>
@@ -1374,7 +1436,33 @@ export function DashboardClient({
                           {s.techName || "-"}
                         </TableCell>
                       </TableRow>
-                    ))
+                      </ContextMenuTrigger>
+                      <ContextMenuContent className="min-w-52">
+                        <ContextMenuItem
+                          onClick={() => {
+                            setNavigatingId(s.id);
+                            router.push(recordHref);
+                          }}
+                        >
+                          <ExternalLink className="mr-2 h-4 w-4" />
+                          {tcm("open")}
+                        </ContextMenuItem>
+                        {s.vehicle && (
+                          <ContextMenuItem onClick={() => router.push(`/vehicles/${s.vehicle?.id}`)}>
+                            <Car className="mr-2 h-4 w-4" />
+                            {tcm("openVehicle")}
+                          </ContextMenuItem>
+                        )}
+                        {rowCustomer && (
+                          <ContextMenuItem onClick={() => router.push(`/customers/${rowCustomer.id}`)}>
+                            <Users className="mr-2 h-4 w-4" />
+                            {tcm("openCustomer")}
+                          </ContextMenuItem>
+                        )}
+                      </ContextMenuContent>
+                      </ContextMenu>
+                      );
+                    })
                   )}
                 </TableBody>
               </Table>
@@ -1480,8 +1568,9 @@ export function DashboardClient({
                     const vehicleLabel = obs.vehicle.licensePlate
                       ?? `${obs.vehicle.year} ${obs.vehicle.make}`;
                     return (
+                      <ContextMenu key={obs.id} modal={false}>
+                      <ContextMenuTrigger asChild>
                       <TableRow
-                        key={obs.id}
                         className="cursor-pointer"
                         onClick={() => router.push(`/vehicles/${obs.vehicle.id}?tab=findings`)}
                       >
@@ -1494,7 +1583,9 @@ export function DashboardClient({
                           </Badge>
                         </TableCell>
                         <TableCell className="py-1.5 font-mono text-xs text-muted-foreground truncate">
-                          {vehicleLabel}
+                          <TableCellLink href={`/vehicles/${obs.vehicle.id}`}>
+                            {vehicleLabel}
+                          </TableCellLink>
                         </TableCell>
                         <TableCell className="py-1.5 text-xs font-medium truncate max-w-0">
                           {obs.description}
@@ -1503,6 +1594,20 @@ export function DashboardClient({
                           {formatRelativeTime(obs.createdAt)}
                         </TableCell>
                       </TableRow>
+                      </ContextMenuTrigger>
+                      <ContextMenuContent className="min-w-52">
+                        <ContextMenuItem
+                          onClick={() => router.push(`/vehicles/${obs.vehicle.id}?tab=findings`)}
+                        >
+                          <ExternalLink className="mr-2 h-4 w-4" />
+                          {tcm("open")}
+                        </ContextMenuItem>
+                        <ContextMenuItem onClick={() => router.push(`/vehicles/${obs.vehicle.id}`)}>
+                          <Car className="mr-2 h-4 w-4" />
+                          {tcm("openVehicle")}
+                        </ContextMenuItem>
+                      </ContextMenuContent>
+                      </ContextMenu>
                     );
                   })}
                 </TableBody>

+ 28 - 4
src/app/(authenticated)/inspections/inspections-client.tsx

@@ -11,8 +11,17 @@ import { Input } from "@/components/ui/input";
 import {
   Table, TableBody, TableCell, TableHead, TableHeader, TableRow,
 } from "@/components/ui/table";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
 import { DataTablePagination } from "@/components/data-table-pagination";
-import { ArrowDown, ArrowUp, ArrowUpDown, Loader2, Plus, Search } from "lucide-react";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
+import { TableCellLink } from "@/components/table-cell-link";
+import { ArrowDown, ArrowUp, ArrowUpDown, Car, ExternalLink, Loader2, Plus, Search } from "lucide-react";
+import { useTranslations } from "next-intl";
 import { NewInspectionDialog } from "@/features/inspections/Components/NewInspectionDialog";
 
 interface InspectionRecord {
@@ -109,6 +118,7 @@ export function InspectionsClient({
   const searchParams = useSearchParams();
   const [isPending, startTransition] = useTransition();
   const [showNewDialog, setShowNewDialog] = useState(false);
+  const tcm = useTranslations("common.contextMenu");
 
   const navigate = useCallback(
     (params: Record<string, string | number | undefined>) => {
@@ -195,6 +205,7 @@ export function InspectionsClient({
       </div>
 
       <div className="rounded-lg border">
+        <TableContextMenuHint />
         <Table className="table-fixed">
           <TableHeader>
             <TableRow>
@@ -230,13 +241,14 @@ export function InspectionsClient({
               </TableRow>
             ) : (
               data.records.map((insp) => (
+                <ContextMenu key={insp.id} modal={false}>
+                <ContextMenuTrigger asChild>
                 <TableRow
-                  key={insp.id}
                   className="cursor-pointer"
                   onClick={() => router.push(`/inspections/${insp.id}`)}
                 >
                   <TableCell>
-                    <div className="min-w-0">
+                    <TableCellLink href={`/vehicles/${insp.vehicle.id}`} block>
                       <p className="truncate font-medium">
                         {insp.vehicle.year} {insp.vehicle.make} {insp.vehicle.model}
                       </p>
@@ -245,7 +257,7 @@ export function InspectionsClient({
                           {insp.vehicle.licensePlate}
                         </p>
                       )}
-                    </div>
+                    </TableCellLink>
                   </TableCell>
                   <TableCell className="hidden truncate md:table-cell text-muted-foreground">
                     {insp.template.name}
@@ -262,6 +274,18 @@ export function InspectionsClient({
                     {formatDate(new Date(insp.createdAt))}
                   </TableCell>
                 </TableRow>
+                </ContextMenuTrigger>
+                <ContextMenuContent className="min-w-52">
+                  <ContextMenuItem onClick={() => router.push(`/inspections/${insp.id}`)}>
+                    <ExternalLink className="mr-2 h-4 w-4" />
+                    {tcm("open")}
+                  </ContextMenuItem>
+                  <ContextMenuItem onClick={() => router.push(`/vehicles/${insp.vehicle.id}`)}>
+                    <Car className="mr-2 h-4 w-4" />
+                    {tcm("openVehicle")}
+                  </ContextMenuItem>
+                </ContextMenuContent>
+                </ContextMenu>
               ))
             )}
           </TableBody>

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

@@ -34,9 +34,17 @@ import {
   DropdownMenuItem,
   DropdownMenuTrigger,
 } from "@/components/ui/dropdown-menu";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuSeparator,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
 import { Badge } from "@/components/ui/badge";
 import { Checkbox } from "@/components/ui/checkbox";
 import { DataTablePagination } from "@/components/data-table-pagination";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
 import { useGlassModal } from "@/components/glass-modal";
 import { useConfirm } from "@/components/confirm-dialog";
 import { InventoryPartForm } from "@/features/inventory/Components/InventoryPartForm";
@@ -389,6 +397,7 @@ export function InventoryClient({
 
       {/* Table */}
       <div className="rounded-lg border">
+        <TableContextMenuHint />
         <Table>
           <TableHeader>
             <TableRow>
@@ -482,8 +491,9 @@ export function InventoryClient({
               data.parts.map((part) => {
                 const isLow = isLowStock(part, lowStockDefault);
                 return (
+                  <ContextMenu key={part.id} modal={false}>
+                  <ContextMenuTrigger asChild>
                   <TableRow
-                    key={part.id}
                     className="cursor-pointer"
                     onClick={() => {
                       setEditPart(part);
@@ -618,6 +628,31 @@ export function InventoryClient({
                       </DropdownMenu>
                     </TableCell>
                   </TableRow>
+                  </ContextMenuTrigger>
+                  <ContextMenuContent className="min-w-52">
+                    <ContextMenuItem
+                      onClick={() => {
+                        setEditPart(part);
+                        setShowForm(true);
+                      }}
+                    >
+                      <Pencil className="mr-2 h-4 w-4" />
+                      {t('actions.edit')}
+                    </ContextMenuItem>
+                    <ContextMenuItem onClick={() => router.push(`/inventory/${part.id}`)}>
+                      <History className="mr-2 h-4 w-4" />
+                      {t('actions.details')}
+                    </ContextMenuItem>
+                    <ContextMenuSeparator />
+                    <ContextMenuItem
+                      variant="destructive"
+                      onClick={() => handleDelete(part.id, part.name)}
+                    >
+                      <Trash2 className="mr-2 h-4 w-4" />
+                      {t('actions.delete')}
+                    </ContextMenuItem>
+                  </ContextMenuContent>
+                  </ContextMenu>
                 );
               })
             )}

+ 25 - 1
src/app/(authenticated)/labor-presets/labor-presets-client.tsx

@@ -21,7 +21,14 @@ import {
   DropdownMenuItem,
   DropdownMenuTrigger,
 } from "@/components/ui/dropdown-menu";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
 import { DataTablePagination } from "@/components/data-table-pagination";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
 import { useGlassModal } from "@/components/glass-modal";
 import { useConfirm } from "@/components/confirm-dialog";
 import { LaborPresetForm } from "@/features/labor-presets/Components/LaborPresetForm";
@@ -167,6 +174,7 @@ export function LaborPresetsClient({
 
       {/* Table */}
       <div className="rounded-lg border">
+        <TableContextMenuHint />
         <Table>
           <TableHeader>
             <TableRow>
@@ -196,8 +204,9 @@ export function LaborPresetsClient({
               </TableRow>
             ) : (
               data.presets.map((preset) => (
+                <ContextMenu key={preset.id} modal={false}>
+                <ContextMenuTrigger asChild>
                 <TableRow
-                  key={preset.id}
                   className="cursor-pointer"
                   onClick={() => handleEdit(preset.id)}
                 >
@@ -251,6 +260,21 @@ export function LaborPresetsClient({
                     </DropdownMenu>
                   </TableCell>
                 </TableRow>
+                </ContextMenuTrigger>
+                <ContextMenuContent className="min-w-52">
+                  <ContextMenuItem onClick={() => handleEdit(preset.id)}>
+                    <Pencil className="mr-2 h-4 w-4" />
+                    {t("actions.edit")}
+                  </ContextMenuItem>
+                  <ContextMenuItem
+                    variant="destructive"
+                    onClick={() => handleDelete(preset.id, preset.name)}
+                  >
+                    <Trash2 className="mr-2 h-4 w-4" />
+                    {t("actions.delete")}
+                  </ContextMenuItem>
+                </ContextMenuContent>
+                </ContextMenu>
               ))
             )}
           </TableBody>

+ 31 - 6
src/app/(authenticated)/observations/observations-client.tsx

@@ -20,9 +20,17 @@ import {
   TableHeader,
   TableRow,
 } from "@/components/ui/table";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
 import { DataTablePagination } from "@/components/data-table-pagination";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
+import { TableCellLink } from "@/components/table-cell-link";
 import { useFormatDate } from "@/lib/use-format-date";
-import { Search, Loader2 } from "lucide-react";
+import { Car, ExternalLink, Search, Loader2 } from "lucide-react";
 
 type ObservationsData = {
   records: {
@@ -77,6 +85,7 @@ export function ObservationsClient({
   const { formatDate } = useFormatDate();
   const t = useTranslations("vehicles.observationsPage");
   const tf = useTranslations("vehicles.findings");
+  const tcm = useTranslations("common.contextMenu");
 
   const navigate = useCallback(
     (params: Record<string, string | number | undefined>) => {
@@ -143,6 +152,7 @@ export function ObservationsClient({
 
       {/* Table */}
       <div className="rounded-md border">
+        <TableContextMenuHint />
         <Table>
           <TableHeader>
             <TableRow>
@@ -166,8 +176,9 @@ export function ObservationsClient({
                 const vehicleLabel = obs.vehicle.licensePlate
                   ?? `${obs.vehicle.year} ${obs.vehicle.make} ${obs.vehicle.model}`;
                 return (
+                  <ContextMenu key={obs.id} modal={false}>
+                  <ContextMenuTrigger asChild>
                   <TableRow
-                    key={obs.id}
                     className="cursor-pointer"
                     onClick={() => router.push(`/vehicles/${obs.vehicle.id}?tab=findings`)}
                   >
@@ -182,14 +193,14 @@ export function ObservationsClient({
                       </Badge>
                     </TableCell>
                     <TableCell>
-                      <div className="flex flex-col">
-                        <span className="font-mono text-xs font-medium">{vehicleLabel}</span>
+                      <TableCellLink href={`/vehicles/${obs.vehicle.id}`} block>
+                        <span className="block font-mono text-xs font-medium">{vehicleLabel}</span>
                         {obs.vehicle.licensePlate && (
-                          <span className="text-[11px] text-muted-foreground">
+                          <span className="block text-[11px] text-muted-foreground">
                             {obs.vehicle.year} {obs.vehicle.make} {obs.vehicle.model}
                           </span>
                         )}
-                      </div>
+                      </TableCellLink>
                     </TableCell>
                     <TableCell className="text-sm font-medium">{obs.description}</TableCell>
                     <TableCell className="hidden md:table-cell text-sm text-muted-foreground truncate max-w-[300px]">
@@ -199,6 +210,20 @@ export function ObservationsClient({
                       {formatDate(new Date(obs.createdAt))}
                     </TableCell>
                   </TableRow>
+                  </ContextMenuTrigger>
+                  <ContextMenuContent className="min-w-52">
+                    <ContextMenuItem
+                      onClick={() => router.push(`/vehicles/${obs.vehicle.id}?tab=findings`)}
+                    >
+                      <ExternalLink className="mr-2 h-4 w-4" />
+                      {tcm("open")}
+                    </ContextMenuItem>
+                    <ContextMenuItem onClick={() => router.push(`/vehicles/${obs.vehicle.id}`)}>
+                      <Car className="mr-2 h-4 w-4" />
+                      {tcm("openVehicle")}
+                    </ContextMenuItem>
+                  </ContextMenuContent>
+                  </ContextMenu>
                 );
               })
             )}

+ 47 - 4
src/app/(authenticated)/quotes/quotes-client.tsx

@@ -17,8 +17,16 @@ import {
   TableHeader,
   TableRow,
 } from '@/components/ui/table'
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from '@/components/ui/context-menu'
 import { DataTablePagination } from '@/components/data-table-pagination'
-import { ArrowDown, ArrowUp, ArrowUpDown, Loader2, Plus, Search } from 'lucide-react'
+import { TableContextMenuHint } from '@/components/table-context-menu-hint'
+import { TableCellLink } from '@/components/table-cell-link'
+import { ArrowDown, ArrowUp, ArrowUpDown, Car, ExternalLink, Loader2, Plus, Search, User } from 'lucide-react'
 import { useFormatCurrency } from '@/components/currency-settings-context'
 import { NewQuoteDialog } from '@/features/quotes/Components/NewQuoteDialog'
 
@@ -89,6 +97,7 @@ export function QuotesClient({
   const searchParams = useSearchParams()
   const [isPending, startTransition] = useTransition()
   const t = useTranslations('quotes')
+  const tcm = useTranslations('common.contextMenu')
 
   // New quote dialog state
   const [showNewDialog, setShowNewDialog] = useState(false)
@@ -190,6 +199,7 @@ export function QuotesClient({
       </div>
 
       <div className="rounded-lg border">
+        <TableContextMenuHint />
         <Table className="table-fixed">
           <TableHeader>
             <TableRow>
@@ -239,8 +249,9 @@ export function QuotesClient({
               </TableRow>
             ) : (
               data.records.map((q) => (
+                <ContextMenu key={q.id} modal={false}>
+                <ContextMenuTrigger asChild>
                 <TableRow
-                  key={q.id}
                   className="cursor-pointer"
                   onClick={() => router.push(`/quotes/${q.id}`)}
                 >
@@ -249,10 +260,22 @@ export function QuotesClient({
                   </TableCell>
                   <TableCell className="truncate font-medium">{q.title}</TableCell>
                   <TableCell className="hidden truncate md:table-cell text-muted-foreground">
-                    {q.customer?.name || '-'}
+                    {q.customer ? (
+                      <TableCellLink href={`/customers/${q.customer.id}`}>
+                        {q.customer.name}
+                      </TableCellLink>
+                    ) : (
+                      '-'
+                    )}
                   </TableCell>
                   <TableCell className="hidden truncate lg:table-cell text-muted-foreground">
-                    {q.vehicle ? `${q.vehicle.year} ${q.vehicle.make} ${q.vehicle.model}` : '-'}
+                    {q.vehicle ? (
+                      <TableCellLink href={`/vehicles/${q.vehicle.id}`}>
+                        {q.vehicle.year} {q.vehicle.make} {q.vehicle.model}
+                      </TableCellLink>
+                    ) : (
+                      '-'
+                    )}
                   </TableCell>
                   <TableCell>
                     <Badge variant="outline" className={`text-xs ${statusColors[q.status] || ''}`}>
@@ -266,6 +289,26 @@ export function QuotesClient({
                     {formatCurrency(q.totalAmount, currencyCode)}
                   </TableCell>
                 </TableRow>
+                </ContextMenuTrigger>
+                <ContextMenuContent className="min-w-52">
+                  <ContextMenuItem onClick={() => router.push(`/quotes/${q.id}`)}>
+                    <ExternalLink className="mr-2 h-4 w-4" />
+                    {tcm('open')}
+                  </ContextMenuItem>
+                  {q.vehicle && (
+                    <ContextMenuItem onClick={() => router.push(`/vehicles/${q.vehicle?.id}`)}>
+                      <Car className="mr-2 h-4 w-4" />
+                      {tcm('openVehicle')}
+                    </ContextMenuItem>
+                  )}
+                  {q.customer && (
+                    <ContextMenuItem onClick={() => router.push(`/customers/${q.customer?.id}`)}>
+                      <User className="mr-2 h-4 w-4" />
+                      {tcm('openCustomer')}
+                    </ContextMenuItem>
+                  )}
+                </ContextMenuContent>
+                </ContextMenu>
               ))
             )}
           </TableBody>

+ 34 - 1
src/app/(authenticated)/vehicles/[id]/findings-table.tsx

@@ -28,6 +28,12 @@ import {
   DropdownMenuItem,
   DropdownMenuTrigger,
 } from "@/components/ui/dropdown-menu";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
 import {
   ChevronLeft,
   ChevronRight,
@@ -214,7 +220,9 @@ export function FindingsTable({
               </TableRow>
             ) : (
               records.map((f) => (
-                <TableRow key={f.id}>
+                <ContextMenu key={f.id} modal={false}>
+                <ContextMenuTrigger asChild>
+                <TableRow>
                   <TableCell className="px-2">
                     <Checkbox
                       checked={selected.has(f.id)}
@@ -282,6 +290,31 @@ export function FindingsTable({
                     </DropdownMenu>
                   </TableCell>
                 </TableRow>
+                </ContextMenuTrigger>
+                <ContextMenuContent className="min-w-52">
+                  <ContextMenuItem onClick={() => onEditFinding(f)}>
+                    <Pencil className="mr-2 h-4 w-4" />
+                    {t("edit")}
+                  </ContextMenuItem>
+                  {f.serviceRecord && (
+                    <ContextMenuItem
+                      onClick={() =>
+                        router.push(`/vehicles/${vehicleId}/service/${f.serviceRecord?.id}`)
+                      }
+                    >
+                      <ExternalLink className="mr-2 h-4 w-4" />
+                      {t("discoveredIn")}
+                    </ContextMenuItem>
+                  )}
+                  <ContextMenuItem
+                    variant="destructive"
+                    onClick={() => onDeleteFinding(f.id)}
+                  >
+                    <Trash2 className="mr-2 h-4 w-4" />
+                    {t("delete")}
+                  </ContextMenuItem>
+                </ContextMenuContent>
+                </ContextMenu>
               ))
             )}
           </TableBody>

+ 28 - 1
src/app/(authenticated)/vehicles/[id]/notes-table.tsx

@@ -25,6 +25,12 @@ import {
   DropdownMenuItem,
   DropdownMenuTrigger,
 } from "@/components/ui/dropdown-menu";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
 import {
   ChevronLeft,
   ChevronRight,
@@ -152,7 +158,9 @@ export function NotesTable({
               </TableRow>
             ) : (
               records.map((n) => (
-                <TableRow key={n.id} className="cursor-pointer" onClick={() => onSelectNote(n)}>
+                <ContextMenu key={n.id} modal={false}>
+                <ContextMenuTrigger asChild>
+                <TableRow className="cursor-pointer" onClick={() => onSelectNote(n)}>
                   <TableCell className="w-[30px] px-2">
                     {n.isPinned && <Pin className="h-3.5 w-3.5 text-primary" />}
                   </TableCell>
@@ -192,6 +200,25 @@ export function NotesTable({
                     </DropdownMenu>
                   </TableCell>
                 </TableRow>
+                </ContextMenuTrigger>
+                <ContextMenuContent className="min-w-52">
+                  <ContextMenuItem onClick={() => onTogglePin(n.id)}>
+                    {n.isPinned ? (
+                      <PinOff className="mr-2 h-4 w-4" />
+                    ) : (
+                      <Pin className="mr-2 h-4 w-4" />
+                    )}
+                    {n.isPinned ? t("unpin") : t("pin")}
+                  </ContextMenuItem>
+                  <ContextMenuItem
+                    variant="destructive"
+                    onClick={() => onDeleteNote(n.id)}
+                  >
+                    <Trash2 className="mr-2 h-4 w-4" />
+                    {t("delete")}
+                  </ContextMenuItem>
+                </ContextMenuContent>
+                </ContextMenu>
               ))
             )}
           </TableBody>

+ 23 - 1
src/app/(authenticated)/vehicles/[id]/service-records-table.tsx

@@ -24,6 +24,12 @@ import {
   TableHeader,
   TableRow,
 } from "@/components/ui/table";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
 import { typeColors, statusColors } from "@/lib/table-utils";
 import { useFormatCurrency } from '@/components/currency-settings-context'
 import { getWarrantyStatus, type WarrantyStatus } from "@/lib/warranty";
@@ -34,6 +40,7 @@ import {
   ChevronsLeft,
   ChevronsRight,
   Download,
+  ExternalLink,
   Loader2,
   Paperclip,
   Plus,
@@ -115,6 +122,7 @@ export function ServiceRecordsTable({
   const [navigatingId, setNavigatingId] = useState<string | null>(null);
   const [isExporting, setIsExporting] = useState(false);
   const t = useTranslations("vehicles.services");
+  const tcm = useTranslations("common.contextMenu");
   const serviceType = useServiceType();
 
   const createUrl = useCallback(
@@ -269,8 +277,9 @@ export function ServiceRecordsTable({
               records.map((record) => {
                 const displayTotal = record.totalAmount > 0 ? record.totalAmount : record.cost;
                 return (
+                  <ContextMenu key={record.id} modal={false}>
+                  <ContextMenuTrigger asChild>
                   <TableRow
-                    key={record.id}
                     className={`cursor-pointer transition-opacity ${navigatingId === record.id ? "opacity-50" : ""}`}
                     onClick={() => {
                       setNavigatingId(record.id);
@@ -339,6 +348,19 @@ export function ServiceRecordsTable({
                       />
                     </TableCell>
                   </TableRow>
+                  </ContextMenuTrigger>
+                  <ContextMenuContent className="min-w-52">
+                    <ContextMenuItem
+                      onClick={() => {
+                        setNavigatingId(record.id);
+                        router.push(`/vehicles/${vehicleId}/service/${record.id}`);
+                      }}
+                    >
+                      <ExternalLink className="mr-2 h-4 w-4" />
+                      {tcm("open")}
+                    </ContextMenuItem>
+                  </ContextMenuContent>
+                  </ContextMenu>
                 );
               })
             )}

+ 70 - 2
src/app/(authenticated)/vehicles/vehicles-client.tsx

@@ -22,7 +22,16 @@ import {
   DropdownMenuItem,
   DropdownMenuTrigger,
 } from '@/components/ui/dropdown-menu'
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuSeparator,
+  ContextMenuTrigger,
+} from '@/components/ui/context-menu'
 import { DataTablePagination } from '@/components/data-table-pagination'
+import { TableContextMenuHint } from '@/components/table-context-menu-hint'
+import { TableCellLink } from '@/components/table-cell-link'
 import { useGlassModal } from '@/components/glass-modal'
 import { useConfirm } from '@/components/confirm-dialog'
 import { VehicleForm } from '@/features/vehicles/Components/VehicleForm'
@@ -36,6 +45,7 @@ import {
   ArrowDown,
   ArrowUp,
   ArrowUpDown,
+  ExternalLink,
   Gauge,
   Grid3X3,
   LayoutGrid,
@@ -46,6 +56,7 @@ import {
   Plus,
   Search,
   Trash2,
+  Users,
   Wrench,
 } from 'lucide-react'
 import { useTranslations } from 'next-intl'
@@ -113,6 +124,7 @@ export function VehiclesClient({
   const [isPending, startTransition] = useTransition()
   const t = useTranslations('vehicles.list')
   const tc = useTranslations('common.buttons')
+  const tcm = useTranslations('common.contextMenu')
   const [showForm, setShowForm] = useState(false)
   const [createCustomerId, setCreateCustomerId] = useState<string | undefined>(undefined)
   const [editVehicle, setEditVehicle] = useState<Vehicle | null>(null)
@@ -307,6 +319,7 @@ export function VehiclesClient({
       ) : view === 'table' ? (
         /* Table view */
         <div className="rounded-lg border">
+          <TableContextMenuHint />
           <Table className="table-fixed">
             <TableHeader>
               <TableRow>
@@ -340,8 +353,9 @@ export function VehiclesClient({
             </TableHeader>
             <TableBody>
               {data.vehicles.map((v) => (
+                <ContextMenu key={v.id} modal={false}>
+                <ContextMenuTrigger asChild>
                 <TableRow
-                  key={v.id}
                   className="cursor-pointer"
                   onClick={() => router.push(`/vehicles/${v.id}`)}
                 >
@@ -352,7 +366,13 @@ export function VehiclesClient({
                     </span>
                   </TableCell>
                   <TableCell className="hidden truncate sm:table-cell text-muted-foreground">
-                    {v.customer?.name || '-'}
+                    {v.customer ? (
+                      <TableCellLink href={`/customers/${v.customer.id}`}>
+                        {v.customer.name}
+                      </TableCellLink>
+                    ) : (
+                      '-'
+                    )}
                   </TableCell>
                   <TableCell className="hidden md:table-cell text-right font-mono text-sm">
                     {new Intl.NumberFormat('en-US').format(v.mileage)}
@@ -413,6 +433,54 @@ export function VehiclesClient({
                     </DropdownMenu>
                   </TableCell>
                 </TableRow>
+                </ContextMenuTrigger>
+                <ContextMenuContent className="min-w-52">
+                  <ContextMenuItem onClick={() => router.push(`/vehicles/${v.id}`)}>
+                    <ExternalLink className="mr-2 h-4 w-4" />
+                    {tcm('open')}
+                  </ContextMenuItem>
+                  {v.customer && (
+                    <ContextMenuItem onClick={() => router.push(`/customers/${v.customer?.id}`)}>
+                      <Users className="mr-2 h-4 w-4" />
+                      {tcm('openCustomer')}
+                    </ContextMenuItem>
+                  )}
+                  <ContextMenuSeparator />
+                  {!isArchived && (
+                    <ContextMenuItem
+                      onClick={() => {
+                        setEditVehicle(v)
+                        setShowForm(true)
+                      }}
+                    >
+                      <Pencil className="mr-2 h-4 w-4" />
+                      {t('edit')}
+                    </ContextMenuItem>
+                  )}
+                  {isArchived ? (
+                    <ContextMenuItem
+                      onClick={() => handleUnarchive(v.id, `${v.year} ${v.make} ${v.model}`)}
+                    >
+                      <ArchiveRestore className="mr-2 h-4 w-4" />
+                      {t('unarchive')}
+                    </ContextMenuItem>
+                  ) : (
+                    <ContextMenuItem
+                      onClick={() => setArchiveTarget({ id: v.id, name: `${v.year} ${v.make} ${v.model}` })}
+                    >
+                      <Archive className="mr-2 h-4 w-4" />
+                      {t('archive')}
+                    </ContextMenuItem>
+                  )}
+                  <ContextMenuItem
+                    variant="destructive"
+                    onClick={() => handleDelete(v.id, `${v.year} ${v.make} ${v.model}`)}
+                  >
+                    <Trash2 className="mr-2 h-4 w-4" />
+                    {t('delete')}
+                  </ContextMenuItem>
+                </ContextMenuContent>
+                </ContextMenu>
               ))}
             </TableBody>
           </Table>

+ 25 - 15
src/app/(authenticated)/work-orders/work-orders-client.tsx

@@ -25,6 +25,8 @@ import {
   ContextMenuTrigger,
 } from "@/components/ui/context-menu";
 import { DataTablePagination } from "@/components/data-table-pagination";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
+import { TableCellLink } from "@/components/table-cell-link";
 import { statusColors } from "@/lib/table-utils";
 import { updateServiceStatus } from "@/features/vehicles/Actions/serviceActions";
 import {
@@ -34,7 +36,6 @@ import {
   Car,
   ExternalLink,
   Loader2,
-  MousePointerClick,
   Plus,
   Search,
   User,
@@ -281,10 +282,7 @@ export function WorkOrdersClient({
 
       {/* Table */}
       <div className="rounded-lg border">
-        <div className="hidden items-center gap-1.5 border-b bg-muted/30 px-3 py-1.5 text-xs text-muted-foreground md:flex">
-          <MousePointerClick className="h-3.5 w-3.5" />
-          {t("contextMenu.hint")}
-        </div>
+        <TableContextMenuHint />
         <Table className="table-fixed">
           <TableHeader>
             <TableRow>
@@ -357,17 +355,29 @@ export function WorkOrdersClient({
                       {r.invoiceNumber || "-"}
                     </TableCell>
                     <TableCell>
-                      <div className="min-w-0">
-                        {r.vehicle?.licensePlate && (
-                          <span className="font-mono text-sm font-medium">{r.vehicle.licensePlate}</span>
-                        )}
-                        <p className="truncate text-xs text-muted-foreground">
-                          {r.vehicle ? `${r.vehicle.year} ${r.vehicle.make} ${r.vehicle.model}` : "-"}
-                        </p>
-                      </div>
+                      {r.vehicle ? (
+                        <TableCellLink href={`/vehicles/${r.vehicle.id}`} block>
+                          {r.vehicle.licensePlate && (
+                            <span className="font-mono text-sm font-medium">{r.vehicle.licensePlate}</span>
+                          )}
+                          <p className="truncate text-xs text-muted-foreground">
+                            {r.vehicle.year} {r.vehicle.make} {r.vehicle.model}
+                          </p>
+                        </TableCellLink>
+                      ) : (
+                        <div className="min-w-0">
+                          <p className="truncate text-xs text-muted-foreground">-</p>
+                        </div>
+                      )}
                     </TableCell>
                     <TableCell className="hidden truncate md:table-cell text-muted-foreground">
-                      {(r.customer ?? r.vehicle?.customer)?.name || "-"}
+                      {rowCustomer ? (
+                        <TableCellLink href={`/customers/${rowCustomer.id}`}>
+                          {rowCustomer.name}
+                        </TableCellLink>
+                      ) : (
+                        "-"
+                      )}
                     </TableCell>
                     <TableCell className="truncate">
                       <span className="font-medium">{r.title}</span>
@@ -393,7 +403,7 @@ export function WorkOrdersClient({
                     </TableCell>
                   </TableRow>
                   </ContextMenuTrigger>
-                  <ContextMenuContent className="w-52">
+                  <ContextMenuContent className="min-w-52">
                     <ContextMenuItem
                       onClick={() => {
                         setNavigatingId(r.id);

+ 40 - 0
src/components/table-cell-link.tsx

@@ -0,0 +1,40 @@
+"use client";
+
+import Link from "next/link";
+import type { ReactNode } from "react";
+import { cn } from "@/lib/utils";
+
+/**
+ * Link to a related record from inside a table cell. Rows are themselves
+ * clickable, so the click is stopped here to navigate to this target rather
+ * than the row's. Being a real anchor, it also supports middle-click,
+ * open-in-new-tab and keyboard focus.
+ *
+ * Pass `block` for cells whose content is a stacked block rather than a
+ * single run of text.
+ */
+export function TableCellLink({
+  href,
+  block = false,
+  className,
+  children,
+}: {
+  href: string;
+  block?: boolean;
+  className?: string;
+  children: ReactNode;
+}) {
+  return (
+    <Link
+      href={href}
+      onClick={(e) => e.stopPropagation()}
+      className={cn(
+        "hover:underline underline-offset-2 focus-visible:underline focus-visible:outline-none",
+        block ? "block min-w-0" : "inline",
+        className
+      )}
+    >
+      {children}
+    </Link>
+  );
+}

+ 18 - 0
src/components/table-context-menu-hint.tsx

@@ -0,0 +1,18 @@
+"use client";
+
+import { MousePointerClick } from "lucide-react";
+import { useTranslations } from "next-intl";
+
+/**
+ * Muted bar that sits above a list table and tells the user its rows carry a
+ * right-click menu. Hidden below md, where there is no right-click to hint at.
+ */
+export function TableContextMenuHint() {
+  const t = useTranslations("common.contextMenu");
+  return (
+    <div className="hidden items-center gap-1.5 border-b bg-muted/30 px-3 py-1.5 text-xs text-muted-foreground md:flex">
+      <MousePointerClick className="h-3.5 w-3.5" />
+      {t("hint")}
+    </div>
+  );
+}

+ 61 - 3
src/features/billing/Components/RecurringInvoicesClient.tsx

@@ -34,11 +34,22 @@ import {
   SelectValue,
 } from "@/components/ui/select";
 import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuSeparator,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
+import { TableCellLink } from "@/components/table-cell-link";
+import {
+  Car,
   Loader2,
   Plus,
   Pause,
   Play,
   Trash2,
+  User,
   Zap,
   X,
 } from "lucide-react";
@@ -136,6 +147,7 @@ export default function RecurringInvoicesClient({
   const formatCurrency = useFormatCurrency();
   const router = useRouter();
   const t = useTranslations("billing");
+  const tcm = useTranslations("common.contextMenu");
   const { formatDate } = useFormatDate();
   const [isPending, startTransition] = useTransition();
   const [showCreate, setShowCreate] = useState(false);
@@ -321,6 +333,7 @@ export default function RecurringInvoicesClient({
       ) : (
         <Card className="border-0 shadow-sm">
           <CardContent className="p-0">
+            <TableContextMenuHint />
             <Table>
               <TableHeader>
                 <TableRow>
@@ -348,12 +361,24 @@ export default function RecurringInvoicesClient({
                   });
 
                   return (
-                    <TableRow key={inv.id}>
+                    <ContextMenu key={inv.id} modal={false}>
+                    <ContextMenuTrigger asChild>
+                    <TableRow>
                       <TableCell className="text-sm font-medium">{inv.title}</TableCell>
                       <TableCell className="text-sm">
-                        {inv.vehicle.year} {inv.vehicle.make} {inv.vehicle.model}
+                        <TableCellLink href={`/vehicles/${inv.vehicle.id}`}>
+                          {inv.vehicle.year} {inv.vehicle.make} {inv.vehicle.model}
+                        </TableCellLink>
+                      </TableCell>
+                      <TableCell className="text-sm">
+                        {inv.vehicle.customer ? (
+                          <TableCellLink href={`/customers/${inv.vehicle.customer.id}`}>
+                            {inv.vehicle.customer.name}
+                          </TableCellLink>
+                        ) : (
+                          "-"
+                        )}
                       </TableCell>
-                      <TableCell className="text-sm">{inv.vehicle.customer?.name ?? "-"}</TableCell>
                       <TableCell className="text-sm">{t(frequencyKey[inv.frequency] ?? inv.frequency)}</TableCell>
                       <TableCell className="text-sm">
                         {formatDate(new Date(inv.nextRunDate))}
@@ -390,6 +415,39 @@ export default function RecurringInvoicesClient({
                         </div>
                       </TableCell>
                     </TableRow>
+                    </ContextMenuTrigger>
+                    <ContextMenuContent className="min-w-52">
+                      <ContextMenuItem onClick={() => router.push(`/vehicles/${inv.vehicle.id}`)}>
+                        <Car className="mr-2 h-4 w-4" />
+                        {tcm("openVehicle")}
+                      </ContextMenuItem>
+                      {inv.vehicle.customer && (
+                        <ContextMenuItem
+                          onClick={() => router.push(`/customers/${inv.vehicle.customer?.id}`)}
+                        >
+                          <User className="mr-2 h-4 w-4" />
+                          {tcm("openCustomer")}
+                        </ContextMenuItem>
+                      )}
+                      <ContextMenuSeparator />
+                      <ContextMenuItem onClick={() => handleToggle(inv.id)} disabled={isPending}>
+                        {inv.isActive ? (
+                          <Pause className="mr-2 h-4 w-4" />
+                        ) : (
+                          <Play className="mr-2 h-4 w-4" />
+                        )}
+                        {inv.isActive ? t("recurring.pause") : t("recurring.resume")}
+                      </ContextMenuItem>
+                      <ContextMenuItem
+                        variant="destructive"
+                        onClick={() => handleDelete(inv.id)}
+                        disabled={isPending}
+                      >
+                        <Trash2 className="mr-2 h-4 w-4" />
+                        {t("recurring.delete")}
+                      </ContextMenuItem>
+                    </ContextMenuContent>
+                    </ContextMenu>
                   );
                 })}
               </TableBody>

+ 23 - 1
src/features/inspections/Components/TemplateListClient.tsx

@@ -10,6 +10,10 @@ import {
 import {
   DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,
 } from "@/components/ui/dropdown-menu";
+import {
+  ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger,
+} from "@/components/ui/context-menu";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
 import {
   AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
   AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle,
@@ -82,6 +86,7 @@ export function TemplateListClient({ templates }: { templates: Template[] }) {
       </div>
 
       <div className="rounded-lg border">
+        <TableContextMenuHint />
         <Table>
           <TableHeader>
             <TableRow>
@@ -101,7 +106,9 @@ export function TemplateListClient({ templates }: { templates: Template[] }) {
               </TableRow>
             ) : (
               templates.map((t) => (
-                <TableRow key={t.id}>
+                <ContextMenu key={t.id} modal={false}>
+                <ContextMenuTrigger asChild>
+                <TableRow>
                   <TableCell>
                     <div>
                       <p className="font-medium">{t.name}</p>
@@ -142,6 +149,21 @@ export function TemplateListClient({ templates }: { templates: Template[] }) {
                     </DropdownMenu>
                   </TableCell>
                 </TableRow>
+                </ContextMenuTrigger>
+                <ContextMenuContent className="min-w-52">
+                  <ContextMenuItem onClick={() => handleEdit(t)}>
+                    <Pencil className="mr-2 h-4 w-4" />
+                    Edit
+                  </ContextMenuItem>
+                  <ContextMenuItem
+                    variant="destructive"
+                    onClick={() => setDeleteId(t.id)}
+                  >
+                    <Trash2 className="mr-2 h-4 w-4" />
+                    Delete
+                  </ContextMenuItem>
+                </ContextMenuContent>
+                </ContextMenu>
               ))
             )}
           </TableBody>

+ 37 - 1
src/features/status-reports/Components/StatusReportList.tsx

@@ -15,6 +15,14 @@ import {
   TableHeader,
   TableRow,
 } from "@/components/ui/table";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuSeparator,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
 import {
   Dialog,
   DialogContent,
@@ -149,6 +157,7 @@ export function StatusReportList({
           </Card>
         ) : (
           <div className="rounded-md border">
+            <TableContextMenuHint />
             <Table>
               <TableHeader>
                 <TableRow>
@@ -161,8 +170,9 @@ export function StatusReportList({
               </TableHeader>
               <TableBody>
                 {initialReports.map((report) => (
+                  <ContextMenu key={report.id} modal={false}>
+                  <ContextMenuTrigger asChild>
                   <TableRow
-                    key={report.id}
                     className="cursor-pointer"
                     onClick={() => setDetailReport(report)}
                   >
@@ -220,6 +230,32 @@ export function StatusReportList({
                       </div>
                     </TableCell>
                   </TableRow>
+                  </ContextMenuTrigger>
+                  <ContextMenuContent className="min-w-52">
+                    <ContextMenuItem onClick={() => openReport(report.publicToken)}>
+                      <ExternalLink className="mr-2 h-4 w-4" />
+                      {t("view")}
+                    </ContextMenuItem>
+                    <ContextMenuItem onClick={() => copyLink(report.publicToken)}>
+                      <Copy className="mr-2 h-4 w-4" />
+                      {t("copyLink")}
+                    </ContextMenuItem>
+                    {customer && (
+                      <ContextMenuItem onClick={() => setSendReportId(report.id)}>
+                        <Send className="mr-2 h-4 w-4" />
+                        {t("send")}
+                      </ContextMenuItem>
+                    )}
+                    <ContextMenuSeparator />
+                    <ContextMenuItem
+                      variant="destructive"
+                      onClick={() => setDeleteReportId(report.id)}
+                    >
+                      <Trash2 className="mr-2 h-4 w-4" />
+                      {t("delete")}
+                    </ContextMenuItem>
+                  </ContextMenuContent>
+                  </ContextMenu>
                 ))}
               </TableBody>
             </Table>

+ 49 - 11
src/features/vehicles/Components/RemindersPageClient.tsx

@@ -35,10 +35,20 @@ import {
   DropdownMenuItem,
   DropdownMenuTrigger,
 } from "@/components/ui/dropdown-menu";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuSeparator,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
+import { TableContextMenuHint } from "@/components/table-context-menu-hint";
+import { TableCellLink } from "@/components/table-cell-link";
 import {
   AlertTriangle,
   Bell,
   CalendarIcon,
+  Car,
   Check,
   CheckCircle2,
   ChevronsUpDown,
@@ -47,6 +57,7 @@ import {
   Pencil,
   Plus,
   Trash2,
+  User,
 } from "lucide-react";
 import { toast } from "sonner";
 import { cn } from "@/lib/utils";
@@ -114,6 +125,7 @@ export function RemindersPageClient({ reminders, vehicles, unitSystem }: Reminde
   const t = useTranslations("reminders");
   const tv = useTranslations("vehicles.reminders");
   const tc = useTranslations("common.buttons");
+  const tcm = useTranslations("common.contextMenu");
   const router = useRouter();
   const modal = useGlassModal();
   const { formatDate } = useFormatDate();
@@ -271,12 +283,14 @@ export function RemindersPageClient({ reminders, vehicles, unitSystem }: Reminde
       ) : (
         <Card className="border-0 shadow-sm">
           <CardContent className="p-0">
+            <TableContextMenuHint />
             <div className="divide-y">
               {filtered.map((r) => {
                 const urgency = getUrgency(r);
                 return (
+                  <ContextMenu key={r.id} modal={false}>
+                  <ContextMenuTrigger asChild>
                   <div
-                    key={r.id}
                     className="flex items-center gap-3 px-4 py-2.5 hover:bg-muted/50 transition-colors"
                   >
                     <button
@@ -309,22 +323,16 @@ export function RemindersPageClient({ reminders, vehicles, unitSystem }: Reminde
                       )}
                       {r.vehicle ? (
                         <span className="text-xs text-muted-foreground">
-                          <span
-                            className="hover:underline cursor-pointer"
-                            onClick={() => router.push(`/vehicles/${r.vehicle?.id}?tab=reminders`)}
-                          >
+                          <TableCellLink href={`/vehicles/${r.vehicle.id}?tab=reminders`}>
                             {r.vehicle.year} {r.vehicle.make} {r.vehicle.model}
                             {r.vehicle.licensePlate && ` · ${r.vehicle.licensePlate}`}
-                          </span>
+                          </TableCellLink>
                         </span>
                       ) : r.customer ? (
                         <span className="text-xs text-muted-foreground">
-                          <span
-                            className="hover:underline cursor-pointer"
-                            onClick={() => router.push(`/customers/${r.customer?.id}`)}
-                          >
+                          <TableCellLink href={`/customers/${r.customer.id}`}>
                             {r.customer.name}
-                          </span>
+                          </TableCellLink>
                         </span>
                       ) : (
                         <span className="text-xs text-muted-foreground">{t("workshopReminder")}</span>
@@ -359,6 +367,36 @@ export function RemindersPageClient({ reminders, vehicles, unitSystem }: Reminde
                       </DropdownMenuContent>
                     </DropdownMenu>
                   </div>
+                  </ContextMenuTrigger>
+                  <ContextMenuContent className="min-w-52">
+                    {r.vehicle && (
+                      <ContextMenuItem
+                        onClick={() => router.push(`/vehicles/${r.vehicle?.id}?tab=reminders`)}
+                      >
+                        <Car className="mr-2 h-4 w-4" />
+                        {tcm("openVehicle")}
+                      </ContextMenuItem>
+                    )}
+                    {r.customer && (
+                      <ContextMenuItem onClick={() => router.push(`/customers/${r.customer?.id}`)}>
+                        <User className="mr-2 h-4 w-4" />
+                        {tcm("openCustomer")}
+                      </ContextMenuItem>
+                    )}
+                    {(r.vehicle || r.customer) && <ContextMenuSeparator />}
+                    <ContextMenuItem onClick={() => openEditForm(r)}>
+                      <Pencil className="mr-2 h-4 w-4" />
+                      {tc("edit")}
+                    </ContextMenuItem>
+                    <ContextMenuItem
+                      variant="destructive"
+                      onClick={() => handleDelete(r.id)}
+                    >
+                      <Trash2 className="mr-2 h-4 w-4" />
+                      {tc("delete")}
+                    </ContextMenuItem>
+                  </ContextMenuContent>
+                  </ContextMenu>
                 );
               })}
             </div>