@dowel-ui/react 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/blocks/admin-dashboard/meta.js +31 -0
- package/dist/blocks/admin-dashboard/meta.js.map +1 -0
- package/dist/blocks/agent-console/meta.js +27 -0
- package/dist/blocks/agent-console/meta.js.map +1 -0
- package/dist/blocks/ai-dashboard/meta.js +25 -0
- package/dist/blocks/ai-dashboard/meta.js.map +1 -0
- package/dist/blocks/ai-workspace/meta.js +33 -0
- package/dist/blocks/ai-workspace/meta.js.map +1 -0
- package/dist/blocks/analytics/meta.js +24 -0
- package/dist/blocks/analytics/meta.js.map +1 -0
- package/dist/blocks/billing/meta.js +25 -0
- package/dist/blocks/billing/meta.js.map +1 -0
- package/dist/blocks/command-center/meta.js +28 -0
- package/dist/blocks/command-center/meta.js.map +1 -0
- package/dist/blocks/crm/meta.js +32 -0
- package/dist/blocks/crm/meta.js.map +1 -0
- package/dist/blocks/onboarding/meta.js +23 -0
- package/dist/blocks/onboarding/meta.js.map +1 -0
- package/dist/components/accordion/accordion.js +1 -1
- package/dist/components/accordion/accordion.js.map +1 -1
- package/dist/components/activity-feed/activity-feed.js +1 -1
- package/dist/components/activity-feed/activity-feed.js.map +1 -1
- package/dist/components/ai-agent-plan/ai-agent-plan.js +1 -1
- package/dist/components/ai-agent-plan/ai-agent-plan.js.map +1 -1
- package/dist/components/ai-extraction-review/ai-extraction-review.js +1 -1
- package/dist/components/ai-extraction-review/ai-extraction-review.js.map +1 -1
- package/dist/components/ai-prompt-input/ai-prompt-input.js +1 -1
- package/dist/components/ai-prompt-input/ai-prompt-input.js.map +1 -1
- package/dist/components/ai-reasoning/ai-reasoning.js +1 -1
- package/dist/components/ai-reasoning/ai-reasoning.js.map +1 -1
- package/dist/components/ai-response/ai-response.js +2 -2
- package/dist/components/ai-response/ai-response.js.map +1 -1
- package/dist/components/ai-tool/ai-tool.js +2 -2
- package/dist/components/ai-tool/ai-tool.js.map +1 -1
- package/dist/components/alert/alert.d.ts +1 -1
- package/dist/components/avatar/avatar.d.ts +1 -1
- package/dist/components/badge/badge.d.ts +1 -1
- package/dist/components/breadcrumb/breadcrumb.d.ts +52 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
- package/dist/components/breadcrumb/breadcrumb.js +141 -0
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
- package/dist/components/breadcrumb/index.d.ts +2 -0
- package/dist/components/breadcrumb/index.js +2 -0
- package/dist/components/breadcrumb/meta.js +17 -0
- package/dist/components/breadcrumb/meta.js.map +1 -0
- package/dist/components/button/button.d.ts +1 -1
- package/dist/components/calendar/calendar.js +2 -2
- package/dist/components/calendar/calendar.js.map +1 -1
- package/dist/components/code-block/code-block.js +1 -1
- package/dist/components/code-block/code-block.js.map +1 -1
- package/dist/components/collapsible/collapsible.d.ts +30 -0
- package/dist/components/collapsible/collapsible.d.ts.map +1 -0
- package/dist/components/collapsible/collapsible.js +38 -0
- package/dist/components/collapsible/collapsible.js.map +1 -0
- package/dist/components/collapsible/index.d.ts +2 -0
- package/dist/components/collapsible/index.js +2 -0
- package/dist/components/collapsible/meta.js +17 -0
- package/dist/components/collapsible/meta.js.map +1 -0
- package/dist/components/combobox/combobox.js +2 -2
- package/dist/components/combobox/combobox.js.map +1 -1
- package/dist/components/command/command.js +1 -1
- package/dist/components/command/command.js.map +1 -1
- package/dist/components/data-table/data-table.d.ts.map +1 -1
- package/dist/components/data-table/data-table.js +4 -1
- package/dist/components/data-table/data-table.js.map +1 -1
- package/dist/components/dialog/dialog.js +2 -2
- package/dist/components/dialog/dialog.js.map +1 -1
- package/dist/components/diff-viewer/diff-viewer.js +4 -4
- package/dist/components/diff-viewer/diff-viewer.js.map +1 -1
- package/dist/components/direction/direction.d.ts +35 -0
- package/dist/components/direction/direction.d.ts.map +1 -0
- package/dist/components/direction/direction.js +14 -0
- package/dist/components/direction/direction.js.map +1 -0
- package/dist/components/direction/index.d.ts +2 -0
- package/dist/components/direction/index.js +2 -0
- package/dist/components/direction/meta.js +17 -0
- package/dist/components/direction/meta.js.map +1 -0
- package/dist/components/drawer/drawer.js +1 -1
- package/dist/components/drawer/drawer.js.map +1 -1
- package/dist/components/dropdown-menu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/dropdown-menu/dropdown-menu.js +9 -8
- package/dist/components/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/input/input.js +1 -1
- package/dist/components/input/input.js.map +1 -1
- package/dist/components/log-viewer/log-viewer.js +2 -2
- package/dist/components/log-viewer/log-viewer.js.map +1 -1
- package/dist/components/meter/meter.js +1 -1
- package/dist/components/meter/meter.js.map +1 -1
- package/dist/components/pagination/pagination.d.ts.map +1 -1
- package/dist/components/pagination/pagination.js +3 -2
- package/dist/components/pagination/pagination.js.map +1 -1
- package/dist/components/permission-matrix/permission-matrix.js +3 -3
- package/dist/components/permission-matrix/permission-matrix.js.map +1 -1
- package/dist/components/progress/progress.d.ts +1 -1
- package/dist/components/record-diff/record-diff.js +4 -4
- package/dist/components/record-diff/record-diff.js.map +1 -1
- package/dist/components/select/select.js +2 -2
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/sheet/sheet.d.ts.map +1 -1
- package/dist/components/sheet/sheet.js +2 -2
- package/dist/components/sheet/sheet.js.map +1 -1
- package/dist/components/sidebar/index.d.ts +2 -0
- package/dist/components/sidebar/index.js +2 -0
- package/dist/components/sidebar/meta.js +17 -0
- package/dist/components/sidebar/meta.js.map +1 -0
- package/dist/components/sidebar/sidebar.d.ts +69 -0
- package/dist/components/sidebar/sidebar.d.ts.map +1 -0
- package/dist/components/sidebar/sidebar.js +256 -0
- package/dist/components/sidebar/sidebar.js.map +1 -0
- package/dist/components/spinner/spinner.d.ts +1 -1
- package/dist/components/table/table.js +2 -2
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/tags-input/tags-input.js +1 -1
- package/dist/components/tags-input/tags-input.js.map +1 -1
- package/dist/components/textarea/index.d.ts +2 -0
- package/dist/components/textarea/index.js +2 -0
- package/dist/components/textarea/meta.js +17 -0
- package/dist/components/textarea/meta.js.map +1 -0
- package/dist/components/textarea/textarea.d.ts +43 -0
- package/dist/components/textarea/textarea.d.ts.map +1 -0
- package/dist/components/textarea/textarea.js +119 -0
- package/dist/components/textarea/textarea.js.map +1 -0
- package/dist/components/time-range-picker/time-range-picker.js +3 -3
- package/dist/components/time-range-picker/time-range-picker.js.map +1 -1
- package/dist/components/toast/toast.d.ts +1 -1
- package/dist/components/toast/toast.d.ts.map +1 -1
- package/dist/components/toast/toast.js +2 -2
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.js +7 -2
- package/dist/lib/styles.d.ts +16 -1
- package/dist/lib/styles.d.ts.map +1 -1
- package/dist/lib/styles.js +16 -1
- package/dist/lib/styles.js.map +1 -1
- package/dist/registry/blocks.d.ts.map +1 -1
- package/dist/registry/blocks.js +27 -9
- package/dist/registry/blocks.js.map +1 -1
- package/dist/registry/components.d.ts.map +1 -1
- package/dist/registry/components.js +60 -50
- package/dist/registry/components.js.map +1 -1
- package/dist/registry/schema.d.ts +8 -0
- package/dist/registry/schema.d.ts.map +1 -1
- package/dist/registry/schema.js.map +1 -1
- package/package.json +5 -4
- package/src/components/accordion/accordion.tsx +1 -1
- package/src/components/activity-feed/activity-feed.tsx +1 -1
- package/src/components/ai-agent-plan/ai-agent-plan.tsx +0 -0
- package/src/components/ai-extraction-review/ai-extraction-review.tsx +1 -1
- package/src/components/ai-prompt-input/ai-prompt-input.tsx +1 -1
- package/src/components/ai-reasoning/ai-reasoning.tsx +1 -1
- package/src/components/ai-response/ai-response.tsx +2 -2
- package/src/components/ai-tool/ai-tool.tsx +2 -2
- package/src/components/breadcrumb/breadcrumb.tsx +157 -0
- package/src/components/breadcrumb/index.ts +10 -0
- package/src/components/breadcrumb/meta.ts +19 -0
- package/src/components/calendar/calendar.tsx +2 -2
- package/src/components/code-block/code-block.tsx +1 -1
- package/src/components/collapsible/collapsible.tsx +63 -0
- package/src/components/collapsible/index.ts +6 -0
- package/src/components/collapsible/meta.ts +16 -0
- package/src/components/combobox/combobox.tsx +2 -2
- package/src/components/command/command.tsx +1 -1
- package/src/components/data-table/data-table.tsx +14 -3
- package/src/components/dialog/dialog.tsx +2 -2
- package/src/components/diff-viewer/diff-viewer.tsx +4 -4
- package/src/components/direction/direction.tsx +38 -0
- package/src/components/direction/index.ts +1 -0
- package/src/components/direction/meta.ts +19 -0
- package/src/components/drawer/drawer.tsx +1 -1
- package/src/components/dropdown-menu/dropdown-menu.tsx +14 -8
- package/src/components/input/input.tsx +1 -1
- package/src/components/log-viewer/log-viewer.tsx +2 -2
- package/src/components/meter/meter.tsx +1 -1
- package/src/components/pagination/pagination.tsx +13 -2
- package/src/components/permission-matrix/permission-matrix.tsx +4 -4
- package/src/components/record-diff/record-diff.tsx +4 -4
- package/src/components/select/select.tsx +2 -2
- package/src/components/sheet/sheet.tsx +8 -4
- package/src/components/sidebar/index.ts +19 -0
- package/src/components/sidebar/meta.ts +25 -0
- package/src/components/sidebar/sidebar.tsx +374 -0
- package/src/components/table/table.tsx +3 -3
- package/src/components/tags-input/tags-input.tsx +1 -1
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/meta.ts +21 -0
- package/src/components/textarea/textarea.tsx +210 -0
- package/src/components/time-range-picker/time-range-picker.tsx +3 -3
- package/src/components/toast/toast.tsx +14 -6
- package/src/index.ts +5 -0
- package/src/lib/styles.ts +16 -0
- package/src/registry/blocks.ts +18 -0
- package/src/registry/components.ts +10 -0
- package/src/registry/schema.ts +8 -0
- package/src/blocks/admin-users/admin-users.tsx +0 -251
- package/src/blocks/admin-users/index.ts +0 -1
- package/src/blocks/admin-users/meta.ts +0 -27
- package/src/blocks/ai-chat/ai-chat.tsx +0 -280
- package/src/blocks/ai-chat/index.ts +0 -7
- package/src/blocks/ai-chat/meta.ts +0 -32
- package/src/blocks/dashboard/dashboard.tsx +0 -192
- package/src/blocks/dashboard/index.ts +0 -6
- package/src/blocks/dashboard/meta.ts +0 -18
- package/src/blocks/forgot-password/forgot-password.tsx +0 -158
- package/src/blocks/forgot-password/index.ts +0 -1
- package/src/blocks/forgot-password/meta.ts +0 -18
- package/src/blocks/login/index.ts +0 -1
- package/src/blocks/login/login.tsx +0 -178
- package/src/blocks/login/meta.ts +0 -18
- package/src/blocks/pricing/index.ts +0 -1
- package/src/blocks/pricing/meta.ts +0 -18
- package/src/blocks/pricing/pricing.tsx +0 -176
- package/src/blocks/settings/index.ts +0 -6
- package/src/blocks/settings/meta.ts +0 -29
- package/src/blocks/settings/settings.tsx +0 -317
- package/src/blocks/signup/index.ts +0 -1
- package/src/blocks/signup/meta.ts +0 -18
- package/src/blocks/signup/signup.tsx +0 -240
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
columnVisibilityFeature,
|
|
5
|
-
createPaginatedRowModel,
|
|
6
|
-
createSortedRowModel,
|
|
7
|
-
rowPaginationFeature,
|
|
8
|
-
rowSortingFeature,
|
|
9
|
-
tableFeatures,
|
|
10
|
-
useTable,
|
|
11
|
-
} from "@tanstack/react-table";
|
|
12
|
-
import { useMemo, useState } from "react";
|
|
13
|
-
|
|
14
|
-
import { Avatar, AvatarFallback } from "@/components/avatar";
|
|
15
|
-
import { Badge } from "@/components/badge";
|
|
16
|
-
import { Button } from "@/components/button";
|
|
17
|
-
import {
|
|
18
|
-
DataTable,
|
|
19
|
-
DataTableColumnHeader,
|
|
20
|
-
DataTablePagination,
|
|
21
|
-
DataTableViewOptions,
|
|
22
|
-
} from "@/components/data-table";
|
|
23
|
-
import {
|
|
24
|
-
DropdownMenu,
|
|
25
|
-
DropdownMenuContent,
|
|
26
|
-
DropdownMenuItem,
|
|
27
|
-
DropdownMenuLabel,
|
|
28
|
-
DropdownMenuSeparator,
|
|
29
|
-
DropdownMenuTrigger,
|
|
30
|
-
} from "@/components/dropdown-menu";
|
|
31
|
-
import { EmptyState, EmptyStateDescription, EmptyStateTitle } from "@/components/empty-state";
|
|
32
|
-
import { Input } from "@/components/input";
|
|
33
|
-
import { Label } from "@/components/label";
|
|
34
|
-
import { cn } from "@/lib/utils";
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* A user administration table.
|
|
38
|
-
*
|
|
39
|
-
* The filter is the part worth reading. Results change as you type, which for
|
|
40
|
-
* a screen reader user is a change they never asked for and cannot see — so the
|
|
41
|
-
* row count is announced politely, and the search input is a labelled control
|
|
42
|
-
* rather than a placeholder-only box.
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
export interface AdminUser {
|
|
46
|
-
id: string;
|
|
47
|
-
name: string;
|
|
48
|
-
email: string;
|
|
49
|
-
role: "Owner" | "Admin" | "Member" | "Viewer";
|
|
50
|
-
status: "Active" | "Invited" | "Suspended";
|
|
51
|
-
lastActive: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface AdminUsersBlockProps {
|
|
55
|
-
users: AdminUser[];
|
|
56
|
-
onInvite?: () => void;
|
|
57
|
-
onEdit?: (user: AdminUser) => void;
|
|
58
|
-
onRemove?: (user: AdminUser) => void;
|
|
59
|
-
className?: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const features = tableFeatures({
|
|
63
|
-
rowSortingFeature,
|
|
64
|
-
rowPaginationFeature,
|
|
65
|
-
columnVisibilityFeature,
|
|
66
|
-
sortedRowModel: createSortedRowModel(),
|
|
67
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
function initials(name: string): string {
|
|
71
|
-
return name
|
|
72
|
-
.split(" ")
|
|
73
|
-
.map((part) => part[0] ?? "")
|
|
74
|
-
.slice(0, 2)
|
|
75
|
-
.join("")
|
|
76
|
-
.toUpperCase();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function AdminUsersBlock({
|
|
80
|
-
users,
|
|
81
|
-
onInvite,
|
|
82
|
-
onEdit,
|
|
83
|
-
onRemove,
|
|
84
|
-
className,
|
|
85
|
-
}: AdminUsersBlockProps) {
|
|
86
|
-
const [query, setQuery] = useState("");
|
|
87
|
-
|
|
88
|
-
// Filtered here rather than through the table's filtering feature, so the
|
|
89
|
-
// block stays usable without wiring that feature up.
|
|
90
|
-
const filtered = useMemo(() => {
|
|
91
|
-
const needle = query.trim().toLowerCase();
|
|
92
|
-
if (!needle) return users;
|
|
93
|
-
return users.filter(
|
|
94
|
-
(user) =>
|
|
95
|
-
user.name.toLowerCase().includes(needle) || user.email.toLowerCase().includes(needle),
|
|
96
|
-
);
|
|
97
|
-
}, [users, query]);
|
|
98
|
-
|
|
99
|
-
const table = useTable({
|
|
100
|
-
features,
|
|
101
|
-
data: filtered,
|
|
102
|
-
initialState: { pagination: { pageIndex: 0, pageSize: 10 } },
|
|
103
|
-
columns: [
|
|
104
|
-
{
|
|
105
|
-
accessorKey: "name",
|
|
106
|
-
header: ({ column }) => <DataTableColumnHeader column={column} title="User" />,
|
|
107
|
-
cell: ({ row }) => (
|
|
108
|
-
<div className="flex items-center gap-3">
|
|
109
|
-
<Avatar size="sm">
|
|
110
|
-
<AvatarFallback>{initials(row.original.name)}</AvatarFallback>
|
|
111
|
-
</Avatar>
|
|
112
|
-
<div className="min-w-0">
|
|
113
|
-
<p className="truncate text-sm font-medium">{row.original.name}</p>
|
|
114
|
-
<p className="truncate text-xs text-muted-foreground">{row.original.email}</p>
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
),
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
accessorKey: "role",
|
|
121
|
-
header: ({ column }) => <DataTableColumnHeader column={column} title="Role" />,
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
accessorKey: "status",
|
|
125
|
-
header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />,
|
|
126
|
-
cell: ({ row }) => (
|
|
127
|
-
<Badge
|
|
128
|
-
size="sm"
|
|
129
|
-
variant={
|
|
130
|
-
row.original.status === "Active"
|
|
131
|
-
? "success"
|
|
132
|
-
: row.original.status === "Suspended"
|
|
133
|
-
? "destructive"
|
|
134
|
-
: "secondary"
|
|
135
|
-
}
|
|
136
|
-
>
|
|
137
|
-
{row.original.status}
|
|
138
|
-
</Badge>
|
|
139
|
-
),
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
accessorKey: "lastActive",
|
|
143
|
-
header: ({ column }) => <DataTableColumnHeader column={column} title="Last active" />,
|
|
144
|
-
cell: ({ row }) => (
|
|
145
|
-
<span className="text-sm text-muted-foreground">{row.original.lastActive}</span>
|
|
146
|
-
),
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
id: "actions",
|
|
150
|
-
// Not an empty header: a th with no text is a column a screen reader
|
|
151
|
-
// cannot name. Hidden visually, present in the table semantics.
|
|
152
|
-
header: () => <span className="sr-only">Actions</span>,
|
|
153
|
-
enableSorting: false,
|
|
154
|
-
cell: ({ row }) => (
|
|
155
|
-
<DropdownMenu>
|
|
156
|
-
<DropdownMenuTrigger asChild>
|
|
157
|
-
<Button
|
|
158
|
-
variant="ghost"
|
|
159
|
-
size="icon-sm"
|
|
160
|
-
// Named per row: four identical "Actions" buttons tell a screen
|
|
161
|
-
// reader user nothing about which row they are on.
|
|
162
|
-
aria-label={`Actions for ${row.original.name}`}
|
|
163
|
-
>
|
|
164
|
-
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
165
|
-
<circle cx="12" cy="5" r="1.5" fill="currentColor" />
|
|
166
|
-
<circle cx="12" cy="12" r="1.5" fill="currentColor" />
|
|
167
|
-
<circle cx="12" cy="19" r="1.5" fill="currentColor" />
|
|
168
|
-
</svg>
|
|
169
|
-
</Button>
|
|
170
|
-
</DropdownMenuTrigger>
|
|
171
|
-
<DropdownMenuContent align="end">
|
|
172
|
-
<DropdownMenuLabel>{row.original.name}</DropdownMenuLabel>
|
|
173
|
-
<DropdownMenuSeparator />
|
|
174
|
-
<DropdownMenuItem
|
|
175
|
-
onSelect={() => {
|
|
176
|
-
onEdit?.(row.original);
|
|
177
|
-
}}
|
|
178
|
-
>
|
|
179
|
-
Edit member
|
|
180
|
-
</DropdownMenuItem>
|
|
181
|
-
<DropdownMenuItem
|
|
182
|
-
variant="destructive"
|
|
183
|
-
onSelect={() => {
|
|
184
|
-
onRemove?.(row.original);
|
|
185
|
-
}}
|
|
186
|
-
>
|
|
187
|
-
Remove from team
|
|
188
|
-
</DropdownMenuItem>
|
|
189
|
-
</DropdownMenuContent>
|
|
190
|
-
</DropdownMenu>
|
|
191
|
-
),
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
<div className={cn("flex flex-col gap-4", className)}>
|
|
198
|
-
<div className="flex flex-wrap items-end justify-between gap-3">
|
|
199
|
-
<div>
|
|
200
|
-
<h1 className="text-xl font-semibold tracking-tight">Team members</h1>
|
|
201
|
-
<p className="mt-1 text-sm text-muted-foreground">
|
|
202
|
-
{users.length} {users.length === 1 ? "person" : "people"} in this workspace.
|
|
203
|
-
</p>
|
|
204
|
-
</div>
|
|
205
|
-
<Button onClick={onInvite}>Invite people</Button>
|
|
206
|
-
</div>
|
|
207
|
-
|
|
208
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
209
|
-
<div className="grid flex-1 gap-1.5 sm:max-w-xs">
|
|
210
|
-
{/* A real label, not a placeholder: placeholders vanish as soon as
|
|
211
|
-
anyone types, taking the field's name with them. */}
|
|
212
|
-
<Label htmlFor="user-filter" className="sr-only">
|
|
213
|
-
Filter by name or email
|
|
214
|
-
</Label>
|
|
215
|
-
<Input
|
|
216
|
-
id="user-filter"
|
|
217
|
-
inputSize="sm"
|
|
218
|
-
type="search"
|
|
219
|
-
placeholder="Filter by name or email…"
|
|
220
|
-
value={query}
|
|
221
|
-
onChange={(event) => {
|
|
222
|
-
setQuery(event.target.value);
|
|
223
|
-
}}
|
|
224
|
-
/>
|
|
225
|
-
</div>
|
|
226
|
-
<DataTableViewOptions table={table} />
|
|
227
|
-
</div>
|
|
228
|
-
|
|
229
|
-
{/* Filtering changes the results without the user asking, so the count is
|
|
230
|
-
announced. Polite, so it waits for a pause in typing. */}
|
|
231
|
-
<p aria-live="polite" className="sr-only">
|
|
232
|
-
{filtered.length} {filtered.length === 1 ? "person" : "people"} match the filter.
|
|
233
|
-
</p>
|
|
234
|
-
|
|
235
|
-
<DataTable
|
|
236
|
-
table={table}
|
|
237
|
-
aria-label="Team members"
|
|
238
|
-
empty={
|
|
239
|
-
<EmptyState size="sm">
|
|
240
|
-
<EmptyStateTitle>No one matches “{query}”</EmptyStateTitle>
|
|
241
|
-
<EmptyStateDescription>
|
|
242
|
-
Try a different name or email address.
|
|
243
|
-
</EmptyStateDescription>
|
|
244
|
-
</EmptyState>
|
|
245
|
-
}
|
|
246
|
-
/>
|
|
247
|
-
|
|
248
|
-
<DataTablePagination table={table} pageSizes={[10, 25, 50]} />
|
|
249
|
-
</div>
|
|
250
|
-
);
|
|
251
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { AdminUsersBlock, type AdminUser, type AdminUsersBlockProps } from "./admin-users";
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { defineMeta } from "@/registry/schema";
|
|
2
|
-
|
|
3
|
-
export const meta = defineMeta({
|
|
4
|
-
name: "admin-users",
|
|
5
|
-
kind: "block",
|
|
6
|
-
title: "Admin — users",
|
|
7
|
-
description: "A team administration table with filtering, sorting and per-row actions.",
|
|
8
|
-
category: "data",
|
|
9
|
-
status: "stable",
|
|
10
|
-
dependencies: ["@tanstack/react-table"],
|
|
11
|
-
registryDependencies: [
|
|
12
|
-
"avatar",
|
|
13
|
-
"badge",
|
|
14
|
-
"button",
|
|
15
|
-
"data-table",
|
|
16
|
-
"dropdown-menu",
|
|
17
|
-
"empty-state",
|
|
18
|
-
"input",
|
|
19
|
-
"label",
|
|
20
|
-
],
|
|
21
|
-
files: ["admin-users.tsx"],
|
|
22
|
-
a11y:
|
|
23
|
-
"Filtering changes the results without the user asking, so the matching count is announced " +
|
|
24
|
-
"politely. The filter has a real label rather than only a placeholder, which disappears the " +
|
|
25
|
-
"moment anyone types. Each row's action button is named after its row — four identical " +
|
|
26
|
-
'"Actions" buttons say nothing about which row you are on.',
|
|
27
|
-
});
|
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useState, type FormEvent, type ReactNode } from "react";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
Conversation,
|
|
7
|
-
ConversationMessages,
|
|
8
|
-
ConversationScrollButton,
|
|
9
|
-
ConversationStatus,
|
|
10
|
-
} from "@/components/ai-conversation";
|
|
11
|
-
import {
|
|
12
|
-
Message,
|
|
13
|
-
MessageActions,
|
|
14
|
-
MessageAvatar,
|
|
15
|
-
MessageBody,
|
|
16
|
-
MessageFooter,
|
|
17
|
-
} from "@/components/ai-message";
|
|
18
|
-
import { ModelSelector, type ModelOption } from "@/components/ai-model-selector";
|
|
19
|
-
import {
|
|
20
|
-
PromptInput,
|
|
21
|
-
PromptInputSubmit,
|
|
22
|
-
PromptInputTextarea,
|
|
23
|
-
PromptInputToolbar,
|
|
24
|
-
} from "@/components/ai-prompt-input";
|
|
25
|
-
import { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-reasoning";
|
|
26
|
-
import { Response, ThinkingIndicator } from "@/components/ai-response";
|
|
27
|
-
import { Source, Sources, SourcesContent, SourcesTrigger } from "@/components/ai-sources";
|
|
28
|
-
import { TokenCount } from "@/components/ai-token-usage";
|
|
29
|
-
import {
|
|
30
|
-
Tool,
|
|
31
|
-
ToolContent,
|
|
32
|
-
ToolHeader,
|
|
33
|
-
ToolPayload,
|
|
34
|
-
ToolSection,
|
|
35
|
-
type ToolStatus,
|
|
36
|
-
} from "@/components/ai-tool";
|
|
37
|
-
import { Button } from "@/components/button";
|
|
38
|
-
import { EmptyState, EmptyStateDescription, EmptyStateTitle } from "@/components/empty-state";
|
|
39
|
-
import { cn } from "@/lib/utils";
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* A complete chat surface.
|
|
43
|
-
*
|
|
44
|
-
* Assembled from the AI components rather than reimplementing any of them, so
|
|
45
|
-
* every decision they encode still holds here: the transcript is a list and not
|
|
46
|
-
* a live region, state is announced separately, and the composer will not send
|
|
47
|
-
* mid-IME-composition.
|
|
48
|
-
*/
|
|
49
|
-
|
|
50
|
-
export interface ChatToolCall {
|
|
51
|
-
id: string;
|
|
52
|
-
name: string;
|
|
53
|
-
status: ToolStatus;
|
|
54
|
-
arguments?: string;
|
|
55
|
-
result?: string;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface ChatSource {
|
|
59
|
-
index: number;
|
|
60
|
-
title: string;
|
|
61
|
-
origin?: string;
|
|
62
|
-
href?: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface ChatMessage {
|
|
66
|
-
id: string;
|
|
67
|
-
from: "user" | "assistant";
|
|
68
|
-
content: string;
|
|
69
|
-
reasoning?: string;
|
|
70
|
-
tools?: ChatToolCall[];
|
|
71
|
-
sources?: ChatSource[];
|
|
72
|
-
tokens?: number;
|
|
73
|
-
/** Still arriving: shows the caret. */
|
|
74
|
-
streaming?: boolean;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface AiChatBlockProps {
|
|
78
|
-
messages: ChatMessage[];
|
|
79
|
-
onSend?: (message: string) => void;
|
|
80
|
-
onStop?: () => void;
|
|
81
|
-
/** A response is in flight. */
|
|
82
|
-
busy?: boolean;
|
|
83
|
-
/** Shown before the first token arrives. */
|
|
84
|
-
waiting?: boolean;
|
|
85
|
-
models?: ModelOption[];
|
|
86
|
-
model?: string;
|
|
87
|
-
onModelChange?: (model: string) => void;
|
|
88
|
-
/** Shown when the transcript is empty. */
|
|
89
|
-
empty?: ReactNode;
|
|
90
|
-
placeholder?: string;
|
|
91
|
-
className?: string;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function AiChatBlock({
|
|
95
|
-
messages,
|
|
96
|
-
onSend,
|
|
97
|
-
onStop,
|
|
98
|
-
busy = false,
|
|
99
|
-
waiting = false,
|
|
100
|
-
models,
|
|
101
|
-
model,
|
|
102
|
-
onModelChange,
|
|
103
|
-
empty,
|
|
104
|
-
placeholder = "Ask anything…",
|
|
105
|
-
className,
|
|
106
|
-
}: AiChatBlockProps) {
|
|
107
|
-
const [draft, setDraft] = useState("");
|
|
108
|
-
|
|
109
|
-
function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
|
110
|
-
event.preventDefault();
|
|
111
|
-
const trimmed = draft.trim();
|
|
112
|
-
if (!trimmed) return;
|
|
113
|
-
onSend?.(trimmed);
|
|
114
|
-
setDraft("");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return (
|
|
118
|
-
<div className={cn("flex h-full min-h-0 flex-col", className)}>
|
|
119
|
-
<Conversation>
|
|
120
|
-
{messages.length === 0 ? (
|
|
121
|
-
<div className="grid h-full place-items-center p-8">
|
|
122
|
-
{empty ?? (
|
|
123
|
-
<EmptyState>
|
|
124
|
-
<EmptyStateTitle>Start a conversation</EmptyStateTitle>
|
|
125
|
-
<EmptyStateDescription>
|
|
126
|
-
Ask a question, paste an error, or describe what you are trying to build.
|
|
127
|
-
</EmptyStateDescription>
|
|
128
|
-
</EmptyState>
|
|
129
|
-
)}
|
|
130
|
-
</div>
|
|
131
|
-
) : (
|
|
132
|
-
<ConversationMessages>
|
|
133
|
-
{messages.map((message) => (
|
|
134
|
-
<Message key={message.id} from={message.from}>
|
|
135
|
-
{message.from === "assistant" ? <MessageAvatar>AI</MessageAvatar> : null}
|
|
136
|
-
<MessageBody from={message.from} className="min-w-0 flex-1">
|
|
137
|
-
{message.reasoning ? (
|
|
138
|
-
<Reasoning className="mb-2">
|
|
139
|
-
<ReasoningTrigger streaming={message.streaming} />
|
|
140
|
-
<ReasoningContent>{message.reasoning}</ReasoningContent>
|
|
141
|
-
</Reasoning>
|
|
142
|
-
) : null}
|
|
143
|
-
|
|
144
|
-
{message.tools?.map((tool) => (
|
|
145
|
-
<Tool key={tool.id} status={tool.status} className="mb-2">
|
|
146
|
-
<ToolHeader name={tool.name} status={tool.status} />
|
|
147
|
-
<ToolContent>
|
|
148
|
-
{tool.arguments ? (
|
|
149
|
-
<ToolSection label="Arguments">
|
|
150
|
-
<ToolPayload label={`${tool.name} arguments`}>
|
|
151
|
-
{tool.arguments}
|
|
152
|
-
</ToolPayload>
|
|
153
|
-
</ToolSection>
|
|
154
|
-
) : null}
|
|
155
|
-
{tool.result ? (
|
|
156
|
-
<ToolSection label="Result">
|
|
157
|
-
<ToolPayload label={`${tool.name} result`}>
|
|
158
|
-
{tool.result}
|
|
159
|
-
</ToolPayload>
|
|
160
|
-
</ToolSection>
|
|
161
|
-
) : null}
|
|
162
|
-
</ToolContent>
|
|
163
|
-
</Tool>
|
|
164
|
-
))}
|
|
165
|
-
|
|
166
|
-
{message.from === "assistant" ? (
|
|
167
|
-
<Response streaming={message.streaming}>{message.content}</Response>
|
|
168
|
-
) : (
|
|
169
|
-
message.content
|
|
170
|
-
)}
|
|
171
|
-
|
|
172
|
-
{message.sources && message.sources.length > 0 ? (
|
|
173
|
-
<Sources className="mt-3">
|
|
174
|
-
<SourcesTrigger count={message.sources.length} />
|
|
175
|
-
<SourcesContent>
|
|
176
|
-
{message.sources.map((source) => (
|
|
177
|
-
<Source
|
|
178
|
-
key={source.index}
|
|
179
|
-
index={source.index}
|
|
180
|
-
title={source.title}
|
|
181
|
-
origin={source.origin}
|
|
182
|
-
href={source.href ?? "#"}
|
|
183
|
-
/>
|
|
184
|
-
))}
|
|
185
|
-
</SourcesContent>
|
|
186
|
-
</Sources>
|
|
187
|
-
) : null}
|
|
188
|
-
|
|
189
|
-
{message.from === "assistant" && !message.streaming ? (
|
|
190
|
-
<>
|
|
191
|
-
<MessageActions>
|
|
192
|
-
<Button variant="ghost" size="icon-sm" aria-label="Copy response">
|
|
193
|
-
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
194
|
-
<rect
|
|
195
|
-
x="9"
|
|
196
|
-
y="9"
|
|
197
|
-
width="12"
|
|
198
|
-
height="12"
|
|
199
|
-
rx="2"
|
|
200
|
-
stroke="currentColor"
|
|
201
|
-
strokeWidth="2"
|
|
202
|
-
/>
|
|
203
|
-
<path
|
|
204
|
-
d="M5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1"
|
|
205
|
-
stroke="currentColor"
|
|
206
|
-
strokeWidth="2"
|
|
207
|
-
strokeLinecap="round"
|
|
208
|
-
/>
|
|
209
|
-
</svg>
|
|
210
|
-
</Button>
|
|
211
|
-
<Button variant="ghost" size="icon-sm" aria-label="Regenerate response">
|
|
212
|
-
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
213
|
-
<path
|
|
214
|
-
d="M20 12a8 8 0 1 1-2.3-5.6M20 4v5h-5"
|
|
215
|
-
stroke="currentColor"
|
|
216
|
-
strokeWidth="2"
|
|
217
|
-
strokeLinecap="round"
|
|
218
|
-
strokeLinejoin="round"
|
|
219
|
-
/>
|
|
220
|
-
</svg>
|
|
221
|
-
</Button>
|
|
222
|
-
</MessageActions>
|
|
223
|
-
{message.tokens === undefined ? null : (
|
|
224
|
-
<MessageFooter>
|
|
225
|
-
<TokenCount value={message.tokens} />
|
|
226
|
-
</MessageFooter>
|
|
227
|
-
)}
|
|
228
|
-
</>
|
|
229
|
-
) : null}
|
|
230
|
-
</MessageBody>
|
|
231
|
-
</Message>
|
|
232
|
-
))}
|
|
233
|
-
|
|
234
|
-
{waiting ? (
|
|
235
|
-
<Message from="assistant">
|
|
236
|
-
<MessageAvatar>AI</MessageAvatar>
|
|
237
|
-
<MessageBody from="assistant">
|
|
238
|
-
<ThinkingIndicator />
|
|
239
|
-
</MessageBody>
|
|
240
|
-
</Message>
|
|
241
|
-
) : null}
|
|
242
|
-
</ConversationMessages>
|
|
243
|
-
)}
|
|
244
|
-
|
|
245
|
-
<ConversationScrollButton />
|
|
246
|
-
</Conversation>
|
|
247
|
-
|
|
248
|
-
{/* State, never content — the transcript above stays navigable at the
|
|
249
|
-
reader's own pace. */}
|
|
250
|
-
<ConversationStatus className="px-4 pb-1">
|
|
251
|
-
{waiting ? "Waiting for a response" : busy ? "Generating response" : ""}
|
|
252
|
-
</ConversationStatus>
|
|
253
|
-
|
|
254
|
-
<div className="border-t border-border p-4">
|
|
255
|
-
<PromptInput busy={busy} onSubmit={handleSubmit}>
|
|
256
|
-
<PromptInputTextarea
|
|
257
|
-
aria-label="Message"
|
|
258
|
-
placeholder={placeholder}
|
|
259
|
-
value={draft}
|
|
260
|
-
onChange={(event) => {
|
|
261
|
-
setDraft(event.target.value);
|
|
262
|
-
}}
|
|
263
|
-
/>
|
|
264
|
-
<PromptInputToolbar>
|
|
265
|
-
{models && models.length > 0 ? (
|
|
266
|
-
<ModelSelector
|
|
267
|
-
aria-label="Model"
|
|
268
|
-
models={models}
|
|
269
|
-
value={model}
|
|
270
|
-
onValueChange={onModelChange}
|
|
271
|
-
triggerSize="sm"
|
|
272
|
-
/>
|
|
273
|
-
) : null}
|
|
274
|
-
<PromptInputSubmit onStop={onStop} />
|
|
275
|
-
</PromptInputToolbar>
|
|
276
|
-
</PromptInput>
|
|
277
|
-
</div>
|
|
278
|
-
</div>
|
|
279
|
-
);
|
|
280
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { defineMeta } from "@/registry/schema";
|
|
2
|
-
|
|
3
|
-
export const meta = defineMeta({
|
|
4
|
-
name: "ai-chat",
|
|
5
|
-
kind: "block",
|
|
6
|
-
title: "AI Chat",
|
|
7
|
-
description:
|
|
8
|
-
"A complete chat surface: transcript, reasoning, tool calls, sources and composer.",
|
|
9
|
-
category: "ai",
|
|
10
|
-
status: "stable",
|
|
11
|
-
dependencies: [],
|
|
12
|
-
registryDependencies: [
|
|
13
|
-
"ai-conversation",
|
|
14
|
-
"ai-message",
|
|
15
|
-
"ai-model-selector",
|
|
16
|
-
"ai-prompt-input",
|
|
17
|
-
"ai-reasoning",
|
|
18
|
-
"ai-response",
|
|
19
|
-
"ai-sources",
|
|
20
|
-
"ai-token-usage",
|
|
21
|
-
"ai-tool",
|
|
22
|
-
"button",
|
|
23
|
-
"empty-state",
|
|
24
|
-
],
|
|
25
|
-
files: ["ai-chat.tsx"],
|
|
26
|
-
a11y:
|
|
27
|
-
"Assembled from the AI components rather than reimplementing them, so their decisions still " +
|
|
28
|
-
"hold: the transcript is a list and not a live region, state is announced separately through " +
|
|
29
|
-
"ConversationStatus, and the composer will not send while an IME composition is active. Tool " +
|
|
30
|
-
"payloads are named per tool, so two open tool calls do not present two regions called " +
|
|
31
|
-
'"Result".',
|
|
32
|
-
});
|