create-einja-app 0.2.17 → 0.2.19

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 (275) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.js +685 -1715
  3. package/dist/cli.js.map +1 -1
  4. package/package.json +2 -2
  5. package/templates/default/.claude/hooks/einja/playwright-resize.sh +12 -2
  6. package/templates/default/.claude/settings.json +16 -0
  7. package/templates/default/.cursor/commands/task-vibe-kanban-loop.md +107 -42
  8. package/templates/default/.env.develop +0 -4
  9. package/templates/default/.env.example +1 -0
  10. package/templates/default/.env.preview +0 -4
  11. package/templates/default/.env.staging +19 -0
  12. package/templates/default/.github/actions/ci/action.yml +39 -0
  13. package/templates/default/.github/actions/migrate/action.yml +39 -0
  14. package/templates/default/.github/actions/neon-export-env/action.yml +28 -0
  15. package/templates/default/.github/actions/setup/action.yml +20 -0
  16. package/templates/default/.github/workflows/claude.yml +1 -0
  17. package/templates/default/.github/workflows/{cleanup-neon-branches.yml → cleanup-pr-preview-db.yml} +28 -24
  18. package/templates/default/.github/workflows/cleanup-pr-preview-on-close.yml +50 -0
  19. package/templates/default/.github/workflows/deploy-pr-preview.yml +398 -0
  20. package/templates/default/.github/workflows/deploy-stable-branches.yml +259 -0
  21. package/templates/default/.github/workflows/release-create-einja-app.yml +95 -0
  22. package/templates/default/.mcp.json +29 -11
  23. package/templates/default/.serena/project.yml +4 -0
  24. package/templates/default/.vscode/settings.json +18 -0
  25. package/templates/default/CLAUDE.md +129 -353
  26. package/templates/default/README.md +5 -14
  27. package/templates/default/apps/admin/next.config.ts +11 -0
  28. package/templates/default/apps/admin/package.json +55 -0
  29. package/templates/default/apps/admin/postcss.config.cjs +5 -0
  30. package/templates/default/apps/admin/src/app/(auth)/forgot-password/page.tsx +97 -0
  31. package/templates/default/apps/admin/src/app/(auth)/layout.tsx +18 -0
  32. package/templates/default/apps/admin/src/app/(auth)/otp/page.tsx +121 -0
  33. package/templates/default/apps/admin/src/app/(auth)/sign-in/page.tsx +145 -0
  34. package/templates/default/apps/admin/src/app/(auth)/sign-up/page.tsx +199 -0
  35. package/templates/default/apps/admin/src/app/(errors)/401/page.tsx +27 -0
  36. package/templates/default/apps/admin/src/app/(errors)/403/page.tsx +28 -0
  37. package/templates/default/apps/admin/src/app/(errors)/500/page.tsx +29 -0
  38. package/templates/default/apps/admin/src/app/(errors)/layout.tsx +7 -0
  39. package/templates/default/apps/admin/src/app/(errors)/maintenance/page.tsx +25 -0
  40. package/templates/default/apps/admin/src/app/dashboard/_components/analytics-chart.tsx +68 -0
  41. package/templates/default/apps/admin/src/app/dashboard/_components/analytics.tsx +182 -0
  42. package/templates/default/apps/admin/src/app/dashboard/_components/dashboard-page.tsx +74 -0
  43. package/templates/default/apps/admin/src/app/dashboard/_components/metric-cards.tsx +49 -0
  44. package/templates/default/apps/admin/src/app/dashboard/_components/overview-chart.tsx +73 -0
  45. package/templates/default/apps/admin/src/app/dashboard/_components/recent-sales.tsx +75 -0
  46. package/templates/default/apps/admin/src/app/dashboard/apps/_components/apps-page.tsx +135 -0
  47. package/templates/default/apps/admin/src/app/dashboard/apps/page.tsx +10 -0
  48. package/templates/default/apps/admin/src/app/dashboard/chats/_components/chat-list.tsx +82 -0
  49. package/templates/default/apps/admin/src/app/dashboard/chats/_components/chat-messages.tsx +194 -0
  50. package/templates/default/apps/admin/src/app/dashboard/chats/_components/chats-page.tsx +99 -0
  51. package/templates/default/apps/admin/src/app/dashboard/chats/_components/new-chat.tsx +118 -0
  52. package/templates/default/apps/admin/src/app/dashboard/chats/page.tsx +10 -0
  53. package/templates/default/apps/admin/src/app/dashboard/layout.tsx +9 -0
  54. package/templates/default/apps/admin/src/app/dashboard/not-found.tsx +14 -0
  55. package/templates/default/apps/admin/src/app/dashboard/page.tsx +10 -0
  56. package/templates/default/apps/admin/src/app/dashboard/settings/_components/content-section.tsx +20 -0
  57. package/templates/default/apps/admin/src/app/dashboard/settings/_components/sidebar-nav.tsx +66 -0
  58. package/templates/default/apps/admin/src/app/dashboard/settings/account/page.tsx +173 -0
  59. package/templates/default/apps/admin/src/app/dashboard/settings/appearance/page.tsx +156 -0
  60. package/templates/default/apps/admin/src/app/dashboard/settings/display/page.tsx +125 -0
  61. package/templates/default/apps/admin/src/app/dashboard/settings/layout.tsx +30 -0
  62. package/templates/default/apps/admin/src/app/dashboard/settings/notifications/page.tsx +196 -0
  63. package/templates/default/apps/admin/src/app/dashboard/settings/page.tsx +5 -0
  64. package/templates/default/apps/admin/src/app/dashboard/settings/profile/page.tsx +176 -0
  65. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/data-table-bulk-actions.tsx +183 -0
  66. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/data-table-row-actions.tsx +79 -0
  67. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-columns.tsx +107 -0
  68. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-dialogs.tsx +71 -0
  69. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-import-dialog.tsx +106 -0
  70. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-multi-delete-dialog.tsx +90 -0
  71. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-mutate-drawer.tsx +207 -0
  72. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-page.tsx +31 -0
  73. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-primary-buttons.tsx +19 -0
  74. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-provider.tsx +37 -0
  75. package/templates/default/apps/admin/src/app/dashboard/tasks/_components/tasks-table.tsx +155 -0
  76. package/templates/default/apps/admin/src/app/dashboard/tasks/page.tsx +14 -0
  77. package/templates/default/apps/admin/src/app/dashboard/users/_components/data-table-bulk-actions.tsx +136 -0
  78. package/templates/default/apps/admin/src/app/dashboard/users/_components/data-table-row-actions.tsx +62 -0
  79. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-action-dialog.tsx +297 -0
  80. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-columns.tsx +121 -0
  81. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-delete-dialog.tsx +72 -0
  82. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-dialogs.tsx +49 -0
  83. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-invite-dialog.tsx +139 -0
  84. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-multi-delete-dialog.tsx +89 -0
  85. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-page.tsx +30 -0
  86. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-primary-buttons.tsx +19 -0
  87. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-provider.tsx +35 -0
  88. package/templates/default/apps/admin/src/app/dashboard/users/_components/users-table.tsx +157 -0
  89. package/templates/default/apps/admin/src/app/dashboard/users/page.tsx +10 -0
  90. package/templates/default/apps/admin/src/app/globals.css +109 -0
  91. package/templates/default/apps/admin/src/app/layout.tsx +32 -0
  92. package/templates/default/apps/admin/src/app/not-found.tsx +14 -0
  93. package/templates/default/apps/admin/src/app/page.tsx +5 -0
  94. package/templates/default/apps/admin/src/components/layout/admin-layout.tsx +16 -0
  95. package/templates/default/apps/admin/src/components/layout/app-sidebar.tsx +52 -0
  96. package/templates/default/apps/admin/src/components/layout/nav-config.ts +131 -0
  97. package/templates/default/apps/admin/src/components/providers/theme-provider.tsx +10 -0
  98. package/templates/default/apps/admin/src/components/shared/long-text.tsx +78 -0
  99. package/templates/default/apps/admin/src/components/shared/search-input.tsx +16 -0
  100. package/templates/default/apps/admin/src/components/shared/select-dropdown.tsx +64 -0
  101. package/templates/default/apps/admin/src/data/apps.tsx +116 -0
  102. package/templates/default/apps/admin/src/data/chats.ts +114 -0
  103. package/templates/default/apps/admin/src/data/tasks.ts +114 -0
  104. package/templates/default/apps/admin/src/data/users.ts +90 -0
  105. package/templates/default/apps/admin/src/hooks/use-dialog-state.ts +17 -0
  106. package/templates/default/apps/admin/src/hooks/use-table-url-state.ts +243 -0
  107. package/templates/default/apps/admin/src/lib/show-submitted-data.tsx +12 -0
  108. package/templates/default/apps/admin/src/types/table.d.ts +9 -0
  109. package/templates/default/apps/admin/tsconfig.json +32 -0
  110. package/templates/default/apps/web/next.config.ts +1 -0
  111. package/templates/default/apps/web/package.json +0 -22
  112. package/templates/default/apps/web/postcss.config.cjs +0 -1
  113. package/templates/default/apps/web/src/app/(authenticated)/dashboard/page.tsx +4 -20
  114. package/templates/default/apps/web/src/app/(authenticated)/data/_components/UserTable.tsx +4 -4
  115. package/templates/default/apps/web/src/app/(authenticated)/data/page.tsx +1 -1
  116. package/templates/default/apps/web/src/app/(authenticated)/profile/page.tsx +1 -1
  117. package/templates/default/apps/web/src/app/error.tsx +8 -70
  118. package/templates/default/apps/web/src/app/global-error.tsx +8 -70
  119. package/templates/default/apps/web/src/app/globals.css +20 -0
  120. package/templates/default/apps/web/src/app/not-found.tsx +5 -39
  121. package/templates/default/apps/web/src/app/page.tsx +27 -203
  122. package/templates/default/apps/web/src/app/signin/page.tsx +27 -191
  123. package/templates/default/apps/web/src/app/signup/page.tsx +33 -240
  124. package/templates/default/apps/web/src/components/dashboard/dashboard-stats.tsx +11 -75
  125. package/templates/default/apps/web/src/components/shared/Sidebar.tsx +3 -3
  126. package/templates/default/apps/web/src/components/shared/header.tsx +17 -112
  127. package/templates/default/apps/web/tsconfig.json +0 -6
  128. package/templates/default/biome.json +1 -2
  129. package/templates/default/components.json +2 -2
  130. package/templates/default/docker-compose.yml +1 -1
  131. package/templates/default/gitignore +4 -0
  132. package/templates/default/package.json +1 -0
  133. package/templates/default/packages/admin-ui/catalog/catalog.css +54 -0
  134. package/templates/default/packages/admin-ui/catalog/catalog.tsx +401 -0
  135. package/templates/default/packages/admin-ui/catalog/index.html +12 -0
  136. package/templates/default/packages/admin-ui/catalog/main.tsx +9 -0
  137. package/templates/default/packages/admin-ui/components.json +21 -0
  138. package/templates/default/packages/admin-ui/package.json +105 -0
  139. package/templates/default/packages/admin-ui/src/command-menu/index.tsx +174 -0
  140. package/templates/default/packages/admin-ui/src/data-table/bulk-actions.tsx +215 -0
  141. package/templates/default/packages/admin-ui/src/data-table/column-header.tsx +73 -0
  142. package/templates/default/packages/admin-ui/src/data-table/data-table.tsx +127 -0
  143. package/templates/default/packages/admin-ui/src/data-table/faceted-filter.tsx +148 -0
  144. package/templates/default/packages/admin-ui/src/data-table/index.tsx +9 -0
  145. package/templates/default/packages/admin-ui/src/data-table/pagination.tsx +101 -0
  146. package/templates/default/packages/admin-ui/src/data-table/toolbar.tsx +87 -0
  147. package/templates/default/packages/admin-ui/src/data-table/view-options.tsx +57 -0
  148. package/templates/default/packages/admin-ui/src/hooks/use-mobile.tsx +23 -0
  149. package/templates/default/packages/admin-ui/src/layout/header.tsx +55 -0
  150. package/templates/default/packages/admin-ui/src/layout/index.ts +10 -0
  151. package/templates/default/packages/admin-ui/src/layout/main.tsx +23 -0
  152. package/templates/default/packages/admin-ui/src/layout/nav-group.tsx +111 -0
  153. package/templates/default/packages/admin-ui/src/layout/nav-user.tsx +114 -0
  154. package/templates/default/packages/admin-ui/src/layout/theme-switch.tsx +40 -0
  155. package/templates/default/packages/admin-ui/src/layout/types.ts +21 -0
  156. package/templates/default/packages/admin-ui/src/lib/utils.ts +6 -0
  157. package/templates/default/packages/admin-ui/src/styles/base.css +65 -0
  158. package/templates/default/packages/admin-ui/src/styles/tokens.css +91 -0
  159. package/templates/default/packages/admin-ui/src/tanstack-table.d.ts +10 -0
  160. package/templates/default/packages/admin-ui/src/ui/alert-dialog.tsx +157 -0
  161. package/templates/default/packages/admin-ui/src/ui/alert.tsx +66 -0
  162. package/templates/default/packages/admin-ui/src/ui/avatar.tsx +53 -0
  163. package/templates/default/packages/admin-ui/src/ui/badge.tsx +46 -0
  164. package/templates/default/packages/admin-ui/src/ui/breadcrumb.tsx +108 -0
  165. package/templates/default/packages/admin-ui/src/ui/button.tsx +59 -0
  166. package/templates/default/packages/admin-ui/src/ui/calendar.tsx +69 -0
  167. package/templates/default/packages/admin-ui/src/ui/card.tsx +92 -0
  168. package/templates/default/packages/admin-ui/src/ui/chart.tsx +345 -0
  169. package/templates/default/packages/admin-ui/src/ui/checkbox.tsx +32 -0
  170. package/templates/default/packages/admin-ui/src/ui/collapsible.tsx +27 -0
  171. package/templates/default/packages/admin-ui/src/ui/command.tsx +161 -0
  172. package/templates/default/packages/admin-ui/src/ui/confirm-dialog.tsx +72 -0
  173. package/templates/default/packages/admin-ui/src/ui/date-picker.tsx +53 -0
  174. package/templates/default/packages/admin-ui/src/ui/dialog.tsx +143 -0
  175. package/templates/default/packages/admin-ui/src/ui/dropdown-menu.tsx +257 -0
  176. package/templates/default/packages/admin-ui/src/ui/form.tsx +168 -0
  177. package/templates/default/packages/admin-ui/src/ui/input-otp.tsx +84 -0
  178. package/templates/default/packages/admin-ui/src/ui/input.tsx +21 -0
  179. package/templates/default/packages/admin-ui/src/ui/label.tsx +24 -0
  180. package/templates/default/packages/admin-ui/src/ui/pagination.tsx +126 -0
  181. package/templates/default/packages/admin-ui/src/ui/password-input.tsx +46 -0
  182. package/templates/default/packages/admin-ui/src/ui/popover.tsx +48 -0
  183. package/templates/default/packages/admin-ui/src/ui/progress.tsx +31 -0
  184. package/templates/default/packages/admin-ui/src/ui/radio-group.tsx +45 -0
  185. package/templates/default/packages/admin-ui/src/ui/scroll-area.tsx +52 -0
  186. package/templates/default/packages/admin-ui/src/ui/select.tsx +185 -0
  187. package/templates/default/packages/admin-ui/src/ui/separator.tsx +28 -0
  188. package/templates/default/packages/admin-ui/src/ui/sheet.tsx +149 -0
  189. package/templates/default/packages/admin-ui/src/ui/sidebar.tsx +728 -0
  190. package/templates/default/packages/admin-ui/src/ui/skeleton.tsx +13 -0
  191. package/templates/default/packages/admin-ui/src/ui/sonner.tsx +25 -0
  192. package/templates/default/packages/admin-ui/src/ui/switch.tsx +31 -0
  193. package/templates/default/packages/admin-ui/src/ui/table.tsx +116 -0
  194. package/templates/default/packages/admin-ui/src/ui/tabs.tsx +66 -0
  195. package/templates/default/packages/admin-ui/src/ui/textarea.tsx +18 -0
  196. package/templates/default/packages/admin-ui/src/ui/toggle-group.tsx +60 -0
  197. package/templates/default/packages/admin-ui/src/ui/toggle.tsx +44 -0
  198. package/templates/default/packages/admin-ui/src/ui/tooltip.tsx +61 -0
  199. package/templates/default/packages/admin-ui/tsconfig.json +8 -0
  200. package/templates/default/packages/admin-ui/vite.config.ts +11 -0
  201. package/templates/default/packages/config/package.json +0 -2
  202. package/templates/default/packages/server-core/package.json +1 -0
  203. package/templates/default/packages/ui/components.json +21 -0
  204. package/templates/default/packages/ui/package.json +42 -5
  205. package/templates/default/packages/ui/src/accordion.tsx +1 -1
  206. package/templates/default/packages/ui/src/alert-dialog.tsx +4 -4
  207. package/templates/default/packages/ui/src/alert.tsx +1 -1
  208. package/templates/default/packages/ui/src/avatar.tsx +1 -1
  209. package/templates/default/packages/ui/src/badge.tsx +1 -1
  210. package/templates/default/packages/ui/src/breadcrumb.tsx +1 -1
  211. package/templates/default/packages/ui/src/button.tsx +1 -1
  212. package/templates/default/packages/ui/src/card.tsx +1 -1
  213. package/templates/default/packages/ui/src/checkbox.tsx +1 -1
  214. package/templates/default/packages/ui/src/dialog.tsx +3 -3
  215. package/templates/default/packages/ui/src/drawer.tsx +3 -3
  216. package/templates/default/packages/ui/src/dropdown-menu.tsx +3 -3
  217. package/templates/default/packages/ui/src/form.tsx +2 -2
  218. package/templates/default/packages/ui/src/hover-card.tsx +2 -2
  219. package/templates/default/packages/ui/src/input.tsx +1 -1
  220. package/templates/default/packages/ui/src/label.tsx +1 -1
  221. package/templates/default/packages/ui/src/pagination.tsx +2 -2
  222. package/templates/default/packages/ui/src/popover.tsx +2 -2
  223. package/templates/default/packages/ui/src/progress.tsx +1 -1
  224. package/templates/default/packages/ui/src/select.tsx +2 -2
  225. package/templates/default/packages/ui/src/separator.tsx +1 -1
  226. package/templates/default/packages/ui/src/skeleton.tsx +1 -1
  227. package/templates/default/packages/ui/src/table.tsx +1 -1
  228. package/templates/default/packages/ui/src/tabs.tsx +1 -1
  229. package/templates/default/packages/ui/src/textarea.tsx +1 -1
  230. package/templates/default/packages/ui/src/tooltip.tsx +3 -3
  231. package/templates/default/packages/ui/src/typography.tsx +1 -1
  232. package/templates/default/packages/ui/tsconfig.json +1 -6
  233. package/templates/default/pnpm-lock.yaml +1319 -936
  234. package/templates/default/postcss.config.cjs +0 -1
  235. package/templates/default/turbo.json +11 -5
  236. package/templates/default/worktree.config.json +5 -0
  237. package/templates/default/.env.ci +0 -32
  238. package/templates/default/.github/workflows/ci.yml +0 -96
  239. package/templates/default/.github/workflows/preview-db.yml +0 -134
  240. package/templates/default/.playwright-mcp/dashboard.png +0 -0
  241. package/templates/default/.playwright-mcp/web-home.png +0 -0
  242. package/templates/default/apps/web/panda.config.ts +0 -114
  243. package/templates/default/apps/web/src/components/ui/accordion.tsx +0 -64
  244. package/templates/default/apps/web/src/components/ui/alert-dialog.tsx +0 -135
  245. package/templates/default/apps/web/src/components/ui/alert.tsx +0 -60
  246. package/templates/default/apps/web/src/components/ui/aspect-ratio.tsx +0 -9
  247. package/templates/default/apps/web/src/components/ui/avatar.tsx +0 -41
  248. package/templates/default/apps/web/src/components/ui/badge.tsx +0 -39
  249. package/templates/default/apps/web/src/components/ui/breadcrumb.tsx +0 -101
  250. package/templates/default/apps/web/src/components/ui/button.tsx +0 -56
  251. package/templates/default/apps/web/src/components/ui/card.tsx +0 -75
  252. package/templates/default/apps/web/src/components/ui/checkbox.tsx +0 -29
  253. package/templates/default/apps/web/src/components/ui/data-table.tsx +0 -189
  254. package/templates/default/apps/web/src/components/ui/dialog-hook.tsx +0 -210
  255. package/templates/default/apps/web/src/components/ui/dialog.tsx +0 -129
  256. package/templates/default/apps/web/src/components/ui/drawer.tsx +0 -124
  257. package/templates/default/apps/web/src/components/ui/dropdown-menu.tsx +0 -228
  258. package/templates/default/apps/web/src/components/ui/form.tsx +0 -152
  259. package/templates/default/apps/web/src/components/ui/hover-card.tsx +0 -38
  260. package/templates/default/apps/web/src/components/ui/input.tsx +0 -21
  261. package/templates/default/apps/web/src/components/ui/label.tsx +0 -21
  262. package/templates/default/apps/web/src/components/ui/pagination.tsx +0 -105
  263. package/templates/default/apps/web/src/components/ui/popover.tsx +0 -42
  264. package/templates/default/apps/web/src/components/ui/progress.tsx +0 -28
  265. package/templates/default/apps/web/src/components/ui/select.tsx +0 -170
  266. package/templates/default/apps/web/src/components/ui/separator.tsx +0 -28
  267. package/templates/default/apps/web/src/components/ui/skeleton.tsx +0 -13
  268. package/templates/default/apps/web/src/components/ui/sonner.tsx +0 -25
  269. package/templates/default/apps/web/src/components/ui/table.tsx +0 -92
  270. package/templates/default/apps/web/src/components/ui/tabs.tsx +0 -54
  271. package/templates/default/apps/web/src/components/ui/textarea.tsx +0 -18
  272. package/templates/default/apps/web/src/components/ui/tooltip.tsx +0 -57
  273. package/templates/default/apps/web/src/components/ui/typography.tsx +0 -158
  274. package/templates/default/packages/config/panda.config.ts +0 -114
  275. package/templates/default/panda.config.ts +0 -114
@@ -0,0 +1,196 @@
1
+ "use client";
2
+
3
+ import { showSubmittedData } from "@/lib/show-submitted-data";
4
+ import { zodResolver } from "@hookform/resolvers/zod";
5
+ import { Button } from "{{packageName}}/admin-ui/ui/button";
6
+ import { Checkbox } from "{{packageName}}/admin-ui/ui/checkbox";
7
+ import {
8
+ Form,
9
+ FormControl,
10
+ FormDescription,
11
+ FormField,
12
+ FormItem,
13
+ FormLabel,
14
+ FormMessage,
15
+ } from "{{packageName}}/admin-ui/ui/form";
16
+ import { RadioGroup, RadioGroupItem } from "{{packageName}}/admin-ui/ui/radio-group";
17
+ import { Switch } from "{{packageName}}/admin-ui/ui/switch";
18
+ import Link from "next/link";
19
+ import { useForm } from "react-hook-form";
20
+ import { z } from "zod";
21
+ import { ContentSection } from "../_components/content-section";
22
+
23
+ const notificationsFormSchema = z.object({
24
+ type: z.enum(["all", "mentions", "none"], {
25
+ required_error: "Please select a notification type.",
26
+ }),
27
+ mobile: z.boolean().default(false).optional(),
28
+ communication_emails: z.boolean().default(false).optional(),
29
+ social_emails: z.boolean().default(false).optional(),
30
+ marketing_emails: z.boolean().default(false).optional(),
31
+ security_emails: z.boolean(),
32
+ });
33
+
34
+ type NotificationsFormValues = z.infer<typeof notificationsFormSchema>;
35
+
36
+ const defaultValues: Partial<NotificationsFormValues> = {
37
+ type: "all",
38
+ communication_emails: false,
39
+ marketing_emails: false,
40
+ social_emails: true,
41
+ security_emails: true,
42
+ };
43
+
44
+ export default function NotificationsPage() {
45
+ const form = useForm<NotificationsFormValues>({
46
+ resolver: zodResolver(notificationsFormSchema),
47
+ defaultValues,
48
+ });
49
+
50
+ function onSubmit(data: NotificationsFormValues) {
51
+ showSubmittedData(data);
52
+ }
53
+
54
+ return (
55
+ <ContentSection title="Notifications" desc="Configure how you receive notifications.">
56
+ <Form {...form}>
57
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
58
+ <FormField
59
+ control={form.control}
60
+ name="type"
61
+ render={({ field }) => (
62
+ <FormItem className="space-y-3">
63
+ <FormLabel>Notify me about...</FormLabel>
64
+ <FormControl>
65
+ <RadioGroup
66
+ onValueChange={field.onChange}
67
+ defaultValue={field.value}
68
+ className="flex flex-col space-y-1"
69
+ >
70
+ <FormItem className="flex items-center space-x-3 space-y-0">
71
+ <FormControl>
72
+ <RadioGroupItem value="all" />
73
+ </FormControl>
74
+ <FormLabel className="font-normal">All new messages</FormLabel>
75
+ </FormItem>
76
+ <FormItem className="flex items-center space-x-3 space-y-0">
77
+ <FormControl>
78
+ <RadioGroupItem value="mentions" />
79
+ </FormControl>
80
+ <FormLabel className="font-normal">Direct messages and mentions</FormLabel>
81
+ </FormItem>
82
+ <FormItem className="flex items-center space-x-3 space-y-0">
83
+ <FormControl>
84
+ <RadioGroupItem value="none" />
85
+ </FormControl>
86
+ <FormLabel className="font-normal">Nothing</FormLabel>
87
+ </FormItem>
88
+ </RadioGroup>
89
+ </FormControl>
90
+ <FormMessage />
91
+ </FormItem>
92
+ )}
93
+ />
94
+ <div>
95
+ <h3 className="mb-4 text-lg font-medium">Email Notifications</h3>
96
+ <div className="space-y-4">
97
+ <FormField
98
+ control={form.control}
99
+ name="communication_emails"
100
+ render={({ field }) => (
101
+ <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
102
+ <div className="space-y-0.5">
103
+ <FormLabel className="text-base">Communication emails</FormLabel>
104
+ <FormDescription>Receive emails about your account activity.</FormDescription>
105
+ </div>
106
+ <FormControl>
107
+ <Switch checked={field.value} onCheckedChange={field.onChange} />
108
+ </FormControl>
109
+ </FormItem>
110
+ )}
111
+ />
112
+ <FormField
113
+ control={form.control}
114
+ name="marketing_emails"
115
+ render={({ field }) => (
116
+ <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
117
+ <div className="space-y-0.5">
118
+ <FormLabel className="text-base">Marketing emails</FormLabel>
119
+ <FormDescription>
120
+ Receive emails about new products, features, and more.
121
+ </FormDescription>
122
+ </div>
123
+ <FormControl>
124
+ <Switch checked={field.value} onCheckedChange={field.onChange} />
125
+ </FormControl>
126
+ </FormItem>
127
+ )}
128
+ />
129
+ <FormField
130
+ control={form.control}
131
+ name="social_emails"
132
+ render={({ field }) => (
133
+ <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
134
+ <div className="space-y-0.5">
135
+ <FormLabel className="text-base">Social emails</FormLabel>
136
+ <FormDescription>
137
+ Receive emails for friend requests, follows, and more.
138
+ </FormDescription>
139
+ </div>
140
+ <FormControl>
141
+ <Switch checked={field.value} onCheckedChange={field.onChange} />
142
+ </FormControl>
143
+ </FormItem>
144
+ )}
145
+ />
146
+ <FormField
147
+ control={form.control}
148
+ name="security_emails"
149
+ render={({ field }) => (
150
+ <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
151
+ <div className="space-y-0.5">
152
+ <FormLabel className="text-base">Security emails</FormLabel>
153
+ <FormDescription>
154
+ Receive emails about your account activity and security.
155
+ </FormDescription>
156
+ </div>
157
+ <FormControl>
158
+ <Switch
159
+ checked={field.value}
160
+ onCheckedChange={field.onChange}
161
+ disabled
162
+ aria-readonly
163
+ />
164
+ </FormControl>
165
+ </FormItem>
166
+ )}
167
+ />
168
+ </div>
169
+ </div>
170
+ <FormField
171
+ control={form.control}
172
+ name="mobile"
173
+ render={({ field }) => (
174
+ <FormItem className="flex flex-row items-start space-x-3 space-y-0">
175
+ <FormControl>
176
+ <Checkbox checked={field.value} onCheckedChange={field.onChange} />
177
+ </FormControl>
178
+ <div className="space-y-1 leading-none">
179
+ <FormLabel>Use different settings for my mobile devices</FormLabel>
180
+ <FormDescription>
181
+ You can manage your mobile notifications in the{" "}
182
+ <Link href="/dashboard/settings" className="underline">
183
+ mobile settings
184
+ </Link>{" "}
185
+ page.
186
+ </FormDescription>
187
+ </div>
188
+ </FormItem>
189
+ )}
190
+ />
191
+ <Button type="submit">Update notifications</Button>
192
+ </form>
193
+ </Form>
194
+ </ContentSection>
195
+ );
196
+ }
@@ -0,0 +1,5 @@
1
+ import { redirect } from "next/navigation";
2
+
3
+ export default function SettingsPage() {
4
+ redirect("/dashboard/settings/profile");
5
+ }
@@ -0,0 +1,176 @@
1
+ "use client";
2
+
3
+ import { showSubmittedData } from "@/lib/show-submitted-data";
4
+ import { zodResolver } from "@hookform/resolvers/zod";
5
+ import { cn } from "{{packageName}}/admin-ui/lib/utils";
6
+ import { Button } from "{{packageName}}/admin-ui/ui/button";
7
+ import {
8
+ Form,
9
+ FormControl,
10
+ FormDescription,
11
+ FormField,
12
+ FormItem,
13
+ FormLabel,
14
+ FormMessage,
15
+ } from "{{packageName}}/admin-ui/ui/form";
16
+ import { Input } from "{{packageName}}/admin-ui/ui/input";
17
+ import {
18
+ Select,
19
+ SelectContent,
20
+ SelectItem,
21
+ SelectTrigger,
22
+ SelectValue,
23
+ } from "{{packageName}}/admin-ui/ui/select";
24
+ import { Textarea } from "{{packageName}}/admin-ui/ui/textarea";
25
+ import Link from "next/link";
26
+ import { useFieldArray, useForm } from "react-hook-form";
27
+ import { z } from "zod";
28
+ import { ContentSection } from "../_components/content-section";
29
+
30
+ const profileFormSchema = z.object({
31
+ username: z
32
+ .string()
33
+ .min(2, "Username must be at least 2 characters.")
34
+ .max(30, "Username must not be longer than 30 characters."),
35
+ email: z.string().email("Please select a valid email."),
36
+ bio: z.string().max(160).min(4),
37
+ urls: z
38
+ .array(
39
+ z.object({
40
+ value: z.string().url("Please enter a valid URL."),
41
+ })
42
+ )
43
+ .optional(),
44
+ });
45
+
46
+ type ProfileFormValues = z.infer<typeof profileFormSchema>;
47
+
48
+ const defaultValues: Partial<ProfileFormValues> = {
49
+ username: "shadcn",
50
+ email: "m@example.com",
51
+ bio: "I own a computer.",
52
+ urls: [{ value: "https://shadcn.com" }, { value: "http://twitter.com/shadcn" }],
53
+ };
54
+
55
+ export default function ProfilePage() {
56
+ const form = useForm<ProfileFormValues>({
57
+ resolver: zodResolver(profileFormSchema),
58
+ defaultValues,
59
+ mode: "onChange",
60
+ });
61
+
62
+ const { fields, append } = useFieldArray({
63
+ name: "urls",
64
+ control: form.control,
65
+ });
66
+
67
+ function onSubmit(data: ProfileFormValues) {
68
+ showSubmittedData(data);
69
+ }
70
+
71
+ return (
72
+ <ContentSection title="Profile" desc="This is how others will see you on the site.">
73
+ <Form {...form}>
74
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
75
+ <FormField
76
+ control={form.control}
77
+ name="username"
78
+ render={({ field }) => (
79
+ <FormItem>
80
+ <FormLabel>Username</FormLabel>
81
+ <FormControl>
82
+ <Input placeholder="shadcn" {...field} />
83
+ </FormControl>
84
+ <FormDescription>
85
+ This is your public display name. It can be your real name or a pseudonym. You can
86
+ only change this once every 30 days.
87
+ </FormDescription>
88
+ <FormMessage />
89
+ </FormItem>
90
+ )}
91
+ />
92
+ <FormField
93
+ control={form.control}
94
+ name="email"
95
+ render={({ field }) => (
96
+ <FormItem>
97
+ <FormLabel>Email</FormLabel>
98
+ <Select onValueChange={field.onChange} defaultValue={field.value}>
99
+ <FormControl>
100
+ <SelectTrigger>
101
+ <SelectValue placeholder="Select a verified email to display" />
102
+ </SelectTrigger>
103
+ </FormControl>
104
+ <SelectContent>
105
+ <SelectItem value="m@example.com">m@example.com</SelectItem>
106
+ <SelectItem value="m@google.com">m@google.com</SelectItem>
107
+ <SelectItem value="m@support.com">m@support.com</SelectItem>
108
+ </SelectContent>
109
+ </Select>
110
+ <FormDescription>
111
+ You can manage verified email addresses in your{" "}
112
+ <Link href="#" className="underline">
113
+ email settings
114
+ </Link>
115
+ .
116
+ </FormDescription>
117
+ <FormMessage />
118
+ </FormItem>
119
+ )}
120
+ />
121
+ <FormField
122
+ control={form.control}
123
+ name="bio"
124
+ render={({ field }) => (
125
+ <FormItem>
126
+ <FormLabel>Bio</FormLabel>
127
+ <FormControl>
128
+ <Textarea
129
+ placeholder="Tell us a little bit about yourself"
130
+ className="resize-none"
131
+ {...field}
132
+ />
133
+ </FormControl>
134
+ <FormDescription>
135
+ You can <span>@mention</span> other users and organizations to link to them.
136
+ </FormDescription>
137
+ <FormMessage />
138
+ </FormItem>
139
+ )}
140
+ />
141
+ <div>
142
+ {fields.map((field, index) => (
143
+ <FormField
144
+ control={form.control}
145
+ key={field.id}
146
+ name={`urls.${index}.value`}
147
+ render={({ field }) => (
148
+ <FormItem>
149
+ <FormLabel className={cn(index !== 0 && "sr-only")}>URLs</FormLabel>
150
+ <FormDescription className={cn(index !== 0 && "sr-only")}>
151
+ Add links to your website, blog, or social media profiles.
152
+ </FormDescription>
153
+ <FormControl>
154
+ <Input {...field} />
155
+ </FormControl>
156
+ <FormMessage />
157
+ </FormItem>
158
+ )}
159
+ />
160
+ ))}
161
+ <Button
162
+ type="button"
163
+ variant="outline"
164
+ size="sm"
165
+ className="mt-2"
166
+ onClick={() => append({ value: "" })}
167
+ >
168
+ Add URL
169
+ </Button>
170
+ </div>
171
+ <Button type="submit">Update profile</Button>
172
+ </form>
173
+ </Form>
174
+ </ContentSection>
175
+ );
176
+ }
@@ -0,0 +1,183 @@
1
+ "use client";
2
+
3
+ import { type Task, priorities, statuses } from "@/data/tasks";
4
+ import { DataTableBulkActions as BulkActionsToolbar } from "{{packageName}}/admin-ui/data-table";
5
+ import { Button } from "{{packageName}}/admin-ui/ui/button";
6
+ import {
7
+ DropdownMenu,
8
+ DropdownMenuContent,
9
+ DropdownMenuItem,
10
+ DropdownMenuTrigger,
11
+ } from "{{packageName}}/admin-ui/ui/dropdown-menu";
12
+ import { Tooltip, TooltipContent, TooltipTrigger } from "{{packageName}}/admin-ui/ui/tooltip";
13
+ import type { Table } from "@tanstack/react-table";
14
+ import { ArrowUpDown, CircleArrowUp, Download, Trash2 } from "lucide-react";
15
+ import { useState } from "react";
16
+ import { toast } from "sonner";
17
+ import { TasksMultiDeleteDialog } from "./tasks-multi-delete-dialog";
18
+
19
+ type DataTableBulkActionsProps<TData> = {
20
+ table: Table<TData>;
21
+ };
22
+
23
+ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
24
+
25
+ export function DataTableBulkActions<TData>({ table }: DataTableBulkActionsProps<TData>) {
26
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
27
+ const selectedRows = table.getFilteredSelectedRowModel().rows;
28
+
29
+ const handleBulkStatusChange = (status: string) => {
30
+ const selectedTasks = selectedRows.map((row) => row.original as Task);
31
+ toast.promise(sleep(2000), {
32
+ loading: "Updating status...",
33
+ success: () => {
34
+ table.resetRowSelection();
35
+ return `Status updated to "${status}" for ${selectedTasks.length} task${selectedTasks.length > 1 ? "s" : ""}.`;
36
+ },
37
+ error: "Error",
38
+ });
39
+ table.resetRowSelection();
40
+ };
41
+
42
+ const handleBulkPriorityChange = (priority: string) => {
43
+ const selectedTasks = selectedRows.map((row) => row.original as Task);
44
+ toast.promise(sleep(2000), {
45
+ loading: "Updating priority...",
46
+ success: () => {
47
+ table.resetRowSelection();
48
+ return `Priority updated to "${priority}" for ${selectedTasks.length} task${selectedTasks.length > 1 ? "s" : ""}.`;
49
+ },
50
+ error: "Error",
51
+ });
52
+ table.resetRowSelection();
53
+ };
54
+
55
+ const handleBulkExport = () => {
56
+ const selectedTasks = selectedRows.map((row) => row.original as Task);
57
+ toast.promise(sleep(2000), {
58
+ loading: "Exporting tasks...",
59
+ success: () => {
60
+ table.resetRowSelection();
61
+ return `Exported ${selectedTasks.length} task${selectedTasks.length > 1 ? "s" : ""} to CSV.`;
62
+ },
63
+ error: "Error",
64
+ });
65
+ table.resetRowSelection();
66
+ };
67
+
68
+ return (
69
+ <>
70
+ <BulkActionsToolbar table={table} entityName="task">
71
+ <DropdownMenu>
72
+ <Tooltip>
73
+ <TooltipTrigger asChild>
74
+ <DropdownMenuTrigger asChild>
75
+ <Button
76
+ variant="outline"
77
+ size="icon"
78
+ className="size-8"
79
+ aria-label="Update status"
80
+ title="Update status"
81
+ >
82
+ <CircleArrowUp />
83
+ <span className="sr-only">Update status</span>
84
+ </Button>
85
+ </DropdownMenuTrigger>
86
+ </TooltipTrigger>
87
+ <TooltipContent>
88
+ <p>Update status</p>
89
+ </TooltipContent>
90
+ </Tooltip>
91
+ <DropdownMenuContent sideOffset={14}>
92
+ {statuses.map((status) => (
93
+ <DropdownMenuItem
94
+ key={status.value}
95
+ onClick={() => handleBulkStatusChange(status.value)}
96
+ >
97
+ {status.icon && <status.icon className="size-4 text-muted-foreground" />}
98
+ {status.label}
99
+ </DropdownMenuItem>
100
+ ))}
101
+ </DropdownMenuContent>
102
+ </DropdownMenu>
103
+
104
+ <DropdownMenu>
105
+ <Tooltip>
106
+ <TooltipTrigger asChild>
107
+ <DropdownMenuTrigger asChild>
108
+ <Button
109
+ variant="outline"
110
+ size="icon"
111
+ className="size-8"
112
+ aria-label="Update priority"
113
+ title="Update priority"
114
+ >
115
+ <ArrowUpDown />
116
+ <span className="sr-only">Update priority</span>
117
+ </Button>
118
+ </DropdownMenuTrigger>
119
+ </TooltipTrigger>
120
+ <TooltipContent>
121
+ <p>Update priority</p>
122
+ </TooltipContent>
123
+ </Tooltip>
124
+ <DropdownMenuContent sideOffset={14}>
125
+ {priorities.map((priority) => (
126
+ <DropdownMenuItem
127
+ key={priority.value}
128
+ onClick={() => handleBulkPriorityChange(priority.value)}
129
+ >
130
+ {priority.icon && <priority.icon className="size-4 text-muted-foreground" />}
131
+ {priority.label}
132
+ </DropdownMenuItem>
133
+ ))}
134
+ </DropdownMenuContent>
135
+ </DropdownMenu>
136
+
137
+ <Tooltip>
138
+ <TooltipTrigger asChild>
139
+ <Button
140
+ variant="outline"
141
+ size="icon"
142
+ onClick={() => handleBulkExport()}
143
+ className="size-8"
144
+ aria-label="Export tasks"
145
+ title="Export tasks"
146
+ >
147
+ <Download />
148
+ <span className="sr-only">Export tasks</span>
149
+ </Button>
150
+ </TooltipTrigger>
151
+ <TooltipContent>
152
+ <p>Export tasks</p>
153
+ </TooltipContent>
154
+ </Tooltip>
155
+
156
+ <Tooltip>
157
+ <TooltipTrigger asChild>
158
+ <Button
159
+ variant="destructive"
160
+ size="icon"
161
+ onClick={() => setShowDeleteConfirm(true)}
162
+ className="size-8"
163
+ aria-label="Delete selected tasks"
164
+ title="Delete selected tasks"
165
+ >
166
+ <Trash2 />
167
+ <span className="sr-only">Delete selected tasks</span>
168
+ </Button>
169
+ </TooltipTrigger>
170
+ <TooltipContent>
171
+ <p>Delete selected tasks</p>
172
+ </TooltipContent>
173
+ </Tooltip>
174
+ </BulkActionsToolbar>
175
+
176
+ <TasksMultiDeleteDialog
177
+ open={showDeleteConfirm}
178
+ onOpenChange={setShowDeleteConfirm}
179
+ table={table}
180
+ />
181
+ </>
182
+ );
183
+ }
@@ -0,0 +1,79 @@
1
+ "use client";
2
+
3
+ import { labels, taskSchema } from "@/data/tasks";
4
+ import { DotsHorizontalIcon } from "@radix-ui/react-icons";
5
+ import { Button } from "{{packageName}}/admin-ui/ui/button";
6
+ import {
7
+ DropdownMenu,
8
+ DropdownMenuContent,
9
+ DropdownMenuItem,
10
+ DropdownMenuRadioGroup,
11
+ DropdownMenuRadioItem,
12
+ DropdownMenuSeparator,
13
+ DropdownMenuShortcut,
14
+ DropdownMenuSub,
15
+ DropdownMenuSubContent,
16
+ DropdownMenuSubTrigger,
17
+ DropdownMenuTrigger,
18
+ } from "{{packageName}}/admin-ui/ui/dropdown-menu";
19
+ import type { Row } from "@tanstack/react-table";
20
+ import { Trash2 } from "lucide-react";
21
+ import { useTasks } from "./tasks-provider";
22
+
23
+ type DataTableRowActionsProps<TData> = {
24
+ row: Row<TData>;
25
+ };
26
+
27
+ export function DataTableRowActions<TData>({ row }: DataTableRowActionsProps<TData>) {
28
+ const task = taskSchema.parse(row.original);
29
+
30
+ const { setOpen, setCurrentRow } = useTasks();
31
+
32
+ return (
33
+ <DropdownMenu modal={false}>
34
+ <DropdownMenuTrigger asChild>
35
+ <Button variant="ghost" className="flex h-8 w-8 p-0 data-[state=open]:bg-muted">
36
+ <DotsHorizontalIcon className="h-4 w-4" />
37
+ <span className="sr-only">Open menu</span>
38
+ </Button>
39
+ </DropdownMenuTrigger>
40
+ <DropdownMenuContent align="end" className="w-[160px]">
41
+ <DropdownMenuItem
42
+ onClick={() => {
43
+ setCurrentRow(task);
44
+ setOpen("update");
45
+ }}
46
+ >
47
+ Edit
48
+ </DropdownMenuItem>
49
+ <DropdownMenuItem disabled>Make a copy</DropdownMenuItem>
50
+ <DropdownMenuItem disabled>Favorite</DropdownMenuItem>
51
+ <DropdownMenuSeparator />
52
+ <DropdownMenuSub>
53
+ <DropdownMenuSubTrigger>Labels</DropdownMenuSubTrigger>
54
+ <DropdownMenuSubContent>
55
+ <DropdownMenuRadioGroup value={task.label}>
56
+ {labels.map((label) => (
57
+ <DropdownMenuRadioItem key={label.value} value={label.value}>
58
+ {label.label}
59
+ </DropdownMenuRadioItem>
60
+ ))}
61
+ </DropdownMenuRadioGroup>
62
+ </DropdownMenuSubContent>
63
+ </DropdownMenuSub>
64
+ <DropdownMenuSeparator />
65
+ <DropdownMenuItem
66
+ onClick={() => {
67
+ setCurrentRow(task);
68
+ setOpen("delete");
69
+ }}
70
+ >
71
+ Delete
72
+ <DropdownMenuShortcut>
73
+ <Trash2 size={16} />
74
+ </DropdownMenuShortcut>
75
+ </DropdownMenuItem>
76
+ </DropdownMenuContent>
77
+ </DropdownMenu>
78
+ );
79
+ }