feat: add vehicle edit page, image gallery with upload, print and CSV export

This commit is contained in:
2026-07-19 19:47:39 +02:00
parent 58fcf4a722
commit 563127c75a
9 changed files with 499 additions and 5 deletions
+33 -3
View File
@@ -11,6 +11,7 @@ import {
type VehicleListParams,
} from '@/lib/vehicles';
import type { PaginatedResponse } from '@/lib/api';
import { exportVehiclesToCsv } from './VehicleActions';
const VEHICLE_TYPES = ['lkw', 'pkw', 'baumaschine', 'stapler', 'transporter'];
const AVAILABILITY_OPTIONS = ['available', 'reserved', 'sold'];
@@ -31,6 +32,7 @@ export function VehicleList() {
const [pageSize] = useState(20);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [exporting, setExporting] = useState(false);
const [filters, setFilters] = useState<VehicleListParams>({
page: 1,
@@ -69,6 +71,23 @@ export function VehicleList() {
setFilters(prev => ({ ...prev, page: newPage }));
};
const handleExportAllCsv = async () => {
setExporting(true);
setError(null);
try {
const data: PaginatedResponse<VehicleResponse> = await listVehicles({
page: 1,
page_size: 1000,
});
exportVehiclesToCsv(data.items);
} catch (err: unknown) {
const apiErr = err as { error?: { message?: string } };
setError(apiErr?.error?.message || 'Failed to export vehicles');
} finally {
setExporting(false);
}
};
const columns = [
{
key: 'make',
@@ -101,9 +120,20 @@ export function VehicleList() {
<div data-testid="vehicle-list" className="space-y-4">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold text-text">Fahrzeuge</h1>
<Button onClick={() => router.push('/de/fahrzeuge/neu')}>
+ Neues Fahrzeug
</Button>
<div className="flex gap-2">
<Button
variant="secondary"
onClick={handleExportAllCsv}
loading={exporting}
disabled={exporting}
data-testid="export-all-csv-button"
>
Alle als CSV exportieren
</Button>
<Button onClick={() => router.push('/de/fahrzeuge/neu')}>
+ Neues Fahrzeug
</Button>
</div>
</div>
<div data-testid="vehicle-filters" className="flex flex-wrap gap-3 p-4 bg-surface rounded-lg border border-border">