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
@@ -0,0 +1,96 @@
1
+ import Image from 'next/image'
2
+
3
+ const links = [
4
+ {
5
+ href: 'https://nextjs.org',
6
+ title: 'Next.js',
7
+ description: 'Find in-depth information about Next.js features and API.'
8
+ },
9
+ {
10
+ href: 'https://typescriptlang.org',
11
+ title: 'TypeScript',
12
+ description:
13
+ 'Learn about TypeScript and how it helps you write better JavaScript.'
14
+ },
15
+ {
16
+ href: 'https://prisma.io',
17
+ title: 'Prisma',
18
+ description: 'Prisma is a next-generation ORM for Node.js and TypeScript.'
19
+ },
20
+ {
21
+ href: 'https://next-auth.js.org',
22
+ title: 'NextAuth.js',
23
+ description:
24
+ 'Authentication for Next.js featuring sign in with GitHub and more.'
25
+ },
26
+ {
27
+ href: 'https://tailwindcss.com',
28
+ title: 'Tailwind CSS',
29
+ description:
30
+ 'A utility-first CSS framework for rapidly building custom designs.'
31
+ },
32
+ {
33
+ href: 'https://shadcn.com',
34
+ title: 'Shadcn UI',
35
+ description: 'An awesome ui component library with tailwindcss.'
36
+ },
37
+ {
38
+ href: 'https://nextjs.org/docs/app/api-reference/functions/server-actions',
39
+ title: 'Server Actions',
40
+ description: 'Learn about Server Actions and how to use them.'
41
+ },
42
+ {
43
+ href: 'https://vercel.com/new',
44
+ title: 'Vercel',
45
+ description:
46
+ 'Instantly deploy your Next.js site to a shareable URL with Vercel.'
47
+ }
48
+ ]
49
+
50
+ const Home = () => {
51
+ return (
52
+ <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">
55
+ Get started by editing&nbsp;
56
+ <code className="font-mono font-bold">src/app/page.tsx</code>
57
+ </p>
58
+ </div>
59
+
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
+ <Image
62
+ className="dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
63
+ src="/next.svg"
64
+ alt="Next.js Logo"
65
+ width={180}
66
+ height={37}
67
+ priority
68
+ />
69
+ </div>
70
+
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
+ {links.map((l, i) => (
73
+ <a
74
+ key={i}
75
+ href={l.href}
76
+ className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
77
+ target="_blank"
78
+ rel="noopener noreferrer"
79
+ >
80
+ <h2 className="mb-3 text-2xl font-semibold">
81
+ {l.title + ' '}
82
+ <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
83
+ -&gt;
84
+ </span>
85
+ </h2>
86
+ <p className="m-0 max-w-[30ch] text-sm opacity-50">
87
+ {l.description}
88
+ </p>
89
+ </a>
90
+ ))}
91
+ </div>
92
+ </div>
93
+ )
94
+ }
95
+
96
+ export default Home
@@ -0,0 +1,7 @@
1
+ import NextAuth from 'next-auth'
2
+
3
+ import { authOptions } from '@/lib/auth'
4
+
5
+ const handler = NextAuth(authOptions)
6
+
7
+ export { handler as GET, handler as POST }
@@ -0,0 +1,34 @@
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'
6
+
7
+ import { ThemeProvider } from '@/components/theme-provider'
8
+ import { Toaster } from '@/components/ui/toaster'
9
+ import { siteConfig } from '@/lib/siteConfig'
10
+ import '@/styles/globals.css'
11
+
12
+ const inter = Inter({ subsets: ['latin'] })
13
+
14
+ export const metadata: Metadata = {
15
+ title: siteConfig.name,
16
+ description: siteConfig.description
17
+ }
18
+
19
+ const RootLayout = ({ children }: { children: ReactNode }) => {
20
+ return (
21
+ <html lang="en" suppressHydrationWarning>
22
+ <body className={inter.className}>
23
+ <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
24
+ {children}
25
+ <Analytics />
26
+ <SpeedInsights />
27
+ <Toaster />
28
+ </ThemeProvider>
29
+ </body>
30
+ </html>
31
+ )
32
+ }
33
+
34
+ export default RootLayout
@@ -0,0 +1,160 @@
1
+ 'use client'
2
+
3
+ // import debounce from 'lodash/debounce'
4
+ import { signOut } from 'next-auth/react'
5
+ import { useTheme } from 'next-themes'
6
+ import { useRouter } from 'next/navigation'
7
+ import { FC, useCallback, useEffect, useState } from 'react'
8
+
9
+ import { Icons } from '@/components/icons'
10
+ import { Button } from '@/components/ui/button'
11
+ import {
12
+ Command,
13
+ CommandDialog,
14
+ CommandEmpty,
15
+ CommandGroup,
16
+ CommandInput,
17
+ CommandItem,
18
+ CommandList,
19
+ CommandSeparator
20
+ } from '@/components/ui/command'
21
+ // import { doSearch, type SearchResult } from '../app/(app)/actions/doSearch'
22
+
23
+ export const AppNavCommand: FC<{
24
+ modules: {
25
+ task: boolean
26
+ }
27
+ }> = ({ modules }) => {
28
+ const [open, setOpen] = useState(false)
29
+ // const [search, setSearch] = useState('')
30
+ // const [searchResults, setSearchResults] = useState<SearchResult[]>([])
31
+ const { setTheme } = useTheme()
32
+ const router = useRouter()
33
+
34
+ useEffect(() => {
35
+ const down = (e: KeyboardEvent) => {
36
+ if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
37
+ e.preventDefault()
38
+ setOpen(open => !open)
39
+ }
40
+ }
41
+
42
+ document.addEventListener('keydown', down)
43
+ return () => document.removeEventListener('keydown', down)
44
+ }, [])
45
+
46
+ // useEffect(() => {
47
+ // if (!search) {
48
+ // setSearchResults([])
49
+ // return
50
+ // }
51
+ // doSearch(search).then(results => setSearchResults(results))
52
+ // }, [search])
53
+
54
+ const runCommand = useCallback((command: () => unknown) => {
55
+ setOpen(false)
56
+ command()
57
+ }, [])
58
+
59
+ // const debouncedSearch = useMemo(
60
+ // () =>
61
+ // debounce((search: string) => {
62
+ // setSearch(search)
63
+ // }, 500),
64
+ // []
65
+ // )
66
+
67
+ return (
68
+ <>
69
+ <Button
70
+ variant="outline"
71
+ className="relative w-36 justify-start text-sm text-muted-foreground lg:w-64"
72
+ onClick={() => setOpen(true)}
73
+ >
74
+ <span className="truncate">Command Menu</span>
75
+ <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
+ <span className="text-xs">⌘</span>K
77
+ </kbd>
78
+ </Button>
79
+ <CommandDialog open={open} onOpenChange={setOpen}>
80
+ <Command>
81
+ <CommandInput
82
+ placeholder="Search..."
83
+ // onChangeCapture={e => {
84
+ // debouncedSearch((e.target as any)?.value || '')
85
+ // }}
86
+ />
87
+ <CommandList>
88
+ <CommandEmpty>No search results found.</CommandEmpty>
89
+ <CommandGroup heading="Suggestions">
90
+ <CommandItem
91
+ onSelect={() => runCommand(() => router.push('/dashboard'))}
92
+ >
93
+ <Icons.dashboard className="mr-2 h-4 w-4" />
94
+ Dashboard
95
+ </CommandItem>
96
+ {modules.task ? (
97
+ <CommandItem
98
+ onSelect={() => runCommand(() => router.push('/tasks'))}
99
+ >
100
+ <Icons.task className="mr-2 h-4 w-4" />
101
+ Tasks
102
+ </CommandItem>
103
+ ) : undefined}
104
+ </CommandGroup>
105
+ <CommandGroup heading="Account">
106
+ <CommandItem
107
+ onSelect={() => runCommand(() => router.push('/settings'))}
108
+ >
109
+ <Icons.settings className="mr-2 h-4 w-4" />
110
+ Settings
111
+ </CommandItem>
112
+ <CommandItem
113
+ onSelect={() =>
114
+ runCommand(() =>
115
+ signOut({
116
+ callbackUrl: `${window.location.origin}/login`
117
+ })
118
+ )
119
+ }
120
+ >
121
+ <Icons.logout className="mr-2 h-4 w-4" />
122
+ Sign Out
123
+ </CommandItem>
124
+ </CommandGroup>
125
+ <CommandSeparator />
126
+ <CommandGroup heading="Theme">
127
+ <CommandItem onSelect={() => runCommand(() => setTheme('light'))}>
128
+ <Icons.sun className="mr-2 h-4 w-4" />
129
+ Light
130
+ </CommandItem>
131
+ <CommandItem onSelect={() => runCommand(() => setTheme('dark'))}>
132
+ <Icons.moon className="mr-2 h-4 w-4" />
133
+ Dark
134
+ </CommandItem>
135
+ <CommandItem
136
+ onSelect={() => runCommand(() => setTheme('system'))}
137
+ >
138
+ <Icons.laptop className="mr-2 h-4 w-4" />
139
+ System
140
+ </CommandItem>
141
+ </CommandGroup>
142
+ {/* {searchResults.length ? (
143
+ <CommandGroup heading="Search Results">
144
+ {searchResults.map((sr, i) => (
145
+ <CommandItem
146
+ key={i}
147
+ onSelect={() => runCommand(() => router.push(sr.link))}
148
+ value={sr.id}
149
+ >
150
+ {sr.title}
151
+ </CommandItem>
152
+ ))}
153
+ </CommandGroup>
154
+ ) : undefined} */}
155
+ </CommandList>
156
+ </Command>
157
+ </CommandDialog>
158
+ </>
159
+ )
160
+ }
@@ -0,0 +1,43 @@
1
+ 'use client'
2
+
3
+ import { useTheme } from 'next-themes'
4
+ import { FC } from 'react'
5
+
6
+ import { Icons } from '@/components/icons'
7
+ import { Button } from '@/components/ui/button'
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuTrigger
13
+ } from '@/components/ui/dropdown-menu'
14
+
15
+ export const DarkModeToggle: FC = () => {
16
+ const { setTheme } = useTheme()
17
+
18
+ return (
19
+ <DropdownMenu>
20
+ <DropdownMenuTrigger asChild>
21
+ <Button variant="ghost" size="icon">
22
+ <Icons.sun className="rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
23
+ <Icons.moon className="absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
24
+ <span className="sr-only">Toggle theme</span>
25
+ </Button>
26
+ </DropdownMenuTrigger>
27
+ <DropdownMenuContent align="end">
28
+ <DropdownMenuItem onClick={() => setTheme('light')}>
29
+ <Icons.sun className="mr-2 h-4 w-4" />
30
+ <span>Light</span>
31
+ </DropdownMenuItem>
32
+ <DropdownMenuItem onClick={() => setTheme('dark')}>
33
+ <Icons.moon className="mr-2 h-4 w-4" />
34
+ <span>Dark</span>
35
+ </DropdownMenuItem>
36
+ <DropdownMenuItem onClick={() => setTheme('system')}>
37
+ <Icons.laptop className="mr-2 h-4 w-4" />
38
+ <span>System</span>
39
+ </DropdownMenuItem>
40
+ </DropdownMenuContent>
41
+ </DropdownMenu>
42
+ )
43
+ }
@@ -0,0 +1,91 @@
1
+ import { type Column } from '@tanstack/react-table'
2
+
3
+ import { Icons } from '@/components/icons'
4
+ import { Button } from '@/components/ui/button'
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuSeparator,
10
+ DropdownMenuTrigger
11
+ } from '@/components/ui/dropdown-menu'
12
+ import { cn } from '@/lib/cn'
13
+
14
+ interface DataTableColumnHeaderProps<TData, TValue>
15
+ extends React.HTMLAttributes<HTMLDivElement> {
16
+ column: Column<TData, TValue>
17
+ title: string
18
+ }
19
+
20
+ export function DataTableColumnHeader<TData, TValue>({
21
+ column,
22
+ title,
23
+ className
24
+ }: DataTableColumnHeaderProps<TData, TValue>) {
25
+ if (!column.getCanSort()) {
26
+ return <div className={cn(className)}>{title}</div>
27
+ }
28
+
29
+ return (
30
+ <div className={cn('flex items-center space-x-2', className)}>
31
+ <DropdownMenu>
32
+ <DropdownMenuTrigger asChild>
33
+ <Button
34
+ aria-label={
35
+ column.getIsSorted() === 'desc'
36
+ ? `Sorted descending. Click to sort ascending.`
37
+ : column.getIsSorted() === 'asc'
38
+ ? `Sorted ascending. Click to sort descending.`
39
+ : `Not sorted. Click to sort ascending.`
40
+ }
41
+ variant="ghost"
42
+ size="sm"
43
+ className="-ml-3 h-8 data-[state=open]:bg-accent"
44
+ >
45
+ <span>{title}</span>
46
+ {column.getIsSorted() === 'desc' ? (
47
+ <Icons.arrowDown className="ml-2 h-4 w-4" aria-hidden="true" />
48
+ ) : column.getIsSorted() === 'asc' ? (
49
+ <Icons.arrowUp className="ml-2 h-4 w-4" aria-hidden="true" />
50
+ ) : (
51
+ <Icons.arrowUpDown className="ml-2 h-4 w-4" aria-hidden="true" />
52
+ )}
53
+ </Button>
54
+ </DropdownMenuTrigger>
55
+ <DropdownMenuContent align="start">
56
+ <DropdownMenuItem
57
+ aria-label="Sort ascending"
58
+ onClick={() => column.toggleSorting(false)}
59
+ >
60
+ <Icons.arrowUp
61
+ className="mr-2 h-3.5 w-3.5 text-muted-foreground/70"
62
+ aria-hidden="true"
63
+ />
64
+ Asc
65
+ </DropdownMenuItem>
66
+ <DropdownMenuItem
67
+ aria-label="Sort descending"
68
+ onClick={() => column.toggleSorting(true)}
69
+ >
70
+ <Icons.arrowDown
71
+ className="mr-2 h-3.5 w-3.5 text-muted-foreground/70"
72
+ aria-hidden="true"
73
+ />
74
+ Desc
75
+ </DropdownMenuItem>
76
+ <DropdownMenuSeparator />
77
+ <DropdownMenuItem
78
+ aria-label="Hide column"
79
+ onClick={() => column.toggleVisibility(false)}
80
+ >
81
+ <Icons.eyeOff
82
+ className="mr-2 h-3.5 w-3.5 text-muted-foreground/70"
83
+ aria-hidden="true"
84
+ />
85
+ Hide
86
+ </DropdownMenuItem>
87
+ </DropdownMenuContent>
88
+ </DropdownMenu>
89
+ </div>
90
+ )
91
+ }
@@ -0,0 +1,149 @@
1
+ import { type Column } from '@tanstack/react-table'
2
+ import { ComponentType } from 'react'
3
+
4
+ import { Icons } from '@/components/icons'
5
+ import { Badge } from '@/components/ui/badge'
6
+ import { Button } from '@/components/ui/button'
7
+ import {
8
+ Command,
9
+ CommandEmpty,
10
+ CommandGroup,
11
+ CommandInput,
12
+ CommandItem,
13
+ CommandList,
14
+ CommandSeparator
15
+ } from '@/components/ui/command'
16
+ import {
17
+ Popover,
18
+ PopoverContent,
19
+ PopoverTrigger
20
+ } from '@/components/ui/popover'
21
+ import { Separator } from '@/components/ui/separator'
22
+ import { cn } from '@/lib/cn'
23
+
24
+ export type FilterOption = {
25
+ label: string
26
+ value: string
27
+ icon?: ComponentType<{ className?: string }>
28
+ }
29
+
30
+ interface DataTableFacetedFilter<TData, TValue> {
31
+ column?: Column<TData, TValue>
32
+ title?: string
33
+ options: FilterOption[]
34
+ }
35
+
36
+ export function DataTableFacetedFilter<TData, TValue>({
37
+ column,
38
+ title,
39
+ options
40
+ }: DataTableFacetedFilter<TData, TValue>) {
41
+ const selectedValues = new Set(column?.getFilterValue() as string[])
42
+
43
+ return (
44
+ <Popover>
45
+ <PopoverTrigger asChild>
46
+ <Button variant="outline" size="sm" className="h-8 border-dashed">
47
+ <Icons.plusCircle className="mr-2 h-4 w-4" />
48
+ {title}
49
+ {selectedValues?.size > 0 && (
50
+ <>
51
+ <Separator orientation="vertical" className="mx-2 h-4" />
52
+ <Badge
53
+ variant="secondary"
54
+ className="rounded-sm px-1 font-normal lg:hidden"
55
+ >
56
+ {selectedValues.size}
57
+ </Badge>
58
+ <div className="hidden space-x-1 lg:flex">
59
+ {selectedValues.size > 2 ? (
60
+ <Badge
61
+ variant="secondary"
62
+ className="rounded-sm px-1 font-normal"
63
+ >
64
+ {selectedValues.size} selected
65
+ </Badge>
66
+ ) : (
67
+ options
68
+ .filter(option => selectedValues.has(option.value))
69
+ .map(option => (
70
+ <Badge
71
+ variant="secondary"
72
+ key={option.value}
73
+ className="rounded-sm px-1 font-normal"
74
+ >
75
+ {option.label}
76
+ </Badge>
77
+ ))
78
+ )}
79
+ </div>
80
+ </>
81
+ )}
82
+ </Button>
83
+ </PopoverTrigger>
84
+ <PopoverContent className="w-[200px] p-0" align="start">
85
+ <Command>
86
+ <CommandInput placeholder={title} />
87
+ <CommandList>
88
+ <CommandEmpty>No results found.</CommandEmpty>
89
+ <CommandGroup>
90
+ {options.map(option => {
91
+ const isSelected = selectedValues.has(option.value)
92
+ return (
93
+ <CommandItem
94
+ key={option.value}
95
+ onSelect={() => {
96
+ if (isSelected) {
97
+ selectedValues.delete(option.value)
98
+ } else {
99
+ selectedValues.add(option.value)
100
+ }
101
+ const filterValues = Array.from(selectedValues)
102
+ column?.setFilterValue(
103
+ filterValues.length ? filterValues : undefined
104
+ )
105
+ }}
106
+ >
107
+ <div
108
+ className={cn(
109
+ 'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
110
+ isSelected
111
+ ? 'bg-primary text-primary-foreground'
112
+ : 'opacity-50 [&_svg]:invisible'
113
+ )}
114
+ >
115
+ <Icons.check
116
+ className={cn('h-4 w-4')}
117
+ aria-hidden="true"
118
+ />
119
+ </div>
120
+ {option.icon && (
121
+ <option.icon
122
+ className="mr-2 h-4 w-4 text-muted-foreground"
123
+ aria-hidden="true"
124
+ />
125
+ )}
126
+ <span>{option.label}</span>
127
+ </CommandItem>
128
+ )
129
+ })}
130
+ </CommandGroup>
131
+ {selectedValues.size > 0 && (
132
+ <>
133
+ <CommandSeparator />
134
+ <CommandGroup>
135
+ <CommandItem
136
+ onSelect={() => column?.setFilterValue(undefined)}
137
+ className="justify-center text-center"
138
+ >
139
+ Clear filters
140
+ </CommandItem>
141
+ </CommandGroup>
142
+ </>
143
+ )}
144
+ </CommandList>
145
+ </Command>
146
+ </PopoverContent>
147
+ </Popover>
148
+ )
149
+ }
@@ -0,0 +1,77 @@
1
+ import { Skeleton } from '@/components/ui/skeleton'
2
+ import {
3
+ Table,
4
+ TableBody,
5
+ TableCell,
6
+ TableHead,
7
+ TableHeader,
8
+ TableRow
9
+ } from '@/components/ui/table'
10
+
11
+ interface DataTableLoadingProps {
12
+ columnCount: number
13
+ rowCount?: number
14
+ }
15
+
16
+ export function DataTableLoading({
17
+ columnCount,
18
+ rowCount = 10
19
+ }: DataTableLoadingProps) {
20
+ return (
21
+ <div className="w-full space-y-3 overflow-auto">
22
+ <div className="flex w-full items-center justify-between space-x-2 overflow-auto p-1">
23
+ <div className="flex flex-1 items-center space-x-2">
24
+ <Skeleton className="h-7 w-[150px] lg:w-[250px]" />
25
+ <Skeleton className="h-7 w-[70px] border-dashed" />
26
+ </div>
27
+ <Skeleton className="ml-auto hidden h-7 w-[70px] lg:flex" />
28
+ </div>
29
+ <div className="rounded-md border">
30
+ <Table>
31
+ <TableHeader>
32
+ {Array.from({ length: 1 }).map((_, i) => (
33
+ <TableRow key={i} className="hover:bg-transparent">
34
+ {Array.from({ length: columnCount }).map((_, i) => (
35
+ <TableHead key={i}>
36
+ <Skeleton className="h-6 w-full" />
37
+ </TableHead>
38
+ ))}
39
+ </TableRow>
40
+ ))}
41
+ </TableHeader>
42
+ <TableBody>
43
+ {Array.from({ length: rowCount }).map((_, i) => (
44
+ <TableRow key={i} className="hover:bg-transparent">
45
+ {Array.from({ length: columnCount }).map((_, i) => (
46
+ <TableCell key={i}>
47
+ <Skeleton className="h-6 w-full" />
48
+ </TableCell>
49
+ ))}
50
+ </TableRow>
51
+ ))}
52
+ </TableBody>
53
+ </Table>
54
+ </div>
55
+ <div className="flex w-full flex-col items-center justify-between gap-4 overflow-auto px-2 py-1 sm:flex-row sm:gap-8">
56
+ <div className="flex-1">
57
+ <Skeleton className="h-8 w-40" />
58
+ </div>
59
+ <div className="flex flex-col items-center gap-4 sm:flex-row sm:gap-6 lg:gap-8">
60
+ <div className="flex items-center space-x-2">
61
+ <Skeleton className="h-8 w-24" />
62
+ <Skeleton className="h-8 w-[70px]" />
63
+ </div>
64
+ <div className="flex w-[100px] items-center justify-center text-sm font-medium">
65
+ <Skeleton className="h-8 w-20" />
66
+ </div>
67
+ <div className="flex items-center space-x-2">
68
+ <Skeleton className="hidden h-8 w-8 lg:block" />
69
+ <Skeleton className="h-8 w-8" />
70
+ <Skeleton className="h-8 w-8" />
71
+ <Skeleton className="hidden h-8 w-8 lg:block" />
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ )
77
+ }