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,31 @@
1
+ 'use client'
2
+
3
+ import { useTheme } from 'next-themes'
4
+ import { Toaster as Sonner } from 'sonner'
5
+
6
+ type ToasterProps = React.ComponentProps<typeof Sonner>
7
+
8
+ const Toaster = ({ ...props }: ToasterProps) => {
9
+ const { theme = 'system' } = useTheme()
10
+
11
+ return (
12
+ <Sonner
13
+ theme={theme as ToasterProps['theme']}
14
+ className="toaster group"
15
+ toastOptions={{
16
+ classNames: {
17
+ toast:
18
+ 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
19
+ description: 'group-[.toast]:text-muted-foreground',
20
+ actionButton:
21
+ 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
22
+ cancelButton:
23
+ 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground'
24
+ }
25
+ }}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ export { Toaster }
@@ -1,4 +1,3 @@
1
- import { User } from '@prisma/client'
2
1
  import { AvatarProps } from '@radix-ui/react-avatar'
3
2
  import { FC } from 'react'
4
3
 
@@ -6,7 +5,10 @@ import { Icons } from '@/components/icons'
6
5
  import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
7
6
 
8
7
  interface UserAvatarProps extends AvatarProps {
9
- user: Pick<User, 'image' | 'name'>
8
+ user: {
9
+ name?: string | null
10
+ image?: string | null
11
+ }
10
12
  }
11
13
 
12
14
  export const UserAvatar: FC<UserAvatarProps> = ({ user, ...props }) => {
@@ -0,0 +1,85 @@
1
+ import { db } from '@/db'
2
+ import { Resource } from '@/db/schema'
3
+ import { eq, or, sql } from 'drizzle-orm'
4
+
5
+ type createResourceInput = {
6
+ id?: string
7
+ filename: string
8
+ key: string
9
+ isTemp: boolean
10
+ url: string
11
+ size: number
12
+ contentType: string
13
+ createdById?: string | null
14
+
15
+ taskId?: string | null
16
+ }
17
+
18
+ export const createResource = async (data: createResourceInput) => {
19
+ const newResource = await db
20
+ .insert(Resource)
21
+ .values({
22
+ ...data
23
+ })
24
+ .returning({
25
+ id: Resource.id
26
+ })
27
+
28
+ const id = newResource[0]?.id
29
+
30
+ if (!id) throw new Error('Failed to create resource')
31
+
32
+ return id
33
+ }
34
+
35
+ export const getResourceById = async (id: string) => {
36
+ return await db.query.Resource.findFirst({
37
+ where: eq(Resource.id, id)
38
+ })
39
+ }
40
+
41
+ type updateResourceByIdInput = {
42
+ id: string
43
+ isTemp?: boolean
44
+ contentType?: string
45
+ size?: number
46
+ updatedById?: string | null
47
+
48
+ taskId?: string | null
49
+ }
50
+
51
+ export const updateResourceById = async (data: updateResourceByIdInput) => {
52
+ return await db
53
+ .update(Resource)
54
+ .set({
55
+ ...data,
56
+ updatedAt: sql`now()`
57
+ })
58
+ .where(eq(Resource.id, data.id))
59
+ }
60
+
61
+ type updateResourceByIdsInput = {
62
+ ids: string[]
63
+ taskId?: string | null
64
+ updatedById?: string | null
65
+ }
66
+
67
+ export const updateResourceByIds = async (data: updateResourceByIdsInput) => {
68
+ return await db
69
+ .update(Resource)
70
+ .set({
71
+ ...data,
72
+ updatedAt: sql`now()`
73
+ })
74
+ .where(or(...data.ids.map(id => eq(Resource.id, id))))
75
+ }
76
+
77
+ export const getResourcesByTaskId = async (taskId: string) => {
78
+ return await db.query.Resource.findMany({
79
+ where: eq(Resource.taskId, taskId)
80
+ })
81
+ }
82
+
83
+ export const deleteResource = async (id: string) => {
84
+ return await db.delete(Resource).where(eq(Resource.id, id))
85
+ }
@@ -0,0 +1,45 @@
1
+ import { InferSelectModel, and, count, eq } from 'drizzle-orm'
2
+
3
+ import { db } from '@/db'
4
+ import { RolePermission } from '@/db/schema'
5
+
6
+ export type RolePermissionType = InferSelectModel<typeof RolePermission>
7
+
8
+ export const checkForRolePermission = async (
9
+ roleId: string,
10
+ permission: RolePermissionType['permission']
11
+ ) => {
12
+ const c = await db
13
+ .select({ count: count() })
14
+ .from(RolePermission)
15
+ .where(
16
+ and(
17
+ eq(RolePermission.roleId, roleId),
18
+ eq(RolePermission.permission, permission)
19
+ )
20
+ )
21
+
22
+ return !!c?.[0]?.count
23
+ }
24
+
25
+ type createRolePermissionsInput = {
26
+ roleId: string
27
+ permissions: RolePermissionType['permission'][]
28
+ }
29
+
30
+ export const createRolePermissions = async (
31
+ data: createRolePermissionsInput
32
+ ) => {
33
+ return await db.insert(RolePermission).values(
34
+ data.permissions.map(p => ({
35
+ roleId: data.roleId,
36
+ permission: p
37
+ }))
38
+ )
39
+ }
40
+
41
+ export const deleteRolePermissionsByRoleId = async (roleId: string) => {
42
+ return await db
43
+ .delete(RolePermission)
44
+ .where(eq(RolePermission.roleId, roleId))
45
+ }
@@ -0,0 +1,105 @@
1
+ import { InferSelectModel, asc, count, desc, eq, ilike, sql } from 'drizzle-orm'
2
+
3
+ import { db } from '@/db'
4
+ import { Role } from '@/db/schema'
5
+ import { type SortOrderEnum } from '@/lib/sortOrderEnum'
6
+
7
+ export type RoleType = InferSelectModel<typeof Role>
8
+
9
+ type getRolesInput = {
10
+ page: number
11
+ limit: number
12
+ sortBy?: keyof RoleType
13
+ sortOrder?: SortOrderEnum
14
+ filters?: {
15
+ name?: string
16
+ }
17
+ }
18
+
19
+ export const getRoles = async ({
20
+ page,
21
+ limit,
22
+ sortBy,
23
+ sortOrder,
24
+ filters
25
+ }: getRolesInput) => {
26
+ const where = filters?.name
27
+ ? ilike(Role.name, `%${filters.name}%`)
28
+ : undefined
29
+
30
+ const [roles, total] = await Promise.all([
31
+ db.query.Role.findMany({
32
+ columns: {
33
+ id: true,
34
+ name: true,
35
+ createdAt: true,
36
+ updatedAt: true
37
+ },
38
+ where,
39
+ orderBy: [
40
+ sortOrder === 'asc'
41
+ ? asc(Role[sortBy || 'createdAt'])
42
+ : desc(Role[sortBy || 'createdAt'])
43
+ ],
44
+ offset: (page - 1) * limit,
45
+ limit
46
+ }),
47
+ db
48
+ .select({
49
+ count: count()
50
+ })
51
+ .from(Role)
52
+ .where(where)
53
+ ])
54
+
55
+ return { roles, total: total[0]?.count || 0 }
56
+ }
57
+
58
+ export const getRoleByIdWithPermissions = async (id: string) => {
59
+ return await db.query.Role.findFirst({
60
+ where: eq(Role.id, id),
61
+ with: {
62
+ permissions: true
63
+ }
64
+ })
65
+ }
66
+
67
+ type createRoleInput = {
68
+ name: string
69
+ }
70
+
71
+ export const createRole = async (data: createRoleInput) => {
72
+ const newRole = await db.insert(Role).values(data).returning({
73
+ id: Role.id
74
+ })
75
+
76
+ const id = newRole[0]?.id
77
+ if (!id) throw new Error('Failed to create role')
78
+
79
+ return id
80
+ }
81
+
82
+ type updateRoleInput = createRoleInput & {
83
+ id: string
84
+ }
85
+
86
+ export const updateRole = async (data: updateRoleInput) => {
87
+ return await db
88
+ .update(Role)
89
+ .set({ ...data, updatedAt: sql`now()` })
90
+ .where(eq(Role.id, data.id))
91
+ }
92
+
93
+ export const deleteRole = async (id: string) => {
94
+ return await db.delete(Role).where(eq(Role.id, id))
95
+ }
96
+
97
+ export const getRolesMini = async () => {
98
+ return await db.query.Role.findMany({
99
+ columns: {
100
+ id: true,
101
+ name: true
102
+ },
103
+ orderBy: [asc(Role.name)]
104
+ })
105
+ }
@@ -0,0 +1,141 @@
1
+ import {
2
+ InferSelectModel,
3
+ asc,
4
+ count,
5
+ desc,
6
+ eq,
7
+ ilike,
8
+ or,
9
+ sql
10
+ } from 'drizzle-orm'
11
+
12
+ import { db } from '@/db'
13
+ import { Task } from '@/db/schema'
14
+ import { type SortOrderEnum } from '@/lib/sortOrderEnum'
15
+
16
+ export type TaskType = InferSelectModel<typeof Task>
17
+
18
+ type getTasksInput = {
19
+ page: number
20
+ limit: number
21
+ sortBy?: keyof TaskType
22
+ sortOrder?: SortOrderEnum
23
+ filters?: {
24
+ title?: string
25
+ status?: TaskType['status'][]
26
+ }
27
+ }
28
+
29
+ export const getTasksWithUser = async ({
30
+ page,
31
+ limit,
32
+ sortBy,
33
+ sortOrder,
34
+ filters
35
+ }: getTasksInput) => {
36
+ const where = or(
37
+ filters?.title ? ilike(Task.title, `%${filters.title}%`) : undefined,
38
+ ...(filters?.status || []).map(f => eq(Task.status, f))
39
+ )
40
+
41
+ const [tasks, total] = await Promise.all([
42
+ db.query.Task.findMany({
43
+ columns: {
44
+ id: true,
45
+ title: true,
46
+ status: true,
47
+ createdAt: true,
48
+ updatedAt: true
49
+ },
50
+ with: {
51
+ createdBy: {
52
+ columns: {
53
+ id: true,
54
+ name: true
55
+ }
56
+ },
57
+ updatedBy: {
58
+ columns: {
59
+ id: true,
60
+ name: true
61
+ }
62
+ }
63
+ },
64
+ where,
65
+ orderBy: [
66
+ sortOrder === 'asc'
67
+ ? asc(Task[sortBy || 'createdAt'])
68
+ : desc(Task[sortBy || 'createdAt'])
69
+ ],
70
+ offset: (page - 1) * limit,
71
+ limit
72
+ }),
73
+ db
74
+ .select({
75
+ count: count()
76
+ })
77
+ .from(Task)
78
+ .where(where)
79
+ ])
80
+
81
+ return { tasks, total: total[0]?.count || 0 }
82
+ }
83
+
84
+ export const getTaskByIdWithUser = async (id: string) => {
85
+ return await db.query.Task.findFirst({
86
+ where: eq(Task.id, id),
87
+ with: {
88
+ createdBy: {
89
+ columns: {
90
+ id: true,
91
+ name: true
92
+ }
93
+ },
94
+ updatedBy: {
95
+ columns: {
96
+ id: true,
97
+ name: true
98
+ }
99
+ }
100
+ }
101
+ })
102
+ }
103
+
104
+ type createTaskInput = {
105
+ title: string
106
+ description?: string | null
107
+ status: TaskType['status']
108
+ createdById: string
109
+ }
110
+
111
+ export const createTask = async (data: createTaskInput) => {
112
+ const newTask = await db.insert(Task).values(data).returning({
113
+ id: Task.id
114
+ })
115
+
116
+ if (!newTask[0]?.id) throw new Error('Failed to create task')
117
+
118
+ return newTask[0].id
119
+ }
120
+
121
+ type updateTaskInput = {
122
+ id: string
123
+ title?: string
124
+ description?: string | null
125
+ status?: TaskType['status']
126
+ updatedById: string
127
+ }
128
+
129
+ export const updateTask = async (data: updateTaskInput) => {
130
+ return await db
131
+ .update(Task)
132
+ .set({
133
+ ...data,
134
+ updatedAt: sql`now()`
135
+ })
136
+ .where(eq(Task.id, data.id))
137
+ }
138
+
139
+ export const deleteTask = async (id: string) => {
140
+ return await db.delete(Task).where(eq(Task.id, id))
141
+ }
@@ -0,0 +1,141 @@
1
+ import {
2
+ InferSelectModel,
3
+ asc,
4
+ count,
5
+ desc,
6
+ eq,
7
+ ilike,
8
+ or,
9
+ sql
10
+ } from 'drizzle-orm'
11
+
12
+ import { db } from '@/db'
13
+ import { User } from '@/db/schema'
14
+ import { type SortOrderEnum } from '@/lib/sortOrderEnum'
15
+
16
+ export type UserType = InferSelectModel<typeof User>
17
+
18
+ type updateUserByIdInput = {
19
+ name?: string | null
20
+ type?: UserType['type']
21
+ roleId?: string | null
22
+ }
23
+
24
+ export const updateUser = async (id: string, data: updateUserByIdInput) => {
25
+ await db
26
+ .update(User)
27
+ .set({ ...data, updatedAt: sql`now()` })
28
+ .where(eq(User.id, id))
29
+ }
30
+
31
+ type getUsersInput = {
32
+ page: number
33
+ limit: number
34
+ sortBy?: keyof UserType
35
+ sortOrder?: SortOrderEnum
36
+ filters?: {
37
+ name?: string
38
+ email?: string
39
+ type?: UserType['type'][]
40
+ }
41
+ }
42
+
43
+ export const getUsersWithRole = async ({
44
+ page,
45
+ limit,
46
+ sortBy,
47
+ sortOrder,
48
+ filters
49
+ }: getUsersInput) => {
50
+ const where = or(
51
+ filters?.name ? ilike(User.name, `%${filters.name}%`) : undefined,
52
+ filters?.email ? ilike(User.email, `%${filters.email}%`) : undefined,
53
+ ...(filters?.type || []).map(f => eq(User.type, f))
54
+ )
55
+
56
+ const [users, total] = await Promise.all([
57
+ db.query.User.findMany({
58
+ columns: {
59
+ id: true,
60
+ name: true,
61
+ email: true,
62
+ type: true,
63
+ roleId: true,
64
+ image: true,
65
+ emailVerified: true,
66
+ createdAt: true,
67
+ updatedAt: true
68
+ },
69
+ with: {
70
+ role: true
71
+ },
72
+ where,
73
+ orderBy: [
74
+ sortOrder === 'asc'
75
+ ? asc(User[sortBy || 'createdAt'])
76
+ : desc(User[sortBy || 'createdAt'])
77
+ ],
78
+ offset: (page - 1) * limit,
79
+ limit
80
+ }),
81
+ db
82
+ .select({
83
+ count: count()
84
+ })
85
+ .from(User)
86
+ .where(where)
87
+ ])
88
+
89
+ return { users, total: total[0]?.count || 0 }
90
+ }
91
+
92
+ export const getUserById = async (id: string) => {
93
+ return await db.query.User.findFirst({
94
+ where: eq(User.id, id),
95
+ columns: {
96
+ id: true,
97
+ name: true,
98
+ email: true,
99
+ emailVerified: true,
100
+ type: true,
101
+ roleId: true,
102
+ createdAt: true,
103
+ updatedAt: true
104
+ }
105
+ })
106
+ }
107
+
108
+ type createUserInput = {
109
+ name?: string | null
110
+ email: string
111
+ type: UserType['type']
112
+ roleId?: string | null
113
+ }
114
+
115
+ export const createUser = async ({
116
+ name,
117
+ email,
118
+ type,
119
+ roleId
120
+ }: createUserInput) => {
121
+ const newUser = await db
122
+ .insert(User)
123
+ .values({
124
+ name,
125
+ email,
126
+ type,
127
+ roleId
128
+ })
129
+ .returning({
130
+ id: User.id
131
+ })
132
+
133
+ const id = newUser[0]?.id
134
+ if (!id) throw new Error('Failed to create user')
135
+
136
+ return id
137
+ }
138
+
139
+ export const deleteUser = async (id: string) => {
140
+ return await db.delete(User).where(eq(User.id, id))
141
+ }
@@ -0,0 +1,14 @@
1
+ import { drizzle } from 'drizzle-orm/postgres-js'
2
+ import postgres from 'postgres'
3
+
4
+ import { env } from '@/env.mjs'
5
+ import * as schema from './schema'
6
+
7
+ const globalForDb = globalThis as unknown as {
8
+ conn: postgres.Sql | undefined
9
+ }
10
+
11
+ const conn = globalForDb.conn ?? postgres(env.DATABASE_URL)
12
+ if (env.NODE_ENV !== 'production') globalForDb.conn = conn
13
+
14
+ export const db = drizzle(conn, { schema })