feat: add dashboard with vehicle fleet overview (refs #2)

Implements responsive dashboard showing:
- Summary cards (vehicle count, upcoming maintenance, recent fuel logs)
- Vehicles needing attention with priority highlighting
- Quick action buttons for navigation
- Loading skeletons and empty states
- Mobile-first responsive layout (320px to 1920px+)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-01 22:35:48 -06:00
parent 0b16b8307f
commit bcb39b9cda
8 changed files with 644 additions and 13 deletions

View File

@@ -0,0 +1,126 @@
/**
* @ai-summary Quick action buttons for common tasks
*/
import React from 'react';
import { GlassCard } from '../../../shared-minimal/components/mobile/GlassCard';
interface QuickAction {
id: string;
title: string;
description: string;
icon: string;
color: string;
bgColor: string;
onClick: () => void;
}
interface QuickActionsProps {
onAddVehicle: () => void;
onLogFuel: () => void;
onViewMaintenance: () => void;
onViewVehicles: () => void;
}
export const QuickActions: React.FC<QuickActionsProps> = ({
onAddVehicle,
onLogFuel,
onViewMaintenance,
onViewVehicles,
}) => {
const actions: QuickAction[] = [
{
id: 'add-vehicle',
title: 'Add Vehicle',
description: 'Register a new vehicle',
icon: '🚗',
color: 'text-blue-600',
bgColor: 'bg-blue-50',
onClick: onAddVehicle,
},
{
id: 'log-fuel',
title: 'Log Fuel',
description: 'Record a fuel purchase',
icon: '⛽',
color: 'text-green-600',
bgColor: 'bg-green-50',
onClick: onLogFuel,
},
{
id: 'view-maintenance',
title: 'Maintenance',
description: 'View maintenance records',
icon: '🔧',
color: 'text-orange-600',
bgColor: 'bg-orange-50',
onClick: onViewMaintenance,
},
{
id: 'view-vehicles',
title: 'My Vehicles',
description: 'View all vehicles',
icon: '📋',
color: 'text-purple-600',
bgColor: 'bg-purple-50',
onClick: onViewVehicles,
},
];
return (
<GlassCard padding="md">
<div className="mb-4">
<h3 className="text-lg font-semibold text-slate-800 dark:text-slate-200">
Quick Actions
</h3>
<p className="text-sm text-slate-500 dark:text-slate-400">
Common tasks and navigation
</p>
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
{actions.map((action) => (
<button
key={action.id}
onClick={action.onClick}
className={`p-4 rounded-2xl ${action.bgColor} dark:bg-opacity-10 border border-transparent hover:border-slate-200 dark:hover:border-slate-700 hover:shadow-md transition-all duration-200 text-left min-h-[100px] sm:min-h-[120px] flex flex-col`}
style={{ minHeight: '44px' }} // Ensure touch target size
>
<div className="text-3xl mb-2">{action.icon}</div>
<div className="flex-1">
<h4 className={`font-semibold ${action.color} dark:opacity-90 text-sm mb-1`}>
{action.title}
</h4>
<p className="text-xs text-slate-500 dark:text-slate-400 hidden sm:block">
{action.description}
</p>
</div>
</button>
))}
</div>
</GlassCard>
);
};
export const QuickActionsSkeleton: React.FC = () => {
return (
<GlassCard padding="md">
<div className="mb-4">
<div className="h-6 bg-slate-100 dark:bg-slate-800 rounded animate-pulse w-32 mb-2" />
<div className="h-4 bg-slate-100 dark:bg-slate-800 rounded animate-pulse w-48" />
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
{[1, 2, 3, 4].map((i) => (
<div
key={i}
className="p-4 rounded-2xl bg-slate-50 dark:bg-slate-800 min-h-[100px] sm:min-h-[120px]"
>
<div className="w-8 h-8 bg-slate-100 dark:bg-slate-700 rounded animate-pulse mb-2" />
<div className="h-4 bg-slate-100 dark:bg-slate-700 rounded animate-pulse w-20 mb-2" />
<div className="h-3 bg-slate-100 dark:bg-slate-700 rounded animate-pulse w-full hidden sm:block" />
</div>
))}
</div>
</GlassCard>
);
};