since3-portal/modules/admin/ui/components/clients-table.tsx

116 lines
5.2 KiB
TypeScript

"use client"
import { useMemo, useState } from "react";
import Link from "next/link";
import { ArrowRightIcon, Building2Icon, Search } from "lucide-react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";
import { AddClientDialog } from "./add-client-dialog";
import { CLIENT_STATUS_CONFIG, type ClientStatus } from "@/modules/admin/lib/client-status";
import { ClientActionsMenu } from "./client-actions-menu";
type Client = {
id: string;
name: string;
email: string;
status: ClientStatus;
};
export function ClientsTable({ clients }: { clients: Client[] }) {
const [query, setQuery] = useState("");
const filteredClients = useMemo(() => {
const normalizedQuery = query.trim().toLowerCase();
if (!normalizedQuery) return clients;
return clients.filter(
(client) =>
client.name.toLowerCase().includes(normalizedQuery) ||
client.email.toLowerCase().includes(normalizedQuery)
);
}, [clients, query]);
return (
<div className="flex w-full flex-col gap-6 md:gap-8">
<div className="flex w-full flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<InputGroup className="w-full rounded-md focus-visible:ring-0 sm:max-w-lg">
<InputGroupInput
placeholder="Search..."
value={query}
onChange={(event) => setQuery(event.target.value)}
/>
<InputGroupAddon>
<Search />
</InputGroupAddon>
</InputGroup>
<AddClientDialog />
</div>
{filteredClients.length === 0 ? (
<p className="p-8 text-center text-muted-foreground">
{clients.length === 0 ? "Noch keine Clients vorhanden." : "Keine Clients gefunden."}
</p>
) : (
<Table>
<TableHeader>
<TableRow>
<TableHead>Client</TableHead>
<TableHead className="hidden sm:table-cell">Status</TableHead>
<TableHead className="w-20 sm:w-24" />
</TableRow>
</TableHeader>
<TableBody>
{filteredClients.map((client) => {
const status = CLIENT_STATUS_CONFIG[client.status];
return (
<TableRow key={client.id}>
<TableCell className="w-full max-w-0 sm:w-auto sm:max-w-none">
<div className="flex items-center gap-3">
<Avatar size="lg" className="shrink-0">
<AvatarFallback>
<Building2Icon className="size-4.5 text-muted-foreground" />
</AvatarFallback>
</Avatar>
<div className="flex min-w-0 flex-col">
<span className="truncate font-medium">{client.name}</span>
<span className="truncate text-small text-muted-foreground">{client.email}</span>
</div>
</div>
</TableCell>
<TableCell className="hidden sm:table-cell">
<Badge variant={status.variant}>{status.label}</Badge>
</TableCell>
<TableCell>
<div className="flex justify-end gap-1">
<ClientActionsMenu client={client} />
<Button
variant="ghost"
size="icon-sm"
nativeButton={false}
render={<Link href={`/admin/clients/${client.id}`} />}
aria-label={`${client.name} öffnen`}
>
<ArrowRightIcon />
</Button>
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
)}
</div>
);
}