infoalloggi-monorepo/apps/infoalloggi/src/components/tables/payments-table.tsx
2025-08-29 16:18:32 +02:00

256 lines
6.7 KiB
TypeScript

import type { ColumnDef } from "@tanstack/react-table";
import { EllipsisVertical, Trash2, Wrench } from "lucide-react";
import { DataTable } from "~/components/custom_ui/data-table";
import { DataTableColumnHeader } from "~/components/custom_ui/dataTable-header";
import type {
PinnedFiltro,
SearchFiltro,
} from "~/components/custom_ui/dataTable-toolbar";
import { Button } from "~/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
import { formatCurrency } from "~/lib/utils";
import PaymentStatusEnum from "~/schemas/public/PaymentStatusEnum";
import type { OrdiniWPagamenti } from "~/server/controllers/ordini.controller";
import { api } from "~/utils/api";
type PaymentsTableProps = {
data: OrdiniWPagamenti["pagamenti"];
isAdmin: boolean;
isUserView?: boolean;
};
const StatusMapping = (status: PaymentStatusEnum | null) => {
switch (status) {
case PaymentStatusEnum.success:
return "Successo";
case PaymentStatusEnum.processing:
return "In lavorazione";
case PaymentStatusEnum.failed:
return "Errore";
default:
return "Non completato";
}
};
const EnumFromString = (value: string) => {
switch (value) {
case "Successo":
return PaymentStatusEnum.success;
case "In lavorazione":
return PaymentStatusEnum.processing;
case "Errore":
return PaymentStatusEnum.failed;
default:
return null;
}
};
export const PaymentsTable = ({
data,
isAdmin,
isUserView = false,
}: PaymentsTableProps) => {
const tabledata = data.map((row) => {
return {
actions: "Azioni",
amount: row.amount_cent,
id: row.id,
paid_at: row.paid_at,
raw_status: row.paymentstatus,
status: StatusMapping(row.paymentstatus),
};
});
const columns_titles = {
actions: "Azioni",
amount: "Importo",
paid_at: "Pagato il",
status: "Stato",
username: "Utente",
};
const status_options = [
{ label: "Inutilizzato", value: "Inutilizzato" },
{ label: "Successo", value: "Successo" },
{ label: "In lavorazione", value: "In lavorazione" },
{ label: "Errore", value: "Errore" },
];
const utils = api.useUtils();
const { mutate: update } = api.servizio.updatePagamento.useMutation({
onSuccess: async () => {
await utils.servizio.getAllPagamenti.invalidate();
await utils.servizio.getOrdini.invalidate();
await utils.servizio.getOrdineById.invalidate({
ordineId: data[0]?.ordine_id,
});
if ((!isAdmin || isUserView) && data[0]) {
await utils.servizio.getUserPagamenti.invalidate({
userId: data[0].userid,
});
}
},
});
const { mutate: remove } = api.servizio.removePagamento.useMutation({
onSuccess: async () => {
await utils.servizio.getAllPagamenti.invalidate();
await utils.servizio.getOrdini.invalidate();
await utils.servizio.getOrdineById.invalidate({
ordineId: data[0]?.ordine_id,
});
if ((!isAdmin || isUserView) && data[0]) {
await utils.servizio.getUserPagamenti.invalidate({
userId: data[0].userid,
});
}
},
});
type Column = (typeof tabledata)[number];
const columns: ColumnDef<Column>[] = [
{
accessorKey: "paid_at",
cell: ({ row }) => {
const date = row.getValue("paid_at");
return new Date(date as Date).toLocaleDateString();
},
enableHiding: false,
header: ({ column }) => (
<DataTableColumnHeader column={column} title={columns_titles.paid_at} />
),
sortingFn: "datetime",
},
{
accessorKey: "status",
filterFn: (row, id, value) => {
return value.includes(row.getValue(id));
},
header: ({ column }) => (
<DataTableColumnHeader column={column} title={columns_titles.status} />
),
},
{
accessorKey: "amount",
cell: ({ row }) => {
return <span>{formatCurrency(row.original.amount / 100)}</span>;
},
header: ({ column }) => (
<DataTableColumnHeader column={column} title={columns_titles.amount} />
),
},
{
cell: ({ row }) => {
const data = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button className="size-8 p-0" variant="ghost">
<span className="sr-only">Open menu</span>
<EllipsisVertical />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Azioni</DropdownMenuLabel>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<Wrench />
<span>Aggiorna status</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuRadioGroup
onValueChange={(v) => {
update({
data: { paymentstatus: EnumFromString(v) },
pagamentoId: data.id,
});
}}
value={StatusMapping(data.raw_status)}
>
<DropdownMenuRadioItem value="Inutilizzato">
Inutilizzato
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="Successo">
Successo
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="In lavorazione">
In lavorazione
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="Errore">
Errore
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<DropdownMenuSeparator />
<DropdownMenuItem
aria-label="Elimina Pagamento"
className="flex items-center gap-3 hover:cursor-pointer"
onClick={() =>
confirm("Sei sicuro di voler eliminare il servizio?") &&
remove({ pagamentoId: data.id })
}
>
<Trash2 className="size-6" />
Elimina Pagamento
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
enableHiding: false,
id: "actions",
},
];
const searchFiltro: SearchFiltro | undefined =
isAdmin && !isUserView
? {
columnName: "username",
placeholder: "Cerca utente...",
}
: undefined;
const pinnedFiltri: PinnedFiltro[] = [
{
columnName: "status",
options: status_options,
title: "Status",
},
];
return (
<div className="w-full py-6">
<DataTable
columns={columns}
columns_titles={columns_titles}
data={tabledata}
defaultColumnVisibility={{
actions: true,
amount: true,
packid: true,
paid_at: true,
status: true,
username: isAdmin && !isUserView,
}}
defaultSort={[{ desc: true, id: "paid_at" }]}
pinnedFiltri={pinnedFiltri}
searchColumn={searchFiltro}
/>
</div>
);
};