@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.
Files changed (218) hide show
  1. package/README.md +1 -1
  2. package/dist/blocks/admin-dashboard/meta.js +31 -0
  3. package/dist/blocks/admin-dashboard/meta.js.map +1 -0
  4. package/dist/blocks/agent-console/meta.js +27 -0
  5. package/dist/blocks/agent-console/meta.js.map +1 -0
  6. package/dist/blocks/ai-dashboard/meta.js +25 -0
  7. package/dist/blocks/ai-dashboard/meta.js.map +1 -0
  8. package/dist/blocks/ai-workspace/meta.js +33 -0
  9. package/dist/blocks/ai-workspace/meta.js.map +1 -0
  10. package/dist/blocks/analytics/meta.js +24 -0
  11. package/dist/blocks/analytics/meta.js.map +1 -0
  12. package/dist/blocks/billing/meta.js +25 -0
  13. package/dist/blocks/billing/meta.js.map +1 -0
  14. package/dist/blocks/command-center/meta.js +28 -0
  15. package/dist/blocks/command-center/meta.js.map +1 -0
  16. package/dist/blocks/crm/meta.js +32 -0
  17. package/dist/blocks/crm/meta.js.map +1 -0
  18. package/dist/blocks/onboarding/meta.js +23 -0
  19. package/dist/blocks/onboarding/meta.js.map +1 -0
  20. package/dist/components/accordion/accordion.js +1 -1
  21. package/dist/components/accordion/accordion.js.map +1 -1
  22. package/dist/components/activity-feed/activity-feed.js +1 -1
  23. package/dist/components/activity-feed/activity-feed.js.map +1 -1
  24. package/dist/components/ai-agent-plan/ai-agent-plan.js +1 -1
  25. package/dist/components/ai-agent-plan/ai-agent-plan.js.map +1 -1
  26. package/dist/components/ai-extraction-review/ai-extraction-review.js +1 -1
  27. package/dist/components/ai-extraction-review/ai-extraction-review.js.map +1 -1
  28. package/dist/components/ai-prompt-input/ai-prompt-input.js +1 -1
  29. package/dist/components/ai-prompt-input/ai-prompt-input.js.map +1 -1
  30. package/dist/components/ai-reasoning/ai-reasoning.js +1 -1
  31. package/dist/components/ai-reasoning/ai-reasoning.js.map +1 -1
  32. package/dist/components/ai-response/ai-response.js +2 -2
  33. package/dist/components/ai-response/ai-response.js.map +1 -1
  34. package/dist/components/ai-tool/ai-tool.js +2 -2
  35. package/dist/components/ai-tool/ai-tool.js.map +1 -1
  36. package/dist/components/alert/alert.d.ts +1 -1
  37. package/dist/components/avatar/avatar.d.ts +1 -1
  38. package/dist/components/badge/badge.d.ts +1 -1
  39. package/dist/components/breadcrumb/breadcrumb.d.ts +52 -0
  40. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
  41. package/dist/components/breadcrumb/breadcrumb.js +141 -0
  42. package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
  43. package/dist/components/breadcrumb/index.d.ts +2 -0
  44. package/dist/components/breadcrumb/index.js +2 -0
  45. package/dist/components/breadcrumb/meta.js +17 -0
  46. package/dist/components/breadcrumb/meta.js.map +1 -0
  47. package/dist/components/button/button.d.ts +1 -1
  48. package/dist/components/calendar/calendar.js +2 -2
  49. package/dist/components/calendar/calendar.js.map +1 -1
  50. package/dist/components/code-block/code-block.js +1 -1
  51. package/dist/components/code-block/code-block.js.map +1 -1
  52. package/dist/components/collapsible/collapsible.d.ts +30 -0
  53. package/dist/components/collapsible/collapsible.d.ts.map +1 -0
  54. package/dist/components/collapsible/collapsible.js +38 -0
  55. package/dist/components/collapsible/collapsible.js.map +1 -0
  56. package/dist/components/collapsible/index.d.ts +2 -0
  57. package/dist/components/collapsible/index.js +2 -0
  58. package/dist/components/collapsible/meta.js +17 -0
  59. package/dist/components/collapsible/meta.js.map +1 -0
  60. package/dist/components/combobox/combobox.js +2 -2
  61. package/dist/components/combobox/combobox.js.map +1 -1
  62. package/dist/components/command/command.js +1 -1
  63. package/dist/components/command/command.js.map +1 -1
  64. package/dist/components/data-table/data-table.d.ts.map +1 -1
  65. package/dist/components/data-table/data-table.js +4 -1
  66. package/dist/components/data-table/data-table.js.map +1 -1
  67. package/dist/components/dialog/dialog.js +2 -2
  68. package/dist/components/dialog/dialog.js.map +1 -1
  69. package/dist/components/diff-viewer/diff-viewer.js +4 -4
  70. package/dist/components/diff-viewer/diff-viewer.js.map +1 -1
  71. package/dist/components/direction/direction.d.ts +35 -0
  72. package/dist/components/direction/direction.d.ts.map +1 -0
  73. package/dist/components/direction/direction.js +14 -0
  74. package/dist/components/direction/direction.js.map +1 -0
  75. package/dist/components/direction/index.d.ts +2 -0
  76. package/dist/components/direction/index.js +2 -0
  77. package/dist/components/direction/meta.js +17 -0
  78. package/dist/components/direction/meta.js.map +1 -0
  79. package/dist/components/drawer/drawer.js +1 -1
  80. package/dist/components/drawer/drawer.js.map +1 -1
  81. package/dist/components/dropdown-menu/dropdown-menu.d.ts.map +1 -1
  82. package/dist/components/dropdown-menu/dropdown-menu.js +9 -8
  83. package/dist/components/dropdown-menu/dropdown-menu.js.map +1 -1
  84. package/dist/components/input/input.js +1 -1
  85. package/dist/components/input/input.js.map +1 -1
  86. package/dist/components/log-viewer/log-viewer.js +2 -2
  87. package/dist/components/log-viewer/log-viewer.js.map +1 -1
  88. package/dist/components/meter/meter.js +1 -1
  89. package/dist/components/meter/meter.js.map +1 -1
  90. package/dist/components/pagination/pagination.d.ts.map +1 -1
  91. package/dist/components/pagination/pagination.js +3 -2
  92. package/dist/components/pagination/pagination.js.map +1 -1
  93. package/dist/components/permission-matrix/permission-matrix.js +3 -3
  94. package/dist/components/permission-matrix/permission-matrix.js.map +1 -1
  95. package/dist/components/progress/progress.d.ts +1 -1
  96. package/dist/components/record-diff/record-diff.js +4 -4
  97. package/dist/components/record-diff/record-diff.js.map +1 -1
  98. package/dist/components/select/select.js +2 -2
  99. package/dist/components/select/select.js.map +1 -1
  100. package/dist/components/sheet/sheet.d.ts.map +1 -1
  101. package/dist/components/sheet/sheet.js +2 -2
  102. package/dist/components/sheet/sheet.js.map +1 -1
  103. package/dist/components/sidebar/index.d.ts +2 -0
  104. package/dist/components/sidebar/index.js +2 -0
  105. package/dist/components/sidebar/meta.js +17 -0
  106. package/dist/components/sidebar/meta.js.map +1 -0
  107. package/dist/components/sidebar/sidebar.d.ts +69 -0
  108. package/dist/components/sidebar/sidebar.d.ts.map +1 -0
  109. package/dist/components/sidebar/sidebar.js +256 -0
  110. package/dist/components/sidebar/sidebar.js.map +1 -0
  111. package/dist/components/spinner/spinner.d.ts +1 -1
  112. package/dist/components/table/table.js +2 -2
  113. package/dist/components/table/table.js.map +1 -1
  114. package/dist/components/tags-input/tags-input.js +1 -1
  115. package/dist/components/tags-input/tags-input.js.map +1 -1
  116. package/dist/components/textarea/index.d.ts +2 -0
  117. package/dist/components/textarea/index.js +2 -0
  118. package/dist/components/textarea/meta.js +17 -0
  119. package/dist/components/textarea/meta.js.map +1 -0
  120. package/dist/components/textarea/textarea.d.ts +43 -0
  121. package/dist/components/textarea/textarea.d.ts.map +1 -0
  122. package/dist/components/textarea/textarea.js +119 -0
  123. package/dist/components/textarea/textarea.js.map +1 -0
  124. package/dist/components/time-range-picker/time-range-picker.js +3 -3
  125. package/dist/components/time-range-picker/time-range-picker.js.map +1 -1
  126. package/dist/components/toast/toast.d.ts +1 -1
  127. package/dist/components/toast/toast.d.ts.map +1 -1
  128. package/dist/components/toast/toast.js +2 -2
  129. package/dist/components/toast/toast.js.map +1 -1
  130. package/dist/index.d.ts +12 -2
  131. package/dist/index.js +7 -2
  132. package/dist/lib/styles.d.ts +16 -1
  133. package/dist/lib/styles.d.ts.map +1 -1
  134. package/dist/lib/styles.js +16 -1
  135. package/dist/lib/styles.js.map +1 -1
  136. package/dist/registry/blocks.d.ts.map +1 -1
  137. package/dist/registry/blocks.js +27 -9
  138. package/dist/registry/blocks.js.map +1 -1
  139. package/dist/registry/components.d.ts.map +1 -1
  140. package/dist/registry/components.js +60 -50
  141. package/dist/registry/components.js.map +1 -1
  142. package/dist/registry/schema.d.ts +8 -0
  143. package/dist/registry/schema.d.ts.map +1 -1
  144. package/dist/registry/schema.js.map +1 -1
  145. package/package.json +5 -4
  146. package/src/components/accordion/accordion.tsx +1 -1
  147. package/src/components/activity-feed/activity-feed.tsx +1 -1
  148. package/src/components/ai-agent-plan/ai-agent-plan.tsx +0 -0
  149. package/src/components/ai-extraction-review/ai-extraction-review.tsx +1 -1
  150. package/src/components/ai-prompt-input/ai-prompt-input.tsx +1 -1
  151. package/src/components/ai-reasoning/ai-reasoning.tsx +1 -1
  152. package/src/components/ai-response/ai-response.tsx +2 -2
  153. package/src/components/ai-tool/ai-tool.tsx +2 -2
  154. package/src/components/breadcrumb/breadcrumb.tsx +157 -0
  155. package/src/components/breadcrumb/index.ts +10 -0
  156. package/src/components/breadcrumb/meta.ts +19 -0
  157. package/src/components/calendar/calendar.tsx +2 -2
  158. package/src/components/code-block/code-block.tsx +1 -1
  159. package/src/components/collapsible/collapsible.tsx +63 -0
  160. package/src/components/collapsible/index.ts +6 -0
  161. package/src/components/collapsible/meta.ts +16 -0
  162. package/src/components/combobox/combobox.tsx +2 -2
  163. package/src/components/command/command.tsx +1 -1
  164. package/src/components/data-table/data-table.tsx +14 -3
  165. package/src/components/dialog/dialog.tsx +2 -2
  166. package/src/components/diff-viewer/diff-viewer.tsx +4 -4
  167. package/src/components/direction/direction.tsx +38 -0
  168. package/src/components/direction/index.ts +1 -0
  169. package/src/components/direction/meta.ts +19 -0
  170. package/src/components/drawer/drawer.tsx +1 -1
  171. package/src/components/dropdown-menu/dropdown-menu.tsx +14 -8
  172. package/src/components/input/input.tsx +1 -1
  173. package/src/components/log-viewer/log-viewer.tsx +2 -2
  174. package/src/components/meter/meter.tsx +1 -1
  175. package/src/components/pagination/pagination.tsx +13 -2
  176. package/src/components/permission-matrix/permission-matrix.tsx +4 -4
  177. package/src/components/record-diff/record-diff.tsx +4 -4
  178. package/src/components/select/select.tsx +2 -2
  179. package/src/components/sheet/sheet.tsx +8 -4
  180. package/src/components/sidebar/index.ts +19 -0
  181. package/src/components/sidebar/meta.ts +25 -0
  182. package/src/components/sidebar/sidebar.tsx +374 -0
  183. package/src/components/table/table.tsx +3 -3
  184. package/src/components/tags-input/tags-input.tsx +1 -1
  185. package/src/components/textarea/index.ts +1 -0
  186. package/src/components/textarea/meta.ts +21 -0
  187. package/src/components/textarea/textarea.tsx +210 -0
  188. package/src/components/time-range-picker/time-range-picker.tsx +3 -3
  189. package/src/components/toast/toast.tsx +14 -6
  190. package/src/index.ts +5 -0
  191. package/src/lib/styles.ts +16 -0
  192. package/src/registry/blocks.ts +18 -0
  193. package/src/registry/components.ts +10 -0
  194. package/src/registry/schema.ts +8 -0
  195. package/src/blocks/admin-users/admin-users.tsx +0 -251
  196. package/src/blocks/admin-users/index.ts +0 -1
  197. package/src/blocks/admin-users/meta.ts +0 -27
  198. package/src/blocks/ai-chat/ai-chat.tsx +0 -280
  199. package/src/blocks/ai-chat/index.ts +0 -7
  200. package/src/blocks/ai-chat/meta.ts +0 -32
  201. package/src/blocks/dashboard/dashboard.tsx +0 -192
  202. package/src/blocks/dashboard/index.ts +0 -6
  203. package/src/blocks/dashboard/meta.ts +0 -18
  204. package/src/blocks/forgot-password/forgot-password.tsx +0 -158
  205. package/src/blocks/forgot-password/index.ts +0 -1
  206. package/src/blocks/forgot-password/meta.ts +0 -18
  207. package/src/blocks/login/index.ts +0 -1
  208. package/src/blocks/login/login.tsx +0 -178
  209. package/src/blocks/login/meta.ts +0 -18
  210. package/src/blocks/pricing/index.ts +0 -1
  211. package/src/blocks/pricing/meta.ts +0 -18
  212. package/src/blocks/pricing/pricing.tsx +0 -176
  213. package/src/blocks/settings/index.ts +0 -6
  214. package/src/blocks/settings/meta.ts +0 -29
  215. package/src/blocks/settings/settings.tsx +0 -317
  216. package/src/blocks/signup/index.ts +0 -1
  217. package/src/blocks/signup/meta.ts +0 -18
  218. package/src/blocks/signup/signup.tsx +0 -240
@@ -1,176 +0,0 @@
1
- "use client";
2
-
3
- import { useState, type ReactNode } from "react";
4
-
5
- import { Badge } from "@/components/badge";
6
- import { Button } from "@/components/button";
7
- import {
8
- Card,
9
- CardContent,
10
- CardDescription,
11
- CardFooter,
12
- CardHeader,
13
- CardTitle,
14
- } from "@/components/card";
15
- import { Label } from "@/components/label";
16
- import { Switch } from "@/components/switch";
17
- import { cn } from "@/lib/utils";
18
-
19
- /**
20
- * A pricing section.
21
- *
22
- * The billing-period toggle is the part that usually goes wrong. It changes
23
- * every price on the page at once, which is a large change nobody asked for in
24
- * words — so the prices update in place and the change is announced, rather
25
- * than silently rewriting the page under the reader.
26
- */
27
-
28
- export interface PricingPlan {
29
- id: string;
30
- name: string;
31
- description: string;
32
- /** Monthly price in whole currency units. `null` means "talk to us". */
33
- monthly: number | null;
34
- /** Yearly price per month. Omit to bill yearly at the monthly rate. */
35
- yearlyPerMonth?: number;
36
- features: string[];
37
- /** Marks the recommended plan. At most one. */
38
- featured?: boolean;
39
- cta?: string;
40
- onSelect?: () => void;
41
- }
42
-
43
- export interface PricingBlockProps {
44
- title?: string;
45
- description?: string;
46
- plans: PricingPlan[];
47
- currency?: string;
48
- /** Shows the monthly/yearly toggle. */
49
- showPeriodToggle?: boolean;
50
- footnote?: ReactNode;
51
- className?: string;
52
- }
53
-
54
- export function PricingBlock({
55
- title = "Pricing",
56
- description = "Start free. Upgrade when you need to.",
57
- plans,
58
- currency = "$",
59
- showPeriodToggle = true,
60
- footnote,
61
- className,
62
- }: PricingBlockProps) {
63
- const [yearly, setYearly] = useState(false);
64
-
65
- function priceOf(plan: PricingPlan): { amount: string; suffix: string } {
66
- if (plan.monthly === null) return { amount: "Custom", suffix: "" };
67
- const value = yearly ? (plan.yearlyPerMonth ?? plan.monthly) : plan.monthly;
68
- return { amount: `${currency}${String(value)}`, suffix: "/month" };
69
- }
70
-
71
- return (
72
- <section aria-labelledby="pricing-heading" className={cn("flex flex-col gap-8", className)}>
73
- <div className="text-center">
74
- <h2 id="pricing-heading" className="text-2xl font-semibold tracking-tight text-balance">
75
- {title}
76
- </h2>
77
- <p className="mx-auto mt-2 max-w-lg text-pretty text-muted-foreground">{description}</p>
78
-
79
- {showPeriodToggle ? (
80
- <div className="mt-6 flex items-center justify-center gap-3">
81
- <Label htmlFor="billing-period" className="text-sm font-normal">
82
- Monthly
83
- </Label>
84
- <Switch
85
- id="billing-period"
86
- checked={yearly}
87
- onCheckedChange={setYearly}
88
- aria-label="Bill yearly"
89
- />
90
- <Label htmlFor="billing-period" className="text-sm font-normal">
91
- Yearly
92
- </Label>
93
- <Badge size="sm" variant="success">
94
- Save 20%
95
- </Badge>
96
- </div>
97
- ) : null}
98
- </div>
99
-
100
- {/* Every price on the page changes at once, which is a big change nobody
101
- asked for out loud. Announcing it politely is the difference between a
102
- helpful toggle and the page silently rewriting itself. */}
103
- <p aria-live="polite" className="sr-only">
104
- Showing {yearly ? "yearly" : "monthly"} pricing.
105
- </p>
106
-
107
- <div className="grid gap-6 md:grid-cols-3">
108
- {plans.map((plan) => {
109
- const price = priceOf(plan);
110
-
111
- return (
112
- <Card
113
- key={plan.id}
114
- className={cn("flex flex-col", plan.featured && "border-primary shadow-lg")}
115
- >
116
- <CardHeader>
117
- <div className="flex items-center justify-between gap-2">
118
- <CardTitle>{plan.name}</CardTitle>
119
- {plan.featured ? <Badge size="sm">Recommended</Badge> : null}
120
- </div>
121
- <CardDescription>{plan.description}</CardDescription>
122
- <p className="mt-3 flex items-baseline gap-1">
123
- <span className="text-3xl font-semibold tabular-nums">{price.amount}</span>
124
- {price.suffix ? (
125
- <span className="text-sm text-muted-foreground">{price.suffix}</span>
126
- ) : null}
127
- </p>
128
- {yearly && plan.monthly !== null && plan.yearlyPerMonth !== undefined ? (
129
- <p className="text-xs text-muted-foreground">Billed yearly.</p>
130
- ) : null}
131
- </CardHeader>
132
-
133
- <CardContent className="flex-1">
134
- <ul className="flex flex-col gap-2 text-sm">
135
- {plan.features.map((feature) => (
136
- <li key={feature} className="flex items-start gap-2">
137
- <svg
138
- viewBox="0 0 24 24"
139
- fill="none"
140
- aria-hidden="true"
141
- className="mt-0.5 size-4 shrink-0 text-success"
142
- >
143
- <path
144
- d="m5 13 4 4L19 7"
145
- stroke="currentColor"
146
- strokeWidth="2.5"
147
- strokeLinecap="round"
148
- strokeLinejoin="round"
149
- />
150
- </svg>
151
- <span className="text-muted-foreground">{feature}</span>
152
- </li>
153
- ))}
154
- </ul>
155
- </CardContent>
156
-
157
- <CardFooter>
158
- <Button
159
- className="w-full"
160
- variant={plan.featured ? "primary" : "outline"}
161
- onClick={plan.onSelect}
162
- >
163
- {plan.cta ?? `Choose ${plan.name}`}
164
- </Button>
165
- </CardFooter>
166
- </Card>
167
- );
168
- })}
169
- </div>
170
-
171
- {footnote ? (
172
- <p className="text-center text-xs text-muted-foreground">{footnote}</p>
173
- ) : null}
174
- </section>
175
- );
176
- }
@@ -1,6 +0,0 @@
1
- export {
2
- SettingsBlock,
3
- type SettingsBlockProps,
4
- type SettingsNotification,
5
- type SettingsProfile,
6
- } from "./settings";
@@ -1,29 +0,0 @@
1
- import { defineMeta } from "@/registry/schema";
2
-
3
- export const meta = defineMeta({
4
- name: "settings",
5
- kind: "block",
6
- title: "Settings",
7
- description: "A settings page with a profile form, immediate toggles and a danger zone.",
8
- category: "form",
9
- status: "stable",
10
- dependencies: [],
11
- registryDependencies: [
12
- "avatar",
13
- "button",
14
- "card",
15
- "dialog",
16
- "form",
17
- "input",
18
- "label",
19
- "separator",
20
- "switch",
21
- "tabs",
22
- ],
23
- files: ["settings.tsx"],
24
- a11y:
25
- "Switches apply immediately and have no Save button — a switch that needs saving is a broken " +
26
- "promise, so staged changes live in the form instead. The delete confirmation names what will " +
27
- "be destroyed and requires typing the account's email, so the destructive button cannot be " +
28
- "reached by muscle memory.",
29
- });
@@ -1,317 +0,0 @@
1
- "use client";
2
-
3
- import { useState, type FormEvent, type ReactNode } from "react";
4
-
5
- import { Avatar, AvatarFallback } from "@/components/avatar";
6
- import { Button } from "@/components/button";
7
- import {
8
- Card,
9
- CardContent,
10
- CardDescription,
11
- CardFooter,
12
- CardHeader,
13
- CardTitle,
14
- } from "@/components/card";
15
- import {
16
- Dialog,
17
- DialogClose,
18
- DialogContent,
19
- DialogDescription,
20
- DialogFooter,
21
- DialogHeader,
22
- DialogTitle,
23
- DialogTrigger,
24
- } from "@/components/dialog";
25
- import {
26
- Form,
27
- FormControl,
28
- FormDescription,
29
- FormField,
30
- FormLabel,
31
- FormMessage,
32
- } from "@/components/form";
33
- import { Input } from "@/components/input";
34
- import { Label } from "@/components/label";
35
- import { Separator } from "@/components/separator";
36
- import { Switch } from "@/components/switch";
37
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/tabs";
38
- import { cn } from "@/lib/utils";
39
-
40
- /**
41
- * A settings page: profile, notifications and a danger zone.
42
- *
43
- * Two things are deliberate. Switches apply immediately and have no Save button
44
- * — a switch that needs saving is a broken promise, so anything staged belongs
45
- * in a form instead. And the destructive action asks for confirmation naming
46
- * exactly what will be destroyed, rather than a generic "Are you sure?".
47
- */
48
-
49
- export interface SettingsProfile {
50
- name: string;
51
- email: string;
52
- bio?: string;
53
- }
54
-
55
- export interface SettingsNotification {
56
- id: string;
57
- label: string;
58
- description: string;
59
- enabled: boolean;
60
- }
61
-
62
- export interface SettingsBlockProps {
63
- profile: SettingsProfile;
64
- notifications: SettingsNotification[];
65
- /** Staged: the profile form has a Save button. */
66
- onSaveProfile?: (profile: SettingsProfile) => void;
67
- /** Immediate: a switch applies as soon as it is flipped. */
68
- onToggleNotification?: (id: string, enabled: boolean) => void;
69
- onDeleteAccount?: () => void;
70
- pending?: boolean;
71
- /** Extra tabs, if the product needs them. */
72
- extraTabs?: { value: string; label: string; content: ReactNode }[];
73
- className?: string;
74
- }
75
-
76
- export function SettingsBlock({
77
- profile,
78
- notifications,
79
- onSaveProfile,
80
- onToggleNotification,
81
- onDeleteAccount,
82
- pending = false,
83
- extraTabs = [],
84
- className,
85
- }: SettingsBlockProps) {
86
- const [name, setName] = useState(profile.name);
87
- const [email, setEmail] = useState(profile.email);
88
- const [bio, setBio] = useState(profile.bio ?? "");
89
- const [touched, setTouched] = useState(false);
90
- const [confirmation, setConfirmation] = useState("");
91
-
92
- const emailError =
93
- touched && !email.includes("@") ? "Enter a valid email address." : undefined;
94
- const nameError = touched && name.trim().length === 0 ? "Enter your name." : undefined;
95
-
96
- function handleSave(event: FormEvent<HTMLFormElement>) {
97
- event.preventDefault();
98
- setTouched(true);
99
- if (!email.includes("@") || name.trim().length === 0) return;
100
- onSaveProfile?.({ name, email, bio });
101
- }
102
-
103
- return (
104
- <div className={cn("flex flex-col gap-6", className)}>
105
- <div>
106
- <h1 className="text-xl font-semibold tracking-tight">Settings</h1>
107
- <p className="mt-1 text-sm text-muted-foreground">
108
- Manage your profile and how you are notified.
109
- </p>
110
- </div>
111
-
112
- <Tabs defaultValue="profile">
113
- <TabsList variant="underline">
114
- <TabsTrigger value="profile" variant="underline">
115
- Profile
116
- </TabsTrigger>
117
- <TabsTrigger value="notifications" variant="underline">
118
- Notifications
119
- </TabsTrigger>
120
- {extraTabs.map((tab) => (
121
- <TabsTrigger key={tab.value} value={tab.value} variant="underline">
122
- {tab.label}
123
- </TabsTrigger>
124
- ))}
125
- <TabsTrigger value="danger" variant="underline">
126
- Danger zone
127
- </TabsTrigger>
128
- </TabsList>
129
-
130
- <TabsContent value="profile">
131
- <Card>
132
- <CardHeader>
133
- <CardTitle asChild>
134
- <h2>Profile</h2>
135
- </CardTitle>
136
- <CardDescription>This is how you appear to your team.</CardDescription>
137
- </CardHeader>
138
- <CardContent>
139
- <Form onSubmit={handleSave} className="gap-4">
140
- <div className="flex items-center gap-4">
141
- <Avatar size="lg">
142
- <AvatarFallback>
143
- {name
144
- .split(" ")
145
- .map((part) => part[0] ?? "")
146
- .slice(0, 2)
147
- .join("")
148
- .toUpperCase()}
149
- </AvatarFallback>
150
- </Avatar>
151
- <Button type="button" variant="outline" size="sm" disabled={pending}>
152
- Change photo
153
- </Button>
154
- </div>
155
-
156
- <FormField name="name" error={nameError}>
157
- <FormLabel>Name</FormLabel>
158
- <FormControl>
159
- <Input
160
- autoComplete="name"
161
- value={name}
162
- disabled={pending}
163
- onChange={(event) => {
164
- setName(event.target.value);
165
- }}
166
- />
167
- </FormControl>
168
- <FormMessage />
169
- </FormField>
170
-
171
- <FormField name="email" error={emailError}>
172
- <FormLabel>Email</FormLabel>
173
- <FormControl>
174
- <Input
175
- type="email"
176
- autoComplete="email"
177
- value={email}
178
- disabled={pending}
179
- onChange={(event) => {
180
- setEmail(event.target.value);
181
- }}
182
- />
183
- </FormControl>
184
- <FormDescription>Used for sign-in and notifications.</FormDescription>
185
- <FormMessage />
186
- </FormField>
187
-
188
- <FormField name="bio">
189
- <FormLabel>Bio</FormLabel>
190
- <FormControl>
191
- <Input
192
- value={bio}
193
- disabled={pending}
194
- placeholder="A sentence about you"
195
- onChange={(event) => {
196
- setBio(event.target.value);
197
- }}
198
- />
199
- </FormControl>
200
- </FormField>
201
-
202
- <div className="flex justify-end">
203
- <Button type="submit" loading={pending}>
204
- Save changes
205
- </Button>
206
- </div>
207
- </Form>
208
- </CardContent>
209
- </Card>
210
- </TabsContent>
211
-
212
- <TabsContent value="notifications">
213
- <Card>
214
- <CardHeader>
215
- <CardTitle asChild>
216
- <h2>Notifications</h2>
217
- </CardTitle>
218
- <CardDescription>
219
- These apply straight away — there is nothing to save.
220
- </CardDescription>
221
- </CardHeader>
222
- <CardContent className="divide-y divide-border p-0">
223
- {notifications.map((notification) => (
224
- <div
225
- key={notification.id}
226
- className="flex items-start justify-between gap-4 px-6 py-4"
227
- >
228
- <div className="grid gap-1">
229
- <Label htmlFor={notification.id}>{notification.label}</Label>
230
- <p className="text-xs text-muted-foreground">{notification.description}</p>
231
- </div>
232
- <Switch
233
- id={notification.id}
234
- checked={notification.enabled}
235
- onCheckedChange={(checked) => {
236
- onToggleNotification?.(notification.id, checked);
237
- }}
238
- />
239
- </div>
240
- ))}
241
- </CardContent>
242
- </Card>
243
- </TabsContent>
244
-
245
- {extraTabs.map((tab) => (
246
- <TabsContent key={tab.value} value={tab.value}>
247
- {tab.content}
248
- </TabsContent>
249
- ))}
250
-
251
- <TabsContent value="danger">
252
- <Card className="border-destructive/30">
253
- <CardHeader>
254
- <CardTitle asChild>
255
- <h2>Delete this account</h2>
256
- </CardTitle>
257
- <CardDescription>
258
- Everything in the workspace is removed permanently. This cannot be undone.
259
- </CardDescription>
260
- </CardHeader>
261
- <CardContent>
262
- <Separator className="mb-4" />
263
- <p className="text-sm text-muted-foreground">
264
- Your projects, deployments and audit history are deleted along with the account.
265
- </p>
266
- </CardContent>
267
- <CardFooter className="justify-end">
268
- <Dialog>
269
- <DialogTrigger asChild>
270
- <Button variant="destructive">Delete account</Button>
271
- </DialogTrigger>
272
- <DialogContent className="max-w-md">
273
- <DialogHeader>
274
- <DialogTitle>Delete {profile.name}&rsquo;s account?</DialogTitle>
275
- {/* Names what is destroyed. "Are you sure?" tells nobody
276
- anything they did not already know. */}
277
- <DialogDescription>
278
- This permanently deletes the account, its projects and all deployment
279
- history. It cannot be undone.
280
- </DialogDescription>
281
- </DialogHeader>
282
-
283
- <div className="grid gap-2">
284
- <Label htmlFor="confirm-delete">
285
- Type <span className="font-mono">{profile.email}</span> to confirm
286
- </Label>
287
- <Input
288
- id="confirm-delete"
289
- value={confirmation}
290
- autoComplete="off"
291
- onChange={(event) => {
292
- setConfirmation(event.target.value);
293
- }}
294
- />
295
- </div>
296
-
297
- <DialogFooter>
298
- <DialogClose asChild>
299
- <Button variant="outline">Cancel</Button>
300
- </DialogClose>
301
- <Button
302
- variant="destructive"
303
- disabled={confirmation !== profile.email}
304
- onClick={onDeleteAccount}
305
- >
306
- Delete account
307
- </Button>
308
- </DialogFooter>
309
- </DialogContent>
310
- </Dialog>
311
- </CardFooter>
312
- </Card>
313
- </TabsContent>
314
- </Tabs>
315
- </div>
316
- );
317
- }
@@ -1 +0,0 @@
1
- export { SignupBlock, estimatePasswordStrength, type SignupBlockProps } from "./signup";
@@ -1,18 +0,0 @@
1
- import { defineMeta } from "@/registry/schema";
2
-
3
- export const meta = defineMeta({
4
- name: "signup",
5
- kind: "block",
6
- title: "Sign up",
7
- description: "An account creation form with a password strength hint and terms acceptance.",
8
- category: "form",
9
- status: "stable",
10
- dependencies: [],
11
- registryDependencies: ["button", "card", "checkbox", "form", "input", "progress"],
12
- files: ["signup.tsx"],
13
- a11y:
14
- "The strength label is text and the bar beside it is aria-hidden, so the estimate is not " +
15
- 'announced twice. autoComplete="new-password" tells password managers to offer a generated ' +
16
- "password rather than an existing one. Strength is a hint, never a gate: the form is validated " +
17
- "on length alone.",
18
- });