nextworks 0.0.1 → 0.1.0-alpha.1

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 (277) hide show
  1. package/README.md +209 -30
  2. package/dist/.gitkeep +0 -0
  3. package/dist/cli_manifests/auth_manifest.json +86 -0
  4. package/dist/cli_manifests/blocks_manifest.json +185 -0
  5. package/dist/cli_manifests/data_manifest.json +51 -0
  6. package/dist/cli_manifests/forms_manifest.json +61 -0
  7. package/dist/commands/admin-posts.d.ts +2 -0
  8. package/dist/commands/admin-posts.d.ts.map +1 -0
  9. package/dist/commands/admin-posts.js +15 -0
  10. package/dist/commands/admin-posts.js.map +1 -0
  11. package/dist/commands/admin-users.d.ts +2 -0
  12. package/dist/commands/admin-users.d.ts.map +1 -0
  13. package/dist/commands/admin-users.js +15 -0
  14. package/dist/commands/admin-users.js.map +1 -0
  15. package/dist/commands/auth-core.d.ts +2 -0
  16. package/dist/commands/auth-core.d.ts.map +1 -0
  17. package/dist/commands/auth-core.js +83 -0
  18. package/dist/commands/auth-core.js.map +1 -0
  19. package/dist/commands/auth-forms.d.ts +2 -0
  20. package/dist/commands/auth-forms.d.ts.map +1 -0
  21. package/dist/commands/auth-forms.js +15 -0
  22. package/dist/commands/auth-forms.js.map +1 -0
  23. package/dist/commands/blocks-options.d.ts +7 -0
  24. package/dist/commands/blocks-options.d.ts.map +1 -0
  25. package/dist/commands/blocks-options.js +19 -0
  26. package/dist/commands/blocks-options.js.map +1 -0
  27. package/dist/commands/blocks.d.ts +7 -0
  28. package/dist/commands/blocks.d.ts.map +1 -0
  29. package/dist/commands/blocks.js +145 -0
  30. package/dist/commands/blocks.js.map +1 -0
  31. package/dist/commands/data.d.ts +3 -0
  32. package/dist/commands/data.d.ts.map +1 -0
  33. package/dist/commands/data.js +88 -0
  34. package/dist/commands/data.js.map +1 -0
  35. package/dist/commands/forms.d.ts +6 -0
  36. package/dist/commands/forms.d.ts.map +1 -0
  37. package/dist/commands/forms.js +107 -0
  38. package/dist/commands/forms.js.map +1 -0
  39. package/dist/commands/remove-auth-core.d.ts +2 -0
  40. package/dist/commands/remove-auth-core.d.ts.map +1 -0
  41. package/dist/commands/remove-auth-core.js +69 -0
  42. package/dist/commands/remove-auth-core.js.map +1 -0
  43. package/dist/commands/remove-blocks.d.ts +2 -0
  44. package/dist/commands/remove-blocks.d.ts.map +1 -0
  45. package/dist/commands/remove-blocks.js +36 -0
  46. package/dist/commands/remove-blocks.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +109 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/kits/auth-core/README.md +106 -0
  52. package/dist/kits/auth-core/app/(protected)/dashboard/page.tsx +8 -0
  53. package/dist/kits/auth-core/app/(protected)/layout.tsx +18 -0
  54. package/dist/kits/auth-core/app/(protected)/settings/profile/page.tsx +15 -0
  55. package/dist/kits/auth-core/app/(protected)/settings/profile/profile-form.tsx +114 -0
  56. package/dist/kits/auth-core/app/api/auth/[...nextauth]/route.ts +1 -0
  57. package/dist/kits/auth-core/app/api/auth/forgot-password/route.ts +114 -0
  58. package/dist/kits/auth-core/app/api/auth/providers/route.ts +6 -0
  59. package/dist/kits/auth-core/app/api/auth/reset-password/route.ts +63 -0
  60. package/dist/kits/auth-core/app/api/auth/send-verify-email/route.ts +6 -0
  61. package/dist/kits/auth-core/app/api/signup/route.ts +41 -0
  62. package/dist/kits/auth-core/app/auth/forgot-password/page.tsx +21 -0
  63. package/dist/kits/auth-core/app/auth/login/page.tsx +5 -0
  64. package/dist/kits/auth-core/app/auth/reset-password/page.tsx +187 -0
  65. package/dist/kits/auth-core/app/auth/signup/page.tsx +5 -0
  66. package/dist/kits/auth-core/app/auth/verify-email/page.tsx +11 -0
  67. package/dist/kits/auth-core/components/admin/admin-header.tsx +57 -0
  68. package/dist/kits/auth-core/components/auth/dashboard.tsx +237 -0
  69. package/dist/kits/auth-core/components/auth/forgot-password-form.tsx +90 -0
  70. package/dist/kits/auth-core/components/auth/login-form.tsx +467 -0
  71. package/dist/kits/auth-core/components/auth/logout-button.tsx +50 -0
  72. package/dist/kits/auth-core/components/auth/minimal-logout-button.tsx +40 -0
  73. package/dist/kits/auth-core/components/auth/signup-form.tsx +468 -0
  74. package/dist/kits/auth-core/components/require-auth.tsx +59 -0
  75. package/dist/kits/auth-core/components/session-provider.tsx +11 -0
  76. package/dist/kits/auth-core/components/ui/README.txt +1 -0
  77. package/dist/kits/auth-core/components/ui/button.tsx +55 -0
  78. package/dist/kits/auth-core/components/ui/input.tsx +25 -0
  79. package/dist/kits/auth-core/components/ui/label.tsx +23 -0
  80. package/dist/kits/auth-core/lib/api/errors.ts +14 -0
  81. package/dist/kits/auth-core/lib/auth-helpers.ts +29 -0
  82. package/dist/kits/auth-core/lib/auth.ts +142 -0
  83. package/dist/kits/auth-core/lib/email/dev-transport.ts +42 -0
  84. package/dist/kits/auth-core/lib/email/index.ts +28 -0
  85. package/dist/kits/auth-core/lib/email/provider-smtp.ts +36 -0
  86. package/dist/kits/auth-core/lib/forms/map-errors.ts +11 -0
  87. package/dist/kits/auth-core/lib/hash.ts +6 -0
  88. package/dist/kits/auth-core/lib/prisma.ts +15 -0
  89. package/dist/kits/auth-core/lib/server/result.ts +45 -0
  90. package/dist/kits/auth-core/lib/utils.ts +6 -0
  91. package/dist/kits/auth-core/lib/validation/forms.ts +88 -0
  92. package/dist/kits/auth-core/package-deps.json +19 -0
  93. package/dist/kits/auth-core/prisma/auth-models.prisma +81 -0
  94. package/dist/kits/auth-core/prisma/schema.prisma +81 -0
  95. package/dist/kits/auth-core/scripts/populate-tokenhash.mjs +26 -0
  96. package/dist/kits/auth-core/scripts/promote-admin.mjs +33 -0
  97. package/dist/kits/auth-core/scripts/seed-demo.mjs +40 -0
  98. package/dist/kits/auth-core/types/next-auth.d.ts +25 -0
  99. package/dist/kits/blocks/README.md +53 -0
  100. package/dist/kits/blocks/app/globals.css +175 -0
  101. package/dist/kits/blocks/app/templates/digitalagency/PresetThemeVars.tsx +80 -0
  102. package/dist/kits/blocks/app/templates/digitalagency/README.md +36 -0
  103. package/dist/kits/blocks/app/templates/digitalagency/components/About.tsx +99 -0
  104. package/dist/kits/blocks/app/templates/digitalagency/components/CTA.tsx +74 -0
  105. package/dist/kits/blocks/app/templates/digitalagency/components/Contact.tsx +227 -0
  106. package/dist/kits/blocks/app/templates/digitalagency/components/Footer.tsx +89 -0
  107. package/dist/kits/blocks/app/templates/digitalagency/components/Hero.tsx +90 -0
  108. package/dist/kits/blocks/app/templates/digitalagency/components/Navbar.tsx +168 -0
  109. package/dist/kits/blocks/app/templates/digitalagency/components/NetworkPattern.tsx +297 -0
  110. package/dist/kits/blocks/app/templates/digitalagency/components/Portfolio.tsx +157 -0
  111. package/dist/kits/blocks/app/templates/digitalagency/components/Pricing.tsx +114 -0
  112. package/dist/kits/blocks/app/templates/digitalagency/components/Process.tsx +59 -0
  113. package/dist/kits/blocks/app/templates/digitalagency/components/Services.tsx +55 -0
  114. package/dist/kits/blocks/app/templates/digitalagency/components/Team.tsx +28 -0
  115. package/dist/kits/blocks/app/templates/digitalagency/components/Testimonials.tsx +65 -0
  116. package/dist/kits/blocks/app/templates/digitalagency/page.tsx +38 -0
  117. package/dist/kits/blocks/app/templates/gallery/PresetThemeVars.tsx +85 -0
  118. package/dist/kits/blocks/app/templates/gallery/page.tsx +303 -0
  119. package/dist/kits/blocks/app/templates/productlaunch/PresetThemeVars.tsx +74 -0
  120. package/dist/kits/blocks/app/templates/productlaunch/README.md +55 -0
  121. package/dist/kits/blocks/app/templates/productlaunch/components/About.tsx +178 -0
  122. package/dist/kits/blocks/app/templates/productlaunch/components/CTA.tsx +93 -0
  123. package/dist/kits/blocks/app/templates/productlaunch/components/Contact.tsx +231 -0
  124. package/dist/kits/blocks/app/templates/productlaunch/components/FAQ.tsx +93 -0
  125. package/dist/kits/blocks/app/templates/productlaunch/components/Features.tsx +84 -0
  126. package/dist/kits/blocks/app/templates/productlaunch/components/Footer.tsx +132 -0
  127. package/dist/kits/blocks/app/templates/productlaunch/components/Hero.tsx +89 -0
  128. package/dist/kits/blocks/app/templates/productlaunch/components/Navbar.tsx +162 -0
  129. package/dist/kits/blocks/app/templates/productlaunch/components/Pricing.tsx +106 -0
  130. package/dist/kits/blocks/app/templates/productlaunch/components/ProcessTimeline.tsx +110 -0
  131. package/dist/kits/blocks/app/templates/productlaunch/components/ServicesGrid.tsx +68 -0
  132. package/dist/kits/blocks/app/templates/productlaunch/components/Team.tsx +104 -0
  133. package/dist/kits/blocks/app/templates/productlaunch/components/Testimonials.tsx +89 -0
  134. package/dist/kits/blocks/app/templates/productlaunch/components/TrustBadges.tsx +76 -0
  135. package/dist/kits/blocks/app/templates/productlaunch/page.tsx +45 -0
  136. package/dist/kits/blocks/app/templates/saasdashboard/PresetThemeVars.tsx +80 -0
  137. package/dist/kits/blocks/app/templates/saasdashboard/README.md +38 -0
  138. package/dist/kits/blocks/app/templates/saasdashboard/components/Contact.tsx +176 -0
  139. package/dist/kits/blocks/app/templates/saasdashboard/components/Dashboard.tsx +293 -0
  140. package/dist/kits/blocks/app/templates/saasdashboard/components/FAQ.tsx +55 -0
  141. package/dist/kits/blocks/app/templates/saasdashboard/components/Features.tsx +91 -0
  142. package/dist/kits/blocks/app/templates/saasdashboard/components/Footer.tsx +77 -0
  143. package/dist/kits/blocks/app/templates/saasdashboard/components/Hero.tsx +105 -0
  144. package/dist/kits/blocks/app/templates/saasdashboard/components/Hero_mask.tsx +127 -0
  145. package/dist/kits/blocks/app/templates/saasdashboard/components/Navbar.tsx +159 -0
  146. package/dist/kits/blocks/app/templates/saasdashboard/components/Pricing.tsx +90 -0
  147. package/dist/kits/blocks/app/templates/saasdashboard/components/SmoothScroll.tsx +97 -0
  148. package/dist/kits/blocks/app/templates/saasdashboard/components/Testimonials.tsx +72 -0
  149. package/dist/kits/blocks/app/templates/saasdashboard/components/TrustBadges.tsx +53 -0
  150. package/dist/kits/blocks/app/templates/saasdashboard/page.tsx +39 -0
  151. package/dist/kits/blocks/components/app-providers.tsx +1 -0
  152. package/dist/kits/blocks/components/enhanced-theme-provider.tsx +195 -0
  153. package/dist/kits/blocks/components/sections/About.tsx +291 -0
  154. package/dist/kits/blocks/components/sections/CTA.tsx +258 -0
  155. package/dist/kits/blocks/components/sections/Contact.tsx +267 -0
  156. package/dist/kits/blocks/components/sections/FAQ.tsx +226 -0
  157. package/dist/kits/blocks/components/sections/Features.tsx +269 -0
  158. package/dist/kits/blocks/components/sections/Footer.tsx +302 -0
  159. package/dist/kits/blocks/components/sections/HeroMotion.tsx +307 -0
  160. package/dist/kits/blocks/components/sections/HeroOverlay.tsx +358 -0
  161. package/dist/kits/blocks/components/sections/HeroSplit.tsx +352 -0
  162. package/dist/kits/blocks/components/sections/Navbar.tsx +353 -0
  163. package/dist/kits/blocks/components/sections/Newsletter.tsx +156 -0
  164. package/dist/kits/blocks/components/sections/PortfolioSimple.tsx +550 -0
  165. package/dist/kits/blocks/components/sections/Pricing.tsx +264 -0
  166. package/dist/kits/blocks/components/sections/ProcessTimeline.tsx +325 -0
  167. package/dist/kits/blocks/components/sections/ServicesGrid.tsx +210 -0
  168. package/dist/kits/blocks/components/sections/Team.tsx +309 -0
  169. package/dist/kits/blocks/components/sections/Testimonials.tsx +158 -0
  170. package/dist/kits/blocks/components/sections/TrustBadges.tsx +162 -0
  171. package/dist/kits/blocks/components/theme-provider.tsx +34 -0
  172. package/dist/kits/blocks/components/ui/alert-dialog.tsx +134 -0
  173. package/dist/kits/blocks/components/ui/brand-node.tsx +121 -0
  174. package/dist/kits/blocks/components/ui/button.tsx +122 -0
  175. package/dist/kits/blocks/components/ui/button_bck.tsx +93 -0
  176. package/dist/kits/blocks/components/ui/card.tsx +95 -0
  177. package/dist/kits/blocks/components/ui/checkbox.tsx +30 -0
  178. package/dist/kits/blocks/components/ui/cta-button.tsx +125 -0
  179. package/dist/kits/blocks/components/ui/dropdown-menu.tsx +201 -0
  180. package/dist/kits/blocks/components/ui/feature-card.tsx +91 -0
  181. package/dist/kits/blocks/components/ui/input.tsx +27 -0
  182. package/dist/kits/blocks/components/ui/label.tsx +29 -0
  183. package/dist/kits/blocks/components/ui/pricing-card.tsx +120 -0
  184. package/dist/kits/blocks/components/ui/select.tsx +25 -0
  185. package/dist/kits/blocks/components/ui/skeleton.tsx +13 -0
  186. package/dist/kits/blocks/components/ui/switch.tsx +78 -0
  187. package/dist/kits/blocks/components/ui/table.tsx +98 -0
  188. package/dist/kits/blocks/components/ui/testimonial-card.tsx +108 -0
  189. package/dist/kits/blocks/components/ui/textarea.tsx +26 -0
  190. package/dist/kits/blocks/components/ui/theme-selector.tsx +247 -0
  191. package/dist/kits/blocks/components/ui/theme-toggle.tsx +74 -0
  192. package/dist/kits/blocks/components/ui/toaster.tsx +7 -0
  193. package/dist/kits/blocks/lib/themes.ts +399 -0
  194. package/dist/kits/blocks/lib/themes_old.ts +37 -0
  195. package/dist/kits/blocks/lib/utils.ts +9 -0
  196. package/dist/kits/blocks/next.config.ts +11 -0
  197. package/dist/kits/blocks/notes/THEME_GUIDE.md +29 -0
  198. package/dist/kits/blocks/notes/THEMING_CONVERSION_SUMMARY.md +14 -0
  199. package/dist/kits/blocks/package-deps.json +22 -0
  200. package/dist/kits/blocks/public/placeholders/gallery/hero-pexels-broken-9945014.avif +0 -0
  201. package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626431.jpg +0 -0
  202. package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626432.jpg +0 -0
  203. package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626434.jpg +0 -0
  204. package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626436.jpg +0 -0
  205. package/dist/kits/blocks/public/placeholders/product_launch/feature_1.png +0 -0
  206. package/dist/kits/blocks/public/placeholders/product_launch/feature_2.png +0 -0
  207. package/dist/kits/blocks/public/placeholders/product_launch/feature_3.png +0 -0
  208. package/dist/kits/blocks/public/placeholders/product_launch/feature_4.png +0 -0
  209. package/dist/kits/blocks/public/placeholders/product_launch/hero.png +0 -0
  210. package/dist/kits/blocks/public/placeholders/saas_dashboard/analytics.png +0 -0
  211. package/dist/kits/blocks/public/placeholders/saas_dashboard/chat.png +0 -0
  212. package/dist/kits/blocks/public/placeholders/saas_dashboard/projectBoard.png +0 -0
  213. package/dist/kits/data/.gitkeep +0 -0
  214. package/dist/kits/data/README.md +104 -0
  215. package/dist/kits/data/app/(protected)/admin/posts/page.tsx +5 -0
  216. package/dist/kits/data/app/(protected)/admin/users/page.tsx +5 -0
  217. package/dist/kits/data/app/api/posts/[id]/route.ts +83 -0
  218. package/dist/kits/data/app/api/posts/route.ts +138 -0
  219. package/dist/kits/data/app/api/seed-demo/route.ts +45 -0
  220. package/dist/kits/data/app/api/users/[id]/route.ts +127 -0
  221. package/dist/kits/data/app/api/users/check-email/route.ts +18 -0
  222. package/dist/kits/data/app/api/users/check-unique/route.ts +27 -0
  223. package/dist/kits/data/app/api/users/route.ts +79 -0
  224. package/dist/kits/data/app/examples/demo/README.md +4 -0
  225. package/dist/kits/data/app/examples/demo/create-post-form.tsx +106 -0
  226. package/dist/kits/data/app/examples/demo/page.tsx +118 -0
  227. package/dist/kits/data/app/examples/demo/seed-demo-button.tsx +37 -0
  228. package/dist/kits/data/components/admin/posts-manager.tsx +719 -0
  229. package/dist/kits/data/components/admin/users-manager.tsx +432 -0
  230. package/dist/kits/data/lib/prisma.ts +15 -0
  231. package/dist/kits/data/lib/server/result.ts +90 -0
  232. package/dist/kits/data/package-deps.json +11 -0
  233. package/dist/kits/data/scripts/seed-demo.mjs +41 -0
  234. package/dist/kits/forms/.gitkeep +0 -0
  235. package/dist/kits/forms/README.md +49 -0
  236. package/dist/kits/forms/app/.gitkeep +0 -0
  237. package/dist/kits/forms/app/api/wizard/route.ts +71 -0
  238. package/dist/kits/forms/app/examples/forms/basic/page.tsx +124 -0
  239. package/dist/kits/forms/app/examples/forms/server-action/form-client.tsx +28 -0
  240. package/dist/kits/forms/app/examples/forms/server-action/page.tsx +71 -0
  241. package/dist/kits/forms/app/examples/forms/wizard/page.tsx +15 -0
  242. package/dist/kits/forms/app/examples/forms/wizard/wizard-client.tsx +2 -0
  243. package/dist/kits/forms/components/.gitkeep +0 -0
  244. package/dist/kits/forms/components/examples/wizard-client.tsx +231 -0
  245. package/dist/kits/forms/components/hooks/useCheckUnique.ts +79 -0
  246. package/dist/kits/forms/components/ui/button.tsx +122 -0
  247. package/dist/kits/forms/components/ui/checkbox.tsx +30 -0
  248. package/dist/kits/forms/components/ui/form/context.ts +33 -0
  249. package/dist/kits/forms/components/ui/form/form-control.tsx +28 -0
  250. package/dist/kits/forms/components/ui/form/form-description.tsx +22 -0
  251. package/dist/kits/forms/components/ui/form/form-field.tsx +36 -0
  252. package/dist/kits/forms/components/ui/form/form-item.tsx +21 -0
  253. package/dist/kits/forms/components/ui/form/form-label.tsx +24 -0
  254. package/dist/kits/forms/components/ui/form/form-message.tsx +29 -0
  255. package/dist/kits/forms/components/ui/form/form.tsx +26 -0
  256. package/dist/kits/forms/components/ui/input.tsx +27 -0
  257. package/dist/kits/forms/components/ui/label.tsx +29 -0
  258. package/dist/kits/forms/components/ui/select.tsx +25 -0
  259. package/dist/kits/forms/components/ui/switch.tsx +78 -0
  260. package/dist/kits/forms/components/ui/textarea.tsx +26 -0
  261. package/dist/kits/forms/lib/.gitkeep +0 -0
  262. package/dist/kits/forms/lib/forms/map-errors.ts +29 -0
  263. package/dist/kits/forms/lib/prisma.ts +16 -0
  264. package/dist/kits/forms/lib/utils.ts +9 -0
  265. package/dist/kits/forms/lib/validation/forms.ts +88 -0
  266. package/dist/kits/forms/lib/validation/wizard.ts +32 -0
  267. package/dist/kits/forms/package-deps.json +17 -0
  268. package/dist/utils/file-operations.d.ts +18 -0
  269. package/dist/utils/file-operations.d.ts.map +1 -0
  270. package/dist/utils/file-operations.js +327 -0
  271. package/dist/utils/file-operations.js.map +1 -0
  272. package/dist/utils/installation-tracker.d.ts +26 -0
  273. package/dist/utils/installation-tracker.d.ts.map +1 -0
  274. package/dist/utils/installation-tracker.js +98 -0
  275. package/dist/utils/installation-tracker.js.map +1 -0
  276. package/package.json +51 -21
  277. package/index.js +0 -1
@@ -0,0 +1,432 @@
1
+ "use client";
2
+
3
+ import React, { useEffect, useMemo, useState, type JSX } from "react";
4
+ import { useForm } from "react-hook-form";
5
+ import { zodResolver } from "@hookform/resolvers/zod";
6
+ import { cn } from "@/lib/utils";
7
+ import { toast } from "sonner";
8
+
9
+ import { Button } from "@/components/ui/button";
10
+ import { Input } from "@/components/ui/input";
11
+ import { Card } from "@/components/ui/card";
12
+ import {
13
+ Table,
14
+ TableBody,
15
+ TableCell,
16
+ TableHead,
17
+ TableHeader,
18
+ TableRow,
19
+ } from "@/components/ui/table";
20
+ import {
21
+ AlertDialog,
22
+ AlertDialogAction,
23
+ AlertDialogCancel,
24
+ AlertDialogContent,
25
+ AlertDialogDescription,
26
+ AlertDialogFooter,
27
+ AlertDialogHeader,
28
+ AlertDialogTitle,
29
+ AlertDialogTrigger,
30
+ } from "@/components/ui/alert-dialog";
31
+ import { Skeleton } from "@/components/ui/skeleton";
32
+ import { Form } from "@/components/ui/form/form";
33
+ import { FormField } from "@/components/ui/form/form-field";
34
+ import { FormItem } from "@/components/ui/form/form-item";
35
+ import { FormLabel } from "@/components/ui/form/form-label";
36
+ import { FormMessage } from "@/components/ui/form/form-message";
37
+ import { FormControl } from "@/components/ui/form/form-control";
38
+ import { userSchema, type UserFormValues } from "@/lib/validation/forms";
39
+ import { mapApiErrorsToForm } from "@/lib/forms/map-errors";
40
+
41
+ export interface UsersManagerSlots {
42
+ container?: { className?: string };
43
+ formCard?: { className?: string };
44
+ listCard?: { className?: string };
45
+ leftHeading?: { className?: string };
46
+ rightHeading?: { className?: string };
47
+ form?: { className?: string };
48
+ submitRow?: { className?: string };
49
+ actionsRow?: { className?: string };
50
+ table?: { className?: string };
51
+ }
52
+
53
+ export interface UsersManagerProps extends UsersManagerSlots {
54
+ className?: string;
55
+ }
56
+
57
+ type User = {
58
+ id: string;
59
+ email: string;
60
+ name?: string | null;
61
+ createdAt?: string;
62
+ };
63
+
64
+ export function UsersManager({
65
+ className,
66
+ container = {
67
+ className:
68
+ "mx-auto grid w-full max-w-5xl gap-6 p-0 sm:gap-6 sm:p-6 md:grid-cols-5 md:gap-1 md:p-1 lg:gap-6 lg:p-6",
69
+ },
70
+ formCard = { className: "p-2 sm:p-6 md:col-span-2" },
71
+ listCard = { className: "p-2 sm:p-6 md:col-span-3" },
72
+ leftHeading = { className: "mb-4 text-lg font-semibold" },
73
+ rightHeading = { className: "mb-3 font-medium" },
74
+ form = { className: "space-y-4" },
75
+ submitRow = { className: "flex items-center gap-3" },
76
+ actionsRow = { className: "flex flex-col gap-2 lg:flex-row" },
77
+ table = { className: "" },
78
+ }: UsersManagerProps): JSX.Element {
79
+ const [users, setUsers] = useState<User[]>([]);
80
+ const [selectedId, setSelectedId] = useState<string | null>(null);
81
+ const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null);
82
+ const [loading, setLoading] = useState(false);
83
+ const [listLoading, setListLoading] = useState(true);
84
+
85
+ const defaultValues = useMemo<UserFormValues>(
86
+ () => ({ email: "", name: "" }),
87
+ [],
88
+ );
89
+
90
+ const formMethods = useForm<UserFormValues>({
91
+ resolver: zodResolver(userSchema),
92
+ defaultValues,
93
+ });
94
+
95
+ const {
96
+ control,
97
+ handleSubmit,
98
+ reset,
99
+ setValue,
100
+ setError: setFieldError,
101
+ formState: { isSubmitting },
102
+ } = formMethods;
103
+
104
+ const [page, setPage] = useState<number>(1);
105
+ const [perPage, setPerPage] = useState<number>(8);
106
+ const [total, setTotal] = useState<number>(0);
107
+
108
+ const fetchUsers = async (opts?: { page?: number; perPage?: number }) => {
109
+ setListLoading(true);
110
+ try {
111
+ const p = opts?.page ?? page;
112
+ const pp = opts?.perPage ?? perPage;
113
+ const params = new URLSearchParams();
114
+ params.set("page", String(p));
115
+ params.set("perPage", String(pp));
116
+
117
+ const res = await fetch(`/api/users?${params.toString()}`, {
118
+ cache: "no-store",
119
+ });
120
+ const payload = await res.json().catch(() => null);
121
+
122
+ let data: unknown = payload ?? [];
123
+ if (payload && typeof payload === "object" && "success" in payload) {
124
+ data = (payload as any).data;
125
+ }
126
+
127
+ const items = Array.isArray(data)
128
+ ? data
129
+ : data && typeof data === "object" && "items" in (data as any)
130
+ ? (data as any).items
131
+ : [];
132
+
133
+ setUsers(items as User[]);
134
+
135
+ if (data && typeof data === "object") {
136
+ const d = data as any;
137
+ if (typeof d.total === "number") setTotal(d.total);
138
+ if (typeof d.page === "number") setPage(d.page);
139
+ if (typeof d.perPage === "number") setPerPage(d.perPage);
140
+ } else if (Array.isArray(data)) {
141
+ setTotal(items.length);
142
+ } else {
143
+ setTotal(0);
144
+ }
145
+
146
+ if (opts?.page) setPage(opts.page);
147
+ if (opts?.perPage) setPerPage(opts.perPage);
148
+ } finally {
149
+ setListLoading(false);
150
+ }
151
+ };
152
+
153
+ useEffect(() => {
154
+ fetchUsers();
155
+ // eslint-disable-next-line react-hooks/exhaustive-deps
156
+ }, [page, perPage]);
157
+
158
+ const createUser = async (values: UserFormValues) => {
159
+ setLoading(true);
160
+ try {
161
+ const res = await fetch("/api/users", {
162
+ method: "POST",
163
+ headers: { "Content-Type": "application/json" },
164
+ body: JSON.stringify(values),
165
+ });
166
+ const payload = await res.json().catch(() => null);
167
+ if (!res.ok || !payload?.success) {
168
+ const msg = payload
169
+ ? mapApiErrorsToForm(formMethods, payload)
170
+ : undefined;
171
+ toast.error(msg || payload?.message || "Create failed");
172
+ return;
173
+ }
174
+ reset(defaultValues);
175
+ setSelectedId(null);
176
+ await fetchUsers();
177
+ toast.success("User created");
178
+ } catch (_e) {
179
+ toast.error("Create failed");
180
+ } finally {
181
+ setLoading(false);
182
+ }
183
+ };
184
+
185
+ const updateUser = async (values: UserFormValues) => {
186
+ if (!selectedId) return;
187
+ setLoading(true);
188
+ try {
189
+ const res = await fetch(`/api/users/${selectedId}`, {
190
+ method: "PUT",
191
+ headers: { "Content-Type": "application/json" },
192
+ body: JSON.stringify({ email: values.email, name: values.name }),
193
+ });
194
+ const payload = await res.json().catch(() => null);
195
+ if (!res.ok || !payload?.success) {
196
+ const msg = payload
197
+ ? mapApiErrorsToForm(formMethods, payload)
198
+ : undefined;
199
+ toast.error(msg || payload?.message || "Update failed");
200
+ return;
201
+ }
202
+ reset(defaultValues);
203
+ setSelectedId(null);
204
+ await fetchUsers();
205
+ toast.success("User updated");
206
+ } catch (_e) {
207
+ toast.error("Update failed");
208
+ } finally {
209
+ setLoading(false);
210
+ }
211
+ };
212
+
213
+ const onEdit = (u: User) => {
214
+ setSelectedId(u.id);
215
+ setValue("email", u.email);
216
+ setValue("name", u.name ?? "");
217
+ };
218
+
219
+ const confirmDelete = (id: string) => setPendingDeleteId(id);
220
+
221
+ const doDelete = async () => {
222
+ if (!pendingDeleteId) return;
223
+ setLoading(true);
224
+ try {
225
+ const res = await fetch(`/api/users/${pendingDeleteId}`, {
226
+ method: "DELETE",
227
+ });
228
+ if (!res.ok) throw new Error("Delete failed");
229
+ toast.success("User deleted");
230
+ await fetchUsers();
231
+ } catch (_e) {
232
+ toast.error("Delete failed");
233
+ } finally {
234
+ setLoading(false);
235
+ setPendingDeleteId(null);
236
+ }
237
+ };
238
+
239
+ const onCancel = () => {
240
+ reset(defaultValues);
241
+ setSelectedId(null);
242
+ };
243
+
244
+ return (
245
+ <main className={cn(container.className, className)}>
246
+ <Card className={cn(formCard.className)}>
247
+ <h2 className={cn(leftHeading.className)}>User Manager</h2>
248
+
249
+ <Form<UserFormValues> methods={formMethods}>
250
+ <form
251
+ onSubmit={handleSubmit(selectedId ? updateUser : createUser)}
252
+ className={cn(form.className)}
253
+ >
254
+ <FormField
255
+ control={control}
256
+ name="email"
257
+ render={({ field }) => (
258
+ <FormItem className="space-y-2">
259
+ <FormLabel>Email</FormLabel>
260
+ <FormControl>
261
+ <Input
262
+ id="email"
263
+ type="email"
264
+ placeholder="user@example.com"
265
+ autoComplete="off"
266
+ {...field}
267
+ />
268
+ </FormControl>
269
+ <FormMessage />
270
+ </FormItem>
271
+ )}
272
+ />
273
+
274
+ <FormField
275
+ control={control}
276
+ name="name"
277
+ render={({ field }) => (
278
+ <FormItem className="space-y-2">
279
+ <FormLabel>Name</FormLabel>
280
+ <FormControl>
281
+ <Input
282
+ id="name"
283
+ placeholder="Optional name"
284
+ autoComplete="off"
285
+ {...field}
286
+ />
287
+ </FormControl>
288
+ <FormMessage />
289
+ </FormItem>
290
+ )}
291
+ />
292
+
293
+ <div className={cn(submitRow.className)}>
294
+ {!selectedId ? (
295
+ <Button type="submit" disabled={isSubmitting || loading}>
296
+ Create User
297
+ </Button>
298
+ ) : (
299
+ <>
300
+ <Button type="submit" disabled={isSubmitting || loading}>
301
+ Update User
302
+ </Button>
303
+ <Button type="button" variant="ghost" onClick={onCancel}>
304
+ Cancel
305
+ </Button>
306
+ </>
307
+ )}
308
+ </div>
309
+ </form>
310
+ </Form>
311
+ </Card>
312
+
313
+ <Card className={cn(listCard.className)}>
314
+ <h3 className={cn(rightHeading.className)}>Users</h3>
315
+ <Table className={cn(table.className)}>
316
+ <TableHeader>
317
+ <TableRow>
318
+ <TableHead>Email</TableHead>
319
+ <TableHead>Name</TableHead>
320
+ <TableHead className="w-[200px]">Actions</TableHead>
321
+ </TableRow>
322
+ </TableHeader>
323
+ <TableBody>
324
+ {listLoading ? (
325
+ <>
326
+ {[0, 1, 2].map((i) => (
327
+ <TableRow key={i}>
328
+ <TableCell>
329
+ <Skeleton className="h-4 w-48" />
330
+ </TableCell>
331
+ <TableCell>
332
+ <Skeleton className="h-4 w-32" />
333
+ </TableCell>
334
+ <TableCell>
335
+ {/* actions cell empty during loading */}
336
+ </TableCell>
337
+ </TableRow>
338
+ ))}
339
+ </>
340
+ ) : (
341
+ <>
342
+ {users.map((u) => (
343
+ <TableRow key={u.id}>
344
+ <TableCell className="font-medium">{u.email}</TableCell>
345
+ <TableCell>{u.name || ""}</TableCell>
346
+ <TableCell>
347
+ <div className={cn(actionsRow.className)}>
348
+ <Button
349
+ size="sm"
350
+ variant="outline"
351
+ onClick={() => onEdit(u)}
352
+ >
353
+ Edit
354
+ </Button>
355
+ <AlertDialog>
356
+ <AlertDialogTrigger asChild>
357
+ <Button
358
+ size="sm"
359
+ variant="destructive"
360
+ onClick={() => confirmDelete(u.id)}
361
+ >
362
+ Delete
363
+ </Button>
364
+ </AlertDialogTrigger>
365
+ <AlertDialogContent>
366
+ <AlertDialogHeader>
367
+ <AlertDialogTitle>Delete user?</AlertDialogTitle>
368
+ <AlertDialogDescription>
369
+ This action cannot be undone. The user will be
370
+ removed permanently.
371
+ </AlertDialogDescription>
372
+ </AlertDialogHeader>
373
+ <AlertDialogFooter>
374
+ <AlertDialogCancel
375
+ onClick={() => setPendingDeleteId(null)}
376
+ >
377
+ Cancel
378
+ </AlertDialogCancel>
379
+ <AlertDialogAction onClick={doDelete}>
380
+ Delete
381
+ </AlertDialogAction>
382
+ </AlertDialogFooter>
383
+ </AlertDialogContent>
384
+ </AlertDialog>
385
+ </div>
386
+ </TableCell>
387
+ </TableRow>
388
+ ))}
389
+ {users.length === 0 && (
390
+ <TableRow>
391
+ <TableCell colSpan={3} className="text-muted-foreground">
392
+ No users yet.
393
+ </TableCell>
394
+ </TableRow>
395
+ )}
396
+ </>
397
+ )}
398
+ </TableBody>
399
+ </Table>
400
+
401
+ <div className="mt-3 flex items-center justify-between">
402
+ <div>
403
+ <Button
404
+ size="sm"
405
+ onClick={() => setPage((p) => Math.max(1, p - 1))}
406
+ disabled={page <= 1 || listLoading}
407
+ >
408
+ Prev
409
+ </Button>
410
+ <Button
411
+ size="sm"
412
+ variant="ghost"
413
+ onClick={() => setPage((p) => p + 1)}
414
+ disabled={
415
+ listLoading ||
416
+ (total > 0 ? page * perPage >= total : users.length === 0)
417
+ }
418
+ >
419
+ Next
420
+ </Button>
421
+ </div>
422
+ <div className="text-muted-foreground text-sm">
423
+ Showing {Math.min((page - 1) * perPage + 1, total || 0)} -{" "}
424
+ {Math.min(page * perPage, total || 0)} of {total}
425
+ </div>
426
+ </div>
427
+ </Card>
428
+ </main>
429
+ );
430
+ }
431
+
432
+ export default UsersManager;
@@ -0,0 +1,15 @@
1
+ import "server-only";
2
+
3
+ import { PrismaClient } from "@prisma/client";
4
+
5
+ const globalForPrisma = globalThis as unknown as {
6
+ prisma: PrismaClient | undefined;
7
+ };
8
+
9
+ export const prisma =
10
+ globalForPrisma.prisma ??
11
+ new PrismaClient({
12
+ log: ["error", "warn"],
13
+ });
14
+
15
+ if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
@@ -0,0 +1,90 @@
1
+ import { NextResponse } from "next/server";
2
+ import type { ZodError } from "zod";
3
+ import type { Prisma } from "@prisma/client";
4
+ import { zodErrorToFieldErrors, type FieldErrors } from "@/lib/api/errors";
5
+
6
+ export type ApiResult<T = unknown> = {
7
+ success: boolean;
8
+ data?: T;
9
+ message?: string;
10
+ errors?: FieldErrors;
11
+ code?: string;
12
+ };
13
+
14
+ export function ok<T>(data?: T, message?: string): ApiResult<T> {
15
+ return { success: true, data, message };
16
+ }
17
+
18
+ export function fail(
19
+ message: string,
20
+ errors?: FieldErrors,
21
+ code?: string,
22
+ ): ApiResult<never> {
23
+ return { success: false, message, errors, code };
24
+ }
25
+
26
+ export function jsonOk<T>(
27
+ data?: T,
28
+ options?: { status?: number; message?: string },
29
+ ) {
30
+ const { status = 200, message } = options || {};
31
+ return NextResponse.json(ok(data, message), { status });
32
+ }
33
+
34
+ export function jsonFail(
35
+ message: string,
36
+ options?: { status?: number; errors?: FieldErrors; code?: string },
37
+ ) {
38
+ const { status = 400, errors, code } = options || {};
39
+ return NextResponse.json(fail(message, errors, code), { status });
40
+ }
41
+
42
+ export function fromZod(error: ZodError, message = "Invalid input") {
43
+ return fail(message, zodErrorToFieldErrors(error), "ZOD_VALIDATION_ERROR");
44
+ }
45
+
46
+ export function jsonFromZod(
47
+ error: ZodError,
48
+ options?: { message?: string; status?: number },
49
+ ) {
50
+ return jsonFail(options?.message ?? "Invalid input", {
51
+ status: options?.status ?? 400,
52
+ errors: zodErrorToFieldErrors(error),
53
+ code: "ZOD_VALIDATION_ERROR",
54
+ });
55
+ }
56
+
57
+ type PrismaKnownRequestErrorLike = { code: string; meta?: unknown };
58
+
59
+ function isPrismaKnownRequestError(
60
+ e: unknown,
61
+ ): e is PrismaKnownRequestErrorLike {
62
+ return !!e && typeof e === "object" && "code" in e;
63
+ }
64
+
65
+ function extractUniqueField(
66
+ err: PrismaKnownRequestErrorLike,
67
+ ): string | undefined {
68
+ const target = (err.meta as any)?.target;
69
+ if (Array.isArray(target) && target.length) return String(target[0]);
70
+ if (typeof target === "string") return String(target.split("_")[0]);
71
+ return undefined;
72
+ }
73
+
74
+ export function jsonFromPrisma(
75
+ error: unknown,
76
+ options?: { defaultMessage?: string },
77
+ ) {
78
+ if (isPrismaKnownRequestError(error) && error.code === "P2002") {
79
+ const field = extractUniqueField(error) ?? "form";
80
+ return jsonFail("Conflict: duplicate value", {
81
+ status: 409,
82
+ errors: { [field]: "Already in use" },
83
+ code: "P2002",
84
+ });
85
+ }
86
+ return jsonFail(options?.defaultMessage ?? "Database error", {
87
+ status: 500,
88
+ code: "PRISMA_ERROR",
89
+ });
90
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "dependencies": {
3
+ "@nextworks/blocks-core": "*",
4
+ "@prisma/client": "^6.16.1",
5
+ "zod": "^3.23.8",
6
+ "date-fns": "^4.1.0"
7
+ },
8
+ "devDependencies": {
9
+ "prisma": "^6.16.1"
10
+ }
11
+ }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ import { PrismaClient } from "@prisma/client";
3
+ const prisma = new PrismaClient();
4
+
5
+ async function main() {
6
+ const adminEmail = process.env.SEED_ADMIN_EMAIL || "admin@example.com";
7
+ const adminPassword = process.env.SEED_ADMIN_PASSWORD || "password123";
8
+
9
+ // Upsert admin user
10
+ const user = await prisma.user.upsert({
11
+ where: { email: adminEmail },
12
+ update: {},
13
+ create: {
14
+ email: adminEmail,
15
+ name: "Admin",
16
+ role: "admin",
17
+ },
18
+ });
19
+
20
+ // Create a sample post (upsert by slug, since slug is unique)
21
+ await prisma.post.upsert({
22
+ where: { slug: "hello-from-nextworks-demo" },
23
+ update: {},
24
+ create: {
25
+ title: "Hello from Nextworks Demo",
26
+ slug: "hello-from-nextworks-demo",
27
+ content: "This is a demo post created by the seed script.",
28
+ author: { connect: { email: adminEmail } },
29
+ published: true,
30
+ },
31
+ });
32
+
33
+ console.log("Seed complete");
34
+ }
35
+
36
+ main()
37
+ .catch((e) => {
38
+ console.error(e);
39
+ process.exit(1);
40
+ })
41
+ .finally(() => process.exit());
File without changes
@@ -0,0 +1,49 @@
1
+ Forms kit (cli/kits/forms)
2
+
3
+ This folder contains the files that the `nextworks` CLI copies into a target Next.js project when you run:
4
+
5
+ ```bash
6
+ npx nextworks add forms
7
+ ```
8
+
9
+ What the kit includes
10
+
11
+ - Form primitives built on React Hook Form + Zod: components/ui/form/\*
12
+ - Select / Checkbox / Switch primitives wired for form usage
13
+ - Shared validation schemas: lib/validation/forms.ts and lib/validation/wizard.ts
14
+ - Form error mapping helper: lib/forms/map-errors.ts
15
+ - Example pages:
16
+ - Basic form: app/examples/forms/basic/page.tsx
17
+ - Server action form: app/examples/forms/server-action/page.tsx and form-client.tsx
18
+ - Wizard example: app/examples/forms/wizard/page.tsx and wizard-client.tsx
19
+ - Wizard API route: app/api/wizard/route.ts
20
+
21
+ Notes
22
+
23
+ - The forms primitives are UI-only and do not require Prisma or NextAuth.
24
+ - The wizard example assumes a basic app/api/wizard/route.ts endpoint; adjust it as needed for your project.
25
+ - Keep this kit folder in sync with cli_manifests/forms_manifest.json.
26
+
27
+ Post-install steps (recommended)
28
+
29
+ 1. Install dependencies (the CLI will merge `package-deps.json` into your `package.json`):
30
+
31
+ ```bash
32
+ npm install
33
+ ```
34
+
35
+ 2. Ensure you have React Hook Form and Zod installed (if not already present). The CLI will attempt to add these via `package-deps.json`.
36
+
37
+ 3. Start your dev server and explore the examples:
38
+
39
+ ```bash
40
+ npm run dev
41
+ ```
42
+
43
+ - /examples/forms/basic
44
+ - /examples/forms/server-action
45
+ - /examples/forms/wizard
46
+
47
+ CLI behavior (for maintainers)
48
+
49
+ - In the Nextworks repo, the CLI copies the files listed in `cli/cli_manifests/forms_manifest.json`. Keep that manifest and this kit folder in sync when editing the repo.
File without changes