create-ej-app 3.1.0 → 4.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 (278) hide show
  1. package/cli.js +5 -1
  2. package/cli.ts +40 -33
  3. package/package.json +4 -4
  4. package/template/base/.env.example +10 -9
  5. package/template/base/.eslintrc.cjs +17 -15
  6. package/template/base/components.json +3 -2
  7. package/template/base/drizzle.config.ts +11 -0
  8. package/template/base/next.config.mjs +1 -12
  9. package/template/base/package.json +63 -56
  10. package/template/base/postcss.config.mjs +8 -0
  11. package/template/base/prettier.config.mjs +11 -0
  12. package/template/base/public/vercel.svg +1 -0
  13. package/template/base/src/app/(app)/dashboard/layout.tsx +14 -0
  14. package/template/base/src/app/(app)/dashboard/page.tsx +0 -7
  15. package/template/base/src/app/(app)/layout.tsx +55 -20
  16. package/template/base/src/app/(app)/roles/[id]/actions.ts +59 -0
  17. package/template/base/src/app/(app)/roles/[id]/page.tsx +46 -0
  18. package/template/base/src/app/(app)/roles/[id]/render.tsx +264 -0
  19. package/template/base/src/app/(app)/roles/[id]/validation.ts +22 -0
  20. package/template/base/src/app/(app)/roles/actions.ts +33 -0
  21. package/template/base/src/app/(app)/roles/page.tsx +52 -0
  22. package/template/base/src/app/(app)/roles/render.tsx +80 -0
  23. package/template/base/src/app/(app)/settings/actions.ts +11 -12
  24. package/template/base/src/app/(app)/settings/layout.tsx +14 -0
  25. package/template/base/src/app/(app)/settings/page.tsx +1 -10
  26. package/template/base/src/app/(app)/settings/render.tsx +11 -17
  27. package/template/base/src/app/(app)/settings/validation.ts +5 -0
  28. package/template/base/src/app/(app)/tasks/[id]/actions.ts +69 -124
  29. package/template/base/src/app/(app)/tasks/[id]/page.tsx +37 -3
  30. package/template/base/src/app/(app)/tasks/[id]/render.tsx +162 -93
  31. package/template/base/src/app/(app)/tasks/[id]/validation.ts +18 -0
  32. package/template/base/src/app/(app)/tasks/actions.ts +34 -64
  33. package/template/base/src/app/(app)/tasks/page.tsx +15 -9
  34. package/template/base/src/app/(app)/tasks/render.tsx +28 -6
  35. package/template/base/src/app/(app)/tasks/statusEnum.ts +3 -0
  36. package/template/base/src/app/(app)/users/[id]/actions.ts +47 -62
  37. package/template/base/src/app/(app)/users/[id]/page.tsx +15 -8
  38. package/template/base/src/app/(app)/users/[id]/render.tsx +119 -34
  39. package/template/base/src/app/(app)/users/[id]/validation.ts +18 -0
  40. package/template/base/src/app/(app)/users/actions.ts +28 -49
  41. package/template/base/src/app/(app)/users/page.tsx +22 -9
  42. package/template/base/src/app/(app)/users/render.tsx +47 -3
  43. package/template/base/src/app/(auth)/layout.tsx +7 -0
  44. package/template/base/src/app/(auth)/login/page.tsx +2 -2
  45. package/template/base/src/app/(auth)/login/render.tsx +146 -0
  46. package/template/base/src/app/(marketing)/layout.tsx +1 -1
  47. package/template/base/src/app/(marketing)/page.tsx +8 -7
  48. package/template/base/src/app/favicon.ico +0 -0
  49. package/template/base/src/app/layout.tsx +14 -17
  50. package/template/base/src/components/app-nav-command.tsx +18 -0
  51. package/template/base/src/components/data-table/data-table-column-header.tsx +2 -2
  52. package/template/base/src/components/icons.tsx +23 -5
  53. package/template/base/src/components/ui/alert-dialog.tsx +3 -3
  54. package/template/base/src/components/ui/command.tsx +2 -2
  55. package/template/base/src/components/ui/dialog.tsx +2 -2
  56. package/template/base/src/components/ui/select.tsx +42 -3
  57. package/template/base/src/components/ui/sonner.tsx +31 -0
  58. package/template/base/src/components/ui/table.tsx +4 -1
  59. package/template/base/src/components/user-avatar.tsx +4 -2
  60. package/template/base/src/data-access/resource.ts +85 -0
  61. package/template/base/src/data-access/role-permission.ts +45 -0
  62. package/template/base/src/data-access/role.ts +105 -0
  63. package/template/base/src/data-access/task.ts +141 -0
  64. package/template/base/src/data-access/user.ts +141 -0
  65. package/template/base/src/db/index.ts +14 -0
  66. package/template/base/src/db/schema.ts +287 -0
  67. package/template/base/src/env.mjs +13 -30
  68. package/template/base/src/hooks/use-lock-body.tsx +2 -1
  69. package/template/base/src/lib/auth.ts +32 -22
  70. package/template/base/src/lib/currentUser.ts +8 -0
  71. package/template/base/src/lib/s3Client.ts +156 -0
  72. package/template/base/src/lib/safe-action.ts +19 -0
  73. package/template/base/src/lib/sortOrderEnum.ts +2 -0
  74. package/template/base/src/use-case/role-permission.ts +19 -0
  75. package/template/base/src/use-case/role.ts +110 -0
  76. package/template/base/src/use-case/task.ts +166 -0
  77. package/template/base/src/use-case/user.ts +121 -0
  78. package/template/base/tailwind.config.ts +9 -7
  79. package/template/base/tsconfig.json +3 -2
  80. package/template/prisma/.env.example +16 -0
  81. package/template/prisma/.eslintrc.cjs +24 -0
  82. package/template/prisma/components.json +17 -0
  83. package/template/prisma/next.config.mjs +15 -0
  84. package/template/prisma/package.json +80 -0
  85. package/template/{base → prisma}/prisma/schema.prisma +0 -1
  86. package/template/prisma/public/next.svg +1 -0
  87. package/template/prisma/src/app/(app)/dashboard/page.tsx +19 -0
  88. package/template/prisma/src/app/(app)/layout.tsx +90 -0
  89. package/template/prisma/src/app/(app)/settings/actions.ts +18 -0
  90. package/template/prisma/src/app/(app)/settings/loading.tsx +28 -0
  91. package/template/prisma/src/app/(app)/settings/page.tsx +28 -0
  92. package/template/prisma/src/app/(app)/settings/render.tsx +113 -0
  93. package/template/prisma/src/app/(app)/tasks/[id]/actions.ts +147 -0
  94. package/template/prisma/src/app/(app)/tasks/[id]/page.tsx +25 -0
  95. package/template/prisma/src/app/(app)/tasks/[id]/render.tsx +413 -0
  96. package/template/prisma/src/app/(app)/tasks/actions.ts +69 -0
  97. package/template/prisma/src/app/(app)/tasks/page.tsx +53 -0
  98. package/template/prisma/src/app/(app)/tasks/render.tsx +107 -0
  99. package/template/prisma/src/app/(app)/users/[id]/actions.ts +83 -0
  100. package/template/prisma/src/app/(app)/users/[id]/page.tsx +32 -0
  101. package/template/prisma/src/app/(app)/users/[id]/render.tsx +240 -0
  102. package/template/prisma/src/app/(app)/users/actions.ts +55 -0
  103. package/template/prisma/src/app/(app)/users/page.tsx +49 -0
  104. package/template/prisma/src/app/(app)/users/render.tsx +80 -0
  105. package/template/prisma/src/app/(auth)/layout.tsx +20 -0
  106. package/template/prisma/src/app/(auth)/login/page.tsx +49 -0
  107. package/template/prisma/src/app/(marketing)/layout.tsx +42 -0
  108. package/template/prisma/src/app/(marketing)/page.tsx +96 -0
  109. package/template/prisma/src/app/api/auth/[...nextauth]/route.ts +7 -0
  110. package/template/prisma/src/app/favicon.ico +0 -0
  111. package/template/prisma/src/app/layout.tsx +34 -0
  112. package/template/prisma/src/components/app-nav-command.tsx +160 -0
  113. package/template/prisma/src/components/dark-mode-toggle.tsx +43 -0
  114. package/template/prisma/src/components/data-table/data-table-column-header.tsx +91 -0
  115. package/template/prisma/src/components/data-table/data-table-faceted-filter.tsx +149 -0
  116. package/template/prisma/src/components/data-table/data-table-loading.tsx +77 -0
  117. package/template/prisma/src/components/data-table/data-table-pagination.tsx +94 -0
  118. package/template/prisma/src/components/data-table/data-table-toolbar.tsx +78 -0
  119. package/template/prisma/src/components/data-table/data-table-types.ts +17 -0
  120. package/template/prisma/src/components/data-table/data-table-view-options.tsx +59 -0
  121. package/template/prisma/src/components/data-table/data-table.tsx +301 -0
  122. package/template/prisma/src/components/data-table/simple-data-table.tsx +109 -0
  123. package/template/prisma/src/components/empty-placeholder.tsx +78 -0
  124. package/template/prisma/src/components/heading.tsx +17 -0
  125. package/template/prisma/src/components/icons.tsx +85 -0
  126. package/template/prisma/src/components/main-nav.tsx +64 -0
  127. package/template/prisma/src/components/shell.tsx +13 -0
  128. package/template/prisma/src/components/side-nav.tsx +51 -0
  129. package/template/prisma/src/components/site-footer.tsx +31 -0
  130. package/template/prisma/src/components/system-info.tsx +36 -0
  131. package/template/prisma/src/components/ui/alert-dialog.tsx +141 -0
  132. package/template/prisma/src/components/ui/avatar.tsx +50 -0
  133. package/template/prisma/src/components/ui/badge.tsx +36 -0
  134. package/template/prisma/src/components/ui/button.tsx +56 -0
  135. package/template/prisma/src/components/ui/card.tsx +79 -0
  136. package/template/prisma/src/components/ui/checkbox.tsx +30 -0
  137. package/template/prisma/src/components/ui/command.tsx +155 -0
  138. package/template/prisma/src/components/ui/dialog.tsx +122 -0
  139. package/template/prisma/src/components/ui/dropdown-menu.tsx +200 -0
  140. package/template/prisma/src/components/ui/form.tsx +176 -0
  141. package/template/prisma/src/components/ui/input.tsx +25 -0
  142. package/template/prisma/src/components/ui/label.tsx +26 -0
  143. package/template/prisma/src/components/ui/popover.tsx +31 -0
  144. package/template/prisma/src/components/ui/select.tsx +160 -0
  145. package/template/prisma/src/components/ui/separator.tsx +31 -0
  146. package/template/prisma/src/components/ui/skeleton.tsx +15 -0
  147. package/template/prisma/src/components/ui/table.tsx +117 -0
  148. package/template/prisma/src/components/user-account-nav.tsx +60 -0
  149. package/template/prisma/src/components/user-avatar.tsx +25 -0
  150. package/template/prisma/src/env.mjs +78 -0
  151. package/template/prisma/src/hooks/use-debounce.ts +17 -0
  152. package/template/prisma/src/hooks/use-lock-body.tsx +12 -0
  153. package/template/prisma/src/lib/auth.ts +80 -0
  154. package/template/prisma/src/lib/cn.ts +4 -0
  155. package/template/prisma/src/lib/delay.ts +2 -0
  156. package/template/prisma/src/lib/formatDate.ts +11 -0
  157. package/template/prisma/src/lib/generateLabel.ts +5 -0
  158. package/template/prisma/src/lib/siteConfig.ts +4 -0
  159. package/template/prisma/src/styles/globals.css +76 -0
  160. package/template/prisma/tailwind.config.ts +80 -0
  161. package/template/prisma/tsconfig.json +46 -0
  162. package/template/{advanced → prisma-advanced}/components.json +2 -1
  163. package/template/{advanced → prisma-advanced}/package.json +10 -10
  164. package/template/{advanced → prisma-advanced}/src/components/icons.tsx +2 -0
  165. package/template/{advanced → prisma-advanced}/src/components/ui/alert-dialog.tsx +3 -3
  166. package/template/{advanced → prisma-advanced}/src/components/ui/command.tsx +1 -1
  167. package/template/{advanced → prisma-advanced}/src/components/ui/dialog.tsx +2 -2
  168. package/template/{advanced → prisma-advanced}/src/components/ui/select.tsx +42 -3
  169. package/template/{advanced → prisma-advanced}/src/components/ui/table.tsx +4 -1
  170. package/template/{advanced → prisma-advanced}/src/env.mjs +4 -1
  171. package/template/{advanced → prisma-advanced}/src/lib/auth.ts +2 -5
  172. package/template/{advanced → prisma-advanced}/tailwind.config.ts +4 -3
  173. /package/template/{advanced/src/styles → base/src/app}/globals.css +0 -0
  174. /package/template/{advanced/src/hooks/use-debounce.ts → base/src/hooks/use-debounce.tsx} +0 -0
  175. /package/template/{base → prisma}/postcss.config.cjs +0 -0
  176. /package/template/{base → prisma}/prettier.config.ts +0 -0
  177. /package/template/{base → prisma}/src/app/(auth)/login/user-auth-form.tsx +0 -0
  178. /package/template/{base → prisma}/src/app/api/upload/route.ts +0 -0
  179. /package/template/{advanced → prisma}/src/components/mobile-nav.tsx +0 -0
  180. /package/template/{base → prisma}/src/components/theme-provider.tsx +0 -0
  181. /package/template/{base → prisma}/src/components/ui/textarea.tsx +0 -0
  182. /package/template/{base → prisma}/src/components/ui/toast.tsx +0 -0
  183. /package/template/{base → prisma}/src/components/ui/toaster.tsx +0 -0
  184. /package/template/{base → prisma}/src/components/ui/use-toast.ts +0 -0
  185. /package/template/{base → prisma}/src/lib/db.ts +0 -0
  186. /package/template/{base → prisma}/src/lib/storageClient.ts +0 -0
  187. /package/template/{base → prisma}/src/styles/editor.css +0 -0
  188. /package/template/{base → prisma}/src/types/UnwrapPromise.ts +0 -0
  189. /package/template/{advanced → prisma-advanced}/.env.example +0 -0
  190. /package/template/{advanced → prisma-advanced}/.eslintrc.cjs +0 -0
  191. /package/template/{advanced → prisma-advanced}/next.config.mjs +0 -0
  192. /package/template/{advanced → prisma-advanced}/postcss.config.cjs +0 -0
  193. /package/template/{advanced → prisma-advanced}/prettier.config.ts +0 -0
  194. /package/template/{advanced → prisma-advanced}/prisma/schema.prisma +0 -0
  195. /package/template/{advanced → prisma-advanced}/public/next.svg +0 -0
  196. /package/template/{advanced → prisma-advanced}/src/app/(app)/dashboard/page.tsx +0 -0
  197. /package/template/{advanced → prisma-advanced}/src/app/(app)/layout.tsx +0 -0
  198. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/actions.ts +0 -0
  199. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/page.tsx +0 -0
  200. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/render.tsx +0 -0
  201. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/actions.ts +0 -0
  202. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/page.tsx +0 -0
  203. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/render.tsx +0 -0
  204. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/actions.ts +0 -0
  205. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/loading.tsx +0 -0
  206. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/page.tsx +0 -0
  207. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/render.tsx +0 -0
  208. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/actions.ts +0 -0
  209. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/page.tsx +0 -0
  210. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/render.tsx +0 -0
  211. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/actions.ts +0 -0
  212. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/page.tsx +0 -0
  213. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/render.tsx +0 -0
  214. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/actions.ts +0 -0
  215. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/page.tsx +0 -0
  216. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/render.tsx +0 -0
  217. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/actions.ts +0 -0
  218. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/page.tsx +0 -0
  219. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/render.tsx +0 -0
  220. /package/template/{advanced → prisma-advanced}/src/app/(auth)/layout.tsx +0 -0
  221. /package/template/{advanced → prisma-advanced}/src/app/(auth)/login/page.tsx +0 -0
  222. /package/template/{advanced → prisma-advanced}/src/app/(auth)/login/user-auth-form.tsx +0 -0
  223. /package/template/{advanced → prisma-advanced}/src/app/(marketing)/layout.tsx +0 -0
  224. /package/template/{advanced → prisma-advanced}/src/app/(marketing)/page.tsx +0 -0
  225. /package/template/{advanced → prisma-advanced}/src/app/api/auth/[...nextauth]/route.ts +0 -0
  226. /package/template/{advanced → prisma-advanced}/src/app/api/upload/route.ts +0 -0
  227. /package/template/{advanced → prisma-advanced}/src/app/favicon.ico +0 -0
  228. /package/template/{advanced → prisma-advanced}/src/app/layout.tsx +0 -0
  229. /package/template/{advanced → prisma-advanced}/src/components/app-nav-command.tsx +0 -0
  230. /package/template/{advanced → prisma-advanced}/src/components/dark-mode-toggle.tsx +0 -0
  231. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-column-header.tsx +0 -0
  232. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-faceted-filter.tsx +0 -0
  233. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-loading.tsx +0 -0
  234. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-pagination.tsx +0 -0
  235. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-toolbar.tsx +0 -0
  236. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-types.ts +0 -0
  237. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-view-options.tsx +0 -0
  238. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table.tsx +0 -0
  239. /package/template/{advanced → prisma-advanced}/src/components/data-table/simple-data-table.tsx +0 -0
  240. /package/template/{advanced → prisma-advanced}/src/components/empty-placeholder.tsx +0 -0
  241. /package/template/{advanced → prisma-advanced}/src/components/heading.tsx +0 -0
  242. /package/template/{advanced → prisma-advanced}/src/components/main-nav.tsx +0 -0
  243. /package/template/{advanced → prisma-advanced}/src/components/shell.tsx +0 -0
  244. /package/template/{advanced → prisma-advanced}/src/components/side-nav.tsx +0 -0
  245. /package/template/{advanced → prisma-advanced}/src/components/site-footer.tsx +0 -0
  246. /package/template/{advanced → prisma-advanced}/src/components/system-info.tsx +0 -0
  247. /package/template/{advanced → prisma-advanced}/src/components/theme-provider.tsx +0 -0
  248. /package/template/{advanced → prisma-advanced}/src/components/ui/avatar.tsx +0 -0
  249. /package/template/{advanced → prisma-advanced}/src/components/ui/badge.tsx +0 -0
  250. /package/template/{advanced → prisma-advanced}/src/components/ui/button.tsx +0 -0
  251. /package/template/{advanced → prisma-advanced}/src/components/ui/card.tsx +0 -0
  252. /package/template/{advanced → prisma-advanced}/src/components/ui/checkbox.tsx +0 -0
  253. /package/template/{advanced → prisma-advanced}/src/components/ui/dropdown-menu.tsx +0 -0
  254. /package/template/{advanced → prisma-advanced}/src/components/ui/form.tsx +0 -0
  255. /package/template/{advanced → prisma-advanced}/src/components/ui/input.tsx +0 -0
  256. /package/template/{advanced → prisma-advanced}/src/components/ui/label.tsx +0 -0
  257. /package/template/{advanced → prisma-advanced}/src/components/ui/popover.tsx +0 -0
  258. /package/template/{advanced → prisma-advanced}/src/components/ui/separator.tsx +0 -0
  259. /package/template/{advanced → prisma-advanced}/src/components/ui/skeleton.tsx +0 -0
  260. /package/template/{advanced → prisma-advanced}/src/components/ui/textarea.tsx +0 -0
  261. /package/template/{advanced → prisma-advanced}/src/components/ui/toast.tsx +0 -0
  262. /package/template/{advanced → prisma-advanced}/src/components/ui/toaster.tsx +0 -0
  263. /package/template/{advanced → prisma-advanced}/src/components/ui/use-toast.ts +0 -0
  264. /package/template/{advanced → prisma-advanced}/src/components/user-account-nav.tsx +0 -0
  265. /package/template/{advanced → prisma-advanced}/src/components/user-avatar.tsx +0 -0
  266. /package/template/{base → prisma-advanced}/src/hooks/use-debounce.ts +0 -0
  267. /package/template/{advanced → prisma-advanced}/src/hooks/use-lock-body.tsx +0 -0
  268. /package/template/{advanced → prisma-advanced}/src/lib/cn.ts +0 -0
  269. /package/template/{advanced → prisma-advanced}/src/lib/db.ts +0 -0
  270. /package/template/{advanced → prisma-advanced}/src/lib/delay.ts +0 -0
  271. /package/template/{advanced → prisma-advanced}/src/lib/formatDate.ts +0 -0
  272. /package/template/{advanced → prisma-advanced}/src/lib/generateLabel.ts +0 -0
  273. /package/template/{advanced → prisma-advanced}/src/lib/siteConfig.ts +0 -0
  274. /package/template/{advanced → prisma-advanced}/src/lib/storageClient.ts +0 -0
  275. /package/template/{advanced → prisma-advanced}/src/styles/editor.css +0 -0
  276. /package/template/{base → prisma-advanced}/src/styles/globals.css +0 -0
  277. /package/template/{advanced → prisma-advanced}/src/types/UnwrapPromise.ts +0 -0
  278. /package/template/{advanced → prisma-advanced}/tsconfig.json +0 -0
package/cli.js CHANGED
@@ -40,10 +40,14 @@ commander_1.program.action(() => __awaiter(void 0, void 0, void 0, function* ()
40
40
  choices: [
41
41
  {
42
42
  name: 'base',
43
+ message: 'Base setup with ts, shadcn, auth, drizzle, User Types and ACLs (roles & permissions)'
44
+ },
45
+ {
46
+ name: 'prisma',
43
47
  message: 'Base setup with ts, shadcn, prisma, auth, editor.js'
44
48
  },
45
49
  {
46
- name: 'advanced',
50
+ name: 'prisma-advanced',
47
51
  message: 'Advanced setup with User Types and ACLs (roles & permissions)'
48
52
  }
49
53
  ]
package/cli.ts CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { program } from 'commander';
4
- import { prompt } from 'enquirer';
5
- import fs from 'fs';
6
- import path from 'path';
3
+ import { program } from 'commander'
4
+ import { prompt } from 'enquirer'
5
+ import fs from 'fs'
6
+ import path from 'path'
7
7
  //@ts-ignore
8
- import copydir from 'copy-dir';
8
+ import copydir from 'copy-dir'
9
9
 
10
10
  interface Response {
11
- projectName: string;
12
- description: string;
13
- template: string;
11
+ projectName: string
12
+ description: string
13
+ template: string
14
14
  }
15
15
 
16
16
  program.action(async () => {
@@ -34,48 +34,55 @@ program.action(async () => {
34
34
  choices: [
35
35
  {
36
36
  name: 'base',
37
+ message:
38
+ 'Base setup with ts, shadcn, auth, drizzle, User Types and ACLs (roles & permissions)'
39
+ },
40
+ {
41
+ name: 'prisma',
37
42
  message: 'Base setup with ts, shadcn, prisma, auth, editor.js'
38
43
  },
39
44
  {
40
- name: 'advanced',
45
+ name: 'prisma-advanced',
41
46
  message:
42
47
  'Advanced setup with User Types and ACLs (roles & permissions)'
43
48
  }
44
49
  ]
45
50
  }
46
- ]);
51
+ ])
47
52
 
48
- const { projectName, description, template } = response;
53
+ const { projectName, description, template } = response
49
54
 
50
- const projectDir = path.join(process.cwd(), projectName);
55
+ const projectDir = path.join(process.cwd(), projectName)
51
56
 
52
57
  if (fs.existsSync(projectDir)) {
53
- console.error(`Error: Directory '${projectName}' already exists.`);
54
- process.exit(1);
58
+ console.error(`Error: Directory '${projectName}' already exists.`)
59
+ process.exit(1)
55
60
  }
56
61
 
57
- fs.mkdirSync(projectDir);
62
+ fs.mkdirSync(projectDir)
58
63
 
59
- const templateDir = path.join(__dirname, 'template', template);
60
- copydir.sync(templateDir, projectDir);
64
+ const templateDir = path.join(__dirname, 'template', template)
65
+ copydir.sync(templateDir, projectDir)
61
66
 
62
- const packageJsonFile = path.join(projectDir, 'package.json');
63
- const packageJson = JSON.parse(fs.readFileSync(packageJsonFile, 'utf-8'));
64
- packageJson.name = projectName;
65
- packageJson.description = description;
66
- fs.writeFileSync(packageJsonFile, JSON.stringify(packageJson, null, 2));
67
+ const packageJsonFile = path.join(projectDir, 'package.json')
68
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonFile, 'utf-8'))
69
+ packageJson.name = projectName
70
+ packageJson.description = description
71
+ fs.writeFileSync(packageJsonFile, JSON.stringify(packageJson, null, 2))
67
72
 
68
- const siteConfigFile = path.join(projectDir, 'src', 'lib', 'siteConfig.ts');
69
- let siteConfig = fs.readFileSync(siteConfigFile, 'utf-8');
70
- siteConfig = siteConfig.replace('Project Name', projectName);
71
- siteConfig = siteConfig.replace('Project Description', description);
72
- fs.writeFileSync(siteConfigFile, siteConfig);
73
+ const siteConfigFile = path.join(projectDir, 'src', 'lib', 'siteConfig.ts')
74
+ let siteConfig = fs.readFileSync(siteConfigFile, 'utf-8')
75
+ siteConfig = siteConfig.replace('Project Name', projectName)
76
+ siteConfig = siteConfig.replace('Project Description', description)
77
+ fs.writeFileSync(siteConfigFile, siteConfig)
73
78
 
74
- const envExampleFile = path.join(projectDir, '.env.example');
75
- const envExample = fs.readFileSync(envExampleFile, 'utf-8');
76
- fs.writeFileSync(path.join(projectDir, '.env'), envExample);
79
+ const envExampleFile = path.join(projectDir, '.env.example')
80
+ const envExample = fs.readFileSync(envExampleFile, 'utf-8')
81
+ fs.writeFileSync(path.join(projectDir, '.env'), envExample)
77
82
 
78
- console.log(`Created a new app called '${projectName}' 🎉\n\nTo get started, run the following commands:\n\ncd ${projectName}\nnpm install\nnpm run dev\n`);
79
- });
83
+ console.log(
84
+ `Created a new app called '${projectName}' 🎉\n\nTo get started, run the following commands:\n\ncd ${projectName}\nnpm install\nnpm run dev\n`
85
+ )
86
+ })
80
87
 
81
- program.parse(process.argv);
88
+ program.parse(process.argv)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ej-app",
3
- "version": "3.1.0",
3
+ "version": "4.0.0",
4
4
  "description": "Bootstrap a next.js app with my preferred setup",
5
5
  "main": "cli.js",
6
6
  "scripts": {
@@ -30,12 +30,12 @@
30
30
  },
31
31
  "homepage": "https://github.com/ejekanshjain/create-ej-app#readme",
32
32
  "dependencies": {
33
- "commander": "^11.1.0",
33
+ "commander": "^12.1.0",
34
34
  "copy-dir": "^1.3.0",
35
35
  "enquirer": "^2.4.1"
36
36
  },
37
37
  "devDependencies": {
38
- "@types/node": "^20.10.5",
39
- "typescript": "^5.3.3"
38
+ "@types/node": "^20.14.9",
39
+ "typescript": "^5.5.2"
40
40
  }
41
41
  }
@@ -1,4 +1,5 @@
1
- DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres?schema=public"
1
+ NODE_ENV="development"
2
+ DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
2
3
  NEXTAUTH_SECRET="secret"
3
4
  NEXTAUTH_URL="http://localhost:3000"
4
5
  EMAIL_SERVER_HOST="example.com"
@@ -6,11 +7,11 @@ EMAIL_SERVER_PORT="123"
6
7
  EMAIL_SERVER_USER="example@example.com"
7
8
  EMAIL_SERVER_PASSWORD="password"
8
9
  EMAIL_FROM="example@example.com"
9
- GOOGLE_ID=""
10
- GOOGLE_SECRET=""
11
- GITHUB_ID=""
12
- GITHUB_SECRET=""
13
- FIREBASE_PROJECT_ID=""
14
- FIREBASE_PRIVATE_KEY=""
15
- FIREBASE_CLIENT_EMAIL=""
16
- FIREBASE_STORAGE_BUCKET=""
10
+ GOOGLE_ID="id"
11
+ GOOGLE_SECRET="secret"
12
+ GITHUB_ID="id"
13
+ GITHUB_SECRET="secret"
14
+ S3_REGION="ap-south-1"
15
+ S3_BUCKET="bucket"
16
+ S3_ACCESS_KEY="key"
17
+ S3_SECRET_KEY="secret"
@@ -1,24 +1,26 @@
1
- // eslint-disable-next-line @typescript-eslint/no-var-requires
2
- const path = require('path')
3
-
4
1
  /** @type {import("eslint").Linter.Config} */
5
2
  const eslintConfig = {
6
- overrides: [
7
- {
8
- extends: [],
9
- files: ['*.ts', '*.tsx'],
10
- parserOptions: {
11
- project: path.join(__dirname, 'tsconfig.json')
12
- }
13
- }
14
- ],
15
3
  parser: '@typescript-eslint/parser',
16
4
  parserOptions: {
17
- project: path.join(__dirname, 'tsconfig.json')
5
+ project: true
18
6
  },
19
- plugins: ['@typescript-eslint'],
7
+ plugins: ['@typescript-eslint', 'drizzle'],
20
8
  extends: ['next/core-web-vitals'],
21
- rules: {}
9
+ rules: {
10
+ '@typescript-eslint/array-type': 'off',
11
+ 'drizzle/enforce-delete-with-where': [
12
+ 'error',
13
+ {
14
+ drizzleObjectName: ['db', 'ctx.db']
15
+ }
16
+ ],
17
+ 'drizzle/enforce-update-with-where': [
18
+ 'error',
19
+ {
20
+ drizzleObjectName: ['db', 'ctx.db']
21
+ }
22
+ ]
23
+ }
22
24
  }
23
25
 
24
26
  module.exports = eslintConfig
@@ -5,9 +5,10 @@
5
5
  "tsx": true,
6
6
  "tailwind": {
7
7
  "config": "tailwind.config.ts",
8
- "css": "src/styles/globals.css",
8
+ "css": "src/app/globals.css",
9
9
  "baseColor": "neutral",
10
- "cssVariables": true
10
+ "cssVariables": true,
11
+ "prefix": ""
11
12
  },
12
13
  "aliases": {
13
14
  "components": "@/components",
@@ -0,0 +1,11 @@
1
+ import { type Config } from 'drizzle-kit'
2
+
3
+ import { env } from '@/env.mjs'
4
+
5
+ export default {
6
+ schema: './src/db/schema.ts',
7
+ dialect: 'postgresql',
8
+ dbCredentials: {
9
+ url: env.DATABASE_URL
10
+ }
11
+ } satisfies Config
@@ -1,15 +1,4 @@
1
- /**
2
- * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
3
- * for Docker builds.
4
- */
5
- await import('./src/env.mjs')
6
-
7
1
  /** @type {import('next').NextConfig} */
8
- const nextConfig = {
9
- i18n: {
10
- locales: ['en'],
11
- defaultLocale: 'en'
12
- }
13
- }
2
+ const nextConfig = {}
14
3
 
15
4
  export default nextConfig
@@ -4,77 +4,84 @@
4
4
  "description": "A Legendary Next.js App",
5
5
  "private": true,
6
6
  "scripts": {
7
- "postinstall": "prisma generate",
7
+ "format": "prettier --write \"src/**/*.{js,cjs,mjs,jsx,ts,tsx}\"",
8
8
  "lint": "next lint",
9
9
  "dev": "next dev",
10
10
  "build": "next build",
11
11
  "start": "next start",
12
- "prisma:push": "prisma db push",
13
- "prisma:gen": "prisma generate",
14
- "prisma:studio": "prisma studio"
12
+ "db:generate": "drizzle-kit generate",
13
+ "db:push": "drizzle-kit push",
14
+ "db:studio": "drizzle-kit studio"
15
15
  },
16
16
  "dependencies": {
17
+ "@auth/drizzle-adapter": "^1.4.1",
18
+ "@aws-sdk/client-s3": "^3.600.0",
19
+ "@aws-sdk/s3-presigned-post": "^3.600.0",
17
20
  "@editorjs/checklist": "^1.6.0",
21
+ "@editorjs/code": "^2.9.0",
18
22
  "@editorjs/delimiter": "^1.4.0",
19
- "@editorjs/editorjs": "^2.28.2",
23
+ "@editorjs/editorjs": "^2.29.1",
20
24
  "@editorjs/header": "^2.8.1",
21
25
  "@editorjs/image": "^2.9.0",
26
+ "@editorjs/inline-code": "^1.5.0",
22
27
  "@editorjs/list": "^1.9.0",
28
+ "@editorjs/nested-list": "^1.4.2",
23
29
  "@editorjs/quote": "^2.6.0",
24
30
  "@editorjs/table": "^2.3.0",
25
- "@ejekanshjain/cloud-storage": "^3.1.0",
26
- "@hookform/resolvers": "^3.3.2",
27
- "@next-auth/prisma-adapter": "^1.0.7",
28
- "@prisma/client": "^5.7.1",
29
- "@radix-ui/react-alert-dialog": "^1.0.5",
30
- "@radix-ui/react-avatar": "^1.0.4",
31
- "@radix-ui/react-checkbox": "^1.0.4",
32
- "@radix-ui/react-dialog": "^1.0.5",
33
- "@radix-ui/react-dropdown-menu": "^2.0.6",
34
- "@radix-ui/react-label": "^2.0.2",
35
- "@radix-ui/react-popover": "^1.0.7",
36
- "@radix-ui/react-select": "^2.0.0",
37
- "@radix-ui/react-separator": "^1.0.3",
38
- "@radix-ui/react-slot": "^1.0.2",
39
- "@radix-ui/react-toast": "^1.1.5",
40
- "@t3-oss/env-nextjs": "^0.7.1",
41
- "@tailwindcss/typography": "^0.5.10",
42
- "@tanstack/react-table": "^8.11.2",
43
- "@types/eslint": "^8.56.0",
44
- "@types/lodash": "^4.14.202",
45
- "@types/node": "^20.10.5",
46
- "@types/nodemailer": "^6.4.14",
47
- "@types/react": "^18.2.45",
48
- "@types/react-dom": "^18.2.18",
49
- "@typescript-eslint/eslint-plugin": "^6.15.0",
50
- "@typescript-eslint/parser": "^6.15.0",
51
- "@vercel/analytics": "^1.1.1",
52
- "@vercel/speed-insights": "^1.0.2",
53
- "autoprefixer": "^10.4.16",
31
+ "@hookform/resolvers": "^3.6.0",
32
+ "@paralleldrive/cuid2": "^2.2.2",
33
+ "@radix-ui/react-alert-dialog": "^1.1.1",
34
+ "@radix-ui/react-avatar": "^1.1.0",
35
+ "@radix-ui/react-checkbox": "^1.1.0",
36
+ "@radix-ui/react-dialog": "^1.1.1",
37
+ "@radix-ui/react-dropdown-menu": "^2.1.1",
38
+ "@radix-ui/react-label": "^2.1.0",
39
+ "@radix-ui/react-popover": "^1.1.1",
40
+ "@radix-ui/react-select": "^2.1.1",
41
+ "@radix-ui/react-separator": "^1.1.0",
42
+ "@radix-ui/react-slot": "^1.1.0",
43
+ "@t3-oss/env-nextjs": "^0.10.1",
44
+ "@tailwindcss/typography": "^0.5.13",
45
+ "@tanstack/react-table": "^8.17.3",
46
+ "@types/eslint": "^8.56.10",
47
+ "@types/node": "^20.14.9",
48
+ "@types/nodemailer": "^6.4.15",
49
+ "@types/react": "^18.3.3",
50
+ "@types/react-dom": "^18.3.0",
51
+ "@typescript-eslint/eslint-plugin": "^7.14.1",
52
+ "@typescript-eslint/parser": "^7.14.1",
54
53
  "class-variance-authority": "^0.7.0",
55
- "clsx": "^2.0.0",
56
- "cmdk": "^0.2.0",
57
- "dayjs": "^1.11.10",
54
+ "clsx": "^2.1.1",
55
+ "cmdk": "^1.0.0",
56
+ "dayjs": "^1.11.11",
57
+ "drizzle-kit": "^0.22.7",
58
+ "drizzle-orm": "^0.31.2",
58
59
  "editorjs-html": "^3.4.3",
59
- "eslint": "^8.56.0",
60
- "eslint-config-next": "^14.0.4",
61
- "lodash": "^4.17.21",
62
- "lucide-react": "^0.302.0",
63
- "next": "^14.0.4",
64
- "next-auth": "^4.24.5",
65
- "next-themes": "^0.2.1",
66
- "nodemailer": "^6.9.7",
67
- "postcss": "^8.4.32",
68
- "prettier": "^3.1.1",
69
- "prettier-plugin-tailwindcss": "^0.5.9",
70
- "prisma": "^5.7.1",
71
- "react": "^18.2.0",
72
- "react-dom": "^18.2.0",
73
- "react-hook-form": "^7.49.2",
74
- "tailwind-merge": "^2.2.0",
75
- "tailwindcss": "^3.4.0",
60
+ "eslint": "^8.57.0",
61
+ "eslint-config-next": "^14.2.4",
62
+ "eslint-plugin-drizzle": "^0.2.3",
63
+ "geist": "^1.3.0",
64
+ "lucide-react": "^0.397.0",
65
+ "next": "^14.2.4",
66
+ "next-auth": "^4.24.7",
67
+ "next-safe-action": "^7.1.3",
68
+ "next-themes": "^0.3.0",
69
+ "nextjs-toploader": "^1.6.12",
70
+ "nodemailer": "^6.9.14",
71
+ "postcss": "^8.4.38",
72
+ "postcss-load-config": "^6.0.1",
73
+ "postgres": "^3.4.4",
74
+ "prettier": "^3.3.2",
75
+ "prettier-plugin-organize-imports": "^3.2.4",
76
+ "prettier-plugin-tailwindcss": "^0.6.5",
77
+ "react": "^18.3.1",
78
+ "react-dom": "^18.3.1",
79
+ "react-hook-form": "^7.52.0",
80
+ "sonner": "^1.5.0",
81
+ "tailwind-merge": "^2.3.0",
82
+ "tailwindcss": "^3.4.4",
76
83
  "tailwindcss-animate": "^1.0.7",
77
- "typescript": "^5.3.3",
78
- "zod": "^3.22.4"
84
+ "typescript": "^5.5.2",
85
+ "zod": "^3.23.8"
79
86
  }
80
87
  }
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {}
5
+ }
6
+ }
7
+
8
+ export default config
@@ -0,0 +1,11 @@
1
+ /** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */
2
+ const config = {
3
+ plugins: ['prettier-plugin-organize-imports', 'prettier-plugin-tailwindcss'],
4
+ arrowParens: 'avoid',
5
+ trailingComma: 'none',
6
+ semi: false,
7
+ singleQuote: true,
8
+ tabWidth: 2
9
+ }
10
+
11
+ export default config
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
@@ -0,0 +1,14 @@
1
+ import { Metadata } from 'next'
2
+ import { ReactNode } from 'react'
3
+
4
+ import { siteConfig } from '@/lib/siteConfig'
5
+
6
+ export const metadata: Metadata = {
7
+ title: 'Dashboard' + ' | ' + siteConfig.name
8
+ }
9
+
10
+ const Layout = ({ children }: { children: ReactNode }) => {
11
+ return <>{children}</>
12
+ }
13
+
14
+ export default Layout
@@ -1,12 +1,5 @@
1
- import { Metadata } from 'next'
2
-
3
1
  import { Heading } from '@/components/heading'
4
2
  import { Shell } from '@/components/shell'
5
- import { siteConfig } from '@/lib/siteConfig'
6
-
7
- export const metadata: Metadata = {
8
- title: 'Dashboard' + ' | ' + siteConfig.name
9
- }
10
3
 
11
4
  const DashboardPage = () => {
12
5
  return (
@@ -7,12 +7,22 @@ import { SideNav } from '@/components/side-nav'
7
7
  import { SiteFooter } from '@/components/site-footer'
8
8
  import { UserAccountNav } from '@/components/user-account-nav'
9
9
  import { getAuthSession } from '@/lib/auth'
10
+ import { checkForRolePermissionUseCase } from '@/use-case/role-permission'
10
11
 
11
12
  const Layout = async ({ children }: { children: ReactNode }) => {
12
13
  const session = await getAuthSession()
13
14
 
14
15
  if (!session?.user) return notFound()
15
16
 
17
+ const isRoot = session.user.type === 'Root'
18
+ const [isTask] = await Promise.all([
19
+ checkForRolePermissionUseCase(
20
+ session.user.type,
21
+ session.user.roleId,
22
+ 'TaskList'
23
+ )
24
+ ])
25
+
16
26
  return (
17
27
  <div className="flex min-h-screen flex-col space-y-6">
18
28
  <header className="sticky top-0 z-40 border-b bg-background">
@@ -24,20 +34,33 @@ const Layout = async ({ children }: { children: ReactNode }) => {
24
34
  title: 'Dashboard',
25
35
  href: '/dashboard'
26
36
  },
27
- {
28
- title: 'Users',
29
- href: '/users'
30
- },
31
- {
32
- title: 'Tasks',
33
- href: '/tasks'
34
- }
37
+ ...(isRoot
38
+ ? [
39
+ {
40
+ title: 'Users',
41
+ href: '/users'
42
+ },
43
+ {
44
+ title: 'Roles',
45
+ href: '/roles'
46
+ }
47
+ ]
48
+ : []),
49
+ ...(isTask
50
+ ? [
51
+ {
52
+ title: 'Tasks',
53
+ href: '/tasks'
54
+ }
55
+ ]
56
+ : [])
35
57
  ]}
36
58
  />
37
59
  <div className="flex items-center justify-center space-x-4">
38
60
  <AppNavCommand
39
61
  modules={{
40
- task: true
62
+ task: true,
63
+ root: session.user.type === 'Root'
41
64
  }}
42
65
  />
43
66
  <UserAccountNav
@@ -59,17 +82,29 @@ const Layout = async ({ children }: { children: ReactNode }) => {
59
82
  href: '/dashboard',
60
83
  icon: 'dashboard'
61
84
  },
62
-
63
- {
64
- title: 'Users',
65
- href: '/users',
66
- icon: 'user'
67
- },
68
- {
69
- title: 'Tasks',
70
- href: '/tasks',
71
- icon: 'task'
72
- },
85
+ ...(isRoot
86
+ ? [
87
+ {
88
+ title: 'Users',
89
+ href: '/users',
90
+ icon: 'user' as const
91
+ },
92
+ {
93
+ title: 'Roles',
94
+ href: '/roles',
95
+ icon: 'mixer' as const
96
+ }
97
+ ]
98
+ : []),
99
+ ...(isTask
100
+ ? [
101
+ {
102
+ title: 'Tasks',
103
+ href: '/tasks',
104
+ icon: 'task' as const
105
+ }
106
+ ]
107
+ : []),
73
108
  {
74
109
  title: 'Settings',
75
110
  href: '/settings',
@@ -0,0 +1,59 @@
1
+ 'use server'
2
+
3
+ import { revalidatePath } from 'next/cache'
4
+ import { z } from 'zod'
5
+
6
+ import { authActionClient } from '@/lib/safe-action'
7
+ import {
8
+ createRoleUseCase,
9
+ deleteRoleUseCase,
10
+ getRoleByIdWithPermissionsUseCase,
11
+ updateRoleUseCase
12
+ } from '@/use-case/role'
13
+ import { RoleCreateUpdateSchema, RoleUpdateServerSchema } from './validation'
14
+
15
+ export const getRoleAction = authActionClient
16
+ .schema(z.string())
17
+ .action(async ({ parsedInput: id, ctx }) => {
18
+ return await getRoleByIdWithPermissionsUseCase(ctx.user.type, id)
19
+ })
20
+
21
+ export const createRoleAction = authActionClient
22
+ .schema(RoleCreateUpdateSchema)
23
+ .action(async ({ parsedInput: { name, permissions }, ctx }) => {
24
+ const id = await createRoleUseCase(ctx.user.type, { name, permissions })
25
+
26
+ revalidatePath('/roles')
27
+ revalidatePath(`/roles/${id}`)
28
+
29
+ return {
30
+ success: true,
31
+ id
32
+ }
33
+ })
34
+
35
+ export const updateRoleAction = authActionClient
36
+ .schema(RoleUpdateServerSchema)
37
+ .action(async ({ parsedInput: { id, name, permissions }, ctx }) => {
38
+ await updateRoleUseCase(ctx.user.type, { id, name, permissions })
39
+
40
+ revalidatePath('/roles')
41
+ revalidatePath(`/roles/${id}`)
42
+
43
+ return {
44
+ success: true
45
+ }
46
+ })
47
+
48
+ export const deleteRoleAction = authActionClient
49
+ .schema(z.string())
50
+ .action(async ({ parsedInput: id, ctx }) => {
51
+ await deleteRoleUseCase(ctx.user.type, id)
52
+
53
+ revalidatePath('/roles')
54
+ revalidatePath(`/roles/${id}`)
55
+
56
+ return {
57
+ success: true
58
+ }
59
+ })