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
@@ -0,0 +1,413 @@
1
+ 'use client'
2
+
3
+ import EditorJS from '@editorjs/editorjs'
4
+ import { zodResolver } from '@hookform/resolvers/zod'
5
+ import { TaskStatus } from '@prisma/client'
6
+ import edjsHTML from 'editorjs-html'
7
+ import Link from 'next/link'
8
+ import { useRouter } from 'next/navigation'
9
+ import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
10
+ import { useForm } from 'react-hook-form'
11
+ import { z } from 'zod'
12
+
13
+ import { Heading } from '@/components/heading'
14
+ import { Icons } from '@/components/icons'
15
+ import { Shell } from '@/components/shell'
16
+ import { SystemInfo } from '@/components/system-info'
17
+ import {
18
+ AlertDialog,
19
+ AlertDialogAction,
20
+ AlertDialogCancel,
21
+ AlertDialogContent,
22
+ AlertDialogDescription,
23
+ AlertDialogFooter,
24
+ AlertDialogHeader,
25
+ AlertDialogTitle,
26
+ AlertDialogTrigger
27
+ } from '@/components/ui/alert-dialog'
28
+ import { Button } from '@/components/ui/button'
29
+ import {
30
+ Form,
31
+ FormControl,
32
+ FormField,
33
+ FormItem,
34
+ FormLabel,
35
+ FormMessage
36
+ } from '@/components/ui/form'
37
+ import { Input } from '@/components/ui/input'
38
+ import {
39
+ Select,
40
+ SelectContent,
41
+ SelectItem,
42
+ SelectTrigger,
43
+ SelectValue
44
+ } from '@/components/ui/select'
45
+ import { toast } from '@/components/ui/use-toast'
46
+ import { formatDateTime, timesAgo } from '@/lib/formatDate'
47
+ import { generateLabel } from '@/lib/generateLabel'
48
+ import '@/styles/editor.css'
49
+ import {
50
+ GetTaskFnDataType,
51
+ createTask,
52
+ deleteTask,
53
+ updateTask
54
+ } from './actions'
55
+
56
+ const TaskSchema = z.object({
57
+ title: z.string().nonempty(),
58
+ status: z.nativeEnum(TaskStatus)
59
+ })
60
+
61
+ type FormData = z.infer<typeof TaskSchema>
62
+
63
+ const statuses = Object.values(TaskStatus).map(x => ({
64
+ label: generateLabel(x),
65
+ value: x
66
+ }))
67
+
68
+ const edjsParser = edjsHTML()
69
+
70
+ export const Render: FC<{
71
+ task: GetTaskFnDataType | undefined
72
+ }> = ({ task }) => {
73
+ const form = useForm<FormData>({
74
+ resolver: zodResolver(TaskSchema),
75
+ defaultValues: {
76
+ title: task?.title ?? '',
77
+ status: task?.status || 'Todo'
78
+ }
79
+ })
80
+ const router = useRouter()
81
+ const [isSaving, setIsSaving] = useState(false)
82
+ const [isDeleting, setIsDeleting] = useState(false)
83
+ const [isEditorMounted, setIsMounted] = useState<boolean>(false)
84
+
85
+ const editorRef = useRef<EditorJS>()
86
+
87
+ const initializeEditor = useCallback(async () => {
88
+ const EditorJS = (await import('@editorjs/editorjs')).default
89
+
90
+ // @ts-ignore
91
+ const Table = (await import('@editorjs/table')).default
92
+ // @ts-ignore
93
+ const List = (await import('@editorjs/list')).default
94
+ // @ts-ignore
95
+ const Image = (await import('@editorjs/image')).default
96
+ // @ts-ignore
97
+ const Header = (await import('@editorjs/header')).default
98
+ // @ts-ignore
99
+ const Quote = (await import('@editorjs/quote')).default
100
+ // @ts-ignore
101
+ const CheckList = (await import('@editorjs/checklist')).default
102
+ // @ts-ignore
103
+ const Delimiter = (await import('@editorjs/delimiter')).default
104
+
105
+ if (!editorRef.current) {
106
+ const editor = new EditorJS({
107
+ holder: 'editor',
108
+ onReady() {
109
+ editorRef.current = editor
110
+ },
111
+ placeholder: 'Type here to write...',
112
+ inlineToolbar: true,
113
+ data: task?.description as any,
114
+ tools: {
115
+ header: Header,
116
+ list: List,
117
+ image: {
118
+ class: Image,
119
+ config: {
120
+ uploader: {
121
+ async uploadByFile(file: any) {
122
+ const formData = new FormData()
123
+ formData.append('file', file)
124
+ const res = await fetch('/api/upload', {
125
+ method: 'POST',
126
+ body: formData
127
+ })
128
+ const json = await res.json()
129
+ if (json.success && json.id && json.url) {
130
+ return {
131
+ success: true,
132
+ file: {
133
+ id: json.id,
134
+ url: json.url
135
+ }
136
+ }
137
+ } else {
138
+ return {
139
+ success: false
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ },
146
+ table: Table,
147
+ checklist: CheckList,
148
+ quote: Quote,
149
+ delimiter: Delimiter
150
+ }
151
+ })
152
+ }
153
+ }, [task?.description])
154
+
155
+ useEffect(() => {
156
+ if (typeof window !== 'undefined') setIsMounted(true)
157
+ }, [])
158
+
159
+ useEffect(() => {
160
+ if (!isEditorMounted) return
161
+
162
+ initializeEditor()
163
+
164
+ return () => {
165
+ editorRef.current?.destroy()
166
+ editorRef.current = undefined
167
+ }
168
+ }, [isEditorMounted, initializeEditor])
169
+
170
+ const editorHTML = useMemo(() => {
171
+ if (!task?.description) return ''
172
+ return edjsParser.parse(task.description as any).join('')
173
+ }, [task?.description])
174
+ console.log(editorHTML)
175
+
176
+ async function onSubmit(data: FormData) {
177
+ setIsSaving(true)
178
+ try {
179
+ const editorData = await editorRef.current?.save()
180
+ const imageIds: string[] = []
181
+ if (editorData?.blocks)
182
+ for (const b of editorData.blocks)
183
+ if (
184
+ b.type === 'image' &&
185
+ b.data &&
186
+ b.data.file &&
187
+ b.data.file.id &&
188
+ b.data.file.url
189
+ )
190
+ imageIds.push(b.data.file.id)
191
+
192
+ if (!task) {
193
+ const newId = await createTask({
194
+ ...data,
195
+ description: editorData,
196
+ imageIds
197
+ })
198
+ router.replace(`/tasks/${newId}`)
199
+ } else {
200
+ await updateTask({
201
+ id: task.id,
202
+ ...data,
203
+ description: editorData,
204
+ imageIds
205
+ })
206
+ }
207
+ toast({
208
+ title: 'Task saved'
209
+ })
210
+ } catch (err) {
211
+ toast({
212
+ title: 'Error saving task',
213
+ variant: 'destructive'
214
+ })
215
+ }
216
+ setIsSaving(false)
217
+ }
218
+
219
+ return (
220
+ <Shell>
221
+ <Heading heading={task ? task.title : 'New Task'} />
222
+ <Form {...form}>
223
+ <form
224
+ className="grid grid-cols-1 gap-3 md:grid-cols-2"
225
+ onSubmit={form.handleSubmit(onSubmit)}
226
+ >
227
+ <div className="col-span-1 mb-1 grid grid-cols-2 gap-2 sm:flex md:col-span-2">
228
+ <Button type="button" onClick={() => router.back()} variant="ghost">
229
+ <Icons.chevronLeft className="mr-2 h-4 w-4" />
230
+ Back
231
+ </Button>
232
+ <Button type="submit" disabled={isSaving || isDeleting}>
233
+ {isSaving ? (
234
+ <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
235
+ ) : (
236
+ <Icons.save className="mr-2 h-4 w-4" />
237
+ )}
238
+ <span>Save</span>
239
+ </Button>
240
+ {task ? (
241
+ <AlertDialog>
242
+ <AlertDialogTrigger asChild>
243
+ <Button
244
+ type="button"
245
+ variant="destructive"
246
+ disabled={isSaving || isDeleting}
247
+ >
248
+ {isDeleting ? (
249
+ <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
250
+ ) : (
251
+ <Icons.delete className="mr-2 h-4 w-4" />
252
+ )}
253
+ Delete
254
+ </Button>
255
+ </AlertDialogTrigger>
256
+ <AlertDialogContent>
257
+ <AlertDialogHeader>
258
+ <AlertDialogTitle>
259
+ Are you absolutely sure?
260
+ </AlertDialogTitle>
261
+ <AlertDialogDescription>
262
+ This action cannot be undone.
263
+ </AlertDialogDescription>
264
+ </AlertDialogHeader>
265
+ <AlertDialogFooter>
266
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
267
+ <AlertDialogAction
268
+ onClick={async () => {
269
+ setIsDeleting(true)
270
+ try {
271
+ await deleteTask(task.id)
272
+ toast({
273
+ title: 'Task deleted'
274
+ })
275
+ router.push('/tasks')
276
+ } catch (err) {
277
+ toast({
278
+ title: 'Error deleting task',
279
+ variant: 'destructive'
280
+ })
281
+ }
282
+ setIsDeleting(false)
283
+ }}
284
+ >
285
+ Continue
286
+ </AlertDialogAction>
287
+ </AlertDialogFooter>
288
+ </AlertDialogContent>
289
+ </AlertDialog>
290
+ ) : undefined}
291
+ {task ? (
292
+ <Link href="/tasks/new">
293
+ <Button
294
+ type="button"
295
+ disabled={isSaving || isDeleting}
296
+ variant="secondary"
297
+ className="w-full"
298
+ >
299
+ <Icons.add className="mr-2 h-4 w-4" />
300
+ New
301
+ </Button>
302
+ </Link>
303
+ ) : undefined}
304
+ </div>
305
+
306
+ <FormField
307
+ control={form.control}
308
+ name="title"
309
+ render={({ field }) => (
310
+ <FormItem>
311
+ <FormLabel>Title</FormLabel>
312
+ <FormControl>
313
+ <Input
314
+ type="text"
315
+ placeholder="Enter a title"
316
+ disabled={isSaving}
317
+ {...field}
318
+ />
319
+ </FormControl>
320
+ <FormMessage />
321
+ </FormItem>
322
+ )}
323
+ />
324
+
325
+ <FormField
326
+ control={form.control}
327
+ name="status"
328
+ render={({ field }) => (
329
+ <FormItem>
330
+ <FormLabel>Status</FormLabel>
331
+ <Select
332
+ onValueChange={field.onChange}
333
+ defaultValue={field.value}
334
+ disabled={isSaving}
335
+ >
336
+ <FormControl>
337
+ <SelectTrigger>
338
+ <SelectValue placeholder="Select status" />
339
+ </SelectTrigger>
340
+ </FormControl>
341
+ <SelectContent>
342
+ {statuses.map(({ label, value }) => (
343
+ <SelectItem key={value} value={value}>
344
+ {label}
345
+ </SelectItem>
346
+ ))}
347
+ </SelectContent>
348
+ </Select>
349
+ <FormMessage />
350
+ </FormItem>
351
+ )}
352
+ />
353
+
354
+ {isEditorMounted ? (
355
+ <div className="col-span-1 md:col-span-2 w-full mt-5 prose prose-neutral mx-auto dark:prose-invert">
356
+ <h3 className="w-full bg-transparent text-3xl font-bold">
357
+ Description
358
+ </h3>
359
+ <div id="editor" className="min-h-[360px] w-full" />
360
+ <p className="text-sm text-gray-500">
361
+ Use{' '}
362
+ <kbd className="rounded-md border bg-muted px-1 text-xs uppercase">
363
+ Tab
364
+ </kbd>{' '}
365
+ to open the command menu.
366
+ </p>
367
+ </div>
368
+ ) : undefined}
369
+ </form>
370
+ </Form>
371
+ {task ? (
372
+ <SystemInfo
373
+ items={[
374
+ {
375
+ label: 'Id',
376
+ value: task.id
377
+ },
378
+ {
379
+ label: 'Created At',
380
+ value: formatDateTime(task.createdAt)
381
+ },
382
+ {
383
+ label: 'Updated At',
384
+ value: timesAgo(task.updatedAt)
385
+ },
386
+ {
387
+ label: 'Created By',
388
+ value: (
389
+ <Link
390
+ href={`/users/${task.createdBy.id}`}
391
+ className="underline underline-offset-4 transition-all hover:text-foreground"
392
+ >
393
+ {task.createdBy.name ?? 'Unknown'}
394
+ </Link>
395
+ )
396
+ },
397
+ {
398
+ label: 'Updated By',
399
+ value: (
400
+ <Link
401
+ href={`/users/${task.updatedBy.id}`}
402
+ className="underline underline-offset-4 transition-all hover:text-foreground"
403
+ >
404
+ {task.updatedBy.name ?? 'Unknown'}
405
+ </Link>
406
+ )
407
+ }
408
+ ]}
409
+ />
410
+ ) : undefined}
411
+ </Shell>
412
+ )
413
+ }
@@ -0,0 +1,69 @@
1
+ 'use server'
2
+
3
+ import { Task, TaskStatus } from '@prisma/client'
4
+
5
+ import { getAuthSession } from '@/lib/auth'
6
+ import { prisma } from '@/lib/db'
7
+ import { UnwrapPromise } from '@/types/UnwrapPromise'
8
+
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
63
+ })
64
+ ])
65
+
66
+ return { tasks, total }
67
+ }
68
+
69
+ export type GetTasksFnDataType = UnwrapPromise<ReturnType<typeof getTasks>>
@@ -0,0 +1,53 @@
1
+ import { Task, TaskStatus } from '@prisma/client'
2
+ import { Metadata } from 'next'
3
+
4
+ import { siteConfig } from '@/lib/siteConfig'
5
+ import { getTasks } from './actions'
6
+ import { Render } from './render'
7
+
8
+ export const metadata: Metadata = {
9
+ title: 'Tasks' + ' | ' + siteConfig.name
10
+ }
11
+
12
+ const TasksPage = async ({
13
+ searchParams
14
+ }: {
15
+ searchParams: {
16
+ page?: string
17
+ limit?: string
18
+ sort?: string
19
+ title?: string
20
+ status?: string
21
+ }
22
+ }) => {
23
+ const page = searchParams.page ? parseInt(searchParams.page) : 1
24
+ const limit = searchParams.limit ? parseInt(searchParams.limit) : 10
25
+
26
+ const [sortBy, sortOrder] =
27
+ typeof searchParams.sort === 'string'
28
+ ? (searchParams.sort.split('.') as [
29
+ keyof Task | undefined,
30
+ 'asc' | 'desc' | undefined
31
+ ])
32
+ : []
33
+
34
+ const title = searchParams.title
35
+
36
+ const status =
37
+ typeof searchParams.status === 'string'
38
+ ? (searchParams.status.split('.') as TaskStatus[])
39
+ : undefined
40
+
41
+ const data = await getTasks({
42
+ page,
43
+ limit,
44
+ sortBy,
45
+ sortOrder,
46
+ title,
47
+ status
48
+ })
49
+
50
+ return <Render data={data} />
51
+ }
52
+
53
+ export default TasksPage
@@ -0,0 +1,107 @@
1
+ 'use client'
2
+
3
+ import { TaskStatus } from '@prisma/client'
4
+ import Link from 'next/link'
5
+ import { FC } from 'react'
6
+
7
+ import { DataTable } from '@/components/data-table/data-table'
8
+ import { DataTableColumnHeader } from '@/components/data-table/data-table-column-header'
9
+ import { Heading } from '@/components/heading'
10
+ import { Icons } from '@/components/icons'
11
+ import { Shell } from '@/components/shell'
12
+ import { Button } from '@/components/ui/button'
13
+ import { formatDateTime, timesAgo } from '@/lib/formatDate'
14
+ import { generateLabel } from '@/lib/generateLabel'
15
+ import { GetTasksFnDataType } from './actions'
16
+
17
+ const statuses = Object.values(TaskStatus).map(x => ({
18
+ label: generateLabel(x),
19
+ value: x
20
+ }))
21
+
22
+ export const Render: FC<{
23
+ data: GetTasksFnDataType
24
+ }> = ({ data }) => {
25
+ return (
26
+ <Shell>
27
+ <Heading heading="Tasks" text="List of all the tasks">
28
+ <Link href="/tasks/new">
29
+ <Button>
30
+ <Icons.add className="mr-2 h-4 w-4" />
31
+ New
32
+ </Button>
33
+ </Link>
34
+ </Heading>
35
+ <DataTable
36
+ columns={[
37
+ {
38
+ accessorKey: 'title',
39
+ header: ({ column }) => (
40
+ <DataTableColumnHeader column={column} title="Title" />
41
+ ),
42
+ cell: ({ row }) => (
43
+ <Link
44
+ className="underline underline-offset-4"
45
+ href={`/tasks/${row.original.id}`}
46
+ >
47
+ {row.original.title}
48
+ </Link>
49
+ )
50
+ },
51
+ {
52
+ accessorKey: 'status',
53
+ header: ({ column }) => (
54
+ <DataTableColumnHeader column={column} title="Status" />
55
+ ),
56
+ cell: ({ row }) => row.original.status.replace('_', ' ')
57
+ },
58
+ {
59
+ accessorKey: 'createdBy',
60
+ header: ({ column }) => (
61
+ <DataTableColumnHeader column={column} title="Created By" />
62
+ ),
63
+ cell: ({ row }) => row.original.createdBy.name,
64
+ enableSorting: false
65
+ },
66
+ {
67
+ accessorKey: 'updatedBy',
68
+ header: ({ column }) => (
69
+ <DataTableColumnHeader column={column} title="Updated By" />
70
+ ),
71
+ cell: ({ row }) => row.original.updatedBy.name,
72
+ enableSorting: false
73
+ },
74
+ {
75
+ accessorKey: 'createdAt',
76
+ header: ({ column }) => (
77
+ <DataTableColumnHeader column={column} title="Created At" />
78
+ ),
79
+ cell: ({ row }) => formatDateTime(row.original.createdAt)
80
+ },
81
+ {
82
+ accessorKey: 'updatedAt',
83
+ header: ({ column }) => (
84
+ <DataTableColumnHeader column={column} title="Updated At" />
85
+ ),
86
+ cell: ({ row }) => timesAgo(row.original.updatedAt)
87
+ }
88
+ ]}
89
+ data={data.tasks}
90
+ total={data.total}
91
+ searchableColumns={[
92
+ {
93
+ id: 'title',
94
+ title: 'Title'
95
+ }
96
+ ]}
97
+ filterableColumns={[
98
+ {
99
+ id: 'status',
100
+ title: 'Status',
101
+ options: statuses
102
+ }
103
+ ]}
104
+ />
105
+ </Shell>
106
+ )
107
+ }