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,176 @@
1
+ import * as LabelPrimitive from '@radix-ui/react-label'
2
+ import { Slot } from '@radix-ui/react-slot'
3
+ import * as React from 'react'
4
+ import {
5
+ Controller,
6
+ ControllerProps,
7
+ FieldPath,
8
+ FieldValues,
9
+ FormProvider,
10
+ useFormContext
11
+ } from 'react-hook-form'
12
+
13
+ import { Label } from '@/components/ui/label'
14
+ import { cn } from '@/lib/cn'
15
+
16
+ const Form = FormProvider
17
+
18
+ type FormFieldContextValue<
19
+ TFieldValues extends FieldValues = FieldValues,
20
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
21
+ > = {
22
+ name: TName
23
+ }
24
+
25
+ const FormFieldContext = React.createContext<FormFieldContextValue>(
26
+ {} as FormFieldContextValue
27
+ )
28
+
29
+ const FormField = <
30
+ TFieldValues extends FieldValues = FieldValues,
31
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
32
+ >({
33
+ ...props
34
+ }: ControllerProps<TFieldValues, TName>) => {
35
+ return (
36
+ <FormFieldContext.Provider value={{ name: props.name }}>
37
+ <Controller {...props} />
38
+ </FormFieldContext.Provider>
39
+ )
40
+ }
41
+
42
+ const useFormField = () => {
43
+ const fieldContext = React.useContext(FormFieldContext)
44
+ const itemContext = React.useContext(FormItemContext)
45
+ const { getFieldState, formState } = useFormContext()
46
+
47
+ const fieldState = getFieldState(fieldContext.name, formState)
48
+
49
+ if (!fieldContext) {
50
+ throw new Error('useFormField should be used within <FormField>')
51
+ }
52
+
53
+ const { id } = itemContext
54
+
55
+ return {
56
+ id,
57
+ name: fieldContext.name,
58
+ formItemId: `${id}-form-item`,
59
+ formDescriptionId: `${id}-form-item-description`,
60
+ formMessageId: `${id}-form-item-message`,
61
+ ...fieldState
62
+ }
63
+ }
64
+
65
+ type FormItemContextValue = {
66
+ id: string
67
+ }
68
+
69
+ const FormItemContext = React.createContext<FormItemContextValue>(
70
+ {} as FormItemContextValue
71
+ )
72
+
73
+ const FormItem = React.forwardRef<
74
+ HTMLDivElement,
75
+ React.HTMLAttributes<HTMLDivElement>
76
+ >(({ className, ...props }, ref) => {
77
+ const id = React.useId()
78
+
79
+ return (
80
+ <FormItemContext.Provider value={{ id }}>
81
+ <div ref={ref} className={cn('space-y-2', className)} {...props} />
82
+ </FormItemContext.Provider>
83
+ )
84
+ })
85
+ FormItem.displayName = 'FormItem'
86
+
87
+ const FormLabel = React.forwardRef<
88
+ React.ElementRef<typeof LabelPrimitive.Root>,
89
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
90
+ >(({ className, ...props }, ref) => {
91
+ const { error, formItemId } = useFormField()
92
+
93
+ return (
94
+ <Label
95
+ ref={ref}
96
+ className={cn(error && 'text-destructive', className)}
97
+ htmlFor={formItemId}
98
+ {...props}
99
+ />
100
+ )
101
+ })
102
+ FormLabel.displayName = 'FormLabel'
103
+
104
+ const FormControl = React.forwardRef<
105
+ React.ElementRef<typeof Slot>,
106
+ React.ComponentPropsWithoutRef<typeof Slot>
107
+ >(({ ...props }, ref) => {
108
+ const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
109
+
110
+ return (
111
+ <Slot
112
+ ref={ref}
113
+ id={formItemId}
114
+ aria-describedby={
115
+ !error
116
+ ? `${formDescriptionId}`
117
+ : `${formDescriptionId} ${formMessageId}`
118
+ }
119
+ aria-invalid={!!error}
120
+ {...props}
121
+ />
122
+ )
123
+ })
124
+ FormControl.displayName = 'FormControl'
125
+
126
+ const FormDescription = React.forwardRef<
127
+ HTMLParagraphElement,
128
+ React.HTMLAttributes<HTMLParagraphElement>
129
+ >(({ className, ...props }, ref) => {
130
+ const { formDescriptionId } = useFormField()
131
+
132
+ return (
133
+ <p
134
+ ref={ref}
135
+ id={formDescriptionId}
136
+ className={cn('text-sm text-muted-foreground', className)}
137
+ {...props}
138
+ />
139
+ )
140
+ })
141
+ FormDescription.displayName = 'FormDescription'
142
+
143
+ const FormMessage = React.forwardRef<
144
+ HTMLParagraphElement,
145
+ React.HTMLAttributes<HTMLParagraphElement>
146
+ >(({ className, children, ...props }, ref) => {
147
+ const { error, formMessageId } = useFormField()
148
+ const body = error ? String(error?.message) : children
149
+
150
+ if (!body) {
151
+ return null
152
+ }
153
+
154
+ return (
155
+ <p
156
+ ref={ref}
157
+ id={formMessageId}
158
+ className={cn('text-sm font-medium text-destructive', className)}
159
+ {...props}
160
+ >
161
+ {body}
162
+ </p>
163
+ )
164
+ })
165
+ FormMessage.displayName = 'FormMessage'
166
+
167
+ export {
168
+ Form,
169
+ FormControl,
170
+ FormDescription,
171
+ FormField,
172
+ FormItem,
173
+ FormLabel,
174
+ FormMessage,
175
+ useFormField
176
+ }
@@ -0,0 +1,25 @@
1
+ import * as React from 'react'
2
+
3
+ import { cn } from '@/lib/cn'
4
+
5
+ export interface InputProps
6
+ extends React.InputHTMLAttributes<HTMLInputElement> {}
7
+
8
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
9
+ ({ className, type, ...props }, ref) => {
10
+ return (
11
+ <input
12
+ type={type}
13
+ className={cn(
14
+ 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
15
+ className
16
+ )}
17
+ ref={ref}
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+ )
23
+ Input.displayName = 'Input'
24
+
25
+ export { Input }
@@ -0,0 +1,26 @@
1
+ 'use client'
2
+
3
+ import * as LabelPrimitive from '@radix-ui/react-label'
4
+ import { cva, type VariantProps } from 'class-variance-authority'
5
+ import * as React from 'react'
6
+
7
+ import { cn } from '@/lib/cn'
8
+
9
+ const labelVariants = cva(
10
+ 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
11
+ )
12
+
13
+ const Label = React.forwardRef<
14
+ React.ElementRef<typeof LabelPrimitive.Root>,
15
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
16
+ VariantProps<typeof labelVariants>
17
+ >(({ className, ...props }, ref) => (
18
+ <LabelPrimitive.Root
19
+ ref={ref}
20
+ className={cn(labelVariants(), className)}
21
+ {...props}
22
+ />
23
+ ))
24
+ Label.displayName = LabelPrimitive.Root.displayName
25
+
26
+ export { Label }
@@ -0,0 +1,31 @@
1
+ 'use client'
2
+
3
+ import * as PopoverPrimitive from '@radix-ui/react-popover'
4
+ import * as React from 'react'
5
+
6
+ import { cn } from '@/lib/cn'
7
+
8
+ const Popover = PopoverPrimitive.Root
9
+
10
+ const PopoverTrigger = PopoverPrimitive.Trigger
11
+
12
+ const PopoverContent = React.forwardRef<
13
+ React.ElementRef<typeof PopoverPrimitive.Content>,
14
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
15
+ >(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
16
+ <PopoverPrimitive.Portal>
17
+ <PopoverPrimitive.Content
18
+ ref={ref}
19
+ align={align}
20
+ sideOffset={sideOffset}
21
+ className={cn(
22
+ 'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ </PopoverPrimitive.Portal>
28
+ ))
29
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName
30
+
31
+ export { Popover, PopoverContent, PopoverTrigger }
@@ -0,0 +1,160 @@
1
+ 'use client'
2
+
3
+ import * as SelectPrimitive from '@radix-ui/react-select'
4
+ import { Check, ChevronDown, ChevronUp } from 'lucide-react'
5
+ import * as React from 'react'
6
+
7
+ import { cn } from '@/lib/cn'
8
+
9
+ const Select = SelectPrimitive.Root
10
+
11
+ const SelectGroup = SelectPrimitive.Group
12
+
13
+ const SelectValue = SelectPrimitive.Value
14
+
15
+ const SelectTrigger = React.forwardRef<
16
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
17
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
18
+ >(({ className, children, ...props }, ref) => (
19
+ <SelectPrimitive.Trigger
20
+ ref={ref}
21
+ className={cn(
22
+ 'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
23
+ className
24
+ )}
25
+ {...props}
26
+ >
27
+ {children}
28
+ <SelectPrimitive.Icon asChild>
29
+ <ChevronDown className="h-4 w-4 opacity-50" />
30
+ </SelectPrimitive.Icon>
31
+ </SelectPrimitive.Trigger>
32
+ ))
33
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
34
+
35
+ const SelectScrollUpButton = React.forwardRef<
36
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
37
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
38
+ >(({ className, ...props }, ref) => (
39
+ <SelectPrimitive.ScrollUpButton
40
+ ref={ref}
41
+ className={cn(
42
+ 'flex cursor-default items-center justify-center py-1',
43
+ className
44
+ )}
45
+ {...props}
46
+ >
47
+ <ChevronUp className="h-4 w-4" />
48
+ </SelectPrimitive.ScrollUpButton>
49
+ ))
50
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
51
+
52
+ const SelectScrollDownButton = React.forwardRef<
53
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
54
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
55
+ >(({ className, ...props }, ref) => (
56
+ <SelectPrimitive.ScrollDownButton
57
+ ref={ref}
58
+ className={cn(
59
+ 'flex cursor-default items-center justify-center py-1',
60
+ className
61
+ )}
62
+ {...props}
63
+ >
64
+ <ChevronDown className="h-4 w-4" />
65
+ </SelectPrimitive.ScrollDownButton>
66
+ ))
67
+ SelectScrollDownButton.displayName =
68
+ SelectPrimitive.ScrollDownButton.displayName
69
+
70
+ const SelectContent = React.forwardRef<
71
+ React.ElementRef<typeof SelectPrimitive.Content>,
72
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
73
+ >(({ className, children, position = 'popper', ...props }, ref) => (
74
+ <SelectPrimitive.Portal>
75
+ <SelectPrimitive.Content
76
+ ref={ref}
77
+ className={cn(
78
+ 'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
79
+ position === 'popper' &&
80
+ 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
81
+ className
82
+ )}
83
+ position={position}
84
+ {...props}
85
+ >
86
+ <SelectScrollUpButton />
87
+ <SelectPrimitive.Viewport
88
+ className={cn(
89
+ 'p-1',
90
+ position === 'popper' &&
91
+ 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
92
+ )}
93
+ >
94
+ {children}
95
+ </SelectPrimitive.Viewport>
96
+ <SelectScrollDownButton />
97
+ </SelectPrimitive.Content>
98
+ </SelectPrimitive.Portal>
99
+ ))
100
+ SelectContent.displayName = SelectPrimitive.Content.displayName
101
+
102
+ const SelectLabel = React.forwardRef<
103
+ React.ElementRef<typeof SelectPrimitive.Label>,
104
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
105
+ >(({ className, ...props }, ref) => (
106
+ <SelectPrimitive.Label
107
+ ref={ref}
108
+ className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
109
+ {...props}
110
+ />
111
+ ))
112
+ SelectLabel.displayName = SelectPrimitive.Label.displayName
113
+
114
+ const SelectItem = React.forwardRef<
115
+ React.ElementRef<typeof SelectPrimitive.Item>,
116
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
117
+ >(({ className, children, ...props }, ref) => (
118
+ <SelectPrimitive.Item
119
+ ref={ref}
120
+ className={cn(
121
+ 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
122
+ className
123
+ )}
124
+ {...props}
125
+ >
126
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
127
+ <SelectPrimitive.ItemIndicator>
128
+ <Check className="h-4 w-4" />
129
+ </SelectPrimitive.ItemIndicator>
130
+ </span>
131
+
132
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
133
+ </SelectPrimitive.Item>
134
+ ))
135
+ SelectItem.displayName = SelectPrimitive.Item.displayName
136
+
137
+ const SelectSeparator = React.forwardRef<
138
+ React.ElementRef<typeof SelectPrimitive.Separator>,
139
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
140
+ >(({ className, ...props }, ref) => (
141
+ <SelectPrimitive.Separator
142
+ ref={ref}
143
+ className={cn('-mx-1 my-1 h-px bg-muted', className)}
144
+ {...props}
145
+ />
146
+ ))
147
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName
148
+
149
+ export {
150
+ Select,
151
+ SelectContent,
152
+ SelectGroup,
153
+ SelectItem,
154
+ SelectLabel,
155
+ SelectScrollDownButton,
156
+ SelectScrollUpButton,
157
+ SelectSeparator,
158
+ SelectTrigger,
159
+ SelectValue
160
+ }
@@ -0,0 +1,31 @@
1
+ 'use client'
2
+
3
+ import * as SeparatorPrimitive from '@radix-ui/react-separator'
4
+ import * as React from 'react'
5
+
6
+ import { cn } from '@/lib/cn'
7
+
8
+ const Separator = React.forwardRef<
9
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
11
+ >(
12
+ (
13
+ { className, orientation = 'horizontal', decorative = true, ...props },
14
+ ref
15
+ ) => (
16
+ <SeparatorPrimitive.Root
17
+ ref={ref}
18
+ decorative={decorative}
19
+ orientation={orientation}
20
+ className={cn(
21
+ 'shrink-0 bg-border',
22
+ orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ )
28
+ )
29
+ Separator.displayName = SeparatorPrimitive.Root.displayName
30
+
31
+ export { Separator }
@@ -0,0 +1,15 @@
1
+ import { cn } from '@/lib/cn'
2
+
3
+ function Skeleton({
4
+ className,
5
+ ...props
6
+ }: React.HTMLAttributes<HTMLDivElement>) {
7
+ return (
8
+ <div
9
+ className={cn('animate-pulse rounded-md bg-muted', className)}
10
+ {...props}
11
+ />
12
+ )
13
+ }
14
+
15
+ export { Skeleton }
@@ -0,0 +1,117 @@
1
+ import * as React from 'react'
2
+
3
+ import { cn } from '@/lib/cn'
4
+
5
+ const Table = React.forwardRef<
6
+ HTMLTableElement,
7
+ React.HTMLAttributes<HTMLTableElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div className="relative w-full overflow-auto">
10
+ <table
11
+ ref={ref}
12
+ className={cn('w-full caption-bottom text-sm', className)}
13
+ {...props}
14
+ />
15
+ </div>
16
+ ))
17
+ Table.displayName = 'Table'
18
+
19
+ const TableHeader = React.forwardRef<
20
+ HTMLTableSectionElement,
21
+ React.HTMLAttributes<HTMLTableSectionElement>
22
+ >(({ className, ...props }, ref) => (
23
+ <thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
24
+ ))
25
+ TableHeader.displayName = 'TableHeader'
26
+
27
+ const TableBody = React.forwardRef<
28
+ HTMLTableSectionElement,
29
+ React.HTMLAttributes<HTMLTableSectionElement>
30
+ >(({ className, ...props }, ref) => (
31
+ <tbody
32
+ ref={ref}
33
+ className={cn('[&_tr:last-child]:border-0', className)}
34
+ {...props}
35
+ />
36
+ ))
37
+ TableBody.displayName = 'TableBody'
38
+
39
+ const TableFooter = React.forwardRef<
40
+ HTMLTableSectionElement,
41
+ React.HTMLAttributes<HTMLTableSectionElement>
42
+ >(({ className, ...props }, ref) => (
43
+ <tfoot
44
+ ref={ref}
45
+ className={cn(
46
+ 'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
47
+ className
48
+ )}
49
+ {...props}
50
+ />
51
+ ))
52
+ TableFooter.displayName = 'TableFooter'
53
+
54
+ const TableRow = React.forwardRef<
55
+ HTMLTableRowElement,
56
+ React.HTMLAttributes<HTMLTableRowElement>
57
+ >(({ className, ...props }, ref) => (
58
+ <tr
59
+ ref={ref}
60
+ className={cn(
61
+ 'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
62
+ className
63
+ )}
64
+ {...props}
65
+ />
66
+ ))
67
+ TableRow.displayName = 'TableRow'
68
+
69
+ const TableHead = React.forwardRef<
70
+ HTMLTableCellElement,
71
+ React.ThHTMLAttributes<HTMLTableCellElement>
72
+ >(({ className, ...props }, ref) => (
73
+ <th
74
+ ref={ref}
75
+ className={cn(
76
+ 'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ ))
82
+ TableHead.displayName = 'TableHead'
83
+
84
+ const TableCell = React.forwardRef<
85
+ HTMLTableCellElement,
86
+ React.TdHTMLAttributes<HTMLTableCellElement>
87
+ >(({ className, ...props }, ref) => (
88
+ <td
89
+ ref={ref}
90
+ className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)}
91
+ {...props}
92
+ />
93
+ ))
94
+ TableCell.displayName = 'TableCell'
95
+
96
+ const TableCaption = React.forwardRef<
97
+ HTMLTableCaptionElement,
98
+ React.HTMLAttributes<HTMLTableCaptionElement>
99
+ >(({ className, ...props }, ref) => (
100
+ <caption
101
+ ref={ref}
102
+ className={cn('mt-4 text-sm text-muted-foreground', className)}
103
+ {...props}
104
+ />
105
+ ))
106
+ TableCaption.displayName = 'TableCaption'
107
+
108
+ export {
109
+ Table,
110
+ TableBody,
111
+ TableCaption,
112
+ TableCell,
113
+ TableFooter,
114
+ TableHead,
115
+ TableHeader,
116
+ TableRow
117
+ }
@@ -0,0 +1,60 @@
1
+ 'use client'
2
+
3
+ import { User } from 'next-auth'
4
+ import { signOut } from 'next-auth/react'
5
+ import Link from 'next/link'
6
+ import { FC, HTMLAttributes } from 'react'
7
+
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuSeparator,
13
+ DropdownMenuTrigger
14
+ } from '@/components/ui/dropdown-menu'
15
+ import { UserAvatar } from '@/components/user-avatar'
16
+
17
+ interface UserAccountNavProps extends HTMLAttributes<HTMLDivElement> {
18
+ user: Pick<User, 'name' | 'image' | 'email'>
19
+ }
20
+
21
+ export const UserAccountNav: FC<UserAccountNavProps> = ({ user }) => {
22
+ return (
23
+ <DropdownMenu>
24
+ <DropdownMenuTrigger>
25
+ <UserAvatar
26
+ user={{ name: user.name || null, image: user.image || null }}
27
+ className="h-8 w-8"
28
+ />
29
+ </DropdownMenuTrigger>
30
+ <DropdownMenuContent align="end">
31
+ <div className="flex items-center justify-start gap-2 p-2">
32
+ <div className="flex flex-col space-y-1 leading-none">
33
+ {user.name && <p className="font-medium">{user.name}</p>}
34
+ {user.email && (
35
+ <p className="w-[200px] truncate text-sm text-muted-foreground">
36
+ {user.email}
37
+ </p>
38
+ )}
39
+ </div>
40
+ </div>
41
+ <DropdownMenuSeparator />
42
+ <DropdownMenuItem asChild>
43
+ <Link href="/settings">Settings</Link>
44
+ </DropdownMenuItem>
45
+ <DropdownMenuSeparator />
46
+ <DropdownMenuItem
47
+ className="cursor-pointer"
48
+ onSelect={event => {
49
+ event.preventDefault()
50
+ signOut({
51
+ callbackUrl: `${window.location.origin}/login`
52
+ })
53
+ }}
54
+ >
55
+ Sign out
56
+ </DropdownMenuItem>
57
+ </DropdownMenuContent>
58
+ </DropdownMenu>
59
+ )
60
+ }
@@ -0,0 +1,25 @@
1
+ import { User } from '@prisma/client'
2
+ import { AvatarProps } from '@radix-ui/react-avatar'
3
+ import { FC } from 'react'
4
+
5
+ import { Icons } from '@/components/icons'
6
+ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
7
+
8
+ interface UserAvatarProps extends AvatarProps {
9
+ user: Pick<User, 'image' | 'name'>
10
+ }
11
+
12
+ export const UserAvatar: FC<UserAvatarProps> = ({ user, ...props }) => {
13
+ return (
14
+ <Avatar {...props}>
15
+ {user.image ? (
16
+ <AvatarImage alt="Picture" src={user.image} />
17
+ ) : (
18
+ <AvatarFallback>
19
+ <span className="sr-only">{user.name}</span>
20
+ <Icons.user className="h-4 w-4" />
21
+ </AvatarFallback>
22
+ )}
23
+ </Avatar>
24
+ )
25
+ }