create-ej-app 3.2.0 → 5.0.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 (316) hide show
  1. package/LICENSE +1 -1
  2. package/cli.js +5 -1
  3. package/cli.ts +40 -33
  4. package/package.json +4 -4
  5. package/template/base/.env.example +10 -9
  6. package/template/base/.eslintrc.cjs +17 -15
  7. package/template/base/components.json +1 -1
  8. package/template/base/drizzle.config.ts +11 -0
  9. package/template/base/next.config.mjs +1 -12
  10. package/template/base/package.json +63 -56
  11. package/template/base/postcss.config.mjs +8 -0
  12. package/template/base/prettier.config.mjs +11 -0
  13. package/template/base/public/vercel.svg +1 -0
  14. package/template/base/src/app/(app)/dashboard/layout.tsx +14 -0
  15. package/template/base/src/app/(app)/dashboard/page.tsx +0 -7
  16. package/template/base/src/app/(app)/layout.tsx +55 -20
  17. package/template/base/src/app/(app)/roles/[id]/actions.ts +59 -0
  18. package/template/base/src/app/(app)/roles/[id]/page.tsx +46 -0
  19. package/template/base/src/app/(app)/roles/[id]/render.tsx +264 -0
  20. package/template/base/src/app/(app)/roles/[id]/validation.ts +22 -0
  21. package/template/base/src/app/(app)/roles/actions.ts +33 -0
  22. package/template/base/src/app/(app)/roles/page.tsx +52 -0
  23. package/template/base/src/app/(app)/roles/render.tsx +80 -0
  24. package/template/base/src/app/(app)/settings/actions.ts +11 -12
  25. package/template/base/src/app/(app)/settings/layout.tsx +14 -0
  26. package/template/base/src/app/(app)/settings/page.tsx +1 -10
  27. package/template/base/src/app/(app)/settings/render.tsx +11 -17
  28. package/template/base/src/app/(app)/settings/validation.ts +5 -0
  29. package/template/base/src/app/(app)/tasks/[id]/actions.ts +69 -124
  30. package/template/base/src/app/(app)/tasks/[id]/page.tsx +37 -3
  31. package/template/base/src/app/(app)/tasks/[id]/render.tsx +162 -93
  32. package/template/base/src/app/(app)/tasks/[id]/validation.ts +18 -0
  33. package/template/base/src/app/(app)/tasks/actions.ts +34 -64
  34. package/template/base/src/app/(app)/tasks/page.tsx +15 -9
  35. package/template/base/src/app/(app)/tasks/render.tsx +28 -6
  36. package/template/base/src/app/(app)/tasks/statusEnum.ts +3 -0
  37. package/template/base/src/app/(app)/users/[id]/actions.ts +47 -62
  38. package/template/base/src/app/(app)/users/[id]/page.tsx +15 -8
  39. package/template/base/src/app/(app)/users/[id]/render.tsx +119 -34
  40. package/template/base/src/app/(app)/users/[id]/validation.ts +18 -0
  41. package/template/base/src/app/(app)/users/actions.ts +28 -49
  42. package/template/base/src/app/(app)/users/page.tsx +22 -9
  43. package/template/base/src/app/(app)/users/render.tsx +47 -3
  44. package/template/base/src/app/(auth)/layout.tsx +7 -0
  45. package/template/base/src/app/(auth)/login/page.tsx +2 -2
  46. package/template/base/src/app/(auth)/login/render.tsx +146 -0
  47. package/template/base/src/app/(marketing)/layout.tsx +1 -1
  48. package/template/base/src/app/(marketing)/page.tsx +8 -7
  49. package/template/base/src/app/favicon.ico +0 -0
  50. package/template/base/src/app/layout.tsx +14 -17
  51. package/template/base/src/components/app-nav-command.tsx +18 -0
  52. package/template/base/src/components/data-table/data-table-column-header.tsx +2 -2
  53. package/template/base/src/components/icons.tsx +21 -5
  54. package/template/base/src/components/ui/alert-dialog.tsx +1 -1
  55. package/template/base/src/components/ui/command.tsx +1 -1
  56. package/template/base/src/components/ui/dialog.tsx +1 -1
  57. package/template/base/src/components/ui/sonner.tsx +31 -0
  58. package/template/base/src/components/user-avatar.tsx +4 -2
  59. package/template/base/src/data-access/resource.ts +85 -0
  60. package/template/base/src/data-access/role-permission.ts +45 -0
  61. package/template/base/src/data-access/role.ts +105 -0
  62. package/template/base/src/data-access/task.ts +141 -0
  63. package/template/base/src/data-access/user.ts +141 -0
  64. package/template/base/src/db/index.ts +14 -0
  65. package/template/base/src/db/schema.ts +287 -0
  66. package/template/base/src/env.mjs +10 -30
  67. package/template/base/src/hooks/use-lock-body.tsx +2 -1
  68. package/template/base/src/lib/auth.ts +32 -23
  69. package/template/base/src/lib/currentUser.ts +8 -0
  70. package/template/base/src/lib/s3Client.ts +156 -0
  71. package/template/base/src/lib/safe-action.ts +19 -0
  72. package/template/base/src/lib/sortOrderEnum.ts +2 -0
  73. package/template/base/src/use-case/role-permission.ts +19 -0
  74. package/template/base/src/use-case/role.ts +110 -0
  75. package/template/base/src/use-case/task.ts +166 -0
  76. package/template/base/src/use-case/user.ts +121 -0
  77. package/template/base/tailwind.config.ts +5 -4
  78. package/template/base/tsconfig.json +3 -2
  79. package/template/new/.env.example +13 -0
  80. package/template/new/components.json +22 -0
  81. package/template/new/drizzle.config.ts +10 -0
  82. package/template/new/eslint.config.mjs +18 -0
  83. package/template/new/next.config.ts +14 -0
  84. package/template/new/package.json +81 -0
  85. package/template/new/postcss.config.mjs +5 -0
  86. package/template/new/prettier.config.mjs +11 -0
  87. package/template/new/public/placeholder.svg +1 -0
  88. package/template/new/src/app/(auth)/login/email-login-form.tsx +97 -0
  89. package/template/new/src/app/(auth)/login/page.tsx +72 -0
  90. package/template/new/src/app/(auth)/login/social-login-buttons.tsx +69 -0
  91. package/template/new/src/app/api/auth/[...all]/route.ts +4 -0
  92. package/template/new/src/app/favicon.ico +0 -0
  93. package/template/new/src/app/globals.css +128 -0
  94. package/template/new/src/app/layout.tsx +50 -0
  95. package/template/new/src/app/page.tsx +3 -0
  96. package/template/new/src/components/loader.tsx +25 -0
  97. package/template/new/src/components/react-query-provider.tsx +12 -0
  98. package/template/new/src/components/screen-size.tsx +40 -0
  99. package/template/new/src/components/theme-toggle.tsx +22 -0
  100. package/template/new/src/components/ui/button.tsx +60 -0
  101. package/template/new/src/components/ui/card.tsx +92 -0
  102. package/template/new/src/components/ui/form.tsx +167 -0
  103. package/template/new/src/components/ui/input.tsx +21 -0
  104. package/template/new/src/components/ui/label.tsx +24 -0
  105. package/template/new/src/components/ui/separator.tsx +28 -0
  106. package/template/new/src/components/ui/sonner.tsx +40 -0
  107. package/template/new/src/db/index.ts +7 -0
  108. package/template/new/src/db/schema.ts +74 -0
  109. package/template/new/src/env.ts +42 -0
  110. package/template/new/src/lib/auth-client.ts +18 -0
  111. package/template/new/src/lib/auth.ts +67 -0
  112. package/template/new/src/lib/safe-action.ts +15 -0
  113. package/template/new/src/lib/toast-message.ts +15 -0
  114. package/template/new/tsconfig.json +49 -0
  115. package/template/prisma/.env.example +16 -0
  116. package/template/prisma/.eslintrc.cjs +24 -0
  117. package/template/prisma/components.json +17 -0
  118. package/template/prisma/next.config.mjs +15 -0
  119. package/template/prisma/package.json +80 -0
  120. package/template/prisma/public/next.svg +1 -0
  121. package/template/prisma/src/app/(app)/dashboard/page.tsx +19 -0
  122. package/template/prisma/src/app/(app)/layout.tsx +90 -0
  123. package/template/prisma/src/app/(app)/settings/actions.ts +18 -0
  124. package/template/prisma/src/app/(app)/settings/loading.tsx +28 -0
  125. package/template/prisma/src/app/(app)/settings/page.tsx +28 -0
  126. package/template/prisma/src/app/(app)/settings/render.tsx +113 -0
  127. package/template/prisma/src/app/(app)/tasks/[id]/actions.ts +147 -0
  128. package/template/prisma/src/app/(app)/tasks/[id]/page.tsx +25 -0
  129. package/template/prisma/src/app/(app)/tasks/[id]/render.tsx +413 -0
  130. package/template/prisma/src/app/(app)/tasks/actions.ts +69 -0
  131. package/template/prisma/src/app/(app)/tasks/page.tsx +53 -0
  132. package/template/prisma/src/app/(app)/tasks/render.tsx +107 -0
  133. package/template/prisma/src/app/(app)/users/[id]/actions.ts +83 -0
  134. package/template/prisma/src/app/(app)/users/[id]/page.tsx +32 -0
  135. package/template/prisma/src/app/(app)/users/[id]/render.tsx +240 -0
  136. package/template/prisma/src/app/(app)/users/actions.ts +55 -0
  137. package/template/prisma/src/app/(app)/users/page.tsx +49 -0
  138. package/template/prisma/src/app/(app)/users/render.tsx +80 -0
  139. package/template/prisma/src/app/(auth)/layout.tsx +20 -0
  140. package/template/prisma/src/app/(auth)/login/page.tsx +49 -0
  141. package/template/prisma/src/app/(marketing)/layout.tsx +42 -0
  142. package/template/prisma/src/app/(marketing)/page.tsx +96 -0
  143. package/template/prisma/src/app/api/auth/[...nextauth]/route.ts +7 -0
  144. package/template/prisma/src/app/favicon.ico +0 -0
  145. package/template/prisma/src/app/layout.tsx +34 -0
  146. package/template/prisma/src/components/app-nav-command.tsx +160 -0
  147. package/template/prisma/src/components/dark-mode-toggle.tsx +43 -0
  148. package/template/prisma/src/components/data-table/data-table-column-header.tsx +91 -0
  149. package/template/prisma/src/components/data-table/data-table-faceted-filter.tsx +149 -0
  150. package/template/prisma/src/components/data-table/data-table-loading.tsx +77 -0
  151. package/template/prisma/src/components/data-table/data-table-pagination.tsx +94 -0
  152. package/template/prisma/src/components/data-table/data-table-toolbar.tsx +78 -0
  153. package/template/prisma/src/components/data-table/data-table-types.ts +17 -0
  154. package/template/prisma/src/components/data-table/data-table-view-options.tsx +59 -0
  155. package/template/prisma/src/components/data-table/data-table.tsx +301 -0
  156. package/template/prisma/src/components/data-table/simple-data-table.tsx +109 -0
  157. package/template/prisma/src/components/empty-placeholder.tsx +78 -0
  158. package/template/prisma/src/components/heading.tsx +17 -0
  159. package/template/prisma/src/components/icons.tsx +85 -0
  160. package/template/prisma/src/components/main-nav.tsx +64 -0
  161. package/template/prisma/src/components/shell.tsx +13 -0
  162. package/template/prisma/src/components/side-nav.tsx +51 -0
  163. package/template/prisma/src/components/site-footer.tsx +31 -0
  164. package/template/prisma/src/components/system-info.tsx +36 -0
  165. package/template/prisma/src/components/ui/alert-dialog.tsx +141 -0
  166. package/template/prisma/src/components/ui/avatar.tsx +50 -0
  167. package/template/prisma/src/components/ui/badge.tsx +36 -0
  168. package/template/prisma/src/components/ui/button.tsx +56 -0
  169. package/template/prisma/src/components/ui/card.tsx +79 -0
  170. package/template/prisma/src/components/ui/checkbox.tsx +30 -0
  171. package/template/prisma/src/components/ui/command.tsx +155 -0
  172. package/template/prisma/src/components/ui/dialog.tsx +122 -0
  173. package/template/prisma/src/components/ui/dropdown-menu.tsx +200 -0
  174. package/template/prisma/src/components/ui/form.tsx +176 -0
  175. package/template/prisma/src/components/ui/input.tsx +25 -0
  176. package/template/prisma/src/components/ui/label.tsx +26 -0
  177. package/template/prisma/src/components/ui/popover.tsx +31 -0
  178. package/template/prisma/src/components/ui/select.tsx +160 -0
  179. package/template/prisma/src/components/ui/separator.tsx +31 -0
  180. package/template/prisma/src/components/ui/skeleton.tsx +15 -0
  181. package/template/prisma/src/components/ui/table.tsx +117 -0
  182. package/template/prisma/src/components/user-account-nav.tsx +60 -0
  183. package/template/prisma/src/components/user-avatar.tsx +25 -0
  184. package/template/prisma/src/env.mjs +78 -0
  185. package/template/prisma/src/hooks/use-debounce.ts +17 -0
  186. package/template/prisma/src/hooks/use-lock-body.tsx +12 -0
  187. package/template/prisma/src/lib/auth.ts +80 -0
  188. package/template/prisma/src/lib/cn.ts +4 -0
  189. package/template/prisma/src/lib/delay.ts +2 -0
  190. package/template/prisma/src/lib/formatDate.ts +11 -0
  191. package/template/prisma/src/lib/generateLabel.ts +5 -0
  192. package/template/prisma/src/lib/siteConfig.ts +4 -0
  193. package/template/prisma/src/styles/globals.css +76 -0
  194. package/template/prisma/tailwind.config.ts +80 -0
  195. package/template/prisma/tsconfig.json +46 -0
  196. package/template/prisma-advanced/src/lib/cn.ts +4 -0
  197. package/template/prisma-advanced/src/lib/delay.ts +2 -0
  198. package/template/prisma-advanced/src/lib/siteConfig.ts +4 -0
  199. /package/template/{advanced/src/styles → base/src/app}/globals.css +0 -0
  200. /package/template/{advanced/src/hooks/use-debounce.ts → base/src/hooks/use-debounce.tsx} +0 -0
  201. /package/template/{advanced → new}/src/lib/cn.ts +0 -0
  202. /package/template/{advanced → new}/src/lib/delay.ts +0 -0
  203. /package/template/{advanced → new}/src/lib/siteConfig.ts +0 -0
  204. /package/template/{base → prisma}/postcss.config.cjs +0 -0
  205. /package/template/{base → prisma}/prettier.config.ts +0 -0
  206. /package/template/{base → prisma}/prisma/schema.prisma +0 -0
  207. /package/template/{base → prisma}/src/app/(auth)/login/user-auth-form.tsx +0 -0
  208. /package/template/{base → prisma}/src/app/api/upload/route.ts +0 -0
  209. /package/template/{advanced → prisma}/src/components/mobile-nav.tsx +0 -0
  210. /package/template/{base → prisma}/src/components/theme-provider.tsx +0 -0
  211. /package/template/{base → prisma}/src/components/ui/textarea.tsx +0 -0
  212. /package/template/{base → prisma}/src/components/ui/toast.tsx +0 -0
  213. /package/template/{base → prisma}/src/components/ui/toaster.tsx +0 -0
  214. /package/template/{base → prisma}/src/components/ui/use-toast.ts +0 -0
  215. /package/template/{base → prisma}/src/lib/db.ts +0 -0
  216. /package/template/{base → prisma}/src/lib/storageClient.ts +0 -0
  217. /package/template/{base → prisma}/src/styles/editor.css +0 -0
  218. /package/template/{base → prisma}/src/types/UnwrapPromise.ts +0 -0
  219. /package/template/{advanced → prisma-advanced}/.env.example +0 -0
  220. /package/template/{advanced → prisma-advanced}/.eslintrc.cjs +0 -0
  221. /package/template/{advanced → prisma-advanced}/components.json +0 -0
  222. /package/template/{advanced → prisma-advanced}/next.config.mjs +0 -0
  223. /package/template/{advanced → prisma-advanced}/package.json +0 -0
  224. /package/template/{advanced → prisma-advanced}/postcss.config.cjs +0 -0
  225. /package/template/{advanced → prisma-advanced}/prettier.config.ts +0 -0
  226. /package/template/{advanced → prisma-advanced}/prisma/schema.prisma +0 -0
  227. /package/template/{advanced → prisma-advanced}/public/next.svg +0 -0
  228. /package/template/{advanced → prisma-advanced}/src/app/(app)/dashboard/page.tsx +0 -0
  229. /package/template/{advanced → prisma-advanced}/src/app/(app)/layout.tsx +0 -0
  230. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/actions.ts +0 -0
  231. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/page.tsx +0 -0
  232. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/render.tsx +0 -0
  233. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/actions.ts +0 -0
  234. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/page.tsx +0 -0
  235. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/render.tsx +0 -0
  236. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/actions.ts +0 -0
  237. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/loading.tsx +0 -0
  238. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/page.tsx +0 -0
  239. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/render.tsx +0 -0
  240. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/actions.ts +0 -0
  241. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/page.tsx +0 -0
  242. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/render.tsx +0 -0
  243. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/actions.ts +0 -0
  244. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/page.tsx +0 -0
  245. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/render.tsx +0 -0
  246. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/actions.ts +0 -0
  247. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/page.tsx +0 -0
  248. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/render.tsx +0 -0
  249. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/actions.ts +0 -0
  250. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/page.tsx +0 -0
  251. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/render.tsx +0 -0
  252. /package/template/{advanced → prisma-advanced}/src/app/(auth)/layout.tsx +0 -0
  253. /package/template/{advanced → prisma-advanced}/src/app/(auth)/login/page.tsx +0 -0
  254. /package/template/{advanced → prisma-advanced}/src/app/(auth)/login/user-auth-form.tsx +0 -0
  255. /package/template/{advanced → prisma-advanced}/src/app/(marketing)/layout.tsx +0 -0
  256. /package/template/{advanced → prisma-advanced}/src/app/(marketing)/page.tsx +0 -0
  257. /package/template/{advanced → prisma-advanced}/src/app/api/auth/[...nextauth]/route.ts +0 -0
  258. /package/template/{advanced → prisma-advanced}/src/app/api/upload/route.ts +0 -0
  259. /package/template/{advanced → prisma-advanced}/src/app/favicon.ico +0 -0
  260. /package/template/{advanced → prisma-advanced}/src/app/layout.tsx +0 -0
  261. /package/template/{advanced → prisma-advanced}/src/components/app-nav-command.tsx +0 -0
  262. /package/template/{advanced → prisma-advanced}/src/components/dark-mode-toggle.tsx +0 -0
  263. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-column-header.tsx +0 -0
  264. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-faceted-filter.tsx +0 -0
  265. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-loading.tsx +0 -0
  266. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-pagination.tsx +0 -0
  267. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-toolbar.tsx +0 -0
  268. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-types.ts +0 -0
  269. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-view-options.tsx +0 -0
  270. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table.tsx +0 -0
  271. /package/template/{advanced → prisma-advanced}/src/components/data-table/simple-data-table.tsx +0 -0
  272. /package/template/{advanced → prisma-advanced}/src/components/empty-placeholder.tsx +0 -0
  273. /package/template/{advanced → prisma-advanced}/src/components/heading.tsx +0 -0
  274. /package/template/{advanced → prisma-advanced}/src/components/icons.tsx +0 -0
  275. /package/template/{advanced → prisma-advanced}/src/components/main-nav.tsx +0 -0
  276. /package/template/{advanced → prisma-advanced}/src/components/shell.tsx +0 -0
  277. /package/template/{advanced → prisma-advanced}/src/components/side-nav.tsx +0 -0
  278. /package/template/{advanced → prisma-advanced}/src/components/site-footer.tsx +0 -0
  279. /package/template/{advanced → prisma-advanced}/src/components/system-info.tsx +0 -0
  280. /package/template/{advanced → prisma-advanced}/src/components/theme-provider.tsx +0 -0
  281. /package/template/{advanced → prisma-advanced}/src/components/ui/alert-dialog.tsx +0 -0
  282. /package/template/{advanced → prisma-advanced}/src/components/ui/avatar.tsx +0 -0
  283. /package/template/{advanced → prisma-advanced}/src/components/ui/badge.tsx +0 -0
  284. /package/template/{advanced → prisma-advanced}/src/components/ui/button.tsx +0 -0
  285. /package/template/{advanced → prisma-advanced}/src/components/ui/card.tsx +0 -0
  286. /package/template/{advanced → prisma-advanced}/src/components/ui/checkbox.tsx +0 -0
  287. /package/template/{advanced → prisma-advanced}/src/components/ui/command.tsx +0 -0
  288. /package/template/{advanced → prisma-advanced}/src/components/ui/dialog.tsx +0 -0
  289. /package/template/{advanced → prisma-advanced}/src/components/ui/dropdown-menu.tsx +0 -0
  290. /package/template/{advanced → prisma-advanced}/src/components/ui/form.tsx +0 -0
  291. /package/template/{advanced → prisma-advanced}/src/components/ui/input.tsx +0 -0
  292. /package/template/{advanced → prisma-advanced}/src/components/ui/label.tsx +0 -0
  293. /package/template/{advanced → prisma-advanced}/src/components/ui/popover.tsx +0 -0
  294. /package/template/{advanced → prisma-advanced}/src/components/ui/select.tsx +0 -0
  295. /package/template/{advanced → prisma-advanced}/src/components/ui/separator.tsx +0 -0
  296. /package/template/{advanced → prisma-advanced}/src/components/ui/skeleton.tsx +0 -0
  297. /package/template/{advanced → prisma-advanced}/src/components/ui/table.tsx +0 -0
  298. /package/template/{advanced → prisma-advanced}/src/components/ui/textarea.tsx +0 -0
  299. /package/template/{advanced → prisma-advanced}/src/components/ui/toast.tsx +0 -0
  300. /package/template/{advanced → prisma-advanced}/src/components/ui/toaster.tsx +0 -0
  301. /package/template/{advanced → prisma-advanced}/src/components/ui/use-toast.ts +0 -0
  302. /package/template/{advanced → prisma-advanced}/src/components/user-account-nav.tsx +0 -0
  303. /package/template/{advanced → prisma-advanced}/src/components/user-avatar.tsx +0 -0
  304. /package/template/{advanced → prisma-advanced}/src/env.mjs +0 -0
  305. /package/template/{base → prisma-advanced}/src/hooks/use-debounce.ts +0 -0
  306. /package/template/{advanced → prisma-advanced}/src/hooks/use-lock-body.tsx +0 -0
  307. /package/template/{advanced → prisma-advanced}/src/lib/auth.ts +0 -0
  308. /package/template/{advanced → prisma-advanced}/src/lib/db.ts +0 -0
  309. /package/template/{advanced → prisma-advanced}/src/lib/formatDate.ts +0 -0
  310. /package/template/{advanced → prisma-advanced}/src/lib/generateLabel.ts +0 -0
  311. /package/template/{advanced → prisma-advanced}/src/lib/storageClient.ts +0 -0
  312. /package/template/{advanced → prisma-advanced}/src/styles/editor.css +0 -0
  313. /package/template/{base → prisma-advanced}/src/styles/globals.css +0 -0
  314. /package/template/{advanced → prisma-advanced}/src/types/UnwrapPromise.ts +0 -0
  315. /package/template/{advanced → prisma-advanced}/tailwind.config.ts +0 -0
  316. /package/template/{advanced → prisma-advanced}/tsconfig.json +0 -0
@@ -1,69 +1,39 @@
1
1
  'use server'
2
2
 
3
- import { Task, TaskStatus } from '@prisma/client'
3
+ import { z } from 'zod'
4
4
 
5
- import { getAuthSession } from '@/lib/auth'
6
- import { prisma } from '@/lib/db'
7
- import { UnwrapPromise } from '@/types/UnwrapPromise'
5
+ import { authActionClient } from '@/lib/safe-action'
6
+ import { SortOrderEnum } from '@/lib/sortOrderEnum'
7
+ import { getTasksWithUserUseCase } from '@/use-case/task'
8
+ import { TaskStatusEnumArr } from './statusEnum'
8
9
 
9
- export const getTasks = async ({
10
- page,
11
- limit,
12
- sortBy,
13
- sortOrder,
14
- title,
15
- status
16
- }: {
17
- page: number
18
- limit: number
19
- sortBy?: keyof Task
20
- sortOrder?: 'asc' | 'desc'
21
- title?: string
22
- status?: TaskStatus[]
23
- }) => {
24
- const session = await getAuthSession()
25
- if (!session) throw new Error('Unauthorized')
26
-
27
- const where = {
28
- title: title
29
- ? { contains: title, mode: 'insensitive' as const }
30
- : undefined,
31
- status: status?.length ? { in: status } : undefined
32
- }
33
- const [tasks, total] = await Promise.all([
34
- prisma.task.findMany({
35
- where,
36
- select: {
37
- id: true,
38
- title: true,
39
- status: true,
40
- createdAt: true,
41
- updatedAt: true,
42
- createdBy: {
43
- select: {
44
- id: true,
45
- name: true
46
- }
47
- },
48
- updatedBy: {
49
- select: {
50
- id: true,
51
- name: true
52
- }
53
- }
54
- },
55
- skip: (page - 1) * limit,
56
- take: limit,
57
- orderBy: {
58
- [sortBy || 'createdAt']: sortOrder || 'desc'
59
- }
60
- }),
61
- prisma.task.count({
62
- where
10
+ export const getTasksAction = authActionClient
11
+ .schema(
12
+ z.object({
13
+ page: z.number().gte(1),
14
+ limit: z.number().gte(1).lte(1000),
15
+ sortBy: z
16
+ .enum(['id', 'title', 'status', 'createdAt', 'updatedAt'])
17
+ .optional(),
18
+ sortOrder: z.enum(SortOrderEnum).optional(),
19
+ title: z.string().optional(),
20
+ status: z.array(z.enum(TaskStatusEnumArr)).optional()
63
21
  })
64
- ])
65
-
66
- return { tasks, total }
67
- }
68
-
69
- export type GetTasksFnDataType = UnwrapPromise<ReturnType<typeof getTasks>>
22
+ )
23
+ .action(async ({ parsedInput, ctx }) => {
24
+ return await getTasksWithUserUseCase(ctx.user, {
25
+ page: parsedInput.page,
26
+ limit: parsedInput.limit,
27
+ sortBy: parsedInput.sortBy,
28
+ sortOrder: parsedInput.sortOrder,
29
+ filters:
30
+ parsedInput.title || parsedInput.status?.length
31
+ ? {
32
+ title: parsedInput.title || undefined,
33
+ status: parsedInput.status?.length
34
+ ? parsedInput.status
35
+ : undefined
36
+ }
37
+ : undefined
38
+ })
39
+ })
@@ -1,8 +1,9 @@
1
- import { Task, TaskStatus } from '@prisma/client'
2
1
  import { Metadata } from 'next'
2
+ import { notFound } from 'next/navigation'
3
3
 
4
+ import { authGuard, getAuthSession } from '@/lib/auth'
4
5
  import { siteConfig } from '@/lib/siteConfig'
5
- import { getTasks } from './actions'
6
+ import { getTasksAction } from './actions'
6
7
  import { Render } from './render'
7
8
 
8
9
  export const metadata: Metadata = {
@@ -20,25 +21,28 @@ const TasksPage = async ({
20
21
  status?: string
21
22
  }
22
23
  }) => {
24
+ const session = await getAuthSession()
25
+ if (!session) return notFound()
26
+
27
+ const g = await authGuard(session, 'TaskList')
28
+ if (!g) return notFound()
29
+
23
30
  const page = searchParams.page ? parseInt(searchParams.page) : 1
24
31
  const limit = searchParams.limit ? parseInt(searchParams.limit) : 10
25
32
 
26
33
  const [sortBy, sortOrder] =
27
34
  typeof searchParams.sort === 'string'
28
- ? (searchParams.sort.split('.') as [
29
- keyof Task | undefined,
30
- 'asc' | 'desc' | undefined
31
- ])
35
+ ? (searchParams.sort.split('.') as [any, 'asc' | 'desc' | undefined])
32
36
  : []
33
37
 
34
38
  const title = searchParams.title
35
39
 
36
40
  const status =
37
41
  typeof searchParams.status === 'string'
38
- ? (searchParams.status.split('.') as TaskStatus[])
42
+ ? (searchParams.status.split('.') as any[])
39
43
  : undefined
40
44
 
41
- const data = await getTasks({
45
+ const tasks = await getTasksAction({
42
46
  page,
43
47
  limit,
44
48
  sortBy,
@@ -47,7 +51,9 @@ const TasksPage = async ({
47
51
  status
48
52
  })
49
53
 
50
- return <Render data={data} />
54
+ if (!tasks?.data) throw new Error('Tasks data not found')
55
+
56
+ return <Render data={tasks.data} />
51
57
  }
52
58
 
53
59
  export default TasksPage
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
 
3
- import { TaskStatus } from '@prisma/client'
3
+ import { InferSelectModel } from 'drizzle-orm'
4
4
  import Link from 'next/link'
5
5
  import { FC } from 'react'
6
6
 
@@ -10,17 +10,35 @@ import { Heading } from '@/components/heading'
10
10
  import { Icons } from '@/components/icons'
11
11
  import { Shell } from '@/components/shell'
12
12
  import { Button } from '@/components/ui/button'
13
+ import { Task } from '@/db/schema'
13
14
  import { formatDateTime, timesAgo } from '@/lib/formatDate'
14
15
  import { generateLabel } from '@/lib/generateLabel'
15
- import { GetTasksFnDataType } from './actions'
16
+ import { TaskStatusEnumArr } from './statusEnum'
16
17
 
17
- const statuses = Object.values(TaskStatus).map(x => ({
18
+ const statuses = TaskStatusEnumArr.map(x => ({
18
19
  label: generateLabel(x),
19
20
  value: x
20
21
  }))
21
22
 
22
23
  export const Render: FC<{
23
- data: GetTasksFnDataType
24
+ data: {
25
+ tasks: {
26
+ id: string
27
+ title: string
28
+ status: InferSelectModel<typeof Task>['status']
29
+ createdBy: {
30
+ id: string
31
+ name?: string | null
32
+ }
33
+ updatedBy?: {
34
+ id: string
35
+ name?: string | null
36
+ } | null
37
+ createdAt: Date
38
+ updatedAt?: Date | null
39
+ }[]
40
+ total: number
41
+ }
24
42
  }> = ({ data }) => {
25
43
  return (
26
44
  <Shell>
@@ -68,7 +86,8 @@ export const Render: FC<{
68
86
  header: ({ column }) => (
69
87
  <DataTableColumnHeader column={column} title="Updated By" />
70
88
  ),
71
- cell: ({ row }) => row.original.updatedBy.name,
89
+ cell: ({ row }) =>
90
+ row.original.updatedBy ? row.original.updatedBy.name : undefined,
72
91
  enableSorting: false
73
92
  },
74
93
  {
@@ -83,7 +102,10 @@ export const Render: FC<{
83
102
  header: ({ column }) => (
84
103
  <DataTableColumnHeader column={column} title="Updated At" />
85
104
  ),
86
- cell: ({ row }) => timesAgo(row.original.updatedAt)
105
+ cell: ({ row }) =>
106
+ row.original.updatedAt
107
+ ? timesAgo(row.original.updatedAt)
108
+ : undefined
87
109
  }
88
110
  ]}
89
111
  data={data.tasks}
@@ -0,0 +1,3 @@
1
+ import { Task } from '@/db/schema'
2
+
3
+ export const TaskStatusEnumArr = Task.status.enumValues
@@ -1,83 +1,68 @@
1
1
  'use server'
2
2
 
3
3
  import { revalidatePath } from 'next/cache'
4
+ import { z } from 'zod'
4
5
 
5
- import { getAuthSession } from '@/lib/auth'
6
- import { prisma } from '@/lib/db'
7
- import { UnwrapPromise } from '@/types/UnwrapPromise'
6
+ import { authActionClient } from '@/lib/safe-action'
7
+ import { getRolesMiniUseCase } from '@/use-case/role'
8
+ import {
9
+ createUserUseCase,
10
+ deleteUserUseCase,
11
+ getUserByIdUseCase,
12
+ updateUserUseCase
13
+ } from '@/use-case/user'
14
+ import { UserCreateUpdateSchema, UserUpdateServerSchema } from './validation'
8
15
 
9
- export const getUser = async (id: string) => {
10
- const session = await getAuthSession()
11
- if (!session) throw new Error('Unauthorized')
12
-
13
- return await prisma.user.findUnique({
14
- where: {
15
- id
16
- }
16
+ export const getUserAction = authActionClient
17
+ .schema(z.string())
18
+ .action(async ({ parsedInput: id, ctx }) => {
19
+ return await getUserByIdUseCase(ctx.user.type, id)
17
20
  })
18
- }
19
21
 
20
- export type GetUserFnDataType = UnwrapPromise<ReturnType<typeof getUser>>
22
+ export const createUserAction = authActionClient
23
+ .schema(UserCreateUpdateSchema)
24
+ .action(async ({ parsedInput, ctx }) => {
25
+ const id = await createUserUseCase(ctx.user.type, parsedInput)
21
26
 
22
- export const createUser = async ({
23
- name,
24
- email
25
- }: {
26
- name: string
27
- email: string
28
- }) => {
29
- const session = await getAuthSession()
30
- if (!session) throw new Error('Unauthorized')
27
+ revalidatePath('/users')
28
+ revalidatePath(`/users/${id}`)
31
29
 
32
- const { id } = await prisma.user.create({
33
- data: {
34
- name,
35
- email
30
+ return {
31
+ success: true,
32
+ id
36
33
  }
37
34
  })
38
35
 
39
- revalidatePath('/users')
40
- revalidatePath(`/users/${id}`)
41
-
42
- return id
43
- }
36
+ export const updateUserAction = authActionClient
37
+ .schema(UserUpdateServerSchema)
38
+ .action(async ({ parsedInput: { id, name, type, roleId }, ctx }) => {
39
+ await updateUserUseCase(ctx.user.type, id, {
40
+ name,
41
+ type,
42
+ roleId
43
+ })
44
44
 
45
- export const updateUser = async ({
46
- id,
47
- name,
48
- email
49
- }: {
50
- id: string
51
- name: string
52
- email: string
53
- }) => {
54
- const session = await getAuthSession()
55
- if (!session) throw new Error('Unauthorized')
45
+ revalidatePath('/users')
46
+ revalidatePath(`/users/${id}`)
56
47
 
57
- await prisma.user.update({
58
- where: {
59
- id
60
- },
61
- data: {
62
- name,
63
- email
48
+ return {
49
+ success: true
64
50
  }
65
51
  })
66
52
 
67
- revalidatePath('/users')
68
- revalidatePath(`/users/${id}`)
69
- }
53
+ export const deleteUserAction = authActionClient
54
+ .schema(z.string())
55
+ .action(async ({ parsedInput: id, ctx }) => {
56
+ await deleteUserUseCase(ctx.user.type, id)
70
57
 
71
- export const deleteUser = async (id: string) => {
72
- const session = await getAuthSession()
73
- if (!session) throw new Error('Unauthorized')
58
+ revalidatePath('/users')
59
+ revalidatePath(`/users/${id}`)
74
60
 
75
- await prisma.user.delete({
76
- where: {
77
- id
78
- }
61
+ return { success: true }
79
62
  })
80
63
 
81
- revalidatePath('/users')
82
- revalidatePath(`/users/${id}`)
83
- }
64
+ export const getRolesMiniAction = authActionClient
65
+ .schema(z.undefined())
66
+ .action(async ({ ctx }) => {
67
+ return await getRolesMiniUseCase(ctx.user.type)
68
+ })
@@ -1,8 +1,9 @@
1
1
  import { Metadata } from 'next'
2
2
  import { notFound } from 'next/navigation'
3
3
 
4
+ import { authGuard, getAuthSession } from '@/lib/auth'
4
5
  import { siteConfig } from '@/lib/siteConfig'
5
- import { GetUserFnDataType, getUser } from './actions'
6
+ import { getRolesMiniAction, getUserAction } from './actions'
6
7
  import { Render } from './render'
7
8
 
8
9
  export const generateMetadata = async (props: {
@@ -18,15 +19,21 @@ const UserPage = async ({
18
19
  id: string
19
20
  }
20
21
  }) => {
21
- let user: GetUserFnDataType | undefined
22
+ const session = await getAuthSession()
23
+ if (!session) return notFound()
22
24
 
23
- if (id !== 'new') {
24
- const x = await getUser(id)
25
- if (!x) return notFound()
26
- user = x
27
- }
25
+ const g = await authGuard(session)
26
+ if (!g) return notFound()
27
+
28
+ const roles = await getRolesMiniAction(undefined)
29
+ if (!roles?.data) return notFound()
30
+
31
+ if (id === 'new') return <Render roles={roles.data} />
32
+
33
+ const user = await getUserAction(id)
34
+ if (!user?.data) return notFound()
28
35
 
29
- return <Render user={user} />
36
+ return <Render user={user.data} roles={roles.data} />
30
37
  }
31
38
 
32
39
  export default UserPage
@@ -5,6 +5,7 @@ import Link from 'next/link'
5
5
  import { useRouter } from 'next/navigation'
6
6
  import { FC, useState } from 'react'
7
7
  import { useForm } from 'react-hook-form'
8
+ import { toast } from 'sonner'
8
9
  import { z } from 'zod'
9
10
 
10
11
  import { Heading } from '@/components/heading'
@@ -32,30 +33,49 @@ import {
32
33
  FormMessage
33
34
  } from '@/components/ui/form'
34
35
  import { Input } from '@/components/ui/input'
35
- import { toast } from '@/components/ui/use-toast'
36
- import { formatDateTime, timesAgo } from '@/lib/formatDate'
37
36
  import {
38
- GetUserFnDataType,
39
- createUser,
40
- deleteUser,
41
- updateUser
42
- } from './actions'
37
+ Select,
38
+ SelectContent,
39
+ SelectItem,
40
+ SelectTrigger,
41
+ SelectValue
42
+ } from '@/components/ui/select'
43
+ import { User } from '@/db/schema'
44
+ import { formatDateTime, timesAgo } from '@/lib/formatDate'
45
+ import { generateLabel } from '@/lib/generateLabel'
46
+ import { InferSelectModel } from 'drizzle-orm'
47
+ import { createUserAction, deleteUserAction, updateUserAction } from './actions'
48
+ import { UserCreateUpdateSchema, UserTypeEnumArr } from './validation'
43
49
 
44
- const UserSchema = z.object({
45
- name: z.string().nonempty(),
46
- email: z.string().email()
47
- })
50
+ type FormData = z.infer<typeof UserCreateUpdateSchema>
48
51
 
49
- type FormData = z.infer<typeof UserSchema>
52
+ const types = UserTypeEnumArr.map(x => ({
53
+ label: generateLabel(x),
54
+ value: x
55
+ }))
50
56
 
51
57
  export const Render: FC<{
52
- user: GetUserFnDataType | undefined
53
- }> = ({ user }) => {
58
+ user?: {
59
+ id: string
60
+ name?: string | null
61
+ email: string
62
+ type: InferSelectModel<typeof User>['type']
63
+ roleId?: string | null
64
+ createdAt: Date
65
+ updatedAt?: Date | null
66
+ } | null
67
+ roles: {
68
+ id: string
69
+ name: string
70
+ }[]
71
+ }> = ({ user, roles }) => {
54
72
  const form = useForm<FormData>({
55
- resolver: zodResolver(UserSchema),
73
+ resolver: zodResolver(UserCreateUpdateSchema),
56
74
  defaultValues: {
57
75
  name: user?.name ?? '',
58
- email: user?.email ?? ''
76
+ email: user?.email ?? '',
77
+ type: user?.type ?? 'Normal',
78
+ roleId: user?.roleId
59
79
  }
60
80
  })
61
81
  const router = useRouter()
@@ -66,21 +86,23 @@ export const Render: FC<{
66
86
  setIsSaving(true)
67
87
  try {
68
88
  if (!user) {
69
- const newId = await createUser(data)
70
- router.replace(`/users/${newId}`)
89
+ const res = await createUserAction(data)
90
+ if (!res?.data?.success)
91
+ throw new Error(res?.serverError || 'No success returned from server')
92
+ if (!res?.data?.id) throw new Error('No id returned from server')
93
+ router.replace(`/users/${res.data.id}`)
71
94
  } else {
72
- await updateUser({
95
+ const res = await updateUserAction({
73
96
  id: user.id,
74
97
  ...data
75
98
  })
99
+ if (!res?.data?.success)
100
+ throw new Error(res?.serverError || 'No success returned from server')
76
101
  }
77
- toast({
78
- title: 'User saved'
79
- })
102
+ toast('User saved')
80
103
  } catch (err) {
81
- toast({
82
- title: 'Error saving user',
83
- variant: 'destructive'
104
+ toast('Error saving user', {
105
+ description: (err as any).message || undefined
84
106
  })
85
107
  }
86
108
  setIsSaving(false)
@@ -140,15 +162,18 @@ export const Render: FC<{
140
162
  onClick={async () => {
141
163
  setIsDeleting(true)
142
164
  try {
143
- await deleteUser(user.id)
144
- toast({
145
- title: 'User deleted'
146
- })
147
- router.push('/users')
165
+ const res = await deleteUserAction(user.id)
166
+ if (!res?.data?.success)
167
+ throw new Error(
168
+ res?.serverError ||
169
+ 'No success returned from server'
170
+ )
171
+ toast('User deleted')
172
+ router.replace('/users')
148
173
  } catch (err) {
149
- toast({
150
- title: 'Error deleting user',
151
- variant: 'destructive'
174
+ toast('Error deleting user', {
175
+ description:
176
+ (err as any).message || 'Something went wrong'
152
177
  })
153
178
  }
154
179
  setIsDeleting(false)
@@ -215,6 +240,66 @@ export const Render: FC<{
215
240
  </FormItem>
216
241
  )}
217
242
  />
243
+
244
+ <FormField
245
+ control={form.control}
246
+ name="type"
247
+ render={({ field }) => (
248
+ <FormItem>
249
+ <FormLabel>Type</FormLabel>
250
+ <Select
251
+ onValueChange={field.onChange}
252
+ defaultValue={field.value}
253
+ disabled={isSaving}
254
+ >
255
+ <FormControl>
256
+ <SelectTrigger>
257
+ <SelectValue placeholder="Select type" />
258
+ </SelectTrigger>
259
+ </FormControl>
260
+ <SelectContent>
261
+ {types.map(({ label, value }) => (
262
+ <SelectItem key={value} value={value}>
263
+ {label}
264
+ </SelectItem>
265
+ ))}
266
+ </SelectContent>
267
+ </Select>
268
+ <FormMessage />
269
+ </FormItem>
270
+ )}
271
+ />
272
+
273
+ {form.getValues('type') === 'Normal' ? (
274
+ <FormField
275
+ control={form.control}
276
+ name="roleId"
277
+ render={({ field }) => (
278
+ <FormItem>
279
+ <FormLabel>Role</FormLabel>
280
+ <Select
281
+ onValueChange={field.onChange}
282
+ defaultValue={field.value || undefined}
283
+ disabled={isSaving}
284
+ >
285
+ <FormControl>
286
+ <SelectTrigger>
287
+ <SelectValue placeholder="Select role" />
288
+ </SelectTrigger>
289
+ </FormControl>
290
+ <SelectContent>
291
+ {roles.map(({ id, name }) => (
292
+ <SelectItem key={id} value={id}>
293
+ {name}
294
+ </SelectItem>
295
+ ))}
296
+ </SelectContent>
297
+ </Select>
298
+ <FormMessage />
299
+ </FormItem>
300
+ )}
301
+ />
302
+ ) : undefined}
218
303
  </form>
219
304
  </Form>
220
305
  {user ? (
@@ -230,7 +315,7 @@ export const Render: FC<{
230
315
  },
231
316
  {
232
317
  label: 'Updated At',
233
- value: timesAgo(user.updatedAt)
318
+ value: user.updatedAt ? timesAgo(user.updatedAt) : '-'
234
319
  }
235
320
  ]}
236
321
  />
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod'
2
+
3
+ import { User } from '@/db/schema'
4
+
5
+ export const UserTypeEnumArr = User.type.enumValues
6
+
7
+ export const UserCreateUpdateSchema = z.object({
8
+ name: z.string().min(1).trim(),
9
+ email: z.string().email().toLowerCase().trim(),
10
+ type: z.enum(UserTypeEnumArr),
11
+ roleId: z.string().optional().nullable()
12
+ })
13
+
14
+ export const UserUpdateServerSchema = UserCreateUpdateSchema.merge(
15
+ z.object({
16
+ id: z.string().min(1)
17
+ })
18
+ )