141 lines
3.3 KiB
TypeScript
141 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { Edit, Eye, Trash2 } from "lucide-react";
|
|
|
|
import DataTable, { type DataTableColumn } from "@/components/common/DataTable";
|
|
import StatusBadge from "@/components/common/StatusBadge";
|
|
import { Button, buttonVariants } from "@/components/ui/button";
|
|
import type { User } from "@/types/user";
|
|
|
|
type Props = {
|
|
users: User[];
|
|
loading: boolean;
|
|
error?: string;
|
|
sortKey: string;
|
|
sortDirection: "asc" | "desc";
|
|
canUpdate: boolean;
|
|
canDelete: boolean;
|
|
onSort: (key: string) => void;
|
|
onEdit: (user: User) => void;
|
|
onDelete: (user: User) => void;
|
|
};
|
|
|
|
function formatDate(value: string) {
|
|
return new Intl.DateTimeFormat("de-DE", {
|
|
day: "2-digit",
|
|
month: "2-digit",
|
|
year: "numeric",
|
|
}).format(new Date(value));
|
|
}
|
|
|
|
export default function UserTable({
|
|
users,
|
|
loading,
|
|
error,
|
|
sortKey,
|
|
sortDirection,
|
|
canUpdate,
|
|
canDelete,
|
|
onSort,
|
|
onEdit,
|
|
onDelete,
|
|
}: Props) {
|
|
const columns: DataTableColumn<User>[] = [
|
|
{
|
|
key: "first_name",
|
|
label: "Vorname",
|
|
sortable: true,
|
|
render: (user) => user.first_name || "-",
|
|
},
|
|
{
|
|
key: "last_name",
|
|
label: "Nachname",
|
|
sortable: true,
|
|
render: (user) => user.last_name || "-",
|
|
},
|
|
{
|
|
key: "username",
|
|
label: "Benutzername",
|
|
sortable: true,
|
|
render: (user) => <span className="font-medium text-slate-900">{user.username}</span>,
|
|
},
|
|
{
|
|
key: "email",
|
|
label: "E-Mail",
|
|
sortable: true,
|
|
render: (user) => user.email,
|
|
},
|
|
{
|
|
key: "role",
|
|
label: "Rolle",
|
|
sortable: true,
|
|
render: (user) => user.primary_role.display_name,
|
|
},
|
|
{
|
|
key: "is_active",
|
|
label: "Status",
|
|
sortable: true,
|
|
render: (user) => <StatusBadge active={user.is_active} />,
|
|
},
|
|
{
|
|
key: "created_at",
|
|
label: "Erstellt",
|
|
sortable: true,
|
|
render: (user) => formatDate(user.created_at),
|
|
},
|
|
{
|
|
key: "actions",
|
|
label: "Aktionen",
|
|
className: "px-4 py-3 text-right",
|
|
render: (user) => (
|
|
<div className="flex justify-end gap-2">
|
|
<Link
|
|
href={`/users/${user.id}`}
|
|
className={buttonVariants({ variant: "ghost", size: "icon-sm" })}
|
|
title="Details"
|
|
>
|
|
<Eye size={16} />
|
|
</Link>
|
|
{canUpdate && (
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
title="Bearbeiten"
|
|
onClick={() => onEdit(user)}
|
|
>
|
|
<Edit size={16} />
|
|
</Button>
|
|
)}
|
|
{canDelete && (
|
|
<Button
|
|
type="button"
|
|
variant="destructive"
|
|
size="icon-sm"
|
|
title="Löschen"
|
|
onClick={() => onDelete(user)}
|
|
>
|
|
<Trash2 size={16} />
|
|
</Button>
|
|
)}
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<DataTable
|
|
columns={columns}
|
|
rows={users}
|
|
rowKey={(user) => user.id}
|
|
sortKey={sortKey}
|
|
sortDirection={sortDirection}
|
|
loading={loading}
|
|
error={error}
|
|
emptyTitle="Keine Benutzer gefunden"
|
|
emptyDescription="Passe Suche oder Filter an oder erstelle einen neuen Benutzer."
|
|
onSort={onSort}
|
|
/>
|
|
);
|
|
}
|