Files
erp-nutzfahrzeuge/frontend/components/vehicles/VehicleDetail.tsx
T

150 lines
5.1 KiB
TypeScript
Raw Normal View History

'use client';
import { useState, useEffect, useCallback } from 'react';
import { useRouter } from 'next/navigation';
import { Button } from '@/components/ui/Button';
import {
getVehicle,
deleteVehicle,
type VehicleResponse,
} from '@/lib/vehicles';
import { MobileDeStatus } from './MobileDeStatus';
import { VehicleImages } from './VehicleImages';
import { VehicleActions } from './VehicleActions';
interface VehicleDetailProps {
vehicleId: string;
}
function formatPrice(price: number): string {
return new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(price);
}
function formatDate(dateStr?: string): string {
if (!dateStr) return '—';
return new Date(dateStr).toLocaleDateString('de-DE');
}
export function VehicleDetail({ vehicleId }: VehicleDetailProps) {
const router = useRouter();
const [vehicle, setVehicle] = useState<VehicleResponse | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchVehicle = useCallback(async () => {
setLoading(true);
setError(null);
try {
const data = await getVehicle(vehicleId);
setVehicle(data);
} catch (err: unknown) {
const apiErr = err as { error?: { message?: string } };
setError(apiErr?.error?.message || 'Failed to load vehicle');
} finally {
setLoading(false);
}
}, [vehicleId]);
useEffect(() => {
fetchVehicle();
}, [fetchVehicle]);
const handleDelete = async () => {
if (!vehicle) return;
if (!confirm('Delete this vehicle?')) return;
try {
await deleteVehicle(vehicle.id);
router.push('/de/fahrzeuge');
} catch (err: unknown) {
const apiErr = err as { error?: { message?: string } };
setError(apiErr?.error?.message || 'Failed to delete vehicle');
}
};
if (loading) {
return (
<div data-testid="vehicle-detail-loading" className="text-center py-8 text-text-muted">
Loading vehicle...
</div>
);
}
if (error) {
return (
<div data-testid="vehicle-detail-error" className="p-4 bg-error/10 text-error rounded-lg">
{error}
</div>
);
}
if (!vehicle) {
return (
<div data-testid="vehicle-detail-not-found" className="text-center py-8 text-text-muted">
Vehicle not found
</div>
);
}
const fields: { label: string; value: string | number | undefined | null }[] = [
{ label: 'Make', value: vehicle.make },
{ label: 'Model', value: vehicle.model },
{ label: 'FIN', value: vehicle.fin },
{ label: 'Year', value: vehicle.year },
{ label: 'First Registration', value: formatDate(vehicle.first_registration) },
{ label: 'Power (kW)', value: vehicle.power_kw },
{ label: 'Power (HP)', value: vehicle.power_hp },
{ label: 'Fuel Type', value: vehicle.fuel_type },
{ label: 'Transmission', value: vehicle.transmission },
{ label: 'Color', value: vehicle.color },
{ label: 'Condition', value: vehicle.condition },
{ label: 'Location', value: vehicle.location },
{ label: 'Availability', value: vehicle.availability },
{ label: 'Price', value: formatPrice(vehicle.price) },
{ label: 'Vehicle Type', value: vehicle.vehicle_type },
{ label: 'LKW Type', value: vehicle.lkw_type },
{ label: 'Machine Type', value: vehicle.machine_type },
{ label: 'Body Type', value: vehicle.body_type },
{ label: 'Operating Hours', value: vehicle.operating_hours },
{ label: 'Operating Hours Unit', value: vehicle.operating_hours_unit },
{ label: 'Mileage (km)', value: vehicle.mileage_km },
{ label: 'Description', value: vehicle.description },
{ label: 'Created At', value: formatDate(vehicle.created_at) },
{ label: 'Updated At', value: formatDate(vehicle.updated_at) },
];
return (
<div data-testid="vehicle-detail" className="space-y-6">
<div className="flex items-center justify-between">
<h1 data-testid="vehicle-detail-title" className="text-2xl font-bold text-text">
{vehicle.make} {vehicle.model}
</h1>
<div className="flex gap-2">
<Button variant="secondary" onClick={() => router.push(`/de/fahrzeuge/${vehicle.id}/bearbeiten`)}>
Edit
</Button>
<Button variant="danger" onClick={handleDelete} data-testid="delete-button">
Delete
</Button>
</div>
</div>
<VehicleActions vehicle={vehicle} />
<div data-testid="vehicle-detail-fields" className="grid grid-cols-2 md:grid-cols-3 gap-4 p-6 bg-surface rounded-lg border border-border">
{fields.map(field => (
<div key={field.label} className="space-y-1">
<dt className="text-sm font-medium text-text-muted">{field.label}</dt>
<dd data-testid={`field-${field.label.toLowerCase().replace(/\s+/g, '_')}`} className="text-text">
{field.value !== null && field.value !== undefined && field.value !== '' ? field.value : '—'}
</dd>
</div>
))}
</div>
<VehicleImages vehicleId={vehicle.id} />
<MobileDeStatus vehicleId={vehicle.id} />
</div>
);
}