feat(auth): enterprise authentication and user management
This commit is contained in:
parent
4bc8b20a21
commit
86a32a942c
36 changed files with 2239 additions and 324 deletions
|
|
@ -1,63 +1,133 @@
|
|||
"use client";
|
||||
|
||||
import { User } from "@/types/user";
|
||||
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";
|
||||
onSort: (key: string) => void;
|
||||
onEdit: (user: User) => void;
|
||||
onDelete: (user: User) => void;
|
||||
};
|
||||
|
||||
export default function UserTable({ users }: Props) {
|
||||
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,
|
||||
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.role,
|
||||
},
|
||||
{
|
||||
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>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
title="Bearbeiten"
|
||||
onClick={() => onEdit(user)}
|
||||
>
|
||||
<Edit size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="icon-sm"
|
||||
title="Löschen"
|
||||
onClick={() => onDelete(user)}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden rounded-xl border bg-white shadow">
|
||||
|
||||
<table className="w-full">
|
||||
|
||||
<thead className="bg-slate-100">
|
||||
|
||||
<tr>
|
||||
|
||||
<th className="p-4 text-left">ID</th>
|
||||
<th className="p-4 text-left">Benutzername</th>
|
||||
<th className="p-4 text-left">E-Mail</th>
|
||||
<th className="p-4 text-center">Aktionen</th>
|
||||
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
{users.map((user) => (
|
||||
|
||||
<tr
|
||||
key={user.id}
|
||||
className="border-t hover:bg-slate-50"
|
||||
>
|
||||
|
||||
<td className="p-4">{user.id}</td>
|
||||
<td className="p-4">{user.username}</td>
|
||||
<td className="p-4">{user.email}</td>
|
||||
|
||||
<td className="p-4 text-center">
|
||||
|
||||
<button className="mr-3 rounded bg-blue-600 px-3 py-1 text-white hover:bg-blue-700">
|
||||
Bearbeiten
|
||||
</button>
|
||||
|
||||
<button className="rounded bg-red-600 px-3 py-1 text-white hover:bg-red-700">
|
||||
Löschen
|
||||
</button>
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
))}
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue