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
@@ -1,55 +1,34 @@
1
1
  'use server'
2
2
 
3
- import { User } from '@prisma/client'
3
+ import { z } from 'zod'
4
4
 
5
- import { getAuthSession } from '@/lib/auth'
6
- import { prisma } from '@/lib/db'
7
- import { UnwrapPromise } from '@/types/UnwrapPromise'
5
+ import { User } from '@/db/schema'
6
+ import { authActionClient } from '@/lib/safe-action'
7
+ import { SortOrderEnum } from '@/lib/sortOrderEnum'
8
+ import { getUsersWithRoleUseCase } from '@/use-case/user'
8
9
 
9
- export const getUsers = async ({
10
- page,
11
- limit,
12
- sortBy,
13
- sortOrder,
14
- name,
15
- email
16
- }: {
17
- page: number
18
- limit: number
19
- sortBy?: keyof User
20
- sortOrder?: 'asc' | 'desc'
21
- name?: string
22
- email?: string
23
- }) => {
24
- const session = await getAuthSession()
25
- if (!session) throw new Error('Unauthorized')
26
-
27
- const where = {
28
- name: name ? { contains: name, mode: 'insensitive' as const } : undefined,
29
- email: email ? { contains: email, mode: 'insensitive' as const } : undefined
30
- }
31
- const [users, total] = await Promise.all([
32
- prisma.user.findMany({
33
- where,
34
- select: {
35
- id: true,
36
- name: true,
37
- email: true,
38
- createdAt: true,
39
- updatedAt: true
40
- },
41
- skip: (page - 1) * limit,
42
- take: limit,
43
- orderBy: {
44
- [sortBy || 'createdAt']: sortOrder || 'desc'
10
+ export const getUsersAction = authActionClient
11
+ .schema(
12
+ z.object({
13
+ page: z.number().gte(1),
14
+ limit: z.number().gte(1).lte(1000),
15
+ sortBy: z.enum(['name']).optional(),
16
+ sortOrder: z.enum(SortOrderEnum).optional(),
17
+ name: z.string().optional(),
18
+ email: z.string().optional(),
19
+ type: z.array(z.enum(User.type.enumValues)).optional()
20
+ })
21
+ )
22
+ .action(async ({ ctx, parsedInput }) => {
23
+ return await getUsersWithRoleUseCase(ctx.user.type, {
24
+ page: parsedInput.page,
25
+ limit: parsedInput.limit,
26
+ sortBy: parsedInput.sortBy,
27
+ sortOrder: parsedInput.sortOrder,
28
+ filters: {
29
+ name: parsedInput.name,
30
+ email: parsedInput.email,
31
+ type: parsedInput.type
45
32
  }
46
- }),
47
- prisma.user.count({
48
- where
49
33
  })
50
- ])
51
-
52
- return { users, total }
53
- }
54
-
55
- export type GetUsersFnDataType = UnwrapPromise<ReturnType<typeof getUsers>>
34
+ })
@@ -1,8 +1,9 @@
1
- import { User } from '@prisma/client'
2
1
  import { Metadata } from 'next'
2
+ import { notFound } from 'next/navigation'
3
3
 
4
+ import { authGuard, getAuthSession } from '@/lib/auth'
4
5
  import { siteConfig } from '@/lib/siteConfig'
5
- import { getUsers } from './actions'
6
+ import { getUsersAction } from './actions'
6
7
  import { Render } from './render'
7
8
 
8
9
  export const metadata: Metadata = {
@@ -18,32 +19,44 @@ const Users = async ({
18
19
  sort?: string
19
20
  name?: string
20
21
  email?: string
22
+ type?: string
21
23
  }
22
24
  }) => {
25
+ const session = await getAuthSession()
26
+ if (!session) return notFound()
27
+
28
+ const g = await authGuard(session)
29
+ if (!g) return notFound()
30
+
23
31
  const page = searchParams.page ? parseInt(searchParams.page) : 1
24
32
  const limit = searchParams.limit ? parseInt(searchParams.limit) : 10
25
33
 
26
34
  const [sortBy, sortOrder] =
27
35
  typeof searchParams.sort === 'string'
28
- ? (searchParams.sort.split('.') as [
29
- keyof User | undefined,
30
- 'asc' | 'desc' | undefined
31
- ])
36
+ ? (searchParams.sort.split('.') as [any, 'asc' | 'desc' | undefined])
32
37
  : []
33
38
 
34
39
  const name = searchParams.name
35
40
  const email = searchParams.email
36
41
 
37
- const data = await getUsers({
42
+ const type =
43
+ typeof searchParams.type === 'string'
44
+ ? (searchParams.type.split('.') as any[])
45
+ : undefined
46
+
47
+ const data = await getUsersAction({
38
48
  page,
39
49
  limit,
40
50
  sortBy,
41
51
  sortOrder,
42
52
  name,
43
- email
53
+ email,
54
+ type
44
55
  })
45
56
 
46
- return <Render data={data} />
57
+ if (!data?.data) throw new Error('Users data not found')
58
+
59
+ return <Render data={data.data} />
47
60
  }
48
61
 
49
62
  export default Users
@@ -9,11 +9,31 @@ import { Heading } from '@/components/heading'
9
9
  import { Icons } from '@/components/icons'
10
10
  import { Shell } from '@/components/shell'
11
11
  import { Button } from '@/components/ui/button'
12
+ import { User } from '@/db/schema'
12
13
  import { formatDateTime, timesAgo } from '@/lib/formatDate'
13
- import { GetUsersFnDataType } from './actions'
14
+ import { generateLabel } from '@/lib/generateLabel'
15
+ import { InferSelectModel } from 'drizzle-orm'
16
+
17
+ const types = User.type.enumValues.map(x => ({
18
+ label: generateLabel(x),
19
+ value: x
20
+ }))
14
21
 
15
22
  export const Render: FC<{
16
- data: GetUsersFnDataType
23
+ data: {
24
+ users: {
25
+ id: string
26
+ name?: string | null
27
+ email: string
28
+ type?: InferSelectModel<typeof User>['type']
29
+ role?: {
30
+ name: string
31
+ } | null
32
+ createdAt: Date
33
+ updatedAt?: Date | null
34
+ }[]
35
+ total: number
36
+ }
17
37
  }> = ({ data }) => {
18
38
  return (
19
39
  <Shell>
@@ -47,6 +67,20 @@ export const Render: FC<{
47
67
  <DataTableColumnHeader column={column} title="Email" />
48
68
  )
49
69
  },
70
+ {
71
+ accessorKey: 'type',
72
+ header: ({ column }) => (
73
+ <DataTableColumnHeader column={column} title="Type" />
74
+ )
75
+ },
76
+ {
77
+ accessorKey: 'role',
78
+ header: ({ column }) => (
79
+ <DataTableColumnHeader column={column} title="Role" />
80
+ ),
81
+ cell: ({ row }) => row.original.role?.name,
82
+ enableSorting: false
83
+ },
50
84
  {
51
85
  accessorKey: 'createdAt',
52
86
  header: ({ column }) => (
@@ -59,7 +93,10 @@ export const Render: FC<{
59
93
  header: ({ column }) => (
60
94
  <DataTableColumnHeader column={column} title="Updated At" />
61
95
  ),
62
- cell: ({ row }) => timesAgo(row.original.updatedAt)
96
+ cell: ({ row }) =>
97
+ row.original.updatedAt
98
+ ? timesAgo(row.original.updatedAt)
99
+ : undefined
63
100
  }
64
101
  ]}
65
102
  data={data.users}
@@ -74,6 +111,13 @@ export const Render: FC<{
74
111
  title: 'Email'
75
112
  }
76
113
  ]}
114
+ filterableColumns={[
115
+ {
116
+ id: 'type',
117
+ title: 'Type',
118
+ options: types
119
+ }
120
+ ]}
77
121
  />
78
122
  </Shell>
79
123
  )
@@ -1,8 +1,15 @@
1
+ import { Metadata } from 'next'
1
2
  import { redirect } from 'next/navigation'
2
3
  import { ReactNode } from 'react'
3
4
 
4
5
  import { SiteFooter } from '@/components/site-footer'
5
6
  import { getAuthSession } from '@/lib/auth'
7
+ import { siteConfig } from '@/lib/siteConfig'
8
+
9
+ export const metadata: Metadata = {
10
+ title: 'Login | ' + siteConfig.name,
11
+ description: siteConfig.description
12
+ }
6
13
 
7
14
  const AuthLayout = async ({ children }: { children: ReactNode }) => {
8
15
  const session = await getAuthSession()
@@ -2,7 +2,7 @@ import Link from 'next/link'
2
2
 
3
3
  import { Icons } from '@/components/icons'
4
4
  import { Button } from '@/components/ui/button'
5
- import { UserAuthForm } from './user-auth-form'
5
+ import { Render } from './render'
6
6
 
7
7
  const LoginPage = () => {
8
8
  return (
@@ -23,7 +23,7 @@ const LoginPage = () => {
23
23
  Enter your email to sign in to your account
24
24
  </p>
25
25
  </div>
26
- <UserAuthForm />
26
+ <Render />
27
27
  <p className="px-8 text-center text-sm text-muted-foreground">
28
28
  By clicking continue, you agree to our{' '}
29
29
  <Link
@@ -0,0 +1,146 @@
1
+ 'use client'
2
+
3
+ import { zodResolver } from '@hookform/resolvers/zod'
4
+ import { signIn } from 'next-auth/react'
5
+ import { useSearchParams } from 'next/navigation'
6
+ import { FC, HTMLAttributes, useState } from 'react'
7
+ import { useForm } from 'react-hook-form'
8
+ import { toast } from 'sonner'
9
+ import { z } from 'zod'
10
+
11
+ import { Icons } from '@/components/icons'
12
+ import { Button } from '@/components/ui/button'
13
+ import { Input } from '@/components/ui/input'
14
+ import { Label } from '@/components/ui/label'
15
+ import { cn } from '@/lib/cn'
16
+
17
+ const userAuthSchema = z.object({
18
+ email: z.string().email()
19
+ })
20
+
21
+ type FormData = z.infer<typeof userAuthSchema>
22
+
23
+ interface RenderProps extends HTMLAttributes<HTMLDivElement> {}
24
+
25
+ export const Render: FC = ({ className, ...props }: RenderProps) => {
26
+ const {
27
+ register,
28
+ handleSubmit,
29
+ formState: { errors }
30
+ } = useForm<FormData>({
31
+ resolver: zodResolver(userAuthSchema)
32
+ })
33
+ const [isLoading, setIsLoading] = useState(false)
34
+ const [isGoogleLoading, setIsGoogleLoading] = useState(false)
35
+ const [isGithubLoading, setIsGithubLoading] = useState(false)
36
+
37
+ const searchParams = useSearchParams()
38
+
39
+ async function onSubmit(data: FormData) {
40
+ setIsLoading(true)
41
+
42
+ const signInResult = await signIn('email', {
43
+ email: data.email.toLowerCase(),
44
+ redirect: false,
45
+ callbackUrl: searchParams?.get('from') || '/dashboard'
46
+ })
47
+
48
+ setIsLoading(false)
49
+
50
+ if (!signInResult?.ok) {
51
+ return toast('Something went wrong.', {
52
+ description: 'Your sign in request failed. Please try again.'
53
+ })
54
+ }
55
+
56
+ return toast('Check your email', {
57
+ description: 'We sent you a login link. Be sure to check your spam too.'
58
+ })
59
+ }
60
+
61
+ return (
62
+ <div className={cn('grid gap-6', className)} {...props}>
63
+ <form onSubmit={handleSubmit(onSubmit)}>
64
+ <div className="grid gap-2">
65
+ <div className="grid gap-1">
66
+ <Label className="sr-only" htmlFor="email">
67
+ Email
68
+ </Label>
69
+ <Input
70
+ id="email"
71
+ placeholder="name@example.com"
72
+ type="email"
73
+ autoCapitalize="none"
74
+ autoComplete="email"
75
+ autoCorrect="off"
76
+ disabled={isLoading}
77
+ {...register('email')}
78
+ />
79
+ {errors?.email && (
80
+ <p className="px-1 text-xs text-red-600">
81
+ {errors.email.message}
82
+ </p>
83
+ )}
84
+ </div>
85
+ <Button
86
+ disabled={isLoading || isGoogleLoading || isGithubLoading}
87
+ type="submit"
88
+ >
89
+ {isLoading && (
90
+ <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
91
+ )}
92
+ Sign in with Email
93
+ </Button>
94
+ </div>
95
+ </form>
96
+ <div className="relative">
97
+ <div className="absolute inset-0 flex items-center">
98
+ <span className="w-full border-t" />
99
+ </div>
100
+ <div className="relative flex justify-center text-xs uppercase">
101
+ <span className="bg-background px-2 text-muted-foreground">
102
+ Or continue with
103
+ </span>
104
+ </div>
105
+ </div>
106
+ <div className="grid gap-2">
107
+ <Button
108
+ variant="outline"
109
+ onClick={async () => {
110
+ setIsGoogleLoading(true)
111
+ await signIn('google', {
112
+ callbackUrl: searchParams?.get('from') || '/dashboard'
113
+ })
114
+ setIsGoogleLoading(false)
115
+ }}
116
+ disabled={isLoading || isGoogleLoading || isGithubLoading}
117
+ >
118
+ {isGoogleLoading ? (
119
+ <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
120
+ ) : (
121
+ <Icons.google className="mr-2 h-4 w-4" />
122
+ )}
123
+ Google
124
+ </Button>
125
+ <Button
126
+ variant="outline"
127
+ onClick={async () => {
128
+ setIsGithubLoading(true)
129
+ await signIn('github', {
130
+ callbackUrl: searchParams?.get('from') || '/dashboard'
131
+ })
132
+ setIsGithubLoading(false)
133
+ }}
134
+ disabled={isLoading || isGoogleLoading || isGithubLoading}
135
+ >
136
+ {isGithubLoading ? (
137
+ <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
138
+ ) : (
139
+ <Icons.github className="mr-2 h-4 w-4" />
140
+ )}
141
+ Github
142
+ </Button>
143
+ </div>
144
+ </div>
145
+ )
146
+ }
@@ -1,3 +1,4 @@
1
+ import Link from 'next/link'
1
2
  import { ReactNode } from 'react'
2
3
 
3
4
  import { Icons } from '@/components/icons'
@@ -5,7 +6,6 @@ import { MainNav } from '@/components/main-nav'
5
6
  import { SiteFooter } from '@/components/site-footer'
6
7
  import { Button } from '@/components/ui/button'
7
8
  import { getAuthSession } from '@/lib/auth'
8
- import Link from 'next/link'
9
9
 
10
10
  const Layout = async ({ children }: { children: ReactNode }) => {
11
11
  const session = await getAuthSession()
@@ -13,9 +13,10 @@ const links = [
13
13
  'Learn about TypeScript and how it helps you write better JavaScript.'
14
14
  },
15
15
  {
16
- href: 'https://prisma.io',
17
- title: 'Prisma',
18
- description: 'Prisma is a next-generation ORM for Node.js and TypeScript.'
16
+ href: 'https://orm.drizzle.team',
17
+ title: 'Drizzle',
18
+ description:
19
+ 'Lightweight and performant TypeScript ORM with developer experience in mind.'
19
20
  },
20
21
  {
21
22
  href: 'https://next-auth.js.org',
@@ -50,14 +51,14 @@ const links = [
50
51
  const Home = () => {
51
52
  return (
52
53
  <div className="flex min-h-[80vh] flex-col items-center justify-between">
53
- <div className="max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
54
- <p className="flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
54
+ <div className="w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
55
+ <p className="flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
55
56
  Get started by editing&nbsp;
56
57
  <code className="font-mono font-bold">src/app/page.tsx</code>
57
58
  </p>
58
59
  </div>
59
60
 
60
- <div className="mt-12 flex place-items-center before:absolute before:h-[300px] before:w-[240px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[120px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px] z-[-1]">
61
+ <div className="before:bg-gradient-radial after:bg-gradient-conic z-[-1] mt-12 flex place-items-center before:absolute before:h-[300px] before:w-[240px] before:-translate-x-1/2 before:rounded-full before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[120px] after:translate-x-1/3 after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px]">
61
62
  <Image
62
63
  className="dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
63
64
  src="/next.svg"
@@ -68,7 +69,7 @@ const Home = () => {
68
69
  />
69
70
  </div>
70
71
 
71
- <div className="my-8 grid text-center lg:max-w-5xl lg:w-full lg:grid-cols-4 lg:text-left gap-2">
72
+ <div className="my-8 grid gap-2 text-center lg:w-full lg:max-w-5xl lg:grid-cols-4 lg:text-left">
72
73
  {links.map((l, i) => (
73
74
  <a
74
75
  key={i}
Binary file
@@ -1,34 +1,31 @@
1
- import { Analytics } from '@vercel/analytics/react'
2
- import { SpeedInsights } from '@vercel/speed-insights/next'
3
- import { Metadata } from 'next'
4
- import { Inter } from 'next/font/google'
5
- import { ReactNode } from 'react'
1
+ import { GeistSans } from 'geist/font/sans'
2
+ import type { Metadata } from 'next'
3
+ import { ThemeProvider } from 'next-themes'
4
+ import NextTopLoader from 'nextjs-toploader'
6
5
 
7
- import { ThemeProvider } from '@/components/theme-provider'
8
- import { Toaster } from '@/components/ui/toaster'
6
+ import { Toaster } from '@/components/ui/sonner'
9
7
  import { siteConfig } from '@/lib/siteConfig'
10
- import '@/styles/globals.css'
11
-
12
- const inter = Inter({ subsets: ['latin'] })
8
+ import './globals.css'
13
9
 
14
10
  export const metadata: Metadata = {
15
11
  title: siteConfig.name,
16
12
  description: siteConfig.description
17
13
  }
18
14
 
19
- const RootLayout = ({ children }: { children: ReactNode }) => {
15
+ export default function RootLayout({
16
+ children
17
+ }: Readonly<{
18
+ children: React.ReactNode
19
+ }>) {
20
20
  return (
21
- <html lang="en" suppressHydrationWarning>
22
- <body className={inter.className}>
21
+ <html lang="en" className={GeistSans.className} suppressHydrationWarning>
22
+ <body className="min-h-screen bg-background font-sans antialiased">
23
23
  <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
24
+ <NextTopLoader color="#777" />
24
25
  {children}
25
- <Analytics />
26
- <SpeedInsights />
27
26
  <Toaster />
28
27
  </ThemeProvider>
29
28
  </body>
30
29
  </html>
31
30
  )
32
31
  }
33
-
34
- export default RootLayout
@@ -23,6 +23,7 @@ import {
23
23
  export const AppNavCommand: FC<{
24
24
  modules: {
25
25
  task: boolean
26
+ root: boolean
26
27
  }
27
28
  }> = ({ modules }) => {
28
29
  const [open, setOpen] = useState(false)
@@ -71,6 +72,7 @@ export const AppNavCommand: FC<{
71
72
  className="relative w-36 justify-start text-sm text-muted-foreground lg:w-64"
72
73
  onClick={() => setOpen(true)}
73
74
  >
75
+ <Icons.terminal className="mr-2 h-4 w-4" />
74
76
  <span className="truncate">Command Menu</span>
75
77
  <kbd className="pointer-events-none absolute right-2 top-2 hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 lg:flex">
76
78
  <span className="text-xs">⌘</span>K
@@ -102,6 +104,22 @@ export const AppNavCommand: FC<{
102
104
  </CommandItem>
103
105
  ) : undefined}
104
106
  </CommandGroup>
107
+ {modules.root ? (
108
+ <CommandGroup heading="Root Actions">
109
+ <CommandItem
110
+ onSelect={() => runCommand(() => router.push('/users'))}
111
+ >
112
+ <Icons.mixer className="mr-2 h-4 w-4" />
113
+ Users
114
+ </CommandItem>
115
+ <CommandItem
116
+ onSelect={() => runCommand(() => router.push('/roles'))}
117
+ >
118
+ <Icons.user className="mr-2 h-4 w-4" />
119
+ Roles
120
+ </CommandItem>
121
+ </CommandGroup>
122
+ ) : undefined}
105
123
  <CommandGroup heading="Account">
106
124
  <CommandItem
107
125
  onSelect={() => runCommand(() => router.push('/settings'))}
@@ -35,8 +35,8 @@ export function DataTableColumnHeader<TData, TValue>({
35
35
  column.getIsSorted() === 'desc'
36
36
  ? `Sorted descending. Click to sort ascending.`
37
37
  : column.getIsSorted() === 'asc'
38
- ? `Sorted ascending. Click to sort descending.`
39
- : `Not sorted. Click to sort ascending.`
38
+ ? `Sorted ascending. Click to sort descending.`
39
+ : `Not sorted. Click to sort ascending.`
40
40
  }
41
41
  variant="ghost"
42
42
  size="sm"
@@ -1,3 +1,4 @@
1
+ import { cn } from '@/lib/cn'
1
2
  import {
2
3
  ArrowDown,
3
4
  ArrowRight,
@@ -10,10 +11,8 @@ import {
10
11
  ChevronsLeft,
11
12
  ChevronsRight,
12
13
  ClipboardList,
13
- Download,
14
14
  Edit,
15
15
  EyeOff,
16
- Github,
17
16
  Laptop,
18
17
  LayoutDashboard,
19
18
  Loader2,
@@ -28,6 +27,7 @@ import {
28
27
  Settings,
29
28
  Settings2,
30
29
  Sun,
30
+ Terminal,
31
31
  Trash,
32
32
  User,
33
33
  X
@@ -42,7 +42,24 @@ export const Icons = {
42
42
  laptop: Laptop,
43
43
  chevronLeft: ChevronLeft,
44
44
  arrowRight: ArrowRight,
45
- github: Github,
45
+ github: ({ ...props }: LucideProps) => (
46
+ <svg
47
+ aria-hidden="true"
48
+ focusable="false"
49
+ data-prefix="fab"
50
+ data-icon="github"
51
+ role="img"
52
+ xmlns="http://www.w3.org/2000/svg"
53
+ viewBox="0 0 496 512"
54
+ {...props}
55
+ className={cn('fill-current', props.className)}
56
+ >
57
+ <path
58
+ fill="currentColor"
59
+ d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"
60
+ ></path>
61
+ </svg>
62
+ ),
46
63
  google: ({ ...props }: LucideProps) => (
47
64
  <svg
48
65
  aria-hidden="true"
@@ -53,6 +70,7 @@ export const Icons = {
53
70
  xmlns="http://www.w3.org/2000/svg"
54
71
  viewBox="0 0 30 30"
55
72
  {...props}
73
+ className={cn('fill-current', props.className)}
56
74
  >
57
75
  <path d="M 15.003906 3 C 8.3749062 3 3 8.373 3 15 C 3 21.627 8.3749062 27 15.003906 27 C 25.013906 27 27.269078 17.707 26.330078 13 L 25 13 L 22.732422 13 L 15 13 L 15 17 L 22.738281 17 C 21.848702 20.448251 18.725955 23 15 23 C 10.582 23 7 19.418 7 15 C 7 10.582 10.582 7 15 7 C 17.009 7 18.839141 7.74575 20.244141 8.96875 L 23.085938 6.1289062 C 20.951937 4.1849063 18.116906 3 15.003906 3 z"></path>
58
76
  </svg>
@@ -67,7 +85,6 @@ export const Icons = {
67
85
  edit: Edit,
68
86
  save: Save,
69
87
  delete: Trash,
70
- download: Download,
71
88
  dashboard: LayoutDashboard,
72
89
  task: ClipboardList,
73
90
  arrowUp: ArrowUp,
@@ -79,5 +96,6 @@ export const Icons = {
79
96
  chevronsLeft: ChevronsLeft,
80
97
  chevronsRight: ChevronsRight,
81
98
  chevronRight: ChevronRight,
82
- mixer: Settings2
99
+ mixer: Settings2,
100
+ terminal: Terminal
83
101
  }
@@ -15,10 +15,10 @@ const AlertDialogPortal = AlertDialogPrimitive.Portal
15
15
  const AlertDialogOverlay = React.forwardRef<
16
16
  React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
17
17
  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
18
- >(({ className, children, ...props }, ref) => (
18
+ >(({ className, ...props }, ref) => (
19
19
  <AlertDialogPrimitive.Overlay
20
20
  className={cn(
21
- 'fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
21
+ 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
22
22
  className
23
23
  )}
24
24
  {...props}
@@ -36,7 +36,7 @@ const AlertDialogContent = React.forwardRef<
36
36
  <AlertDialogPrimitive.Content
37
37
  ref={ref}
38
38
  className={cn(
39
- 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full',
39
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
40
40
  className
41
41
  )}
42
42
  {...props}