4
0
Эх сурвалжийг харах

Redesign dashboard with multi-column grid, SMS card, and card visibility (#8)

* add sms integration for the most common vendors

* new message page

* add pagination to message loading

* sms templates

* improved dashboard with more customizable layout

* hide when not in use
Bernt Christian Egeland 7 сар өмнө
parent
commit
668f783998

+ 644 - 469
src/app/(authenticated)/dashboard-client.tsx

@@ -14,6 +14,8 @@ import {
   TableHeader,
   TableRow,
 } from "@/components/ui/table";
+import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
+import { Switch } from "@/components/ui/switch";
 import { statusColors } from "@/lib/table-utils";
 import {
   AlertTriangle,
@@ -27,6 +29,9 @@ import {
   FileText,
   Gauge,
   Loader2,
+  BellRing,
+  MessageSquare,
+  SlidersHorizontal,
   Users,
   X,
 } from "lucide-react";
@@ -35,6 +40,7 @@ import { dismissMaintenance } from "@/features/vehicles/Actions/dismissMaintenan
 import { updateQuoteRequestStatus } from "@/features/inspections/Actions/quoteRequestActions";
 import { acknowledgeQuoteResponse } from "@/features/quotes/Actions/quoteResponseActions";
 import { convertQuoteToServiceRecord } from "@/features/quotes/Actions/quoteActions";
+import { useDashboardVisibility, DASHBOARD_CARDS } from "@/hooks/use-dashboard-visibility";
 
 interface ServiceItem {
   id: string;
@@ -141,6 +147,31 @@ interface DashboardQuoteResponse {
   vehicle: { id: string; make: string; model: string; year: number } | null;
 }
 
+interface SmsThread {
+  customerId: string;
+  customerName: string;
+  customerPhone: string | null;
+  lastMessage: {
+    id: string;
+    body: string;
+    direction: string;
+    status: string;
+    createdAt: string | Date;
+  };
+}
+
+interface DashboardNotification {
+  id: string;
+  type: string;
+  title: string;
+  message: string;
+  entityType: string;
+  entityId: string;
+  entityUrl: string;
+  read: boolean;
+  createdAt: string | Date;
+}
+
 export function DashboardClient({
   stats,
   currencyCode = "USD",
@@ -151,6 +182,9 @@ export function DashboardClient({
   completedInspections = [],
   quoteRequests = [],
   quoteResponses = [],
+  smsThreads = [],
+  smsEnabled = false,
+  notifications = [],
 }: {
   stats: DashboardStats;
   currencyCode?: string;
@@ -161,6 +195,9 @@ export function DashboardClient({
   completedInspections?: DashboardInspection[];
   quoteRequests?: DashboardQuoteRequest[];
   quoteResponses?: DashboardQuoteResponse[];
+  smsThreads?: SmsThread[];
+  smsEnabled?: boolean;
+  notifications?: DashboardNotification[];
 }) {
   const distUnit = unitSystem === "metric" ? "km" : "mi";
   const router = useRouter();
@@ -168,6 +205,22 @@ export function DashboardClient({
   const [navigatingId, setNavigatingId] = useState<string | null>(null);
   const [dismissingId, setDismissingId] = useState<string | null>(null);
   const [isPending, startTransition] = useTransition();
+  const { toggleCard, isVisible } = useDashboardVisibility();
+
+  const formatRelativeTime = (date: string | Date) => {
+    const now = new Date();
+    const d = new Date(date);
+    const diffMs = now.getTime() - d.getTime();
+    const diffMins = Math.floor(diffMs / 60000);
+    const diffHours = Math.floor(diffMs / 3600000);
+    const diffDays = Math.floor(diffMs / 86400000);
+
+    if (diffMins < 1) return "now";
+    if (diffMins < 60) return `${diffMins}m`;
+    if (diffHours < 24) return `${diffHours}h`;
+    if (diffDays < 7) return `${diffDays}d`;
+    return d.toLocaleDateString([], { month: "short", day: "numeric" });
+  };
 
   const handleDismiss = (vehicleId: string) => {
     setDismissingId(vehicleId);
@@ -178,7 +231,7 @@ export function DashboardClient({
   };
 
   return (
-    <div className="space-y-6">
+    <div className="space-y-4">
       {/* Quick stats row */}
       <div className={`grid grid-cols-2 gap-3 ${stats.isAdmin ? "sm:grid-cols-4" : "sm:grid-cols-2"}`}>
         <Card className="border-0 shadow-sm">
@@ -225,309 +278,527 @@ export function DashboardClient({
         )}
       </div>
 
-      {/* Vehicles Due for Service */}
-      {vehiclesDueForService.length > 0 && (
-        <Card className="border-0 shadow-sm">
-          <CardHeader className="pb-1">
-            <CardTitle className="flex items-center gap-2 text-base">
-              <Gauge className="h-4 w-4" />
-              Predicted Maintenance Due
-            </CardTitle>
-            <p className="text-xs text-muted-foreground">
-              Based on service history and estimated daily mileage
-            </p>
-          </CardHeader>
-          <CardContent className="p-0">
-            <div className="divide-y">
-              {vehiclesDueForService.map((v) => (
-                <div
-                  key={v.vehicleId}
-                  className="flex items-center justify-between px-5 py-3 cursor-pointer hover:bg-muted/50 transition-colors"
-                  onClick={() => router.push(`/vehicles/${v.vehicleId}`)}
-                >
-                  <div className="flex items-center gap-3 min-w-0">
-                    <div className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${
-                      v.status === "overdue" ? "bg-red-500/10" : "bg-amber-500/10"
-                    }`}>
-                      {v.status === "overdue" ? (
-                        <AlertTriangle className="h-4 w-4 text-red-500" />
-                      ) : (
-                        <Clock className="h-4 w-4 text-amber-500" />
-                      )}
-                    </div>
-                    <div className="min-w-0">
-                      <p className="font-medium text-sm truncate">
-                        {v.year} {v.make} {v.model}
-                      </p>
-                      <p className="text-xs text-muted-foreground truncate">
-                        {v.licensePlate && `${v.licensePlate} · `}
-                        Est. {v.predictedMileage.toLocaleString()} {distUnit}
-                        {" · "}
-                        {v.mileageSinceLastService.toLocaleString()} {distUnit} since last service
-                        {" · "}
-                        {v.confidencePercent}% certainty
-                      </p>
-                    </div>
-                  </div>
-                  <div className="shrink-0 ml-3 flex items-center gap-1.5">
-                    {v.status === "overdue" ? (
-                      <Badge variant="destructive" className="text-[10px]">
-                        Overdue
-                      </Badge>
-                    ) : (
-                      <Badge className="bg-amber-500/15 text-amber-600 border-amber-500/20 text-[10px]">
-                        Approaching
-                      </Badge>
-                    )}
-                    <Button
-                      variant="ghost"
-                      size="icon"
-                      className="h-6 w-6 text-muted-foreground hover:text-foreground"
-                      disabled={dismissingId === v.vehicleId}
-                      onClick={(e) => {
-                        e.stopPropagation();
-                        handleDismiss(v.vehicleId);
-                      }}
-                    >
-                      {dismissingId === v.vehicleId ? (
-                        <Loader2 className="h-3.5 w-3.5 animate-spin" />
-                      ) : (
-                        <X className="h-3.5 w-3.5" />
-                      )}
-                    </Button>
-                  </div>
-                </div>
+      {/* Customize Dashboard */}
+      <div className="flex justify-end">
+        <Popover>
+          <PopoverTrigger asChild>
+            <Button variant="outline" size="sm" className="h-8 text-xs gap-1.5">
+              <SlidersHorizontal className="h-3.5 w-3.5" />
+              Customize
+            </Button>
+          </PopoverTrigger>
+          <PopoverContent align="end" className="w-56 p-3">
+            <p className="text-sm font-medium mb-2">Show cards</p>
+            <div className="space-y-2">
+              {DASHBOARD_CARDS.filter((c) => smsEnabled ? c.id !== "notifications" : c.id !== "sms").map((card) => (
+                <label key={card.id} className="flex items-center justify-between gap-2 cursor-pointer">
+                  <span className="text-sm">{card.label}</span>
+                  <Switch
+                    size="sm"
+                    checked={isVisible(card.id)}
+                    onCheckedChange={() => toggleCard(card.id)}
+                  />
+                </label>
               ))}
             </div>
-          </CardContent>
-        </Card>
-      )}
-
-      {/* Upcoming Reminders */}
-      {upcomingReminders.length > 0 && (
-        <Card className="border-0 shadow-sm">
-          <CardHeader className="pb-3">
-            <CardTitle className="flex items-center gap-2 text-base">
-              <Bell className="h-4 w-4" />
-              Upcoming Reminders
-            </CardTitle>
-          </CardHeader>
-          <CardContent className="p-0">
-            <div className="divide-y">
-              {upcomingReminders.map((r) => {
-                const now = new Date();
-                const sevenDaysFromNow = new Date(now);
-                sevenDaysFromNow.setDate(sevenDaysFromNow.getDate() + 7);
-                const isOverdue = r.dueDate && new Date(r.dueDate) < now;
-                const isDueSoon = r.dueDate && !isOverdue && new Date(r.dueDate) <= sevenDaysFromNow;
+          </PopoverContent>
+        </Popover>
+      </div>
 
-                return (
+      {/* Card grid */}
+      <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
+        {/* Vehicles Due for Service */}
+        {isVisible("maintenance") && vehiclesDueForService.length > 0 && (
+          <Card className="border-0 shadow-sm">
+            <CardHeader className="pb-1">
+              <CardTitle className="flex items-center gap-2 text-base">
+                <Gauge className="h-4 w-4" />
+                Predicted Maintenance Due
+              </CardTitle>
+              <p className="text-xs text-muted-foreground">
+                Based on service history and estimated daily mileage
+              </p>
+            </CardHeader>
+            <CardContent className="p-0">
+              <div className="divide-y">
+                {vehiclesDueForService.map((v) => (
                   <div
-                    key={r.id}
+                    key={v.vehicleId}
                     className="flex items-center justify-between px-5 py-3 cursor-pointer hover:bg-muted/50 transition-colors"
-                    onClick={() => router.push(`/vehicles/${r.vehicle.id}`)}
+                    onClick={() => router.push(`/vehicles/${v.vehicleId}`)}
                   >
                     <div className="flex items-center gap-3 min-w-0">
                       <div className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${
-                        isOverdue ? "bg-red-500/10" : isDueSoon ? "bg-amber-500/10" : "bg-primary/10"
+                        v.status === "overdue" ? "bg-red-500/10" : "bg-amber-500/10"
                       }`}>
-                        {isOverdue ? (
+                        {v.status === "overdue" ? (
                           <AlertTriangle className="h-4 w-4 text-red-500" />
-                        ) : isDueSoon ? (
-                          <Clock className="h-4 w-4 text-amber-500" />
                         ) : (
-                          <Bell className="h-4 w-4 text-primary" />
+                          <Clock className="h-4 w-4 text-amber-500" />
                         )}
                       </div>
                       <div className="min-w-0">
-                        <p className="font-medium text-sm truncate">{r.title}</p>
+                        <p className="font-medium text-sm truncate">
+                          {v.year} {v.make} {v.model}
+                        </p>
                         <p className="text-xs text-muted-foreground truncate">
-                          {r.vehicle.year} {r.vehicle.make} {r.vehicle.model}
-                          {r.vehicle.licensePlate && ` · ${r.vehicle.licensePlate}`}
+                          {v.licensePlate && `${v.licensePlate} · `}
+                          Est. {v.predictedMileage.toLocaleString()} {distUnit}
+                          {" · "}
+                          {v.mileageSinceLastService.toLocaleString()} {distUnit} since last service
+                          {" · "}
+                          {v.confidencePercent}% certainty
                         </p>
                       </div>
                     </div>
-                    <div className="shrink-0 ml-3">
-                      {isOverdue && (
+                    <div className="shrink-0 ml-3 flex items-center gap-1.5">
+                      {v.status === "overdue" ? (
                         <Badge variant="destructive" className="text-[10px]">
                           Overdue
                         </Badge>
-                      )}
-                      {isDueSoon && (
+                      ) : (
                         <Badge className="bg-amber-500/15 text-amber-600 border-amber-500/20 text-[10px]">
-                          Due Soon
+                          Approaching
                         </Badge>
                       )}
-                      {r.dueDate && !isOverdue && !isDueSoon && (
-                        <span className="text-xs text-muted-foreground">
-                          {formatDate(new Date(r.dueDate))}
-                        </span>
-                      )}
+                      <Button
+                        variant="ghost"
+                        size="icon"
+                        className="h-6 w-6 text-muted-foreground hover:text-foreground"
+                        disabled={dismissingId === v.vehicleId}
+                        onClick={(e) => {
+                          e.stopPropagation();
+                          handleDismiss(v.vehicleId);
+                        }}
+                      >
+                        {dismissingId === v.vehicleId ? (
+                          <Loader2 className="h-3.5 w-3.5 animate-spin" />
+                        ) : (
+                          <X className="h-3.5 w-3.5" />
+                        )}
+                      </Button>
                     </div>
                   </div>
-                );
-              })}
-            </div>
-          </CardContent>
-        </Card>
-      )}
+                ))}
+              </div>
+            </CardContent>
+          </Card>
+        )}
 
-      {/* Inspections */}
-      {(inProgressInspections.length > 0 || completedInspections.length > 0) && (
-        <Card className="border-0 shadow-sm">
-          <CardHeader className="pb-3">
-            <CardTitle className="flex items-center gap-2 text-base">
-              <ClipboardCheck className="h-4 w-4" />
-              Inspections
-            </CardTitle>
-          </CardHeader>
-          <CardContent className="p-0">
-            <div className="divide-y">
-              {inProgressInspections.map((insp) => {
-                const total = insp.items.length;
-                const pass = insp.items.filter((i) => i.condition === "pass").length;
-                const fail = insp.items.filter((i) => i.condition === "fail").length;
-                const attention = insp.items.filter((i) => i.condition === "attention").length;
-                const inspected = pass + fail + attention;
+        {/* Upcoming Reminders */}
+        {isVisible("reminders") && upcomingReminders.length > 0 && (
+          <Card className="border-0 shadow-sm">
+            <CardHeader className="pb-3">
+              <CardTitle className="flex items-center gap-2 text-base">
+                <Bell className="h-4 w-4" />
+                Upcoming Reminders
+              </CardTitle>
+            </CardHeader>
+            <CardContent className="p-0">
+              <div className="divide-y">
+                {upcomingReminders.map((r) => {
+                  const now = new Date();
+                  const sevenDaysFromNow = new Date(now);
+                  sevenDaysFromNow.setDate(sevenDaysFromNow.getDate() + 7);
+                  const isOverdue = r.dueDate && new Date(r.dueDate) < now;
+                  const isDueSoon = r.dueDate && !isOverdue && new Date(r.dueDate) <= sevenDaysFromNow;
 
-                return (
+                  return (
+                    <div
+                      key={r.id}
+                      className="flex items-center justify-between px-5 py-3 cursor-pointer hover:bg-muted/50 transition-colors"
+                      onClick={() => router.push(`/vehicles/${r.vehicle.id}`)}
+                    >
+                      <div className="flex items-center gap-3 min-w-0">
+                        <div className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${
+                          isOverdue ? "bg-red-500/10" : isDueSoon ? "bg-amber-500/10" : "bg-primary/10"
+                        }`}>
+                          {isOverdue ? (
+                            <AlertTriangle className="h-4 w-4 text-red-500" />
+                          ) : isDueSoon ? (
+                            <Clock className="h-4 w-4 text-amber-500" />
+                          ) : (
+                            <Bell className="h-4 w-4 text-primary" />
+                          )}
+                        </div>
+                        <div className="min-w-0">
+                          <p className="font-medium text-sm truncate">{r.title}</p>
+                          <p className="text-xs text-muted-foreground truncate">
+                            {r.vehicle.year} {r.vehicle.make} {r.vehicle.model}
+                            {r.vehicle.licensePlate && ` · ${r.vehicle.licensePlate}`}
+                          </p>
+                        </div>
+                      </div>
+                      <div className="shrink-0 ml-3">
+                        {isOverdue && (
+                          <Badge variant="destructive" className="text-[10px]">
+                            Overdue
+                          </Badge>
+                        )}
+                        {isDueSoon && (
+                          <Badge className="bg-amber-500/15 text-amber-600 border-amber-500/20 text-[10px]">
+                            Due Soon
+                          </Badge>
+                        )}
+                        {r.dueDate && !isOverdue && !isDueSoon && (
+                          <span className="text-xs text-muted-foreground">
+                            {formatDate(new Date(r.dueDate))}
+                          </span>
+                        )}
+                      </div>
+                    </div>
+                  );
+                })}
+              </div>
+            </CardContent>
+          </Card>
+        )}
+
+        {/* SMS Messages */}
+        {isVisible("sms") && smsEnabled && smsThreads.length > 0 && (
+          <Card className="border-0 shadow-sm">
+            <CardHeader className="pb-3">
+              <div className="flex items-center justify-between">
+                <CardTitle className="flex items-center gap-2 text-base">
+                  <MessageSquare className="h-4 w-4" />
+                  Recent Messages
+                </CardTitle>
+                <Button
+                  variant="ghost"
+                  size="sm"
+                  className="h-7 text-xs gap-1"
+                  onClick={() => router.push("/messages")}
+                >
+                  View All
+                  <ArrowRight className="h-3 w-3" />
+                </Button>
+              </div>
+            </CardHeader>
+            <CardContent className="p-0">
+              <div className="divide-y">
+                {smsThreads.map((thread) => (
                   <div
-                    key={insp.id}
+                    key={thread.customerId}
                     className="flex items-center justify-between px-5 py-3 cursor-pointer hover:bg-muted/50 transition-colors"
-                    onClick={() => router.push(`/inspections/${insp.id}`)}
+                    onClick={() => router.push("/messages")}
                   >
                     <div className="flex items-center gap-3 min-w-0">
-                      <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-blue-500/10">
-                        <ClipboardCheck className="h-4 w-4 text-blue-500" />
+                      <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10">
+                        <MessageSquare className="h-4 w-4 text-primary" />
                       </div>
                       <div className="min-w-0">
-                        <p className="font-medium text-sm truncate">
-                          {insp.vehicle.year} {insp.vehicle.make} {insp.vehicle.model}
-                        </p>
+                        <p className="font-medium text-sm truncate">{thread.customerName}</p>
                         <p className="text-xs text-muted-foreground truncate">
-                          {insp.vehicle.licensePlate && `${insp.vehicle.licensePlate} · `}
-                          {insp.template.name} · {inspected}/{total} items
+                          {thread.lastMessage.direction === "outbound" ? "You: " : ""}
+                          {thread.lastMessage.body}
                         </p>
                       </div>
                     </div>
-                    <div className="shrink-0 ml-3 flex items-center gap-2">
-                      {total > 0 && (
-                        <div className="hidden sm:flex h-2 w-20 overflow-hidden rounded-full bg-gray-200">
-                          <div className="bg-emerald-500" style={{ width: `${(pass / total) * 100}%` }} />
-                          <div className="bg-red-500" style={{ width: `${(fail / total) * 100}%` }} />
-                          <div className="bg-amber-500" style={{ width: `${(attention / total) * 100}%` }} />
-                        </div>
-                      )}
-                      <Badge className="bg-blue-500/15 text-blue-600 border-blue-500/20 text-[10px]">
-                        In Progress
-                      </Badge>
-                    </div>
+                    <span className="shrink-0 ml-3 text-xs text-muted-foreground">
+                      {formatRelativeTime(thread.lastMessage.createdAt)}
+                    </span>
                   </div>
-                );
-              })}
-              {completedInspections.map((insp) => {
-                const total = insp.items.length;
-                const pass = insp.items.filter((i) => i.condition === "pass").length;
-                const fail = insp.items.filter((i) => i.condition === "fail").length;
-                const attention = insp.items.filter((i) => i.condition === "attention").length;
+                ))}
+              </div>
+            </CardContent>
+          </Card>
+        )}
 
-                return (
+        {/* Recent Notifications (shown when SMS is not configured) */}
+        {isVisible("notifications") && !smsEnabled && notifications.length > 0 && (
+          <Card className="border-0 shadow-sm">
+            <CardHeader className="pb-3">
+              <div className="flex items-center justify-between">
+                <CardTitle className="flex items-center gap-2 text-base">
+                  <BellRing className="h-4 w-4" />
+                  Recent Notifications
+                </CardTitle>
+              </div>
+            </CardHeader>
+            <CardContent className="p-0">
+              <div className="divide-y">
+                {notifications.slice(0, 5).map((n) => (
                   <div
-                    key={insp.id}
+                    key={n.id}
                     className="flex items-center justify-between px-5 py-3 cursor-pointer hover:bg-muted/50 transition-colors"
-                    onClick={() => router.push(`/inspections/${insp.id}`)}
+                    onClick={() => router.push(n.entityUrl)}
                   >
                     <div className="flex items-center gap-3 min-w-0">
-                      <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-emerald-500/10">
-                        <Check className="h-4 w-4 text-emerald-500" />
+                      <div className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${n.read ? "bg-muted" : "bg-primary/10"}`}>
+                        <BellRing className={`h-4 w-4 ${n.read ? "text-muted-foreground" : "text-primary"}`} />
                       </div>
                       <div className="min-w-0">
-                        <p className="font-medium text-sm truncate">
-                          {insp.vehicle.year} {insp.vehicle.make} {insp.vehicle.model}
-                        </p>
-                        <p className="text-xs text-muted-foreground truncate">
-                          {insp.vehicle.licensePlate && `${insp.vehicle.licensePlate} · `}
-                          {insp.template.name} · {formatDate(new Date(insp.createdAt))}
-                        </p>
+                        <p className="font-medium text-sm truncate">{n.title}</p>
+                        <p className="text-xs text-muted-foreground truncate">{n.message}</p>
                       </div>
                     </div>
-                    <div className="shrink-0 ml-3 flex items-center gap-2">
-                      {total > 0 && (
-                        <div className="hidden sm:flex h-2 w-20 overflow-hidden rounded-full bg-gray-200">
-                          <div className="bg-emerald-500" style={{ width: `${(pass / total) * 100}%` }} />
-                          <div className="bg-red-500" style={{ width: `${(fail / total) * 100}%` }} />
-                          <div className="bg-amber-500" style={{ width: `${(attention / total) * 100}%` }} />
+                    <span className="shrink-0 ml-3 text-xs text-muted-foreground">
+                      {formatRelativeTime(n.createdAt)}
+                    </span>
+                  </div>
+                ))}
+              </div>
+            </CardContent>
+          </Card>
+        )}
+
+        {/* Inspections */}
+        {isVisible("inspections") && (inProgressInspections.length > 0 || completedInspections.length > 0) && (
+          <Card className="border-0 shadow-sm">
+            <CardHeader className="pb-3">
+              <CardTitle className="flex items-center gap-2 text-base">
+                <ClipboardCheck className="h-4 w-4" />
+                Inspections
+              </CardTitle>
+            </CardHeader>
+            <CardContent className="p-0">
+              <div className="divide-y">
+                {inProgressInspections.map((insp) => {
+                  const total = insp.items.length;
+                  const pass = insp.items.filter((i) => i.condition === "pass").length;
+                  const fail = insp.items.filter((i) => i.condition === "fail").length;
+                  const attention = insp.items.filter((i) => i.condition === "attention").length;
+                  const inspected = pass + fail + attention;
+
+                  return (
+                    <div
+                      key={insp.id}
+                      className="flex items-center justify-between px-5 py-3 cursor-pointer hover:bg-muted/50 transition-colors"
+                      onClick={() => router.push(`/inspections/${insp.id}`)}
+                    >
+                      <div className="flex items-center gap-3 min-w-0">
+                        <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-blue-500/10">
+                          <ClipboardCheck className="h-4 w-4 text-blue-500" />
                         </div>
-                      )}
-                      <Badge className="bg-emerald-500/15 text-emerald-600 border-emerald-500/20 text-[10px]">
-                        Completed
-                      </Badge>
+                        <div className="min-w-0">
+                          <p className="font-medium text-sm truncate">
+                            {insp.vehicle.year} {insp.vehicle.make} {insp.vehicle.model}
+                          </p>
+                          <p className="text-xs text-muted-foreground truncate">
+                            {insp.vehicle.licensePlate && `${insp.vehicle.licensePlate} · `}
+                            {insp.template.name} · {inspected}/{total} items
+                          </p>
+                        </div>
+                      </div>
+                      <div className="shrink-0 ml-3 flex items-center gap-2">
+                        {total > 0 && (
+                          <div className="hidden sm:flex h-2 w-20 overflow-hidden rounded-full bg-gray-200">
+                            <div className="bg-emerald-500" style={{ width: `${(pass / total) * 100}%` }} />
+                            <div className="bg-red-500" style={{ width: `${(fail / total) * 100}%` }} />
+                            <div className="bg-amber-500" style={{ width: `${(attention / total) * 100}%` }} />
+                          </div>
+                        )}
+                        <Badge className="bg-blue-500/15 text-blue-600 border-blue-500/20 text-[10px]">
+                          In Progress
+                        </Badge>
+                      </div>
                     </div>
-                  </div>
-                );
-              })}
-            </div>
-          </CardContent>
-        </Card>
-      )}
+                  );
+                })}
+                {completedInspections.map((insp) => {
+                  const total = insp.items.length;
+                  const pass = insp.items.filter((i) => i.condition === "pass").length;
+                  const fail = insp.items.filter((i) => i.condition === "fail").length;
+                  const attention = insp.items.filter((i) => i.condition === "attention").length;
 
-      {/* Quote Requests */}
-      {quoteRequests.length > 0 && (
-        <Card className="border-0 shadow-sm">
-          <CardHeader className="pb-3">
-            <CardTitle className="flex items-center gap-2 text-base">
-              <FileText className="h-4 w-4" />
-              Quote Requests
-            </CardTitle>
-            <p className="text-xs text-muted-foreground">
-              Customers requesting quotes from shared inspections
-            </p>
-          </CardHeader>
-          <CardContent className="p-0">
-            <div className="divide-y">
-              {quoteRequests.map((req) => {
-                const selectedItems = req.inspection.items.filter((i) =>
-                  req.selectedItemIds.includes(i.id)
-                );
-                return (
+                  return (
+                    <div
+                      key={insp.id}
+                      className="flex items-center justify-between px-5 py-3 cursor-pointer hover:bg-muted/50 transition-colors"
+                      onClick={() => router.push(`/inspections/${insp.id}`)}
+                    >
+                      <div className="flex items-center gap-3 min-w-0">
+                        <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-emerald-500/10">
+                          <Check className="h-4 w-4 text-emerald-500" />
+                        </div>
+                        <div className="min-w-0">
+                          <p className="font-medium text-sm truncate">
+                            {insp.vehicle.year} {insp.vehicle.make} {insp.vehicle.model}
+                          </p>
+                          <p className="text-xs text-muted-foreground truncate">
+                            {insp.vehicle.licensePlate && `${insp.vehicle.licensePlate} · `}
+                            {insp.template.name} · {formatDate(new Date(insp.createdAt))}
+                          </p>
+                        </div>
+                      </div>
+                      <div className="shrink-0 ml-3 flex items-center gap-2">
+                        {total > 0 && (
+                          <div className="hidden sm:flex h-2 w-20 overflow-hidden rounded-full bg-gray-200">
+                            <div className="bg-emerald-500" style={{ width: `${(pass / total) * 100}%` }} />
+                            <div className="bg-red-500" style={{ width: `${(fail / total) * 100}%` }} />
+                            <div className="bg-amber-500" style={{ width: `${(attention / total) * 100}%` }} />
+                          </div>
+                        )}
+                        <Badge className="bg-emerald-500/15 text-emerald-600 border-emerald-500/20 text-[10px]">
+                          Completed
+                        </Badge>
+                      </div>
+                    </div>
+                  );
+                })}
+              </div>
+            </CardContent>
+          </Card>
+        )}
+
+        {/* Quote Requests */}
+        {isVisible("quoteRequests") && quoteRequests.length > 0 && (
+          <Card className="border-0 shadow-sm">
+            <CardHeader className="pb-3">
+              <CardTitle className="flex items-center gap-2 text-base">
+                <FileText className="h-4 w-4" />
+                Quote Requests
+              </CardTitle>
+              <p className="text-xs text-muted-foreground">
+                Customers requesting quotes from shared inspections
+              </p>
+            </CardHeader>
+            <CardContent className="p-0">
+              <div className="divide-y">
+                {quoteRequests.map((req) => {
+                  const selectedItems = req.inspection.items.filter((i) =>
+                    req.selectedItemIds.includes(i.id)
+                  );
+                  return (
+                    <div
+                      key={req.id}
+                      className="flex items-center justify-between px-5 py-3"
+                    >
+                      <div
+                        className="flex items-center gap-3 min-w-0 cursor-pointer hover:opacity-80"
+                        onClick={() => router.push(`/inspections/${req.inspection.id}`)}
+                      >
+                        <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10">
+                          <FileText className="h-4 w-4 text-primary" />
+                        </div>
+                        <div className="min-w-0">
+                          <p className="font-medium text-sm truncate">
+                            {req.inspection.vehicle.year} {req.inspection.vehicle.make} {req.inspection.vehicle.model}
+                          </p>
+                          <p className="text-xs text-muted-foreground truncate">
+                            {req.inspection.vehicle.customer?.name && `${req.inspection.vehicle.customer.name} · `}
+                            {selectedItems.length} item{selectedItems.length !== 1 ? "s" : ""} requested
+                            {req.message && ` · "${req.message}"`}
+                          </p>
+                        </div>
+                      </div>
+                      <div className="shrink-0 ml-3 flex items-center gap-1.5">
+                        <Badge className="bg-primary/10 text-primary border-primary/20 text-[10px]">
+                          Pending
+                        </Badge>
+                        <Button
+                          variant="outline"
+                          size="sm"
+                          className="h-7 text-xs"
+                          onClick={() => {
+                            startTransition(async () => {
+                              await updateQuoteRequestStatus(req.id, "quoted");
+                            });
+                            router.push(`/quotes/new?fromInspection=${req.inspection.id}`);
+                          }}
+                        >
+                          Create Quote
+                        </Button>
+                        <Button
+                          variant="ghost"
+                          size="icon"
+                          className="h-6 w-6 text-muted-foreground hover:text-foreground"
+                          onClick={() => {
+                            startTransition(async () => {
+                              await updateQuoteRequestStatus(req.id, "dismissed");
+                            });
+                          }}
+                        >
+                          <X className="h-3.5 w-3.5" />
+                        </Button>
+                      </div>
+                    </div>
+                  );
+                })}
+              </div>
+            </CardContent>
+          </Card>
+        )}
+
+        {/* Customer Quote Responses */}
+        {isVisible("quoteResponses") && quoteResponses.length > 0 && (
+          <Card className="border-0 shadow-sm">
+            <CardHeader className="pb-3">
+              <CardTitle className="flex items-center gap-2 text-base">
+                <FileText className="h-4 w-4" />
+                Customer Quote Responses
+              </CardTitle>
+              <p className="text-xs text-muted-foreground">
+                Quotes that customers have accepted or requested changes on
+              </p>
+            </CardHeader>
+            <CardContent className="p-0">
+              <div className="divide-y">
+                {quoteResponses.map((resp) => (
                   <div
-                    key={req.id}
+                    key={resp.id}
                     className="flex items-center justify-between px-5 py-3"
                   >
                     <div
                       className="flex items-center gap-3 min-w-0 cursor-pointer hover:opacity-80"
-                      onClick={() => router.push(`/inspections/${req.inspection.id}`)}
+                      onClick={() => router.push(`/quotes/${resp.id}`)}
                     >
-                      <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10">
-                        <FileText className="h-4 w-4 text-primary" />
+                      <div className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${
+                        resp.status === "accepted" ? "bg-emerald-500/10" : "bg-orange-500/10"
+                      }`}>
+                        {resp.status === "accepted" ? (
+                          <Check className="h-4 w-4 text-emerald-500" />
+                        ) : (
+                          <FileText className="h-4 w-4 text-orange-500" />
+                        )}
                       </div>
                       <div className="min-w-0">
                         <p className="font-medium text-sm truncate">
-                          {req.inspection.vehicle.year} {req.inspection.vehicle.make} {req.inspection.vehicle.model}
+                          {resp.title}
+                          {resp.quoteNumber && <span className="ml-1.5 text-muted-foreground font-normal">({resp.quoteNumber})</span>}
                         </p>
                         <p className="text-xs text-muted-foreground truncate">
-                          {req.inspection.vehicle.customer?.name && `${req.inspection.vehicle.customer.name} · `}
-                          {selectedItems.length} item{selectedItems.length !== 1 ? "s" : ""} requested
-                          {req.message && ` · "${req.message}"`}
+                          {resp.customer?.name && `${resp.customer.name} · `}
+                          {resp.status === "accepted" ? "Accepted" : "Changes requested"}
+                          {resp.customerMessage && ` · "${resp.customerMessage}"`}
                         </p>
                       </div>
                     </div>
                     <div className="shrink-0 ml-3 flex items-center gap-1.5">
-                      <Badge className="bg-primary/10 text-primary border-primary/20 text-[10px]">
-                        Pending
-                      </Badge>
+                      {resp.status === "accepted" ? (
+                        <Badge className="bg-emerald-500/10 text-emerald-600 border-emerald-500/20 text-[10px]">
+                          Accepted
+                        </Badge>
+                      ) : (
+                        <Badge className="bg-orange-500/10 text-orange-600 border-orange-500/20 text-[10px]">
+                          Changes Requested
+                        </Badge>
+                      )}
+                      {resp.status === "accepted" && resp.vehicleId && (
+                        <Button
+                          variant="outline"
+                          size="sm"
+                          className="h-7 text-xs"
+                          onClick={() => {
+                            startTransition(async () => {
+                              const result = await convertQuoteToServiceRecord(resp.id, resp.vehicleId!);
+                              if (result.success && result.data) {
+                                router.push(`/vehicles/${resp.vehicleId}/service/${result.data.id}`);
+                              }
+                            });
+                          }}
+                        >
+                          <ArrowRight className="mr-1 h-3 w-3" />
+                          Convert to Work Order
+                        </Button>
+                      )}
                       <Button
                         variant="outline"
                         size="sm"
                         className="h-7 text-xs"
-                        onClick={() => {
-                          startTransition(async () => {
-                            await updateQuoteRequestStatus(req.id, "quoted");
-                          });
-                          router.push(`/quotes/new?fromInspection=${req.inspection.id}`);
-                        }}
+                        onClick={() => router.push(`/quotes/${resp.id}`)}
                       >
-                        Create Quote
+                        View Quote
                       </Button>
                       <Button
                         variant="ghost"
@@ -535,7 +806,7 @@ export function DashboardClient({
                         className="h-6 w-6 text-muted-foreground hover:text-foreground"
                         onClick={() => {
                           startTransition(async () => {
-                            await updateQuoteRequestStatus(req.id, "dismissed");
+                            await acknowledgeQuoteResponse(resp.id);
                           });
                         }}
                       >
@@ -543,251 +814,155 @@ export function DashboardClient({
                       </Button>
                     </div>
                   </div>
-                );
-              })}
-            </div>
-          </CardContent>
-        </Card>
-      )}
-
-      {/* Customer Quote Responses */}
-      {quoteResponses.length > 0 && (
-        <Card className="border-0 shadow-sm">
-          <CardHeader className="pb-3">
-            <CardTitle className="flex items-center gap-2 text-base">
-              <FileText className="h-4 w-4" />
-              Customer Quote Responses
-            </CardTitle>
-            <p className="text-xs text-muted-foreground">
-              Quotes that customers have accepted or requested changes on
-            </p>
-          </CardHeader>
-          <CardContent className="p-0">
-            <div className="divide-y">
-              {quoteResponses.map((resp) => (
-                <div
-                  key={resp.id}
-                  className="flex items-center justify-between px-5 py-3"
-                >
-                  <div
-                    className="flex items-center gap-3 min-w-0 cursor-pointer hover:opacity-80"
-                    onClick={() => router.push(`/quotes/${resp.id}`)}
-                  >
-                    <div className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${
-                      resp.status === "accepted" ? "bg-emerald-500/10" : "bg-orange-500/10"
-                    }`}>
-                      {resp.status === "accepted" ? (
-                        <Check className="h-4 w-4 text-emerald-500" />
-                      ) : (
-                        <FileText className="h-4 w-4 text-orange-500" />
-                      )}
-                    </div>
-                    <div className="min-w-0">
-                      <p className="font-medium text-sm truncate">
-                        {resp.title}
-                        {resp.quoteNumber && <span className="ml-1.5 text-muted-foreground font-normal">({resp.quoteNumber})</span>}
-                      </p>
-                      <p className="text-xs text-muted-foreground truncate">
-                        {resp.customer?.name && `${resp.customer.name} · `}
-                        {resp.status === "accepted" ? "Accepted" : "Changes requested"}
-                        {resp.customerMessage && ` · "${resp.customerMessage}"`}
-                      </p>
-                    </div>
-                  </div>
-                  <div className="shrink-0 ml-3 flex items-center gap-1.5">
-                    {resp.status === "accepted" ? (
-                      <Badge className="bg-emerald-500/10 text-emerald-600 border-emerald-500/20 text-[10px]">
-                        Accepted
-                      </Badge>
-                    ) : (
-                      <Badge className="bg-orange-500/10 text-orange-600 border-orange-500/20 text-[10px]">
-                        Changes Requested
-                      </Badge>
-                    )}
-                    {resp.status === "accepted" && resp.vehicleId && (
-                      <Button
-                        variant="outline"
-                        size="sm"
-                        className="h-7 text-xs"
-                        onClick={() => {
-                          startTransition(async () => {
-                            const result = await convertQuoteToServiceRecord(resp.id, resp.vehicleId!);
-                            if (result.success && result.data) {
-                              router.push(`/vehicles/${resp.vehicleId}/service/${result.data.id}`);
-                            }
-                          });
-                        }}
-                      >
-                        <ArrowRight className="mr-1 h-3 w-3" />
-                        Convert to Work Order
-                      </Button>
-                    )}
-                    <Button
-                      variant="outline"
-                      size="sm"
-                      className="h-7 text-xs"
-                      onClick={() => router.push(`/quotes/${resp.id}`)}
-                    >
-                      View Quote
-                    </Button>
-                    <Button
-                      variant="ghost"
-                      size="icon"
-                      className="h-6 w-6 text-muted-foreground hover:text-foreground"
-                      onClick={() => {
-                        startTransition(async () => {
-                          await acknowledgeQuoteResponse(resp.id);
-                        });
-                      }}
-                    >
-                      <X className="h-3.5 w-3.5" />
-                    </Button>
-                  </div>
-                </div>
-              ))}
-            </div>
-          </CardContent>
-        </Card>
-      )}
+                ))}
+              </div>
+            </CardContent>
+          </Card>
+        )}
 
-      {/* Recent Completed table */}
-      <Card className="border-0 shadow-sm">
-        <CardHeader className="pb-3">
-          <CardTitle className="text-base">Recent Completed</CardTitle>
-        </CardHeader>
-        <CardContent className="p-0">
-          <Table>
-            <TableHeader>
-              <TableRow>
-                <TableHead className="w-22.5">Date</TableHead>
-                <TableHead>Vehicle</TableHead>
-                <TableHead className="hidden sm:table-cell">Customer</TableHead>
-                <TableHead>Title</TableHead>
-                {stats.isAdmin && <TableHead className="w-22.5 text-right">Total</TableHead>}
-              </TableRow>
-            </TableHeader>
-            <TableBody>
-              {stats.recentServices.length === 0 ? (
-                <TableRow>
-                  <TableCell colSpan={stats.isAdmin ? 5 : 4} className="h-20 text-center text-muted-foreground">
-                    No completed services yet
-                  </TableCell>
-                </TableRow>
-              ) : (
-                stats.recentServices.map((s) => {
-                  const displayTotal = s.totalAmount > 0 ? s.totalAmount : s.cost;
-                  return (
-                    <TableRow
-                      key={s.id}
-                      className={`cursor-pointer transition-opacity ${navigatingId === s.id ? "opacity-50" : ""}`}
-                      onClick={() => {
-                        setNavigatingId(s.id);
-                        router.push(`/vehicles/${s.vehicle.id}/service/${s.id}`);
-                      }}
-                    >
-                      <TableCell className="font-mono text-xs">
-                        {formatDate(new Date(s.serviceDate))}
+        {/* Recent Completed table */}
+        {isVisible("recentCompleted") && (
+          <Card className="border-0 shadow-sm lg:col-span-2">
+            <CardHeader className="pb-3">
+              <CardTitle className="text-base">Recent Completed</CardTitle>
+            </CardHeader>
+            <CardContent className="p-0">
+              <Table>
+                <TableHeader>
+                  <TableRow>
+                    <TableHead className="w-22.5">Date</TableHead>
+                    <TableHead>Vehicle</TableHead>
+                    <TableHead className="hidden sm:table-cell">Customer</TableHead>
+                    <TableHead>Title</TableHead>
+                    {stats.isAdmin && <TableHead className="w-22.5 text-right">Total</TableHead>}
+                  </TableRow>
+                </TableHeader>
+                <TableBody>
+                  {stats.recentServices.length === 0 ? (
+                    <TableRow>
+                      <TableCell colSpan={stats.isAdmin ? 5 : 4} className="h-20 text-center text-muted-foreground">
+                        No completed services yet
                       </TableCell>
-                      <TableCell>
-                        <div>
-                          {s.vehicle.licensePlate && (
-                            <span className="font-mono text-sm">{s.vehicle.licensePlate}</span>
+                    </TableRow>
+                  ) : (
+                    stats.recentServices.map((s) => {
+                      const displayTotal = s.totalAmount > 0 ? s.totalAmount : s.cost;
+                      return (
+                        <TableRow
+                          key={s.id}
+                          className={`cursor-pointer transition-opacity ${navigatingId === s.id ? "opacity-50" : ""}`}
+                          onClick={() => {
+                            setNavigatingId(s.id);
+                            router.push(`/vehicles/${s.vehicle.id}/service/${s.id}`);
+                          }}
+                        >
+                          <TableCell className="font-mono text-xs">
+                            {formatDate(new Date(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.year} {s.vehicle.make} {s.vehicle.model}
+                              </p>
+                            </div>
+                          </TableCell>
+                          <TableCell className="hidden sm:table-cell text-muted-foreground">
+                            {s.vehicle.customer?.name || "-"}
+                          </TableCell>
+                          <TableCell className="font-medium">{s.title}</TableCell>
+                          {stats.isAdmin && (
+                            <TableCell className="text-right font-semibold">
+                              <span className="inline-flex items-center gap-2">
+                                {navigatingId === s.id && (
+                                  <Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
+                                )}
+                                {formatCurrency(displayTotal, currencyCode)}
+                              </span>
+                            </TableCell>
                           )}
-                          <p className="text-xs text-muted-foreground">
-                            {s.vehicle.year} {s.vehicle.make} {s.vehicle.model}
-                          </p>
-                        </div>
-                      </TableCell>
-                      <TableCell className="hidden sm:table-cell text-muted-foreground">
-                        {s.vehicle.customer?.name || "-"}
+                        </TableRow>
+                      );
+                    })
+                  )}
+                </TableBody>
+              </Table>
+            </CardContent>
+          </Card>
+        )}
+
+        {/* Active Jobs table */}
+        {isVisible("activeJobs") && (
+          <Card className="border-0 shadow-sm lg:col-span-2">
+            <CardHeader className="pb-3">
+              <CardTitle className="text-base">Active Jobs</CardTitle>
+            </CardHeader>
+            <CardContent className="p-0">
+              <Table>
+                <TableHeader>
+                  <TableRow>
+                    <TableHead>Vehicle</TableHead>
+                    <TableHead className="hidden sm:table-cell">Customer</TableHead>
+                    <TableHead>Title</TableHead>
+                    <TableHead className="w-27.5">Status</TableHead>
+                    <TableHead className="hidden md:table-cell">Tech</TableHead>
+                  </TableRow>
+                </TableHeader>
+                <TableBody>
+                  {stats.todaysServices.length === 0 ? (
+                    <TableRow>
+                      <TableCell colSpan={5} className="h-20 text-center text-muted-foreground">
+                        No active jobs
                       </TableCell>
-                      <TableCell className="font-medium">{s.title}</TableCell>
-                      {stats.isAdmin && (
-                        <TableCell className="text-right font-semibold">
+                    </TableRow>
+                  ) : (
+                    stats.todaysServices.map((s) => (
+                      <TableRow
+                        key={s.id}
+                        className={`cursor-pointer transition-opacity ${navigatingId === s.id ? "opacity-50" : ""}`}
+                        onClick={() => {
+                          setNavigatingId(s.id);
+                          router.push(`/vehicles/${s.vehicle.id}/service/${s.id}`);
+                        }}
+                      >
+                        <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.year} {s.vehicle.make} {s.vehicle.model}
+                            </p>
+                          </div>
+                        </TableCell>
+                        <TableCell className="hidden sm:table-cell text-muted-foreground">
+                          {s.vehicle.customer?.name || "-"}
+                        </TableCell>
+                        <TableCell className="font-medium">{s.title}</TableCell>
+                        <TableCell>
                           <span className="inline-flex items-center gap-2">
                             {navigatingId === s.id && (
                               <Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
                             )}
-                            {formatCurrency(displayTotal, currencyCode)}
+                            <Badge variant="outline" className={`text-xs ${statusColors[s.status] || ""}`}>
+                              {s.status}
+                            </Badge>
                           </span>
                         </TableCell>
-                      )}
-                    </TableRow>
-                  );
-                })
-              )}
-            </TableBody>
-          </Table>
-        </CardContent>
-      </Card>
-
-            {/* Active Jobs table */}
-      <Card className="border-0 shadow-sm">
-        <CardHeader className="pb-3">
-          <CardTitle className="text-base">Active Jobs</CardTitle>
-        </CardHeader>
-        <CardContent className="p-0">
-          <Table>
-            <TableHeader>
-              <TableRow>
-                <TableHead>Vehicle</TableHead>
-                <TableHead className="hidden sm:table-cell">Customer</TableHead>
-                <TableHead>Title</TableHead>
-                <TableHead className="w-27.5">Status</TableHead>
-                <TableHead className="hidden md:table-cell">Tech</TableHead>
-              </TableRow>
-            </TableHeader>
-            <TableBody>
-              {stats.todaysServices.length === 0 ? (
-                <TableRow>
-                  <TableCell colSpan={5} className="h-20 text-center text-muted-foreground">
-                    No active jobs
-                  </TableCell>
-                </TableRow>
-              ) : (
-                stats.todaysServices.map((s) => (
-                  <TableRow
-                    key={s.id}
-                    className={`cursor-pointer transition-opacity ${navigatingId === s.id ? "opacity-50" : ""}`}
-                    onClick={() => {
-                      setNavigatingId(s.id);
-                      router.push(`/vehicles/${s.vehicle.id}/service/${s.id}`);
-                    }}
-                  >
-                    <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.year} {s.vehicle.make} {s.vehicle.model}
-                        </p>
-                      </div>
-                    </TableCell>
-                    <TableCell className="hidden sm:table-cell text-muted-foreground">
-                      {s.vehicle.customer?.name || "-"}
-                    </TableCell>
-                    <TableCell className="font-medium">{s.title}</TableCell>
-                    <TableCell>
-                      <span className="inline-flex items-center gap-2">
-                        {navigatingId === s.id && (
-                          <Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
-                        )}
-                        <Badge variant="outline" className={`text-xs ${statusColors[s.status] || ""}`}>
-                          {s.status}
-                        </Badge>
-                      </span>
-                    </TableCell>
-                    <TableCell className="hidden md:table-cell text-sm text-muted-foreground">
-                      {s.techName || "-"}
-                    </TableCell>
-                  </TableRow>
-                ))
-              )}
-            </TableBody>
-          </Table>
-        </CardContent>
-      </Card>
+                        <TableCell className="hidden md:table-cell text-sm text-muted-foreground">
+                          {s.techName || "-"}
+                        </TableCell>
+                      </TableRow>
+                    ))
+                  )}
+                </TableBody>
+              </Table>
+            </CardContent>
+          </Card>
+        )}
+      </div>
     </div>
   );
 }

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

@@ -5,11 +5,19 @@ import { getVehiclesDueForService } from "@/features/vehicles/Actions/predictedM
 import { getInspectionsPaginated } from "@/features/inspections/Actions/inspectionActions";
 import { getQuoteRequests } from "@/features/inspections/Actions/quoteRequestActions";
 import { getQuoteResponses } from "@/features/quotes/Actions/quoteResponseActions";
+import { getAuthContext } from "@/lib/get-auth-context";
+import { getFeatures } from "@/lib/features";
+import { getRecentSmsThreads } from "@/features/sms/Actions/smsActions";
+import { getNotifications } from "@/features/notifications/Actions/notificationActions";
 import { DashboardClient } from "./dashboard-client";
 import { PageHeader } from "@/components/page-header";
 
 export default async function DashboardPage() {
-  const [result, settingsResult, remindersResult, maintenanceResult, inProgressResult, completedResult, quoteRequestsResult, quoteResponsesResult] = await Promise.all([
+  const auth = await getAuthContext();
+  const features = auth ? await getFeatures(auth.organizationId) : null;
+  const smsEnabled = features?.sms ?? false;
+
+  const [result, settingsResult, remindersResult, maintenanceResult, inProgressResult, completedResult, quoteRequestsResult, quoteResponsesResult, smsResult, notificationsResult] = await Promise.all([
     getDashboardStats(),
     getSettings([SETTING_KEYS.CURRENCY_CODE, SETTING_KEYS.UNIT_SYSTEM]),
     getUpcomingReminders(),
@@ -18,6 +26,8 @@ export default async function DashboardPage() {
     getInspectionsPaginated({ status: "completed", pageSize: 5 }),
     getQuoteRequests(),
     getQuoteResponses(),
+    smsEnabled ? getRecentSmsThreads(0, 5) : Promise.resolve(null),
+    getNotifications(),
   ]);
 
   if (!result.success || !result.data) {
@@ -36,6 +46,8 @@ export default async function DashboardPage() {
   const settings = settingsResult.success && settingsResult.data ? settingsResult.data : {};
   const currencyCode = settings[SETTING_KEYS.CURRENCY_CODE] || "USD";
   const unitSystem = (settings[SETTING_KEYS.UNIT_SYSTEM] || "imperial") as "metric" | "imperial";
+  const smsThreads = smsResult && smsResult.success && smsResult.data ? smsResult.data.threads : [];
+  const notifications = notificationsResult.success && notificationsResult.data ? notificationsResult.data.notifications : [];
 
   return (
     <>
@@ -51,6 +63,9 @@ export default async function DashboardPage() {
           completedInspections={completedResult.success && completedResult.data ? completedResult.data.records : []}
           quoteRequests={quoteRequestsResult.success && quoteRequestsResult.data ? quoteRequestsResult.data : []}
           quoteResponses={quoteResponsesResult.success && quoteResponsesResult.data ? quoteResponsesResult.data : []}
+          smsThreads={smsThreads}
+          smsEnabled={smsEnabled}
+          notifications={notifications}
         />
       </div>
     </>

+ 48 - 0
src/hooks/use-dashboard-visibility.ts

@@ -0,0 +1,48 @@
+"use client";
+
+import { useState, useCallback } from "react";
+
+export const DASHBOARD_CARDS = [
+  { id: "maintenance", label: "Predicted Maintenance" },
+  { id: "reminders", label: "Upcoming Reminders" },
+  { id: "inspections", label: "Inspections" },
+  { id: "quoteRequests", label: "Quote Requests" },
+  { id: "quoteResponses", label: "Quote Responses" },
+  { id: "sms", label: "Recent Messages" },
+  { id: "notifications", label: "Recent Notifications" },
+  { id: "recentCompleted", label: "Recent Completed" },
+  { id: "activeJobs", label: "Active Jobs" },
+] as const;
+
+const STORAGE_KEY = "torqvoice-dashboard-hidden";
+
+export function useDashboardVisibility() {
+  const [hidden, setHidden] = useState<Set<string>>(() => {
+    if (typeof window !== "undefined") {
+      try {
+        const stored = localStorage.getItem(STORAGE_KEY);
+        if (stored) return new Set(JSON.parse(stored));
+      } catch {
+        // ignore malformed data
+      }
+    }
+    return new Set();
+  });
+
+  const toggleCard = useCallback((id: string) => {
+    setHidden((prev) => {
+      const next = new Set(prev);
+      if (next.has(id)) {
+        next.delete(id);
+      } else {
+        next.add(id);
+      }
+      localStorage.setItem(STORAGE_KEY, JSON.stringify([...next]));
+      return next;
+    });
+  }, []);
+
+  const isVisible = useCallback((id: string) => !hidden.has(id), [hidden]);
+
+  return { toggleCard, isVisible };
+}