2026-07-14 12:03:19 +02:00
|
|
|
'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';
|
2026-07-19 19:47:39 +02:00
|
|
|
import { VehicleImages } from './VehicleImages';
|
|
|
|
|
import { VehicleActions } from './VehicleActions';
|
2026-07-14 12:03:19 +02:00
|
|
|
|
|
|
|
|
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>
|
|
|
|
|
|
2026-07-19 19:47:39 +02:00
|
|
|
<VehicleActions vehicle={vehicle} />
|
|
|
|
|
|
2026-07-14 12:03:19 +02:00
|
|
|
<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>
|
|
|
|
|
|
2026-07-19 19:47:39 +02:00
|
|
|
<VehicleImages vehicleId={vehicle.id} />
|
|
|
|
|
|
2026-07-14 12:03:19 +02:00
|
|
|
<MobileDeStatus vehicleId={vehicle.id} />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|