-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathusers-client.tsx
More file actions
153 lines (139 loc) · 5.57 KB
/
Copy pathusers-client.tsx
File metadata and controls
153 lines (139 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"use client";
import { useMemo, useState } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ArrowDownAZ, ArrowUpZA, Search } from "lucide-react";
import { UsersDataTable } from "./users-data-table";
import { usersColumns } from "./users-columns";
import type { UserRow } from "../profile-role-label";
const USER_SUBSCRIPTION_VIEW_TABS = [
{ value: "all", label: "All Users" },
{ value: "active", label: "Active" },
{ value: "inactive", label: "Inactive" },
] as const;
export type UserSubscriptionViewTab =
(typeof USER_SUBSCRIPTION_VIEW_TABS)[number]["value"];
type SortDir = "asc" | "desc";
export interface RoleFilterOption {
value: string;
label: string;
}
interface UsersClientProps {
users: UserRow[];
roleFilterOptions: RoleFilterOption[];
}
export function UsersClient({ users, roleFilterOptions }: UsersClientProps) {
const [tab, setTab] = useState<UserSubscriptionViewTab>("all");
const [roleFilter, setRoleFilter] = useState<string>("all");
const [nameQuery, setNameQuery] = useState<string>("");
const [sortDir, setSortDir] = useState<SortDir>("asc");
const filtered = useMemo(() => {
const query = nameQuery.trim().toLowerCase();
return users
.filter((u) => {
if (!query) return true;
return `${u.firstName} ${u.lastName}`.toLowerCase().includes(query);
})
.filter((u) => {
if (tab === "all") return true;
if (tab === "active") return u.isActive;
return !u.isActive;
})
.filter((u) => {
if (roleFilter === "all") return true;
return u.role === roleFilter;
})
.sort((a, b) => {
const nameA = `${a.firstName} ${a.lastName}`.toLowerCase();
const nameB = `${b.firstName} ${b.lastName}`.toLowerCase();
return sortDir === "asc"
? nameA.localeCompare(nameB)
: nameB.localeCompare(nameA);
});
}, [users, nameQuery, tab, roleFilter, sortDir]);
return (
<div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
<Tabs
value={tab}
onValueChange={(v) => setTab(v as UserSubscriptionViewTab)}
className="flex min-h-0 w-full min-w-0 flex-1 flex-col gap-2 overflow-hidden"
>
<div className="flex w-full min-w-0 shrink-0 flex-wrap items-center justify-between gap-x-3 gap-y-2">
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-2 sm:gap-3">
<div className="relative p-0.5 min-w-[min(100%,10rem)] max-w-full grow sm:max-w-xs sm:grow-0 sm:basis-56">
<Search className="absolute left-3 top-1/2 z-10 h-4 w-4 -translate-y-1/2 text-muted-foreground pointer-events-none" />
<Input
id="users-search"
placeholder="Search by name..."
value={nameQuery}
onChange={(e) => setNameQuery(e.target.value)}
className="w-full min-w-0 pl-9"
/>
</div>
<TabsList className="h-auto min-h-8 max-w-full min-w-0 flex-wrap justify-start border border-border">
{USER_SUBSCRIPTION_VIEW_TABS.map(({ value, label }) => (
<TabsTrigger key={value} value={value}>
{label}
</TabsTrigger>
))}
</TabsList>
</div>
<div className="flex min-w-0 shrink-0 flex-wrap items-center justify-end gap-2">
<Select value={roleFilter} onValueChange={setRoleFilter}>
<SelectTrigger
id="users-role-filter"
className="w-[min(100%,11rem)] min-w-[8.5rem] sm:w-[140px]"
>
<SelectValue placeholder="All Roles" />
</SelectTrigger>
<SelectContent>
{roleFilterOptions.map(({ value, label }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
<Button
id="users-sort-toggle"
variant="outline"
size="icon"
className="shrink-0"
onClick={() =>
setSortDir((prev) => (prev === "asc" ? "desc" : "asc"))
}
aria-label={
sortDir === "asc" ? "Sort Z to A" : "Sort A to Z"
}
>
{sortDir === "asc" ? (
<ArrowDownAZ className="h-4 w-4" />
) : (
<ArrowUpZA className="h-4 w-4" />
)}
</Button>
</div>
</div>
<TabsContent
value={tab}
className="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden focus-visible:outline-none"
>
<UsersDataTable
columns={usersColumns}
data={filtered}
emptyMessage="No users match the current filters."
rowLabel={(n) => `${n} user${n === 1 ? "" : "s"}`}
/>
</TabsContent>
</Tabs>
</div>
);
}