import { useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; import Forbidden from "../components/Forbidden"; import { Skeleton } from "boneyard-js/react"; import VehiclesFixture from "../fixtures/VehiclesFixture"; import { motion, AnimatePresence } from "framer-motion"; import ConfirmModal from "../components/ConfirmModal"; import useModalLock from "../hooks/useModalLock"; import { formatKm } from "../utils/formatters"; import apiFetch from "../utils/api"; import FormField from "../components/FormField"; import { vehicleListOptions } from "../lib/queries/vehicles"; const API_BASE = "/api/admin"; interface Vehicle { id: number; spz: string; name: string; brand?: string; model?: string; initial_km: number; current_km: number; trip_count: number; is_active: boolean | number; } interface VehicleForm { spz: string; name: string; brand: string; model: string; initial_km: number; is_active: boolean; } export default function Vehicles() { const alert = useAlert(); const { hasPermission } = useAuth(); const queryClient = useQueryClient(); const { data: vehicles = [], isPending } = useQuery(vehicleListOptions()); const [showModal, setShowModal] = useState(false); const [editingVehicle, setEditingVehicle] = useState(null); const [form, setForm] = useState({ spz: "", name: "", brand: "", model: "", initial_km: 0, is_active: true, }); const [errors, setErrors] = useState>({}); const [deleteConfirm, setDeleteConfirm] = useState<{ show: boolean; vehicle: Vehicle | null; }>({ show: false, vehicle: null }); useModalLock(showModal); if (!hasPermission("vehicles.manage")) return ; const openCreateModal = () => { setEditingVehicle(null); setForm({ spz: "", name: "", brand: "", model: "", initial_km: 0, is_active: true, }); setErrors({}); setShowModal(true); }; const openEditModal = (vehicle: Vehicle) => { setEditingVehicle(vehicle); setForm({ spz: vehicle.spz, name: vehicle.name, brand: vehicle.brand || "", model: vehicle.model || "", initial_km: vehicle.initial_km, is_active: Boolean(vehicle.is_active), }); setErrors({}); setShowModal(true); }; const handleSubmit = async () => { const newErrors: Record = {}; if (!form.spz) newErrors.spz = "Zadejte SPZ"; if (!form.name) newErrors.name = "Zadejte název"; setErrors(newErrors); if (Object.keys(newErrors).length > 0) return; try { const url = editingVehicle ? `${API_BASE}/vehicles/${editingVehicle.id}` : `${API_BASE}/vehicles`; const method = editingVehicle ? "PUT" : "POST"; const response = await apiFetch(url, { method, headers: { "Content-Type": "application/json" }, body: JSON.stringify(form), }); const result = await response.json(); if (result.success) { setShowModal(false); queryClient.invalidateQueries({ queryKey: ["vehicles"] }); queryClient.invalidateQueries({ queryKey: ["trips"] }); alert.success(result.message); } else { alert.error(result.error); } } catch { alert.error("Chyba připojení"); } }; const handleDelete = async () => { if (!deleteConfirm.vehicle) return; try { const response = await apiFetch( `${API_BASE}/vehicles/${deleteConfirm.vehicle.id}`, { method: "DELETE", }, ); const result = await response.json(); if (result.success) { setDeleteConfirm({ show: false, vehicle: null }); queryClient.invalidateQueries({ queryKey: ["vehicles"] }); queryClient.invalidateQueries({ queryKey: ["trips"] }); alert.success(result.message); } else { alert.error(result.error); } } catch { alert.error("Chyba připojení"); } }; const toggleActive = async (vehicle: Vehicle) => { try { const response = await apiFetch(`${API_BASE}/vehicles/${vehicle.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ is_active: !vehicle.is_active }), }); const result = await response.json(); if (result.success) { queryClient.invalidateQueries({ queryKey: ["vehicles"] }); queryClient.invalidateQueries({ queryKey: ["trips"] }); alert.success( vehicle.is_active ? "Vozidlo bylo deaktivováno" : "Vozidlo bylo aktivováno", ); } else { alert.error(result.error); } } catch { alert.error("Chyba připojení"); } }; return ( }>

Správa vozidel

{vehicles.length === 0 && (

Zatím nejsou žádná vozidla.

)} {vehicles.length > 0 && (
{vehicles.map((vehicle) => ( ))}
SPZ Název Značka / Model Počáteční km Aktuální km Počet jízd Stav Akce
{vehicle.spz} {vehicle.name} {vehicle.brand || vehicle.model ? `${vehicle.brand || ""} ${vehicle.model || ""}`.trim() : "—"} {formatKm(vehicle.initial_km)} km {formatKm(vehicle.current_km)} km {vehicle.trip_count}
)}
{/* Add/Edit Modal */} {showModal && (
setShowModal(false)} />

{editingVehicle ? "Upravit vozidlo" : "Přidat vozidlo"}

{ setForm({ ...form, spz: e.target.value.toUpperCase(), }); setErrors((prev) => ({ ...prev, spz: "" })); }} className="admin-form-input" placeholder="1AB 2345" aria-invalid={!!errors.spz} /> { setForm({ ...form, name: e.target.value }); setErrors((prev) => ({ ...prev, name: "" })); }} className="admin-form-input" placeholder="Služební #1" aria-invalid={!!errors.name} />
setForm({ ...form, brand: e.target.value }) } className="admin-form-input" placeholder="Škoda" /> setForm({ ...form, model: e.target.value }) } className="admin-form-input" placeholder="Octavia Combi" />
setForm({ ...form, initial_km: parseInt(e.target.value) || 0, }) } className="admin-form-input" min="0" /> Stav tachometru při přidání vozidla
)} {/* Delete Confirmation */} setDeleteConfirm({ show: false, vehicle: null })} onConfirm={handleDelete} title="Smazat vozidlo" message={ deleteConfirm.vehicle ? `Opravdu chcete smazat vozidlo ${deleteConfirm.vehicle.spz} - ${deleteConfirm.vehicle.name}?` : "" } confirmText="Smazat" confirmVariant="danger" />
); }