create-ej-app 3.2.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 (276) 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 +1 -1
  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 +21 -5
  53. package/template/base/src/components/ui/alert-dialog.tsx +1 -1
  54. package/template/base/src/components/ui/command.tsx +1 -1
  55. package/template/base/src/components/ui/dialog.tsx +1 -1
  56. package/template/base/src/components/ui/sonner.tsx +31 -0
  57. package/template/base/src/components/user-avatar.tsx +4 -2
  58. package/template/base/src/data-access/resource.ts +85 -0
  59. package/template/base/src/data-access/role-permission.ts +45 -0
  60. package/template/base/src/data-access/role.ts +105 -0
  61. package/template/base/src/data-access/task.ts +141 -0
  62. package/template/base/src/data-access/user.ts +141 -0
  63. package/template/base/src/db/index.ts +14 -0
  64. package/template/base/src/db/schema.ts +287 -0
  65. package/template/base/src/env.mjs +10 -30
  66. package/template/base/src/hooks/use-lock-body.tsx +2 -1
  67. package/template/base/src/lib/auth.ts +32 -23
  68. package/template/base/src/lib/currentUser.ts +8 -0
  69. package/template/base/src/lib/s3Client.ts +156 -0
  70. package/template/base/src/lib/safe-action.ts +19 -0
  71. package/template/base/src/lib/sortOrderEnum.ts +2 -0
  72. package/template/base/src/use-case/role-permission.ts +19 -0
  73. package/template/base/src/use-case/role.ts +110 -0
  74. package/template/base/src/use-case/task.ts +166 -0
  75. package/template/base/src/use-case/user.ts +121 -0
  76. package/template/base/tailwind.config.ts +5 -4
  77. package/template/base/tsconfig.json +3 -2
  78. package/template/prisma/.env.example +16 -0
  79. package/template/prisma/.eslintrc.cjs +24 -0
  80. package/template/prisma/components.json +17 -0
  81. package/template/prisma/next.config.mjs +15 -0
  82. package/template/prisma/package.json +80 -0
  83. package/template/prisma/public/next.svg +1 -0
  84. package/template/prisma/src/app/(app)/dashboard/page.tsx +19 -0
  85. package/template/prisma/src/app/(app)/layout.tsx +90 -0
  86. package/template/prisma/src/app/(app)/settings/actions.ts +18 -0
  87. package/template/prisma/src/app/(app)/settings/loading.tsx +28 -0
  88. package/template/prisma/src/app/(app)/settings/page.tsx +28 -0
  89. package/template/prisma/src/app/(app)/settings/render.tsx +113 -0
  90. package/template/prisma/src/app/(app)/tasks/[id]/actions.ts +147 -0
  91. package/template/prisma/src/app/(app)/tasks/[id]/page.tsx +25 -0
  92. package/template/prisma/src/app/(app)/tasks/[id]/render.tsx +413 -0
  93. package/template/prisma/src/app/(app)/tasks/actions.ts +69 -0
  94. package/template/prisma/src/app/(app)/tasks/page.tsx +53 -0
  95. package/template/prisma/src/app/(app)/tasks/render.tsx +107 -0
  96. package/template/prisma/src/app/(app)/users/[id]/actions.ts +83 -0
  97. package/template/prisma/src/app/(app)/users/[id]/page.tsx +32 -0
  98. package/template/prisma/src/app/(app)/users/[id]/render.tsx +240 -0
  99. package/template/prisma/src/app/(app)/users/actions.ts +55 -0
  100. package/template/prisma/src/app/(app)/users/page.tsx +49 -0
  101. package/template/prisma/src/app/(app)/users/render.tsx +80 -0
  102. package/template/prisma/src/app/(auth)/layout.tsx +20 -0
  103. package/template/prisma/src/app/(auth)/login/page.tsx +49 -0
  104. package/template/prisma/src/app/(marketing)/layout.tsx +42 -0
  105. package/template/prisma/src/app/(marketing)/page.tsx +96 -0
  106. package/template/prisma/src/app/api/auth/[...nextauth]/route.ts +7 -0
  107. package/template/prisma/src/app/favicon.ico +0 -0
  108. package/template/prisma/src/app/layout.tsx +34 -0
  109. package/template/prisma/src/components/app-nav-command.tsx +160 -0
  110. package/template/prisma/src/components/dark-mode-toggle.tsx +43 -0
  111. package/template/prisma/src/components/data-table/data-table-column-header.tsx +91 -0
  112. package/template/prisma/src/components/data-table/data-table-faceted-filter.tsx +149 -0
  113. package/template/prisma/src/components/data-table/data-table-loading.tsx +77 -0
  114. package/template/prisma/src/components/data-table/data-table-pagination.tsx +94 -0
  115. package/template/prisma/src/components/data-table/data-table-toolbar.tsx +78 -0
  116. package/template/prisma/src/components/data-table/data-table-types.ts +17 -0
  117. package/template/prisma/src/components/data-table/data-table-view-options.tsx +59 -0
  118. package/template/prisma/src/components/data-table/data-table.tsx +301 -0
  119. package/template/prisma/src/components/data-table/simple-data-table.tsx +109 -0
  120. package/template/prisma/src/components/empty-placeholder.tsx +78 -0
  121. package/template/prisma/src/components/heading.tsx +17 -0
  122. package/template/prisma/src/components/icons.tsx +85 -0
  123. package/template/prisma/src/components/main-nav.tsx +64 -0
  124. package/template/prisma/src/components/shell.tsx +13 -0
  125. package/template/prisma/src/components/side-nav.tsx +51 -0
  126. package/template/prisma/src/components/site-footer.tsx +31 -0
  127. package/template/prisma/src/components/system-info.tsx +36 -0
  128. package/template/prisma/src/components/ui/alert-dialog.tsx +141 -0
  129. package/template/prisma/src/components/ui/avatar.tsx +50 -0
  130. package/template/prisma/src/components/ui/badge.tsx +36 -0
  131. package/template/prisma/src/components/ui/button.tsx +56 -0
  132. package/template/prisma/src/components/ui/card.tsx +79 -0
  133. package/template/prisma/src/components/ui/checkbox.tsx +30 -0
  134. package/template/prisma/src/components/ui/command.tsx +155 -0
  135. package/template/prisma/src/components/ui/dialog.tsx +122 -0
  136. package/template/prisma/src/components/ui/dropdown-menu.tsx +200 -0
  137. package/template/prisma/src/components/ui/form.tsx +176 -0
  138. package/template/prisma/src/components/ui/input.tsx +25 -0
  139. package/template/prisma/src/components/ui/label.tsx +26 -0
  140. package/template/prisma/src/components/ui/popover.tsx +31 -0
  141. package/template/prisma/src/components/ui/select.tsx +160 -0
  142. package/template/prisma/src/components/ui/separator.tsx +31 -0
  143. package/template/prisma/src/components/ui/skeleton.tsx +15 -0
  144. package/template/prisma/src/components/ui/table.tsx +117 -0
  145. package/template/prisma/src/components/user-account-nav.tsx +60 -0
  146. package/template/prisma/src/components/user-avatar.tsx +25 -0
  147. package/template/prisma/src/env.mjs +78 -0
  148. package/template/prisma/src/hooks/use-debounce.ts +17 -0
  149. package/template/prisma/src/hooks/use-lock-body.tsx +12 -0
  150. package/template/prisma/src/lib/auth.ts +80 -0
  151. package/template/prisma/src/lib/cn.ts +4 -0
  152. package/template/prisma/src/lib/delay.ts +2 -0
  153. package/template/prisma/src/lib/formatDate.ts +11 -0
  154. package/template/prisma/src/lib/generateLabel.ts +5 -0
  155. package/template/prisma/src/lib/siteConfig.ts +4 -0
  156. package/template/prisma/src/styles/globals.css +76 -0
  157. package/template/prisma/tailwind.config.ts +80 -0
  158. package/template/prisma/tsconfig.json +46 -0
  159. /package/template/{advanced/src/styles → base/src/app}/globals.css +0 -0
  160. /package/template/{advanced/src/hooks/use-debounce.ts → base/src/hooks/use-debounce.tsx} +0 -0
  161. /package/template/{base → prisma}/postcss.config.cjs +0 -0
  162. /package/template/{base → prisma}/prettier.config.ts +0 -0
  163. /package/template/{base → prisma}/prisma/schema.prisma +0 -0
  164. /package/template/{base → prisma}/src/app/(auth)/login/user-auth-form.tsx +0 -0
  165. /package/template/{base → prisma}/src/app/api/upload/route.ts +0 -0
  166. /package/template/{advanced → prisma}/src/components/mobile-nav.tsx +0 -0
  167. /package/template/{base → prisma}/src/components/theme-provider.tsx +0 -0
  168. /package/template/{base → prisma}/src/components/ui/textarea.tsx +0 -0
  169. /package/template/{base → prisma}/src/components/ui/toast.tsx +0 -0
  170. /package/template/{base → prisma}/src/components/ui/toaster.tsx +0 -0
  171. /package/template/{base → prisma}/src/components/ui/use-toast.ts +0 -0
  172. /package/template/{base → prisma}/src/lib/db.ts +0 -0
  173. /package/template/{base → prisma}/src/lib/storageClient.ts +0 -0
  174. /package/template/{base → prisma}/src/styles/editor.css +0 -0
  175. /package/template/{base → prisma}/src/types/UnwrapPromise.ts +0 -0
  176. /package/template/{advanced → prisma-advanced}/.env.example +0 -0
  177. /package/template/{advanced → prisma-advanced}/.eslintrc.cjs +0 -0
  178. /package/template/{advanced → prisma-advanced}/components.json +0 -0
  179. /package/template/{advanced → prisma-advanced}/next.config.mjs +0 -0
  180. /package/template/{advanced → prisma-advanced}/package.json +0 -0
  181. /package/template/{advanced → prisma-advanced}/postcss.config.cjs +0 -0
  182. /package/template/{advanced → prisma-advanced}/prettier.config.ts +0 -0
  183. /package/template/{advanced → prisma-advanced}/prisma/schema.prisma +0 -0
  184. /package/template/{advanced → prisma-advanced}/public/next.svg +0 -0
  185. /package/template/{advanced → prisma-advanced}/src/app/(app)/dashboard/page.tsx +0 -0
  186. /package/template/{advanced → prisma-advanced}/src/app/(app)/layout.tsx +0 -0
  187. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/actions.ts +0 -0
  188. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/page.tsx +0 -0
  189. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/render.tsx +0 -0
  190. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/actions.ts +0 -0
  191. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/page.tsx +0 -0
  192. /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/render.tsx +0 -0
  193. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/actions.ts +0 -0
  194. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/loading.tsx +0 -0
  195. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/page.tsx +0 -0
  196. /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/render.tsx +0 -0
  197. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/actions.ts +0 -0
  198. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/page.tsx +0 -0
  199. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/render.tsx +0 -0
  200. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/actions.ts +0 -0
  201. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/page.tsx +0 -0
  202. /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/render.tsx +0 -0
  203. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/actions.ts +0 -0
  204. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/page.tsx +0 -0
  205. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/render.tsx +0 -0
  206. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/actions.ts +0 -0
  207. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/page.tsx +0 -0
  208. /package/template/{advanced → prisma-advanced}/src/app/(app)/users/render.tsx +0 -0
  209. /package/template/{advanced → prisma-advanced}/src/app/(auth)/layout.tsx +0 -0
  210. /package/template/{advanced → prisma-advanced}/src/app/(auth)/login/page.tsx +0 -0
  211. /package/template/{advanced → prisma-advanced}/src/app/(auth)/login/user-auth-form.tsx +0 -0
  212. /package/template/{advanced → prisma-advanced}/src/app/(marketing)/layout.tsx +0 -0
  213. /package/template/{advanced → prisma-advanced}/src/app/(marketing)/page.tsx +0 -0
  214. /package/template/{advanced → prisma-advanced}/src/app/api/auth/[...nextauth]/route.ts +0 -0
  215. /package/template/{advanced → prisma-advanced}/src/app/api/upload/route.ts +0 -0
  216. /package/template/{advanced → prisma-advanced}/src/app/favicon.ico +0 -0
  217. /package/template/{advanced → prisma-advanced}/src/app/layout.tsx +0 -0
  218. /package/template/{advanced → prisma-advanced}/src/components/app-nav-command.tsx +0 -0
  219. /package/template/{advanced → prisma-advanced}/src/components/dark-mode-toggle.tsx +0 -0
  220. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-column-header.tsx +0 -0
  221. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-faceted-filter.tsx +0 -0
  222. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-loading.tsx +0 -0
  223. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-pagination.tsx +0 -0
  224. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-toolbar.tsx +0 -0
  225. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-types.ts +0 -0
  226. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-view-options.tsx +0 -0
  227. /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table.tsx +0 -0
  228. /package/template/{advanced → prisma-advanced}/src/components/data-table/simple-data-table.tsx +0 -0
  229. /package/template/{advanced → prisma-advanced}/src/components/empty-placeholder.tsx +0 -0
  230. /package/template/{advanced → prisma-advanced}/src/components/heading.tsx +0 -0
  231. /package/template/{advanced → prisma-advanced}/src/components/icons.tsx +0 -0
  232. /package/template/{advanced → prisma-advanced}/src/components/main-nav.tsx +0 -0
  233. /package/template/{advanced → prisma-advanced}/src/components/shell.tsx +0 -0
  234. /package/template/{advanced → prisma-advanced}/src/components/side-nav.tsx +0 -0
  235. /package/template/{advanced → prisma-advanced}/src/components/site-footer.tsx +0 -0
  236. /package/template/{advanced → prisma-advanced}/src/components/system-info.tsx +0 -0
  237. /package/template/{advanced → prisma-advanced}/src/components/theme-provider.tsx +0 -0
  238. /package/template/{advanced → prisma-advanced}/src/components/ui/alert-dialog.tsx +0 -0
  239. /package/template/{advanced → prisma-advanced}/src/components/ui/avatar.tsx +0 -0
  240. /package/template/{advanced → prisma-advanced}/src/components/ui/badge.tsx +0 -0
  241. /package/template/{advanced → prisma-advanced}/src/components/ui/button.tsx +0 -0
  242. /package/template/{advanced → prisma-advanced}/src/components/ui/card.tsx +0 -0
  243. /package/template/{advanced → prisma-advanced}/src/components/ui/checkbox.tsx +0 -0
  244. /package/template/{advanced → prisma-advanced}/src/components/ui/command.tsx +0 -0
  245. /package/template/{advanced → prisma-advanced}/src/components/ui/dialog.tsx +0 -0
  246. /package/template/{advanced → prisma-advanced}/src/components/ui/dropdown-menu.tsx +0 -0
  247. /package/template/{advanced → prisma-advanced}/src/components/ui/form.tsx +0 -0
  248. /package/template/{advanced → prisma-advanced}/src/components/ui/input.tsx +0 -0
  249. /package/template/{advanced → prisma-advanced}/src/components/ui/label.tsx +0 -0
  250. /package/template/{advanced → prisma-advanced}/src/components/ui/popover.tsx +0 -0
  251. /package/template/{advanced → prisma-advanced}/src/components/ui/select.tsx +0 -0
  252. /package/template/{advanced → prisma-advanced}/src/components/ui/separator.tsx +0 -0
  253. /package/template/{advanced → prisma-advanced}/src/components/ui/skeleton.tsx +0 -0
  254. /package/template/{advanced → prisma-advanced}/src/components/ui/table.tsx +0 -0
  255. /package/template/{advanced → prisma-advanced}/src/components/ui/textarea.tsx +0 -0
  256. /package/template/{advanced → prisma-advanced}/src/components/ui/toast.tsx +0 -0
  257. /package/template/{advanced → prisma-advanced}/src/components/ui/toaster.tsx +0 -0
  258. /package/template/{advanced → prisma-advanced}/src/components/ui/use-toast.ts +0 -0
  259. /package/template/{advanced → prisma-advanced}/src/components/user-account-nav.tsx +0 -0
  260. /package/template/{advanced → prisma-advanced}/src/components/user-avatar.tsx +0 -0
  261. /package/template/{advanced → prisma-advanced}/src/env.mjs +0 -0
  262. /package/template/{base → prisma-advanced}/src/hooks/use-debounce.ts +0 -0
  263. /package/template/{advanced → prisma-advanced}/src/hooks/use-lock-body.tsx +0 -0
  264. /package/template/{advanced → prisma-advanced}/src/lib/auth.ts +0 -0
  265. /package/template/{advanced → prisma-advanced}/src/lib/cn.ts +0 -0
  266. /package/template/{advanced → prisma-advanced}/src/lib/db.ts +0 -0
  267. /package/template/{advanced → prisma-advanced}/src/lib/delay.ts +0 -0
  268. /package/template/{advanced → prisma-advanced}/src/lib/formatDate.ts +0 -0
  269. /package/template/{advanced → prisma-advanced}/src/lib/generateLabel.ts +0 -0
  270. /package/template/{advanced → prisma-advanced}/src/lib/siteConfig.ts +0 -0
  271. /package/template/{advanced → prisma-advanced}/src/lib/storageClient.ts +0 -0
  272. /package/template/{advanced → prisma-advanced}/src/styles/editor.css +0 -0
  273. /package/template/{base → prisma-advanced}/src/styles/globals.css +0 -0
  274. /package/template/{advanced → prisma-advanced}/src/types/UnwrapPromise.ts +0 -0
  275. /package/template/{advanced → prisma-advanced}/tailwind.config.ts +0 -0
  276. /package/template/{advanced → prisma-advanced}/tsconfig.json +0 -0
@@ -0,0 +1,110 @@
1
+ import {
2
+ createRole,
3
+ deleteRole,
4
+ getRoleByIdWithPermissions,
5
+ getRoles,
6
+ getRolesMini,
7
+ updateRole,
8
+ type RoleType
9
+ } from '@/data-access/role'
10
+ import {
11
+ RolePermissionType,
12
+ createRolePermissions,
13
+ deleteRolePermissionsByRoleId
14
+ } from '@/data-access/role-permission'
15
+ import { type UserType } from '@/data-access/user'
16
+ import { type SortOrderEnum } from '@/lib/sortOrderEnum'
17
+
18
+ type getRolesUseCaseInput = {
19
+ page: number
20
+ limit: number
21
+ sortBy?: keyof RoleType
22
+ sortOrder?: SortOrderEnum
23
+ filters?: {
24
+ name?: string
25
+ }
26
+ }
27
+
28
+ export const getRolesUseCase = async (
29
+ currentUserType: UserType['type'],
30
+ { page, limit, sortBy, sortOrder, filters }: getRolesUseCaseInput
31
+ ) => {
32
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
33
+
34
+ if (limit < 1 || limit > 1000) throw new Error('Invalid limit')
35
+ if (page < 1) throw new Error('Invalid page')
36
+
37
+ return await getRoles({
38
+ page,
39
+ limit,
40
+ sortBy,
41
+ sortOrder,
42
+ filters
43
+ })
44
+ }
45
+
46
+ export const getRolesMiniUseCase = async (
47
+ currentUserType: UserType['type']
48
+ ) => {
49
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
50
+ return await getRolesMini()
51
+ }
52
+
53
+ export const getRoleByIdWithPermissionsUseCase = async (
54
+ currentUserType: UserType['type'],
55
+ id: string
56
+ ) => {
57
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
58
+ return await getRoleByIdWithPermissions(id)
59
+ }
60
+
61
+ type createRoleUseCaseInput = {
62
+ name: string
63
+ permissions: RolePermissionType['permission'][]
64
+ }
65
+
66
+ export const createRoleUseCase = async (
67
+ currentUserType: UserType['type'],
68
+ data: createRoleUseCaseInput
69
+ ) => {
70
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
71
+ const roleId = await createRole({
72
+ name: data.name
73
+ })
74
+
75
+ if (data.permissions.length)
76
+ await createRolePermissions({
77
+ roleId,
78
+ permissions: data.permissions
79
+ })
80
+
81
+ return roleId
82
+ }
83
+
84
+ type updateRoleUseCaseInput = createRoleUseCaseInput & {
85
+ id: string
86
+ }
87
+
88
+ export const updateRoleUseCase = async (
89
+ currentUserType: UserType['type'],
90
+ data: updateRoleUseCaseInput
91
+ ) => {
92
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
93
+ await updateRole({
94
+ id: data.id,
95
+ name: data.name
96
+ })
97
+ await deleteRolePermissionsByRoleId(data.id)
98
+ await createRolePermissions({
99
+ roleId: data.id,
100
+ permissions: data.permissions
101
+ })
102
+ }
103
+
104
+ export const deleteRoleUseCase = async (
105
+ currentUserType: UserType['type'],
106
+ id: string
107
+ ) => {
108
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
109
+ return await deleteRole(id)
110
+ }
@@ -0,0 +1,166 @@
1
+ import {
2
+ getResourcesByTaskId,
3
+ updateResourceByIds
4
+ } from '@/data-access/resource'
5
+ import {
6
+ TaskType,
7
+ createTask,
8
+ deleteTask,
9
+ getTaskByIdWithUser,
10
+ getTasksWithUser,
11
+ updateTask
12
+ } from '@/data-access/task'
13
+ import { CurrentUser } from '@/lib/currentUser'
14
+ import { deleteFile } from '@/lib/s3Client'
15
+ import { type SortOrderEnum } from '@/lib/sortOrderEnum'
16
+ import { checkForRolePermissionUseCase } from './role-permission'
17
+
18
+ const handleTaskImages = async (
19
+ imageIds: string[],
20
+ taskId: string,
21
+ updatedById: string
22
+ ) => {
23
+ const prevImages = await getResourcesByTaskId(taskId)
24
+
25
+ for (const p of prevImages) {
26
+ if (imageIds.includes(p.id)) continue
27
+ await deleteFile(p.id)
28
+ }
29
+
30
+ if (imageIds.length)
31
+ await updateResourceByIds({
32
+ ids: imageIds,
33
+ taskId,
34
+ updatedById
35
+ })
36
+ }
37
+
38
+ type getUsersUseCaseInput = {
39
+ page: number
40
+ limit: number
41
+ sortBy?: keyof TaskType
42
+ sortOrder?: SortOrderEnum
43
+ filters?: {
44
+ title?: string
45
+ status?: TaskType['status'][]
46
+ }
47
+ }
48
+
49
+ export const getTasksWithUserUseCase = async (
50
+ currentUser: CurrentUser,
51
+ { page, limit, sortBy, sortOrder, filters }: getUsersUseCaseInput
52
+ ) => {
53
+ const c = await checkForRolePermissionUseCase(
54
+ currentUser.type,
55
+ currentUser.roleId,
56
+ 'TaskList'
57
+ )
58
+ if (!c) throw new Error('Unauthorized')
59
+
60
+ if (limit < 1 || limit > 1000) throw new Error('Invalid limit')
61
+ if (page < 1) throw new Error('Invalid page')
62
+
63
+ return await getTasksWithUser({
64
+ page,
65
+ limit,
66
+ sortBy,
67
+ sortOrder,
68
+ filters: filters
69
+ ? {
70
+ title: filters.title,
71
+ status: filters.status
72
+ }
73
+ : undefined
74
+ })
75
+ }
76
+
77
+ export const getTaskByIdWithUserUseCase = async (
78
+ currentUser: CurrentUser,
79
+ id: string
80
+ ) => {
81
+ const c = await checkForRolePermissionUseCase(
82
+ currentUser.type,
83
+ currentUser.roleId,
84
+ 'TaskView'
85
+ )
86
+ if (!c) throw new Error('Unauthorized')
87
+
88
+ return await getTaskByIdWithUser(id)
89
+ }
90
+
91
+ type createTaskUseCaseInput = {
92
+ title: string
93
+ description?: string | null
94
+ status: TaskType['status']
95
+ imageIds?: string[] | null
96
+ }
97
+
98
+ export const createTaskUseCase = async (
99
+ currentUser: CurrentUser,
100
+ data: createTaskUseCaseInput
101
+ ) => {
102
+ const c = await checkForRolePermissionUseCase(
103
+ currentUser.type,
104
+ currentUser.roleId,
105
+ 'TaskCreate'
106
+ )
107
+ if (!c) throw new Error('Unauthorized')
108
+
109
+ const taskId = await createTask({
110
+ title: data.title,
111
+ description: data.description,
112
+ status: data.status,
113
+ createdById: currentUser.id
114
+ })
115
+
116
+ if (taskId)
117
+ await handleTaskImages(data.imageIds || [], taskId, currentUser.id)
118
+
119
+ return taskId
120
+ }
121
+
122
+ type updateTaskUseCaseInput = {
123
+ id: string
124
+ title?: string
125
+ description?: string | null
126
+ status?: TaskType['status']
127
+ imageIds?: string[] | null
128
+ }
129
+
130
+ export const updateTaskUseCase = async (
131
+ currentUser: CurrentUser,
132
+ data: updateTaskUseCaseInput
133
+ ) => {
134
+ const c = await checkForRolePermissionUseCase(
135
+ currentUser.type,
136
+ currentUser.roleId,
137
+ 'TaskUpdate'
138
+ )
139
+ if (!c) throw new Error('Unauthorized')
140
+
141
+ const result = await updateTask({
142
+ id: data.id,
143
+ title: data.title,
144
+ description: data.description,
145
+ status: data.status,
146
+ updatedById: currentUser.id
147
+ })
148
+
149
+ await handleTaskImages(data.imageIds || [], data.id, currentUser.id)
150
+
151
+ return result
152
+ }
153
+
154
+ export const deleteTaskUseCase = async (
155
+ currentUser: CurrentUser,
156
+ id: string
157
+ ) => {
158
+ const c = await checkForRolePermissionUseCase(
159
+ currentUser.type,
160
+ currentUser.roleId,
161
+ 'TaskDelete'
162
+ )
163
+ if (!c) throw new Error('Unauthorized')
164
+
165
+ return await deleteTask(id)
166
+ }
@@ -0,0 +1,121 @@
1
+ import {
2
+ UserType,
3
+ createUser,
4
+ deleteUser,
5
+ getUserById,
6
+ getUsersWithRole,
7
+ updateUser
8
+ } from '@/data-access/user'
9
+ import { type SortOrderEnum } from '@/lib/sortOrderEnum'
10
+
11
+ type UpdateUserNameUseCaseInput = {
12
+ name: string
13
+ }
14
+
15
+ export const updateCurrentUserNameUseCase = async (
16
+ currentUserId: string,
17
+ data: UpdateUserNameUseCaseInput
18
+ ) => {
19
+ await updateUser(currentUserId, {
20
+ name: data.name
21
+ })
22
+ }
23
+
24
+ type getUsersUseCaseInput = {
25
+ page: number
26
+ limit: number
27
+ sortBy?: keyof UserType
28
+ sortOrder?: SortOrderEnum
29
+ filters?: {
30
+ name?: string
31
+ email?: string
32
+ type?: UserType['type'][]
33
+ }
34
+ }
35
+
36
+ export const getUsersWithRoleUseCase = async (
37
+ currentUserType: UserType['type'],
38
+ { page, limit, sortBy, sortOrder, filters }: getUsersUseCaseInput
39
+ ) => {
40
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
41
+
42
+ if (limit < 1 || limit > 1000) throw new Error('Invalid limit')
43
+ if (page < 1) throw new Error('Invalid page')
44
+
45
+ return await getUsersWithRole({
46
+ page,
47
+ limit,
48
+ sortBy,
49
+ sortOrder,
50
+ filters: filters
51
+ ? {
52
+ name: filters.name,
53
+ email: filters.email,
54
+ type: filters.type
55
+ }
56
+ : undefined
57
+ })
58
+ }
59
+
60
+ export const getUserByIdUseCase = async (
61
+ currentUserType: UserType['type'],
62
+ id: string
63
+ ) => {
64
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
65
+
66
+ return await getUserById(id)
67
+ }
68
+
69
+ type createUserUseCaseInput = {
70
+ name?: string | null
71
+ email: string
72
+ type: UserType['type']
73
+ roleId?: string | null
74
+ }
75
+
76
+ export const createUserUseCase = async (
77
+ currentUserType: UserType['type'],
78
+ data: createUserUseCaseInput
79
+ ) => {
80
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
81
+
82
+ if (data.type === 'Root') data.roleId = null
83
+
84
+ data.email = data.email.trim().toLowerCase()
85
+
86
+ return await createUser({
87
+ name: data.name,
88
+ email: data.email,
89
+ type: data.type,
90
+ roleId: data.roleId
91
+ })
92
+ }
93
+
94
+ type updateUserUseCaseInput = {
95
+ name: string
96
+ type: UserType['type']
97
+ roleId?: string | null
98
+ }
99
+
100
+ export const updateUserUseCase = async (
101
+ currentUserType: UserType['type'],
102
+ id: string,
103
+ data: updateUserUseCaseInput
104
+ ) => {
105
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
106
+
107
+ await updateUser(id, {
108
+ name: data.name,
109
+ type: data.type,
110
+ roleId: data.roleId
111
+ })
112
+ }
113
+
114
+ export const deleteUserUseCase = async (
115
+ currentUserType: UserType['type'],
116
+ id: string
117
+ ) => {
118
+ if (currentUserType !== 'Root') throw new Error('Unauthorized')
119
+
120
+ await deleteUser(id)
121
+ }
@@ -1,4 +1,5 @@
1
1
  import type { Config } from 'tailwindcss'
2
+ import { fontFamily } from 'tailwindcss/defaultTheme'
2
3
 
3
4
  const config = {
4
5
  darkMode: ['class'],
@@ -13,10 +14,10 @@ const config = {
13
14
  }
14
15
  },
15
16
  extend: {
16
- backgroundImage: {
17
- 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
18
- 'gradient-conic':
19
- 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))'
17
+ extend: {
18
+ fontFamily: {
19
+ sans: ['var(--font-geist-sans)', ...fontFamily.sans]
20
+ }
20
21
  },
21
22
  colors: {
22
23
  border: 'hsl(var(--border))',
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2017",
3
+ "target": "ES2022",
4
4
  "lib": ["DOM", "DOM.Iterable", "ESNext"],
5
5
  "allowJs": true,
6
6
  "checkJs": true,
@@ -19,7 +19,7 @@
19
19
  "noEmit": true,
20
20
  "esModuleInterop": true,
21
21
  "module": "ESNext",
22
- "moduleResolution": "Bundler",
22
+ "moduleResolution": "bundler",
23
23
  "resolveJsonModule": true,
24
24
  "isolatedModules": true,
25
25
  "jsx": "preserve",
@@ -38,6 +38,7 @@
38
38
  "next-env.d.ts",
39
39
  "**/*.ts",
40
40
  "**/*.tsx",
41
+ "**/*.js",
41
42
  "**/*.cjs",
42
43
  "**/*.mjs",
43
44
  ".next/types/**/*.ts"
@@ -0,0 +1,16 @@
1
+ DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres?schema=public"
2
+ NEXTAUTH_SECRET="secret"
3
+ NEXTAUTH_URL="http://localhost:3000"
4
+ EMAIL_SERVER_HOST="example.com"
5
+ EMAIL_SERVER_PORT="123"
6
+ EMAIL_SERVER_USER="example@example.com"
7
+ EMAIL_SERVER_PASSWORD="password"
8
+ 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=""
@@ -0,0 +1,24 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
2
+ const path = require('path')
3
+
4
+ /** @type {import("eslint").Linter.Config} */
5
+ const eslintConfig = {
6
+ overrides: [
7
+ {
8
+ extends: [],
9
+ files: ['*.ts', '*.tsx'],
10
+ parserOptions: {
11
+ project: path.join(__dirname, 'tsconfig.json')
12
+ }
13
+ }
14
+ ],
15
+ parser: '@typescript-eslint/parser',
16
+ parserOptions: {
17
+ project: path.join(__dirname, 'tsconfig.json')
18
+ },
19
+ plugins: ['@typescript-eslint'],
20
+ extends: ['next/core-web-vitals'],
21
+ rules: {}
22
+ }
23
+
24
+ module.exports = eslintConfig
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "default",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "tailwind.config.ts",
8
+ "css": "src/styles/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/cn"
16
+ }
17
+ }
@@ -0,0 +1,15 @@
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
+ /** @type {import('next').NextConfig} */
8
+ const nextConfig = {
9
+ i18n: {
10
+ locales: ['en'],
11
+ defaultLocale: 'en'
12
+ }
13
+ }
14
+
15
+ export default nextConfig
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "my-app",
3
+ "version": "1.0.0",
4
+ "description": "A Legendary Next.js App",
5
+ "private": true,
6
+ "scripts": {
7
+ "postinstall": "prisma generate",
8
+ "lint": "next lint",
9
+ "dev": "next dev",
10
+ "build": "next build",
11
+ "start": "next start",
12
+ "db:push": "prisma db push",
13
+ "db:gen": "prisma generate",
14
+ "db:studio": "prisma studio"
15
+ },
16
+ "dependencies": {
17
+ "@editorjs/checklist": "^1.6.0",
18
+ "@editorjs/delimiter": "^1.4.0",
19
+ "@editorjs/editorjs": "^2.28.2",
20
+ "@editorjs/header": "^2.8.1",
21
+ "@editorjs/image": "^2.9.0",
22
+ "@editorjs/list": "^1.9.0",
23
+ "@editorjs/quote": "^2.6.0",
24
+ "@editorjs/table": "^2.3.0",
25
+ "@ejekanshjain/cloud-storage": "^3.2.0",
26
+ "@hookform/resolvers": "^3.3.3",
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.46",
48
+ "@types/react-dom": "^18.2.18",
49
+ "@typescript-eslint/eslint-plugin": "^6.16.0",
50
+ "@typescript-eslint/parser": "^6.16.0",
51
+ "@vercel/analytics": "^1.1.1",
52
+ "@vercel/speed-insights": "^1.0.2",
53
+ "autoprefixer": "^10.4.16",
54
+ "class-variance-authority": "^0.7.0",
55
+ "clsx": "^2.0.0",
56
+ "cmdk": "^0.2.0",
57
+ "dayjs": "^1.11.10",
58
+ "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.303.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.10",
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",
76
+ "tailwindcss-animate": "^1.0.7",
77
+ "typescript": "^5.3.3",
78
+ "zod": "^3.22.4"
79
+ }
80
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1,19 @@
1
+ import { Metadata } from 'next'
2
+
3
+ import { Heading } from '@/components/heading'
4
+ import { Shell } from '@/components/shell'
5
+ import { siteConfig } from '@/lib/siteConfig'
6
+
7
+ export const metadata: Metadata = {
8
+ title: 'Dashboard' + ' | ' + siteConfig.name
9
+ }
10
+
11
+ const DashboardPage = () => {
12
+ return (
13
+ <Shell>
14
+ <Heading heading="Dashboard" />
15
+ </Shell>
16
+ )
17
+ }
18
+
19
+ export default DashboardPage