MVP with new UX

This commit is contained in:
Eric Gullickson
2025-08-09 17:45:54 -05:00
parent 8f5117a4e2
commit d60c3ec00e
18 changed files with 1572 additions and 573 deletions

View File

@@ -1,11 +1,12 @@
/**
* @ai-summary Vehicle card component
* @ai-summary Vehicle card component with Material Design 3
*/
import React from 'react';
import { Card, CardContent, CardActionArea, Box, Typography, IconButton } from '@mui/material';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import { Vehicle } from '../types/vehicles.types';
import { Card } from '../../../shared-minimal/components/Card';
import { Button } from '../../../shared-minimal/components/Button';
interface VehicleCardProps {
vehicle: Vehicle;
@@ -14,51 +15,96 @@ interface VehicleCardProps {
onSelect: (id: string) => void;
}
const CarThumb: React.FC<{ color?: string }> = ({ color = "#F2EAEA" }) => (
<Box
sx={{
height: 96,
bgcolor: color,
borderRadius: 2,
mb: 2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
/>
);
export const VehicleCard: React.FC<VehicleCardProps> = ({
vehicle,
onEdit,
onDelete,
onSelect,
}) => {
const displayName = vehicle.nickname ||
`${vehicle.year} ${vehicle.make} ${vehicle.model}`;
return (
<Card className="hover:shadow-md transition-shadow cursor-pointer" onClick={() => onSelect(vehicle.id)}>
<div className="flex justify-between items-start">
<div className="flex-1">
<h3 className="text-lg font-semibold text-gray-900">
{vehicle.nickname || `${vehicle.year} ${vehicle.make} ${vehicle.model}`}
</h3>
<p className="text-sm text-gray-500 mt-1">VIN: {vehicle.vin}</p>
<Card
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
'&:hover': {
boxShadow: 3,
},
transition: 'box-shadow 0.2s ease-in-out'
}}
>
<CardActionArea
onClick={() => onSelect(vehicle.id)}
sx={{ flexGrow: 1 }}
>
<CardContent>
<CarThumb color={vehicle.color || "#F2EAEA"} />
<Typography variant="h6" sx={{ fontWeight: 700, mb: 1 }}>
{displayName}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 0.5 }}>
VIN: {vehicle.vin}
</Typography>
{vehicle.licensePlate && (
<p className="text-sm text-gray-500">License: {vehicle.licensePlate}</p>
<Typography variant="body2" color="text.secondary" sx={{ mb: 0.5 }}>
License: {vehicle.licensePlate}
</Typography>
)}
<p className="text-sm text-gray-600 mt-2">
<Typography variant="body2" color="text.primary" sx={{ mt: 1, fontWeight: 500 }}>
Odometer: {vehicle.odometerReading.toLocaleString()} miles
</p>
</div>
<div className="flex gap-2">
<Button
size="sm"
variant="secondary"
onClick={(e) => {
e.stopPropagation();
onEdit(vehicle);
}}
>
Edit
</Button>
<Button
size="sm"
variant="danger"
onClick={(e) => {
e.stopPropagation();
onDelete(vehicle.id);
}}
>
Delete
</Button>
</div>
</div>
</Typography>
</CardContent>
</CardActionArea>
<Box sx={{
display: 'flex',
justifyContent: 'flex-end',
gap: 1,
p: 2,
pt: 0
}}>
<IconButton
size="small"
onClick={(e) => {
e.stopPropagation();
onEdit(vehicle);
}}
sx={{ color: 'text.secondary' }}
>
<EditIcon fontSize="small" />
</IconButton>
<IconButton
size="small"
onClick={(e) => {
e.stopPropagation();
onDelete(vehicle.id);
}}
sx={{ color: 'error.main' }}
>
<DeleteIcon fontSize="small" />
</IconButton>
</Box>
</Card>
);
};