import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import IconButton from "@mui/material/IconButton"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; import Forbidden from "../components/Forbidden"; import { formatKm } from "../utils/formatters"; import { vehicleListOptions } from "../lib/queries/vehicles"; import { useApiMutation, apiErrorMessage } from "../lib/queries/mutations"; import { Button, Card, DataTable, Modal, ConfirmDialog, Field, TextField, SwitchField, StatusChip, LoadingState, PageEnter, type DataColumn, } from "../ui"; 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; } const PlusIcon = ( ); const EditIcon = ( ); const DeleteIcon = ( ); export default function Vehicles() { const alert = useAlert(); const { hasPermission } = useAuth(); const { data: vehicles = [], isPending } = useQuery({ ...vehicleListOptions(), select: (data) => data as unknown as Vehicle[], }); 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 }); const [saving, setSaving] = useState(false); const saveVehicle = useApiMutation({ url: () => editingVehicle ? `${API_BASE}/vehicles/${editingVehicle.id}` : `${API_BASE}/vehicles`, method: () => (editingVehicle ? "PUT" : "POST"), invalidate: ["vehicles", "trips"], onSuccess: () => { setShowModal(false); alert.success( editingVehicle ? "Vozidlo bylo upraveno" : "Vozidlo bylo vytvořeno", ); }, }); const deleteVehicle = useApiMutation({ url: (id) => `${API_BASE}/vehicles/${id}`, method: () => "DELETE", invalidate: ["vehicles", "trips"], onSuccess: () => { setDeleteConfirm({ show: false, vehicle: null }); alert.success("Vozidlo bylo smazáno"); }, }); const toggleVehicle = useApiMutation< { id: number; is_active: boolean }, void >({ url: (input) => `${API_BASE}/vehicles/${input.id}`, method: () => "PUT", invalidate: ["vehicles", "trips"], onSuccess: (_data, input) => { alert.success( input.is_active ? "Vozidlo bylo aktivováno" : "Vozidlo bylo deaktivováno", ); }, onError: (err) => { alert.error(apiErrorMessage(err, "Nepodařilo se změnit stav vozidla")); }, }); 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; setSaving(true); try { await saveVehicle.mutateAsync(form); } catch (e) { alert.error(e instanceof Error ? e.message : "Chyba připojení"); } finally { setSaving(false); } }; const handleDelete = async () => { if (!deleteConfirm.vehicle) return; try { await deleteVehicle.mutateAsync(deleteConfirm.vehicle.id); } catch (e) { alert.error(e instanceof Error ? e.message : "Chyba připojení"); } }; const toggleActive = (vehicle: Vehicle) => { toggleVehicle.mutate({ id: vehicle.id, is_active: !vehicle.is_active, }); }; if (isPending) { return ; } const columns: DataColumn[] = [ { key: "spz", header: "SPZ", mono: true, bold: true, render: (v) => v.spz }, { key: "name", header: "Název", render: (v) => v.name }, { key: "brand", header: "Značka / Model", render: (v) => v.brand || v.model ? `${v.brand || ""} ${v.model || ""}`.trim() : "—", }, { key: "initial_km", header: "Počáteční km", align: "right", mono: true, render: (v) => `${formatKm(v.initial_km)} km`, }, { key: "current_km", header: "Aktuální km", align: "right", mono: true, bold: true, render: (v) => `${formatKm(v.current_km)} km`, }, { key: "trips", header: "Počet jízd", align: "right", mono: true, render: (v) => v.trip_count, }, { key: "status", header: "Stav", render: (v) => ( toggleActive(v)} title="Kliknutím přepnete stav" /> ), }, { key: "actions", header: "Akce", align: "right", render: (v) => ( openEditModal(v)} aria-label="Upravit" title="Upravit" > {EditIcon} setDeleteConfirm({ show: true, vehicle: v })} aria-label="Smazat" title="Smazat" > {DeleteIcon} ), }, ]; return ( Správa vozidel columns={columns} rows={vehicles} rowKey={(v) => v.id} rowInactive={(v) => !v.is_active} empty={ Zatím nejsou žádná vozidla. } /> setShowModal(false)} onSubmit={handleSubmit} title={editingVehicle ? "Upravit vozidlo" : "Přidat vozidlo"} loading={saving} > { setForm({ ...form, spz: e.target.value.toUpperCase() }); setErrors((prev) => ({ ...prev, spz: "" })); }} /> { setForm({ ...form, name: e.target.value }); setErrors((prev) => ({ ...prev, name: "" })); }} /> setForm({ ...form, brand: e.target.value })} /> setForm({ ...form, model: e.target.value })} /> setForm({ ...form, initial_km: parseInt(e.target.value, 10) || 0, }) } /> setForm({ ...form, is_active: v })} /> 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" /> ); }