infoalloggi-monorepo/apps/infoalloggi/src/components/tables/users-table.tsx

321 lines
8.2 KiB
TypeScript
Raw Normal View History

2025-08-04 17:45:44 +02:00
import type { ColumnDef } from "@tanstack/react-table";
2025-08-28 18:27:07 +02:00
import {
Crown,
Mail,
MessagesSquare,
MoreHorizontal,
Paperclip,
Search,
ShoppingBag,
Tickets,
User,
UserCog,
} from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/router";
import toast from "react-hot-toast";
2025-08-04 17:45:44 +02:00
import { DataTable } from "~/components/custom_ui/data-table";
import { DataTableColumnHeader } from "~/components/custom_ui/dataTable-header";
import type {
2025-08-28 18:27:07 +02:00
PinnedFiltro,
SearchFiltro,
2025-08-04 17:45:44 +02:00
} from "~/components/custom_ui/dataTable-toolbar";
2025-08-28 18:27:07 +02:00
import { Button } from "~/components/ui/button";
2025-08-04 17:45:44 +02:00
import {
2025-08-28 18:27:07 +02:00
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
2025-08-04 17:45:44 +02:00
} from "~/components/ui/dropdown-menu";
2025-08-28 18:27:07 +02:00
import { UserAvatar } from "~/components/user_avatar";
2025-08-04 17:45:44 +02:00
import { cn } from "~/lib/utils";
2025-08-28 18:27:07 +02:00
import type { Users } from "~/schemas/public/Users";
2025-08-04 17:45:44 +02:00
import { api } from "~/utils/api";
2025-08-28 18:27:07 +02:00
2025-08-04 17:45:44 +02:00
type UserWChatInfo = Pick<
2025-08-28 18:27:07 +02:00
Users,
"id" | "username" | "email" | "isAdmin" | "isBlocked" | "created_at"
2025-08-04 17:45:44 +02:00
> & { chatid: string | null };
export const UsersTable = (props: { data: UserWChatInfo[] }) => {
2025-08-28 18:27:07 +02:00
const router = useRouter();
const { mutateAsync: swap } = api.auth.swapUser.useMutation({
onMutate: async () => {
await router.push("/area-riservata/load-page");
},
2025-08-28 18:27:07 +02:00
onSuccess: async () => {
toast.success("Utente cambiato con successo");
window.location.replace("/area-riservata/dashboard");
2025-08-28 18:27:07 +02:00
},
});
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const { data } = props;
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const tabledata = data.map((user) => {
return {
2025-08-29 16:18:32 +02:00
actions: "Azioni",
chatid: user.chatid,
created_at: user.created_at,
2025-08-28 18:27:07 +02:00
email: user.email,
2025-08-29 16:18:32 +02:00
id: user.id,
2025-08-28 18:27:07 +02:00
isAdmin: user.isAdmin ? "Admin" : "Utente",
isBanned: user.isBlocked ? "Bloccato" : "Attivo",
2025-08-29 16:18:32 +02:00
username: user.username,
2025-08-28 18:27:07 +02:00
};
});
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const admin_options = [
{ label: "Admin", value: "Admin" },
{ label: "Utente", value: "Utente" },
];
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const columns_titles = {
2025-08-29 16:18:32 +02:00
actions: "Azioni",
created_at: "Creato il",
2025-08-28 18:27:07 +02:00
email: "Email",
isAdmin: "Ruolo",
isBanned: "Status",
2025-08-29 16:18:32 +02:00
username: "User",
2025-08-28 18:27:07 +02:00
};
type Column = (typeof tabledata)[number];
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const columns: ColumnDef<Column>[] = [
{
accessorKey: "username",
cell: ({ row }) => {
const user = row.original;
return (
<Link
aria-label="Visualizza Utente"
className="flex items-center gap-2"
href={`/area-riservata/admin/user-view/edit-user/${user.id}`}
>
<UserAvatar
className="size-8"
userId={user.id}
2025-08-29 16:18:32 +02:00
username={user.username}
2025-08-28 18:27:07 +02:00
/>
2025-10-10 16:18:43 +02:00
<span className="font-semibold text-sm">{user.username}</span>
2025-08-28 18:27:07 +02:00
</Link>
);
},
2025-08-29 16:18:32 +02:00
enableHiding: false,
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title={columns_titles.username}
/>
),
2025-08-28 18:27:07 +02:00
},
{
2025-08-29 16:18:32 +02:00
accessorKey: "email",
2025-08-28 18:27:07 +02:00
header: ({ column }) => (
<DataTableColumnHeader column={column} title={columns_titles.email} />
),
},
{
2025-08-29 16:18:32 +02:00
accessorKey: "isAdmin",
2025-08-28 18:27:07 +02:00
cell: ({ row }) => {
const isadmin = admin_options.find(
(status) => status.value === row.getValue("isAdmin"),
);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (!isadmin) {
return null;
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return (
<div
className={cn(
"flex w-[100px] items-center",
isadmin.value === "Admin" && "text-green-500",
)}
>
{isadmin.value === "Admin" && <Crown className="size-4" />}
<span>{isadmin.label}</span>
</div>
);
},
filterFn: (row, id, value) => {
return value.includes(row.getValue(id));
},
header: ({ column }) => (
2025-08-29 16:18:32 +02:00
<DataTableColumnHeader column={column} title={columns_titles.isAdmin} />
2025-08-28 18:27:07 +02:00
),
2025-08-29 16:18:32 +02:00
},
{
2025-08-28 18:27:07 +02:00
accessorKey: "isBanned",
filterFn: (row, id, value) => {
return value.includes(row.getValue(id));
},
header: ({ column }) => (
<DataTableColumnHeader
column={column}
2025-08-29 16:18:32 +02:00
title={columns_titles.isBanned}
2025-08-28 18:27:07 +02:00
/>
),
2025-08-29 16:18:32 +02:00
},
{
2025-08-28 18:27:07 +02:00
accessorKey: "created_at",
cell: ({ row }) => {
const date = row.getValue("created_at");
return new Date(date as Date).toLocaleDateString();
},
enableHiding: false,
header: ({ column }) => (
2025-08-29 16:18:32 +02:00
<DataTableColumnHeader
column={column}
title={columns_titles.created_at}
/>
2025-08-28 18:27:07 +02:00
),
2025-08-29 16:18:32 +02:00
sortingFn: "datetime",
},
{
accessorKey: "actions",
2025-08-28 18:27:07 +02:00
cell: ({ row }) => {
const user = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
2025-08-29 16:18:32 +02:00
<Button className="size-8 p-0" variant="ghost">
2025-08-28 18:27:07 +02:00
<span className="sr-only">Apri menu</span>
<MoreHorizontal className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Azioni</DropdownMenuLabel>
<DropdownMenuItem>
<Link
aria-label="Visualizza Servizio"
className="flex w-full items-center gap-2"
href={`/area-riservata/admin/user-view/servizio/${user.id}`}
>
<Tickets /> Servizio
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link
aria-label="Visualizza Ricerca"
className="flex w-full items-center gap-2"
href={`/area-riservata/admin/user-view/ricerca/${user.id}`}
>
<Search className="text-muted-foreground" /> Ricerca
2025-08-28 18:27:07 +02:00
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link
aria-label="Visualizza Comunicazioni"
className="flex w-full items-center gap-2"
href={`/area-riservata/admin/user-view/comunicazioni/${user.id}`}
>
<Mail className="text-muted-foreground" /> Comunicazioni
2025-08-28 18:27:07 +02:00
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link
aria-label="Visualizza Profilo"
className="flex w-full items-center gap-2"
href={`/area-riservata/admin/user-view/edit-user/${user.id}`}
>
<UserCog className="text-muted-foreground" /> Profilo
2025-08-28 18:27:07 +02:00
</Link>
</DropdownMenuItem>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
<DropdownMenuItem>
<Link
aria-label="Visualizza Ordini"
className="flex w-full items-center gap-2"
href={`/area-riservata/admin/user-view/ordini/${user.id}`}
>
<ShoppingBag className="text-muted-foreground" /> Ordini
2025-08-28 18:27:07 +02:00
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link
aria-label="Visualizza Allegati"
className="flex w-full items-center gap-2"
href={`/area-riservata/admin/user-view/allegati/${user.id}`}
>
<Paperclip className="text-muted-foreground" /> Allegati
2025-08-28 18:27:07 +02:00
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Link
aria-label="Visualizza Chat"
className={cn(
"flex w-full items-center gap-2",
!user.chatid && "text-neutral-200",
)}
href={
user.chatid
? {
pathname: "/area-riservata/admin/chats",
query: { chatId: user.chatid },
}
: "/area-riservata/admin/chats"
}
>
<MessagesSquare /> Chat
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<button
aria-label="Login as User"
className="flex w-full items-center gap-2"
onClick={async () => await swap({ id: user.id })}
2025-08-29 16:18:32 +02:00
type="button"
2025-08-28 18:27:07 +02:00
>
<User /> Login
</button>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
enableHiding: false,
enableSorting: false,
2025-08-29 16:18:32 +02:00
header: ({ column }) => (
<DataTableColumnHeader column={column} title={columns_titles.actions} />
),
2025-08-28 18:27:07 +02:00
},
];
const pinnedFiltri: PinnedFiltro[] = [
{
columnName: "isAdmin",
options: [
{ label: "Admin", value: "Admin" },
{ label: "Utente", value: "Utente" },
],
2025-08-29 16:18:32 +02:00
title: "Ruolo",
2025-08-28 18:27:07 +02:00
},
{
columnName: "isBanned",
options: [
{ label: "Bloccato", value: "Bloccato" },
{ label: "Attivo", value: "Attivo" },
],
2025-08-29 16:18:32 +02:00
title: "Status",
2025-08-28 18:27:07 +02:00
},
];
const searchFiltro: SearchFiltro = {
columnName: "username",
placeholder: "Filtra utenti...",
};
return (
<div className="mx-auto w-full px-2 py-10">
<DataTable
columns={columns}
2025-08-29 16:18:32 +02:00
columns_titles={columns_titles}
2025-08-28 18:27:07 +02:00
data={tabledata}
2025-08-29 16:18:32 +02:00
defaultSort={[{ desc: true, id: "created_at" }]}
2025-08-28 18:27:07 +02:00
pinnedFiltri={pinnedFiltri}
searchColumn={searchFiltro}
/>
</div>
);
2025-08-04 17:45:44 +02:00
};