import AddIcon from "@mui/icons-material/Add"; import DeleteIcon from "@mui/icons-material/Delete"; import EditIcon from "@mui/icons-material/Edit"; import { Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, IconButton, InputAdornment, MenuItem, Paper, Table, TableBody, TableCell, TableHead, TableRow, TextField, Tooltip, Typography } from "@mui/material"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { fetchContracts, removeContract } from "../api/contracts"; import PageHeader from "../components/PageHeader"; import { useSnackbar } from "../hooks/useSnackbar"; import { Contract } from "../types"; import { formatCurrency, formatDate } from "../utils/date"; export default function ContractsList() { const navigate = useNavigate(); const queryClient = useQueryClient(); const { showMessage } = useSnackbar(); const { t } = useTranslation(); const [contractToDelete, setContractToDelete] = useState(null); const { data: contracts, isLoading, isError } = useQuery({ queryKey: ["contracts", "list"], queryFn: () => fetchContracts({ limit: 500 }) }); const [search, setSearch] = useState(""); const [category, setCategory] = useState("all"); const categories = useMemo(() => { const values = new Set(); contracts?.forEach((contract) => { if (contract.category) values.add(contract.category); }); return Array.from(values).sort(); }, [contracts]); const normalizedContracts = useMemo(() => { if (!contracts) return [] as Contract[]; if (Array.isArray(contracts)) return contracts as Contract[]; if (typeof (contracts as any).results === "object" && Array.isArray((contracts as any).results)) { return (contracts as any).results as Contract[]; } return [] as Contract[]; }, [contracts]); const filtered = useMemo(() => { return normalizedContracts.filter((contract) => { const searchMatch = !search || [contract.title, contract.provider, contract.notes, contract.category] .filter(Boolean) .some((field) => field!.toLowerCase().includes(search.toLowerCase())); const categoryMatch = category === "all" || contract.category === category; return searchMatch && categoryMatch; }); }, [contracts, search, category]); const deleteMutation = useMutation({ mutationFn: (contractId: number) => removeContract(contractId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["contracts"] }); showMessage(t("contracts.deleted"), "success"); }, onError: (error: Error) => showMessage(error.message ?? t("contracts.deleteError"), "error") }); const handleDeleteConfirm = () => { if (!contractToDelete) return; deleteMutation.mutate(contractToDelete.id); setContractToDelete(null); }; return ( <> } onClick={() => navigate("/contracts/new")}> {t("contracts.new")} } /> setSearch(event.target.value)} sx={{ flex: { xs: "1 1 100%", md: "1 1 320px" } }} InputProps={{ startAdornment: 🔍 }} /> setCategory(event.target.value)} sx={{ width: 200 }} > {t("contracts.filterAll")} {categories.map((item) => ( {item} ))} {t("contracts.columns.title")} {t("contracts.columns.provider")} {t("contracts.columns.category")} {t("contracts.columns.price")} {t("contracts.columns.end")} {t("contracts.columns.tags")} {t("contracts.columns.actions")} {isLoading && ( {t("contracts.loading")} )} {isError && ( {t("dashboard.contractsError")} )} {!isLoading && !isError && filtered.length === 0 && ( {t("contracts.empty")} )} {filtered.map((contract) => ( navigate(`/contracts/${contract.id}`)} > {contract.title} #{contract.id} {contract.provider ?? "–"} {contract.category ?? "–"} {formatCurrency(contract.price, contract.currency ?? "EUR")} {formatDate(contract.contractEndDate)} {(contract.tags ?? []).map((tag) => ( ))} { event.stopPropagation(); navigate(`/contracts/${contract.id}/edit`); }} > { event.stopPropagation(); setContractToDelete(contract); }} > ))}
setContractToDelete(null)} aria-labelledby="delete-contract-title" > {t("contracts.deleteTitle")} {t("contracts.deleteConfirm", { title: contractToDelete?.title ?? "" })} ); }