mirror of
https://git.coastit.co.za/caitlin/CRM_ENVR.git
synced 2026-07-10 11:15:43 +02:00
163 lines
5.0 KiB
TypeScript
163 lines
5.0 KiB
TypeScript
"use client"
|
|
|
|
import { useMemo, useState } from "react"
|
|
import { ColumnDef } from "@tanstack/react-table"
|
|
import { DataTable } from "@/components/shared/data-table"
|
|
import { DataTableColumnHeader } from "@/components/shared/data-table-column-header"
|
|
import { UserStatusBadge } from "./user-status-badge"
|
|
import { UserFormDialog } from "./user-form-dialog"
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Checkbox } from "@/components/ui/checkbox"
|
|
import { User } from "@/types"
|
|
import { Pencil, Trash2 } from "lucide-react"
|
|
import { toast } from "sonner"
|
|
|
|
interface UsersTableProps {
|
|
data: User[]
|
|
loading?: boolean
|
|
onUserDeleted?: () => void
|
|
}
|
|
|
|
export function UsersTable({ data, loading, onUserDeleted }: UsersTableProps) {
|
|
const [editingUser, setEditingUser] = useState<User | null>(null)
|
|
const [editOpen, setEditOpen] = useState(false)
|
|
|
|
const handleDelete = async (id: string) => {
|
|
if (!confirm("Are you sure you want to delete this user?")) return
|
|
try {
|
|
const res = await fetch(`/api/users/${id}`, { method: "DELETE" })
|
|
if (!res.ok) throw new Error()
|
|
toast.success("User deleted")
|
|
onUserDeleted?.()
|
|
} catch {
|
|
toast.error("Failed to delete user")
|
|
}
|
|
}
|
|
|
|
const columns: ColumnDef<User>[] = useMemo(
|
|
() => [
|
|
{
|
|
id: "select",
|
|
header: ({ table }) => (
|
|
<Checkbox
|
|
checked={table.getIsAllPageRowsSelected()}
|
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
|
aria-label="Select all"
|
|
/>
|
|
),
|
|
cell: ({ row }) => (
|
|
<Checkbox
|
|
checked={row.getIsSelected()}
|
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
|
aria-label="Select row"
|
|
/>
|
|
),
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: "name",
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Name" />
|
|
),
|
|
cell: ({ row }) => (
|
|
<div className="flex items-center gap-3">
|
|
<Avatar className="h-8 w-8">
|
|
<AvatarImage src={row.original.avatar} />
|
|
<AvatarFallback>{row.original.name[0]}</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<p className="font-medium">{row.original.name}</p>
|
|
<p className="text-xs text-muted-foreground">{row.original.email}</p>
|
|
</div>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: "email",
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Email" />
|
|
),
|
|
cell: ({ row }) => (
|
|
<span className="text-muted-foreground">{row.getValue("email")}</span>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: "role",
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Role" />
|
|
),
|
|
cell: ({ row }) => {
|
|
const role = row.getValue("role") as string
|
|
const colors: Record<string, string> = {
|
|
super_admin: "bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/20",
|
|
admin: "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/20",
|
|
sales_user: "bg-purple-500/10 text-purple-600 dark:text-purple-400 border-purple-500/20",
|
|
developer: "bg-zinc-500/10 text-zinc-600 dark:text-zinc-400 border-zinc-500/20",
|
|
}
|
|
return (
|
|
<Badge variant="outline" className={`capitalize ${colors[role] || ""}`}>
|
|
{role.replace("_", " ")}
|
|
</Badge>
|
|
)
|
|
},
|
|
},
|
|
{
|
|
accessorKey: "active",
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Status" />
|
|
),
|
|
cell: ({ row }) => <UserStatusBadge active={row.getValue("active")} />,
|
|
},
|
|
{
|
|
id: "actions",
|
|
cell: ({ row }) => {
|
|
const user = row.original
|
|
return (
|
|
<div className="flex items-center gap-1">
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="h-8 w-8"
|
|
onClick={() => {
|
|
setEditingUser(user)
|
|
setEditOpen(true)
|
|
}}
|
|
>
|
|
<Pencil className="h-4 w-4" />
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="h-8 w-8 text-muted-foreground hover:text-destructive"
|
|
onClick={() => handleDelete(user.id)}
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
)
|
|
},
|
|
},
|
|
],
|
|
[]
|
|
)
|
|
|
|
return (
|
|
<>
|
|
<DataTable
|
|
columns={columns}
|
|
data={data}
|
|
loading={loading}
|
|
emptyMessage="No users found."
|
|
/>
|
|
<UserFormDialog
|
|
open={editOpen}
|
|
onOpenChange={setEditOpen}
|
|
user={editingUser}
|
|
onUserCreated={onUserDeleted}
|
|
/>
|
|
</>
|
|
)
|
|
}
|