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,125 @@
/**
* @ai-summary Main dashboard screen component showing fleet overview
*/
import React from 'react';
import { SummaryCards, SummaryCardsSkeleton } from './SummaryCards';
import { VehicleAttention, VehicleAttentionSkeleton } from './VehicleAttention';
import { QuickActions, QuickActionsSkeleton } from './QuickActions';
import { useDashboardSummary, useVehiclesNeedingAttention } from '../hooks/useDashboardData';
import { GlassCard } from '../../../shared-minimal/components/mobile/GlassCard';
import { MobileScreen } from '../../../core/store';
import { Vehicle } from '../../vehicles/types/vehicles.types';
interface DashboardScreenProps {
onNavigate?: (screen: MobileScreen, metadata?: Record<string, any>) => void;
onVehicleClick?: (vehicle: Vehicle) => void;
}
export const DashboardScreen: React.FC<DashboardScreenProps> = ({
onNavigate,
onVehicleClick
}) => {
const { data: summary, isLoading: summaryLoading, error: summaryError } = useDashboardSummary();
const { data: vehiclesNeedingAttention, isLoading: attentionLoading, error: attentionError } = useVehiclesNeedingAttention();
// Error state
if (summaryError || attentionError) {
return (
<div className="space-y-4">
<GlassCard>
<div className="text-center py-12">
<div className="text-4xl mb-3"></div>
<h2 className="text-lg font-semibold text-slate-800 dark:text-slate-200 mb-2">
Unable to Load Dashboard
</h2>
<p className="text-slate-500 dark:text-slate-400 mb-4">
There was an error loading your dashboard data
</p>
<button
onClick={() => window.location.reload()}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
style={{ minHeight: '44px' }} // Touch target size
>
Retry
</button>
</div>
</GlassCard>
</div>
);
}
// Loading state
if (summaryLoading || attentionLoading || !summary || !vehiclesNeedingAttention) {
return (
<div className="space-y-6">
<SummaryCardsSkeleton />
<VehicleAttentionSkeleton />
<QuickActionsSkeleton />
</div>
);
}
// Empty state - no vehicles
if (summary.totalVehicles === 0) {
return (
<div className="space-y-6">
<GlassCard>
<div className="text-center py-12">
<div className="text-6xl mb-4">🚗</div>
<h2 className="text-xl font-semibold text-slate-800 dark:text-slate-200 mb-3">
Welcome to MotoVaultPro
</h2>
<p className="text-slate-500 dark:text-slate-400 mb-6 max-w-md mx-auto">
Get started by adding your first vehicle to track fuel logs, maintenance, and more
</p>
<button
onClick={() => onNavigate?.('Vehicles')}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
style={{ minHeight: '44px' }} // Touch target size
>
Add Your First Vehicle
</button>
</div>
</GlassCard>
</div>
);
}
// Main dashboard view
return (
<div className="space-y-6">
{/* Summary Cards */}
<SummaryCards summary={summary} />
{/* Vehicles Needing Attention */}
{vehiclesNeedingAttention && vehiclesNeedingAttention.length > 0 && (
<VehicleAttention
vehicles={vehiclesNeedingAttention}
onVehicleClick={(vehicleId) => {
const vehicle = vehiclesNeedingAttention.find(v => v.id === vehicleId);
if (vehicle && onVehicleClick) {
onVehicleClick(vehicle);
}
}}
/>
)}
{/* Quick Actions */}
<QuickActions
onAddVehicle={() => onNavigate?.('Vehicles')}
onLogFuel={() => onNavigate?.('Log Fuel')}
onViewMaintenance={() => onNavigate?.('Vehicles')} // Navigate to vehicles then maintenance
onViewVehicles={() => onNavigate?.('Vehicles')}
/>
{/* Footer Hint */}
<div className="text-center py-4">
<p className="text-xs text-slate-400 dark:text-slate-500">
Dashboard updates every 2 minutes
</p>
</div>
</div>
);
};