create-zhx-monorepo 0.2.2 → 2.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 (349) hide show
  1. package/README.md +1 -1
  2. package/bin/index.js +0 -0
  3. package/package.json +3 -3
  4. package/templates/monorepo-starter/apps/dashboard/components.json +20 -0
  5. package/templates/monorepo-starter/apps/dashboard/eslint.config.js +1 -0
  6. package/templates/monorepo-starter/apps/dashboard/next-env.d.ts +6 -0
  7. package/templates/monorepo-starter/apps/dashboard/next.config.mjs +30 -0
  8. package/templates/monorepo-starter/apps/dashboard/package.json +43 -0
  9. package/templates/monorepo-starter/apps/dashboard/postcss.config.mjs +1 -0
  10. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/admin/users/[id]/edit/page.tsx +8 -0
  11. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/admin/users/[id]/page.tsx +104 -0
  12. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/admin/users/new/page.tsx +7 -0
  13. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/admin/users/page.tsx +88 -0
  14. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/dashboard/data.json +614 -0
  15. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/dashboard/page.tsx +21 -0
  16. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/layout.tsx +40 -0
  17. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/media/page.tsx +7 -0
  18. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/users/account/page.tsx +75 -0
  19. package/templates/monorepo-starter/apps/dashboard/src/app/auth/[type]/page.tsx +19 -0
  20. package/templates/monorepo-starter/apps/dashboard/src/app/auth/layout.tsx +11 -0
  21. package/templates/monorepo-starter/apps/dashboard/src/app/auth/verify/page.tsx +11 -0
  22. package/templates/monorepo-starter/apps/dashboard/src/app/favicon.ico +0 -0
  23. package/templates/monorepo-starter/apps/dashboard/src/app/layout.tsx +28 -0
  24. package/templates/monorepo-starter/apps/dashboard/src/components/forms/AuthForm.tsx +345 -0
  25. package/templates/monorepo-starter/apps/dashboard/src/components/forms/CUUserForm.tsx +223 -0
  26. package/templates/monorepo-starter/apps/dashboard/src/components/forms/MediaForm.tsx +35 -0
  27. package/templates/monorepo-starter/apps/dashboard/src/components/forms/SocialAuthField.tsx +56 -0
  28. package/templates/monorepo-starter/apps/dashboard/src/components/layout/AppSidebar.tsx +141 -0
  29. package/templates/monorepo-starter/apps/dashboard/src/components/layout/DashboardChart.tsx +293 -0
  30. package/templates/monorepo-starter/apps/dashboard/src/components/layout/DashboardStats.tsx +104 -0
  31. package/templates/monorepo-starter/apps/dashboard/src/components/layout/Header.tsx +21 -0
  32. package/templates/monorepo-starter/apps/dashboard/src/components/layout/NavMain.tsx +99 -0
  33. package/templates/monorepo-starter/apps/dashboard/src/components/media/LibraryPopup.tsx +25 -0
  34. package/templates/monorepo-starter/apps/dashboard/src/components/media/MediaCard.tsx +175 -0
  35. package/templates/monorepo-starter/apps/dashboard/src/components/media/MediaDetails.tsx +58 -0
  36. package/templates/monorepo-starter/apps/dashboard/src/components/media/MediaGrid.tsx +51 -0
  37. package/templates/monorepo-starter/apps/dashboard/src/components/media/MediaUploader.tsx +182 -0
  38. package/templates/monorepo-starter/apps/dashboard/src/components/media/index.tsx +96 -0
  39. package/templates/monorepo-starter/apps/dashboard/src/components/shared/DateWrapper.tsx +43 -0
  40. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericArrayField.tsx +189 -0
  41. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericCUForm.tsx +124 -0
  42. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericDetailsPage.tsx +291 -0
  43. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericFormTable.tsx +189 -0
  44. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericTable.tsx +244 -0
  45. package/templates/monorepo-starter/apps/dashboard/src/components/shared/ListPage.tsx +134 -0
  46. package/templates/monorepo-starter/apps/dashboard/src/components/shared/Pagination.tsx +120 -0
  47. package/templates/monorepo-starter/apps/dashboard/src/components/shared/SearchToolbar.tsx +133 -0
  48. package/templates/monorepo-starter/apps/dashboard/src/components/shared/ThemeSwitch.tsx +41 -0
  49. package/templates/monorepo-starter/apps/dashboard/src/components/shared/VerifyAuthPage.tsx +109 -0
  50. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/CUFormSkeleton.tsx +31 -0
  51. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/DashboardSke1eton.tsx +135 -0
  52. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/DetailsPageSkeleton.tsx +64 -0
  53. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/RootLayoutSkeleton.tsx +106 -0
  54. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/TableSkeleton.tsx +77 -0
  55. package/templates/monorepo-starter/apps/dashboard/src/components/user/UserAccountSection.tsx +530 -0
  56. package/templates/monorepo-starter/apps/dashboard/src/components/user/UserCard.tsx +54 -0
  57. package/templates/monorepo-starter/apps/dashboard/src/components/user/UserProfileSection.tsx +239 -0
  58. package/templates/monorepo-starter/apps/dashboard/src/components/user/UserSessions.tsx +154 -0
  59. package/templates/monorepo-starter/apps/dashboard/src/hooks/admin.ts +100 -0
  60. package/templates/monorepo-starter/apps/dashboard/src/hooks/auth.ts +59 -0
  61. package/templates/monorepo-starter/apps/dashboard/src/hooks/crud.ts +275 -0
  62. package/templates/monorepo-starter/apps/dashboard/src/hooks/lookup.ts +38 -0
  63. package/templates/monorepo-starter/apps/dashboard/src/hooks/media.ts +34 -0
  64. package/templates/monorepo-starter/apps/dashboard/src/hooks/user.ts +58 -0
  65. package/templates/monorepo-starter/apps/dashboard/src/lib/constants.ts +61 -0
  66. package/templates/monorepo-starter/apps/dashboard/src/providers/index.tsx +15 -0
  67. package/templates/monorepo-starter/apps/dashboard/src/providers/media-library.tsx +48 -0
  68. package/templates/monorepo-starter/apps/dashboard/tsconfig.json +20 -0
  69. package/templates/monorepo-starter/apps/web/components.json +20 -20
  70. package/templates/monorepo-starter/apps/web/eslint.config.js +1 -0
  71. package/templates/monorepo-starter/apps/web/next-env.d.ts +6 -6
  72. package/templates/monorepo-starter/apps/web/next.config.mjs +20 -6
  73. package/templates/monorepo-starter/apps/web/package.json +36 -31
  74. package/templates/monorepo-starter/apps/web/postcss.config.mjs +1 -1
  75. package/templates/monorepo-starter/apps/web/src/app/(root)/layout.tsx +2 -2
  76. package/templates/monorepo-starter/apps/web/src/app/favicon.ico +0 -0
  77. package/templates/monorepo-starter/apps/web/src/app/layout.tsx +13 -9
  78. package/templates/monorepo-starter/apps/web/src/components/Footer.tsx +5 -7
  79. package/templates/monorepo-starter/apps/web/src/components/Header.tsx +5 -9
  80. package/templates/monorepo-starter/apps/web/tsconfig.json +20 -20
  81. package/templates/monorepo-starter/eslint.config.mjs +1 -1
  82. package/templates/monorepo-starter/gitignore +13 -4
  83. package/templates/monorepo-starter/package.json +49 -23
  84. package/templates/monorepo-starter/packages/config/eslint/next.mjs +18 -0
  85. package/templates/monorepo-starter/packages/config/eslint/node.mjs +44 -0
  86. package/templates/monorepo-starter/packages/config/eslint/react.mjs +68 -0
  87. package/templates/monorepo-starter/packages/config/package.json +23 -32
  88. package/templates/monorepo-starter/packages/config/typescript/node.json +25 -0
  89. package/templates/monorepo-starter/packages/config/typescript/react.json +20 -3
  90. package/templates/monorepo-starter/packages/contracts/eslint.config.mjs +1 -0
  91. package/templates/monorepo-starter/packages/contracts/package.json +51 -0
  92. package/templates/monorepo-starter/packages/contracts/src/admin/dto.ts +6 -0
  93. package/templates/monorepo-starter/packages/contracts/src/admin/index.ts +2 -0
  94. package/templates/monorepo-starter/packages/contracts/src/admin/schema.ts +28 -0
  95. package/templates/monorepo-starter/packages/contracts/src/admin/types.d.ts +27 -0
  96. package/templates/monorepo-starter/packages/contracts/src/auth/dto.ts +24 -0
  97. package/templates/monorepo-starter/packages/contracts/src/auth/index.ts +2 -0
  98. package/templates/monorepo-starter/packages/contracts/src/auth/schema.ts +35 -0
  99. package/templates/monorepo-starter/packages/contracts/src/auth/types.d.ts +73 -0
  100. package/templates/monorepo-starter/packages/contracts/src/lib/enums.ts +55 -0
  101. package/templates/monorepo-starter/packages/contracts/src/lib/index.ts +4 -0
  102. package/templates/monorepo-starter/packages/contracts/src/lib/prisma.ts +2 -0
  103. package/templates/monorepo-starter/packages/contracts/src/lib/schema.ts +61 -0
  104. package/templates/monorepo-starter/packages/contracts/src/lib/types.d.ts +149 -0
  105. package/templates/monorepo-starter/packages/contracts/src/lib/utils.ts +8 -0
  106. package/templates/monorepo-starter/packages/contracts/src/media/dto.ts +12 -0
  107. package/templates/monorepo-starter/packages/contracts/src/media/index.ts +2 -0
  108. package/templates/monorepo-starter/packages/contracts/src/media/schema.ts +27 -0
  109. package/templates/monorepo-starter/packages/contracts/src/media/types.d.ts +36 -0
  110. package/templates/monorepo-starter/packages/contracts/src/notification/dto.ts +6 -0
  111. package/templates/monorepo-starter/packages/contracts/src/notification/index.ts +2 -0
  112. package/templates/monorepo-starter/packages/contracts/src/notification/schema.ts +7 -0
  113. package/templates/monorepo-starter/packages/contracts/src/notification/types.d.ts +22 -0
  114. package/templates/monorepo-starter/packages/contracts/src/user/dto.ts +4 -0
  115. package/templates/monorepo-starter/packages/contracts/src/user/index.ts +2 -0
  116. package/templates/monorepo-starter/packages/contracts/src/user/schema.ts +16 -0
  117. package/templates/monorepo-starter/packages/contracts/src/user/types.d.ts +36 -0
  118. package/templates/monorepo-starter/packages/contracts/tsconfig.json +12 -0
  119. package/templates/monorepo-starter/packages/contracts/tsup.config.ts +20 -0
  120. package/templates/monorepo-starter/packages/sdk/eslint.config.mjs +1 -0
  121. package/templates/monorepo-starter/packages/sdk/package.json +31 -0
  122. package/templates/monorepo-starter/packages/sdk/src/admin/index.ts +31 -0
  123. package/templates/monorepo-starter/packages/sdk/src/auth/index.ts +71 -0
  124. package/templates/monorepo-starter/packages/sdk/src/lib/api-client.ts +71 -0
  125. package/templates/monorepo-starter/packages/sdk/src/lib/index.ts +2 -0
  126. package/templates/monorepo-starter/packages/sdk/src/lib/types.ts +40 -0
  127. package/templates/monorepo-starter/packages/sdk/src/media/index.ts +27 -0
  128. package/templates/monorepo-starter/packages/sdk/src/notification/index.ts +10 -0
  129. package/templates/monorepo-starter/packages/sdk/src/public/index.ts +6 -0
  130. package/templates/monorepo-starter/packages/sdk/src/user/index.ts +7 -0
  131. package/templates/monorepo-starter/packages/sdk/tsconfig.json +12 -0
  132. package/templates/monorepo-starter/packages/shared/eslint.config.mjs +1 -0
  133. package/templates/monorepo-starter/packages/shared/package.json +36 -0
  134. package/templates/monorepo-starter/packages/shared/src/constants/app.ts +4 -0
  135. package/templates/monorepo-starter/packages/shared/src/constants/index.ts +1 -0
  136. package/templates/monorepo-starter/packages/shared/src/utils/date.ts +137 -0
  137. package/templates/monorepo-starter/packages/shared/src/utils/identifier.ts +11 -0
  138. package/templates/monorepo-starter/packages/shared/src/utils/index.ts +2 -0
  139. package/templates/monorepo-starter/packages/shared/tsconfig.json +12 -0
  140. package/templates/monorepo-starter/packages/shared/tsup.config.ts +16 -0
  141. package/templates/monorepo-starter/packages/templates/eslint.config.mjs +1 -0
  142. package/templates/monorepo-starter/packages/templates/package.json +38 -0
  143. package/templates/monorepo-starter/packages/templates/src/email/auth/disableMfa.tsx +40 -0
  144. package/templates/monorepo-starter/packages/templates/src/email/auth/index.ts +10 -0
  145. package/templates/monorepo-starter/packages/templates/src/email/auth/resetPassword.tsx +41 -0
  146. package/templates/monorepo-starter/packages/templates/src/email/auth/securityAlert.tsx +21 -0
  147. package/templates/monorepo-starter/packages/templates/src/email/auth/setPassword.tsx +42 -0
  148. package/templates/monorepo-starter/packages/templates/src/email/auth/signin.tsx +19 -0
  149. package/templates/monorepo-starter/packages/templates/src/email/auth/signup.tsx +24 -0
  150. package/templates/monorepo-starter/packages/templates/src/email/auth/updateIdentifier.tsx +79 -0
  151. package/templates/monorepo-starter/packages/templates/src/email/auth/updateMfa.tsx +40 -0
  152. package/templates/monorepo-starter/packages/templates/src/email/auth/verifyIdentifier.tsx +42 -0
  153. package/templates/monorepo-starter/packages/templates/src/email/auth/verifyMfa.tsx +23 -0
  154. package/templates/monorepo-starter/packages/templates/src/email/components/actionBlock.tsx +39 -0
  155. package/templates/monorepo-starter/packages/templates/src/email/components/footer.tsx +0 -0
  156. package/templates/monorepo-starter/packages/templates/src/email/components/greeting.tsx +11 -0
  157. package/templates/monorepo-starter/packages/templates/src/email/components/header.tsx +15 -0
  158. package/templates/monorepo-starter/packages/templates/src/email/components/layout.tsx +33 -0
  159. package/templates/monorepo-starter/packages/templates/src/email/components/numericCode.tsx +16 -0
  160. package/templates/monorepo-starter/packages/templates/src/email/index.ts +2 -0
  161. package/templates/monorepo-starter/packages/templates/src/email/service/accountReactivated.tsx +31 -0
  162. package/templates/monorepo-starter/packages/templates/src/email/service/accountSuspended.tsx +37 -0
  163. package/templates/monorepo-starter/packages/templates/src/email/service/index.ts +2 -0
  164. package/templates/monorepo-starter/packages/templates/src/email/test.tsx +141 -0
  165. package/templates/monorepo-starter/packages/templates/src/index.ts +1 -0
  166. package/templates/monorepo-starter/packages/templates/src/lib/constants.ts +22 -0
  167. package/templates/monorepo-starter/packages/templates/src/lib/utils.ts +19 -0
  168. package/templates/monorepo-starter/packages/templates/src/types/global.d.ts +37 -0
  169. package/templates/monorepo-starter/packages/templates/tsconfig.json +13 -0
  170. package/templates/monorepo-starter/packages/templates/tsup.config.ts +23 -0
  171. package/templates/monorepo-starter/packages/ui/components.json +1 -1
  172. package/templates/monorepo-starter/packages/ui/eslint.config.mjs +1 -4
  173. package/templates/monorepo-starter/packages/ui/package.json +81 -38
  174. package/templates/monorepo-starter/packages/ui/src/components/accordion.tsx +66 -0
  175. package/templates/monorepo-starter/packages/ui/src/components/airport-search-field.tsx +203 -0
  176. package/templates/monorepo-starter/packages/ui/src/components/alert-dialog.tsx +157 -0
  177. package/templates/monorepo-starter/packages/ui/src/components/alert.tsx +66 -0
  178. package/templates/monorepo-starter/packages/ui/src/components/aspect-ratio.tsx +11 -0
  179. package/templates/monorepo-starter/packages/ui/src/components/avatar.tsx +53 -0
  180. package/templates/monorepo-starter/packages/ui/src/components/badge.tsx +46 -0
  181. package/templates/monorepo-starter/packages/ui/src/components/breadcrumb.tsx +109 -0
  182. package/templates/monorepo-starter/packages/ui/src/components/button-group.tsx +83 -0
  183. package/templates/monorepo-starter/packages/ui/src/components/button.tsx +2 -1
  184. package/templates/monorepo-starter/packages/ui/src/components/calendar.tsx +220 -0
  185. package/templates/monorepo-starter/packages/ui/src/components/card.tsx +92 -0
  186. package/templates/monorepo-starter/packages/ui/src/components/carousel.tsx +241 -0
  187. package/templates/monorepo-starter/packages/ui/src/components/chart.tsx +357 -0
  188. package/templates/monorepo-starter/packages/ui/src/components/checkbox.tsx +32 -0
  189. package/templates/monorepo-starter/packages/ui/src/components/collapsible.tsx +33 -0
  190. package/templates/monorepo-starter/packages/ui/src/components/combobox-field.tsx +181 -0
  191. package/templates/monorepo-starter/packages/ui/src/components/combobox.tsx +309 -0
  192. package/templates/monorepo-starter/packages/ui/src/components/command.tsx +184 -0
  193. package/templates/monorepo-starter/packages/ui/src/components/context-menu.tsx +252 -0
  194. package/templates/monorepo-starter/packages/ui/src/components/date-field.tsx +65 -0
  195. package/templates/monorepo-starter/packages/ui/src/components/dialog.tsx +143 -0
  196. package/templates/monorepo-starter/packages/ui/src/components/direction.tsx +22 -0
  197. package/templates/monorepo-starter/packages/ui/src/components/drawer.tsx +135 -0
  198. package/templates/monorepo-starter/packages/ui/src/components/dropdown-menu.tsx +257 -0
  199. package/templates/monorepo-starter/packages/ui/src/components/empty.tsx +104 -0
  200. package/templates/monorepo-starter/packages/ui/src/components/field.tsx +248 -0
  201. package/templates/monorepo-starter/packages/ui/src/components/form.tsx +197 -0
  202. package/templates/monorepo-starter/packages/ui/src/components/hover-card.tsx +44 -0
  203. package/templates/monorepo-starter/packages/ui/src/components/input-field.tsx +116 -0
  204. package/templates/monorepo-starter/packages/ui/src/components/input-group.tsx +170 -0
  205. package/templates/monorepo-starter/packages/ui/src/components/input-otp.tsx +77 -0
  206. package/templates/monorepo-starter/packages/ui/src/components/input.tsx +44 -0
  207. package/templates/monorepo-starter/packages/ui/src/components/item.tsx +193 -0
  208. package/templates/monorepo-starter/packages/ui/src/components/kbd.tsx +28 -0
  209. package/templates/monorepo-starter/packages/ui/src/components/label.tsx +24 -0
  210. package/templates/monorepo-starter/packages/ui/src/components/menubar.tsx +276 -0
  211. package/templates/monorepo-starter/packages/ui/src/components/native-select.tsx +53 -0
  212. package/templates/monorepo-starter/packages/ui/src/components/navigation-menu.tsx +168 -0
  213. package/templates/monorepo-starter/packages/ui/src/components/otp-modal.tsx +125 -0
  214. package/templates/monorepo-starter/packages/ui/src/components/pagination.tsx +127 -0
  215. package/templates/monorepo-starter/packages/ui/src/components/popover.tsx +48 -0
  216. package/templates/monorepo-starter/packages/ui/src/components/progress.tsx +31 -0
  217. package/templates/monorepo-starter/packages/ui/src/components/radio-group.tsx +45 -0
  218. package/templates/monorepo-starter/packages/ui/src/components/resizable.tsx +53 -0
  219. package/templates/monorepo-starter/packages/ui/src/components/scroll-area.tsx +58 -0
  220. package/templates/monorepo-starter/packages/ui/src/components/select-field.tsx +165 -0
  221. package/templates/monorepo-starter/packages/ui/src/components/select.tsx +190 -0
  222. package/templates/monorepo-starter/packages/ui/src/components/separator.tsx +28 -0
  223. package/templates/monorepo-starter/packages/ui/src/components/sheet.tsx +139 -0
  224. package/templates/monorepo-starter/packages/ui/src/components/sidebar.tsx +728 -0
  225. package/templates/monorepo-starter/packages/ui/src/components/skeleton.tsx +13 -0
  226. package/templates/monorepo-starter/packages/ui/src/components/slider.tsx +63 -0
  227. package/templates/monorepo-starter/packages/ui/src/components/sonner.tsx +57 -0
  228. package/templates/monorepo-starter/packages/ui/src/components/spinner.tsx +16 -0
  229. package/templates/monorepo-starter/packages/ui/src/components/switch-field.tsx +22 -0
  230. package/templates/monorepo-starter/packages/ui/src/components/switch.tsx +35 -0
  231. package/templates/monorepo-starter/packages/ui/src/components/table.tsx +120 -0
  232. package/templates/monorepo-starter/packages/ui/src/components/tabs.tsx +66 -0
  233. package/templates/monorepo-starter/packages/ui/src/components/textarea.tsx +18 -0
  234. package/templates/monorepo-starter/packages/ui/src/components/toggle-group.tsx +83 -0
  235. package/templates/monorepo-starter/packages/ui/src/components/toggle.tsx +47 -0
  236. package/templates/monorepo-starter/packages/ui/src/components/tooltip.tsx +61 -0
  237. package/templates/monorepo-starter/packages/ui/src/hooks/use-confirm.ts +12 -0
  238. package/templates/monorepo-starter/packages/ui/src/hooks/use-dialog.ts +12 -0
  239. package/templates/monorepo-starter/packages/ui/src/hooks/use-mobile.ts +21 -0
  240. package/templates/monorepo-starter/packages/ui/src/lib/utils.ts +38 -3
  241. package/templates/monorepo-starter/packages/ui/src/provider/confirm-dialog.tsx +99 -0
  242. package/templates/monorepo-starter/packages/ui/src/provider/dialog.tsx +62 -0
  243. package/templates/monorepo-starter/packages/ui/src/provider/index.tsx +30 -0
  244. package/templates/monorepo-starter/packages/ui/src/provider/react-query.tsx +11 -0
  245. package/templates/monorepo-starter/{apps/web/src/providers → packages/ui/src/provider}/theme.tsx +5 -7
  246. package/templates/monorepo-starter/packages/ui/src/styles/globals.css +109 -61
  247. package/templates/monorepo-starter/packages/ui/src/types/airports-data.d.ts +21 -0
  248. package/templates/monorepo-starter/packages/ui/tsconfig.json +1 -3
  249. package/templates/monorepo-starter/pnpm-lock.yaml +17765 -12441
  250. package/templates/monorepo-starter/pnpm-workspace.yaml +3 -16
  251. package/templates/monorepo-starter/server/.env.example +7 -5
  252. package/templates/monorepo-starter/server/eslint.config.mjs +1 -4
  253. package/templates/monorepo-starter/server/package.json +129 -97
  254. package/templates/monorepo-starter/server/prisma/schema.prisma +170 -92
  255. package/templates/monorepo-starter/server/prisma/seed-functions.ts +0 -0
  256. package/templates/monorepo-starter/server/prisma/seed.ts +0 -0
  257. package/templates/monorepo-starter/server/prisma.config.ts +9 -9
  258. package/templates/monorepo-starter/server/scripts/admin-bootstrap.ts +61 -0
  259. package/templates/monorepo-starter/server/scripts/gen-module.sh +20 -0
  260. package/templates/monorepo-starter/server/scripts/gen-soft-delete.ts +52 -0
  261. package/templates/monorepo-starter/server/src/app.module.ts +35 -28
  262. package/templates/monorepo-starter/server/src/lib/constants/index.ts +59 -0
  263. package/templates/monorepo-starter/server/src/lib/decorators/boolean-query.decorator.ts +18 -0
  264. package/templates/monorepo-starter/server/src/lib/decorators/logger.decorator.ts +1 -1
  265. package/templates/monorepo-starter/server/src/lib/decorators/oauth.decorator.ts +7 -0
  266. package/templates/monorepo-starter/server/src/lib/decorators/user.decorator.ts +10 -0
  267. package/templates/monorepo-starter/server/src/lib/filters/exceptions.filter.ts +169 -24
  268. package/templates/monorepo-starter/server/src/lib/guards/auth.guard.ts +29 -65
  269. package/templates/monorepo-starter/server/src/lib/guards/oauth.guard.ts +28 -0
  270. package/templates/monorepo-starter/server/src/lib/interceptors/response.interceptor.ts +42 -5
  271. package/templates/monorepo-starter/server/src/lib/middleware/cache.middleware.ts +24 -0
  272. package/templates/monorepo-starter/server/src/lib/schemas/env.schema.ts +25 -5
  273. package/templates/monorepo-starter/server/src/lib/utils/client.utils.ts +95 -0
  274. package/templates/monorepo-starter/server/src/lib/utils/cookie.util.ts +4 -3
  275. package/templates/monorepo-starter/server/src/main.ts +20 -10
  276. package/templates/monorepo-starter/server/src/modules/admin/admin.controller.ts +54 -0
  277. package/templates/monorepo-starter/server/src/modules/admin/admin.module.ts +13 -0
  278. package/templates/monorepo-starter/server/src/modules/admin/admin.service.ts +134 -0
  279. package/templates/monorepo-starter/server/src/modules/auth/auth.controller.ts +60 -18
  280. package/templates/monorepo-starter/server/src/modules/auth/auth.module.ts +3 -2
  281. package/templates/monorepo-starter/server/src/modules/auth/auth.service.ts +354 -307
  282. package/templates/monorepo-starter/server/src/modules/auth/oauth.controller.ts +50 -28
  283. package/templates/monorepo-starter/server/src/modules/auth/oauth.service.ts +59 -34
  284. package/templates/monorepo-starter/server/src/modules/auth/otp.service.ts +29 -36
  285. package/templates/monorepo-starter/server/src/modules/cache/cache.module.ts +28 -0
  286. package/templates/monorepo-starter/server/src/modules/cache/cache.service.ts +33 -0
  287. package/templates/monorepo-starter/server/src/modules/env/env.service.ts +4 -3
  288. package/templates/monorepo-starter/server/src/modules/logger/logger.module.ts +1 -0
  289. package/templates/monorepo-starter/server/src/modules/logger/logger.service.ts +1 -1
  290. package/templates/monorepo-starter/server/src/modules/logger/winston.config.ts +15 -1
  291. package/templates/monorepo-starter/server/src/modules/media/cloudinary.provider.ts +14 -0
  292. package/templates/monorepo-starter/server/src/modules/media/cloudinary.service.ts +62 -0
  293. package/templates/monorepo-starter/server/src/modules/media/media.controller.ts +72 -0
  294. package/templates/monorepo-starter/server/src/modules/media/media.module.ts +13 -0
  295. package/templates/monorepo-starter/server/src/modules/media/media.service.ts +146 -0
  296. package/templates/monorepo-starter/server/src/modules/notification/email.service.ts +30 -0
  297. package/templates/monorepo-starter/server/src/modules/notification/messaging.service.ts +50 -0
  298. package/templates/monorepo-starter/server/src/modules/notification/notification.controller.ts +44 -0
  299. package/templates/monorepo-starter/server/src/modules/notification/notification.module.ts +7 -3
  300. package/templates/monorepo-starter/server/src/modules/notification/notification.service.ts +121 -78
  301. package/templates/monorepo-starter/server/src/modules/notification/push.service.ts +92 -0
  302. package/templates/monorepo-starter/server/src/modules/prisma/prisma.extension.ts +84 -56
  303. package/templates/monorepo-starter/server/src/modules/prisma/prisma.service.ts +10 -9
  304. package/templates/monorepo-starter/server/src/modules/prisma/soft-delete.models.ts +7 -0
  305. package/templates/monorepo-starter/server/src/modules/public/public.controller.ts +3 -2
  306. package/templates/monorepo-starter/server/src/modules/public/public.module.ts +5 -4
  307. package/templates/monorepo-starter/server/src/modules/public/public.service.ts +7 -4
  308. package/templates/monorepo-starter/server/src/modules/scheduler/cleanup.service.ts +15 -7
  309. package/templates/monorepo-starter/server/src/modules/scheduler/scheduler.module.ts +2 -0
  310. package/templates/monorepo-starter/server/src/modules/token/token.module.ts +3 -4
  311. package/templates/monorepo-starter/server/src/modules/token/token.service.ts +205 -76
  312. package/templates/monorepo-starter/server/src/modules/user/user.controller.ts +21 -0
  313. package/templates/monorepo-starter/server/src/modules/user/user.module.ts +12 -0
  314. package/templates/monorepo-starter/server/src/modules/user/user.service.ts +38 -0
  315. package/templates/monorepo-starter/server/src/types/express.d.ts +6 -2
  316. package/templates/monorepo-starter/server/src/types/index.ts +5 -0
  317. package/templates/monorepo-starter/server/tsconfig.json +17 -12
  318. package/templates/monorepo-starter/server/tsup.config.ts +7 -2
  319. package/templates/monorepo-starter/tsconfig.json +1 -1
  320. package/templates/monorepo-starter/turbo.json +1 -1
  321. package/templates/monorepo-starter/apps/web/eslint.config.mjs +0 -4
  322. package/templates/monorepo-starter/apps/web/src/app/(auth)/layout.tsx +0 -7
  323. package/templates/monorepo-starter/apps/web/src/app/globals.css +0 -1
  324. package/templates/monorepo-starter/apps/web/src/components/ThemeSwitch.tsx +0 -34
  325. package/templates/monorepo-starter/apps/web/src/providers/index.tsx +0 -10
  326. package/templates/monorepo-starter/apps/web/src/types/index.d.ts +0 -16
  327. package/templates/monorepo-starter/packages/config/eslint/base.js +0 -31
  328. package/templates/monorepo-starter/packages/config/eslint/nest.js +0 -26
  329. package/templates/monorepo-starter/packages/config/eslint/next.js +0 -39
  330. package/templates/monorepo-starter/packages/config/eslint/react.js +0 -30
  331. package/templates/monorepo-starter/packages/config/typescript/base.json +0 -31
  332. package/templates/monorepo-starter/packages/config/typescript/nest.json +0 -14
  333. package/templates/monorepo-starter/packages/config/typescript/next.json +0 -10
  334. package/templates/monorepo-starter/packages/ui/tsconfig.lint.json +0 -8
  335. package/templates/monorepo-starter/server/README.md +0 -63
  336. package/templates/monorepo-starter/server/scripts/generate.sh +0 -14
  337. package/templates/monorepo-starter/server/src/lib/dto/auth.dto.ts +0 -64
  338. package/templates/monorepo-starter/server/src/lib/dto/security-setting.dto.ts +0 -21
  339. package/templates/monorepo-starter/server/src/lib/pipes/validation.pipe.ts +0 -7
  340. package/templates/monorepo-starter/server/src/lib/templates/notification.templates.ts +0 -243
  341. package/templates/monorepo-starter/server/src/lib/utils/general.util.ts +0 -24
  342. package/templates/monorepo-starter/server/src/modules/auth/role.service.ts +0 -99
  343. package/templates/monorepo-starter/server/src/modules/auth/security-setting.service.ts +0 -102
  344. package/templates/monorepo-starter/server/src/modules/template/template.module.ts +0 -8
  345. package/templates/monorepo-starter/server/src/modules/template/template.service.ts +0 -33
  346. package/templates/monorepo-starter/server/src/types/index.d.ts +0 -13
  347. package/templates/monorepo-starter/server/test/app.e2e-spec.ts +0 -25
  348. package/templates/monorepo-starter/server/test/jest-e2e.json +0 -9
  349. /package/templates/monorepo-starter/{packages/ui/src/hooks → apps/dashboard/public}/.gitkeep +0 -0
@@ -0,0 +1,357 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as RechartsPrimitive from "recharts";
5
+
6
+ import { cn } from "@workspace/ui/lib/utils";
7
+
8
+ // Format: { THEME_NAME: CSS_SELECTOR }
9
+ const THEMES = { light: "", dark: ".dark" } as const;
10
+
11
+ export type ChartConfig = {
12
+ [k in string]: {
13
+ label?: React.ReactNode;
14
+ icon?: React.ComponentType;
15
+ } & (
16
+ | { color?: string; theme?: never }
17
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
18
+ );
19
+ };
20
+
21
+ type ChartContextProps = {
22
+ config: ChartConfig;
23
+ };
24
+
25
+ const ChartContext = React.createContext<ChartContextProps | null>(null);
26
+
27
+ function useChart() {
28
+ const context = React.useContext(ChartContext);
29
+
30
+ if (!context) {
31
+ throw new Error("useChart must be used within a <ChartContainer />");
32
+ }
33
+
34
+ return context;
35
+ }
36
+
37
+ function ChartContainer({
38
+ id,
39
+ className,
40
+ children,
41
+ config,
42
+ ...props
43
+ }: React.ComponentProps<"div"> & {
44
+ config: ChartConfig;
45
+ children: React.ComponentProps<
46
+ typeof RechartsPrimitive.ResponsiveContainer
47
+ >["children"];
48
+ }) {
49
+ const uniqueId = React.useId();
50
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
51
+
52
+ return (
53
+ <ChartContext.Provider value={{ config }}>
54
+ <div
55
+ data-slot="chart"
56
+ data-chart={chartId}
57
+ className={cn(
58
+ "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
59
+ className,
60
+ )}
61
+ {...props}
62
+ >
63
+ <ChartStyle id={chartId} config={config} />
64
+ <RechartsPrimitive.ResponsiveContainer>
65
+ {children}
66
+ </RechartsPrimitive.ResponsiveContainer>
67
+ </div>
68
+ </ChartContext.Provider>
69
+ );
70
+ }
71
+
72
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
73
+ const colorConfig = Object.entries(config).filter(
74
+ ([, config]) => config.theme || config.color,
75
+ );
76
+
77
+ if (!colorConfig.length) {
78
+ return null;
79
+ }
80
+
81
+ return (
82
+ <style
83
+ dangerouslySetInnerHTML={{
84
+ __html: Object.entries(THEMES)
85
+ .map(
86
+ ([theme, prefix]) => `
87
+ ${prefix} [data-chart=${id}] {
88
+ ${colorConfig
89
+ .map(([key, itemConfig]) => {
90
+ const color =
91
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
92
+ itemConfig.color;
93
+ return color ? ` --color-${key}: ${color};` : null;
94
+ })
95
+ .join("\n")}
96
+ }
97
+ `,
98
+ )
99
+ .join("\n"),
100
+ }}
101
+ />
102
+ );
103
+ };
104
+
105
+ const ChartTooltip = RechartsPrimitive.Tooltip;
106
+
107
+ function ChartTooltipContent({
108
+ active,
109
+ payload,
110
+ className,
111
+ indicator = "dot",
112
+ hideLabel = false,
113
+ hideIndicator = false,
114
+ label,
115
+ labelFormatter,
116
+ labelClassName,
117
+ formatter,
118
+ color,
119
+ nameKey,
120
+ labelKey,
121
+ }: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
122
+ React.ComponentProps<"div"> & {
123
+ hideLabel?: boolean;
124
+ hideIndicator?: boolean;
125
+ indicator?: "line" | "dot" | "dashed";
126
+ nameKey?: string;
127
+ labelKey?: string;
128
+ }) {
129
+ const { config } = useChart();
130
+
131
+ const tooltipLabel = React.useMemo(() => {
132
+ if (hideLabel || !payload?.length) {
133
+ return null;
134
+ }
135
+
136
+ const [item] = payload;
137
+ const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
138
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
139
+ const value =
140
+ !labelKey && typeof label === "string"
141
+ ? config[label as keyof typeof config]?.label || label
142
+ : itemConfig?.label;
143
+
144
+ if (labelFormatter) {
145
+ return (
146
+ <div className={cn("font-medium", labelClassName)}>
147
+ {labelFormatter(value, payload)}
148
+ </div>
149
+ );
150
+ }
151
+
152
+ if (!value) {
153
+ return null;
154
+ }
155
+
156
+ return <div className={cn("font-medium", labelClassName)}>{value}</div>;
157
+ }, [
158
+ label,
159
+ labelFormatter,
160
+ payload,
161
+ hideLabel,
162
+ labelClassName,
163
+ config,
164
+ labelKey,
165
+ ]);
166
+
167
+ if (!active || !payload?.length) {
168
+ return null;
169
+ }
170
+
171
+ const nestLabel = payload.length === 1 && indicator !== "dot";
172
+
173
+ return (
174
+ <div
175
+ className={cn(
176
+ "border-border/50 bg-background grid min-w-32 items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
177
+ className,
178
+ )}
179
+ >
180
+ {!nestLabel ? tooltipLabel : null}
181
+ <div className="grid gap-1.5">
182
+ {payload
183
+ .filter((item) => item.type !== "none")
184
+ .map((item, index) => {
185
+ const key = `${nameKey || item.name || item.dataKey || "value"}`;
186
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
187
+ const indicatorColor = color || item.payload.fill || item.color;
188
+
189
+ return (
190
+ <div
191
+ key={item.dataKey}
192
+ className={cn(
193
+ "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
194
+ indicator === "dot" && "items-center",
195
+ )}
196
+ >
197
+ {formatter && item?.value !== undefined && item.name ? (
198
+ formatter(item.value, item.name, item, index, item.payload)
199
+ ) : (
200
+ <>
201
+ {itemConfig?.icon ? (
202
+ <itemConfig.icon />
203
+ ) : (
204
+ !hideIndicator && (
205
+ <div
206
+ className={cn(
207
+ "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
208
+ {
209
+ "h-2.5 w-2.5": indicator === "dot",
210
+ "w-1": indicator === "line",
211
+ "w-0 border-[1.5px] border-dashed bg-transparent":
212
+ indicator === "dashed",
213
+ "my-0.5": nestLabel && indicator === "dashed",
214
+ },
215
+ )}
216
+ style={
217
+ {
218
+ "--color-bg": indicatorColor,
219
+ "--color-border": indicatorColor,
220
+ } as React.CSSProperties
221
+ }
222
+ />
223
+ )
224
+ )}
225
+ <div
226
+ className={cn(
227
+ "flex flex-1 justify-between leading-none",
228
+ nestLabel ? "items-end" : "items-center",
229
+ )}
230
+ >
231
+ <div className="grid gap-1.5">
232
+ {nestLabel ? tooltipLabel : null}
233
+ <span className="text-muted-foreground">
234
+ {itemConfig?.label || item.name}
235
+ </span>
236
+ </div>
237
+ {item.value && (
238
+ <span className="text-foreground font-mono font-medium tabular-nums">
239
+ {item.value.toLocaleString()}
240
+ </span>
241
+ )}
242
+ </div>
243
+ </>
244
+ )}
245
+ </div>
246
+ );
247
+ })}
248
+ </div>
249
+ </div>
250
+ );
251
+ }
252
+
253
+ const ChartLegend = RechartsPrimitive.Legend;
254
+
255
+ function ChartLegendContent({
256
+ className,
257
+ hideIcon = false,
258
+ payload,
259
+ verticalAlign = "bottom",
260
+ nameKey,
261
+ }: React.ComponentProps<"div"> &
262
+ Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
263
+ hideIcon?: boolean;
264
+ nameKey?: string;
265
+ }) {
266
+ const { config } = useChart();
267
+
268
+ if (!payload?.length) {
269
+ return null;
270
+ }
271
+
272
+ return (
273
+ <div
274
+ className={cn(
275
+ "flex items-center justify-center gap-4",
276
+ verticalAlign === "top" ? "pb-3" : "pt-3",
277
+ className,
278
+ )}
279
+ >
280
+ {payload
281
+ .filter((item) => item.type !== "none")
282
+ .map((item) => {
283
+ const key = `${nameKey || item.dataKey || "value"}`;
284
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
285
+
286
+ return (
287
+ <div
288
+ key={item.value}
289
+ className={cn(
290
+ "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3",
291
+ )}
292
+ >
293
+ {itemConfig?.icon && !hideIcon ? (
294
+ <itemConfig.icon />
295
+ ) : (
296
+ <div
297
+ className="h-2 w-2 shrink-0 rounded-[2px]"
298
+ style={{
299
+ backgroundColor: item.color,
300
+ }}
301
+ />
302
+ )}
303
+ {itemConfig?.label}
304
+ </div>
305
+ );
306
+ })}
307
+ </div>
308
+ );
309
+ }
310
+
311
+ // Helper to extract item config from a payload.
312
+ function getPayloadConfigFromPayload(
313
+ config: ChartConfig,
314
+ payload: unknown,
315
+ key: string,
316
+ ) {
317
+ if (typeof payload !== "object" || payload === null) {
318
+ return undefined;
319
+ }
320
+
321
+ const payloadPayload =
322
+ "payload" in payload &&
323
+ typeof payload.payload === "object" &&
324
+ payload.payload !== null
325
+ ? payload.payload
326
+ : undefined;
327
+
328
+ let configLabelKey: string = key;
329
+
330
+ if (
331
+ key in payload &&
332
+ typeof payload[key as keyof typeof payload] === "string"
333
+ ) {
334
+ configLabelKey = payload[key as keyof typeof payload] as string;
335
+ } else if (
336
+ payloadPayload &&
337
+ key in payloadPayload &&
338
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
339
+ ) {
340
+ configLabelKey = payloadPayload[
341
+ key as keyof typeof payloadPayload
342
+ ] as string;
343
+ }
344
+
345
+ return configLabelKey in config
346
+ ? config[configLabelKey]
347
+ : config[key as keyof typeof config];
348
+ }
349
+
350
+ export {
351
+ ChartContainer,
352
+ ChartTooltip,
353
+ ChartTooltipContent,
354
+ ChartLegend,
355
+ ChartLegendContent,
356
+ ChartStyle,
357
+ };
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
5
+ import { CheckIcon } from "lucide-react";
6
+
7
+ import { cn } from "@workspace/ui/lib/utils";
8
+
9
+ function Checkbox({
10
+ className,
11
+ ...props
12
+ }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
13
+ return (
14
+ <CheckboxPrimitive.Root
15
+ data-slot="checkbox"
16
+ className={cn(
17
+ "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4! shrink-0 rounded-xs border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
18
+ className,
19
+ )}
20
+ {...props}
21
+ >
22
+ <CheckboxPrimitive.Indicator
23
+ data-slot="checkbox-indicator"
24
+ className="grid place-content-center text-current transition-none"
25
+ >
26
+ <CheckIcon className="size-3.5" />
27
+ </CheckboxPrimitive.Indicator>
28
+ </CheckboxPrimitive.Root>
29
+ );
30
+ }
31
+
32
+ export { Checkbox };
@@ -0,0 +1,33 @@
1
+ "use client"
2
+
3
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4
+
5
+ function Collapsible({
6
+ ...props
7
+ }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
8
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
9
+ }
10
+
11
+ function CollapsibleTrigger({
12
+ ...props
13
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
14
+ return (
15
+ <CollapsiblePrimitive.CollapsibleTrigger
16
+ data-slot="collapsible-trigger"
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+
22
+ function CollapsibleContent({
23
+ ...props
24
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
25
+ return (
26
+ <CollapsiblePrimitive.CollapsibleContent
27
+ data-slot="collapsible-content"
28
+ {...props}
29
+ />
30
+ )
31
+ }
32
+
33
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent }
@@ -0,0 +1,181 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Check, ChevronsUpDown } from "lucide-react";
5
+ import { cn } from "../lib/utils";
6
+
7
+ import { Button } from "./button";
8
+ import {
9
+ Command,
10
+ CommandEmpty,
11
+ CommandGroup,
12
+ CommandInput,
13
+ CommandItem,
14
+ CommandList,
15
+ } from "./command";
16
+ import { Popover, PopoverContent, PopoverTrigger } from "./popover";
17
+ import { FormField, type BaseFieldProps } from "./form";
18
+ import type { ApiException } from "@workspace/sdk";
19
+
20
+ export type ComboboxOption<TData, TValue = TData[keyof TData] & string> = {
21
+ id?: string;
22
+ key: TValue & string;
23
+ value: TValue;
24
+ label: string | React.ReactNode;
25
+ content: string | React.ReactNode;
26
+ };
27
+
28
+ export type UseEntitiesResult<TKey extends string, TData> = {
29
+ data?: BaseQueryResponse & {
30
+ [K in TKey]: TData[];
31
+ };
32
+ isFetching: boolean;
33
+ fetchError: ApiException | null;
34
+ };
35
+
36
+ interface ComboboxFieldProps<
37
+ TFormData,
38
+ TData,
39
+ TQuery,
40
+ TKey extends string,
41
+ > extends BaseFieldProps<TFormData> {
42
+ dataKey: TKey;
43
+ useQuery: (args?: TQuery) => UseEntitiesResult<TKey, TData>;
44
+ queryArgs?: TQuery;
45
+
46
+ getOption: (item: TData) => ComboboxOption<TData>;
47
+
48
+ disabled?: boolean;
49
+ placeholder?: string;
50
+ multiple?: boolean;
51
+ }
52
+
53
+ export function ComboboxField<
54
+ TFormData,
55
+ TData extends BaseResponse,
56
+ TQuery,
57
+ TKey extends string,
58
+ >({
59
+ dataKey,
60
+ useQuery,
61
+ queryArgs,
62
+ getOption,
63
+ disabled,
64
+ multiple = false,
65
+ ...props
66
+ }: ComboboxFieldProps<TFormData, TData, TQuery, TKey>) {
67
+ const [open, setOpen] = React.useState(false);
68
+
69
+ const { data, isFetching } = useQuery(queryArgs);
70
+
71
+ const options = React.useMemo(() => {
72
+ if (!data) return [];
73
+ return data[dataKey]?.map((d) => {
74
+ const option = getOption(d);
75
+ return { ...option, id: option.id ?? d.id };
76
+ });
77
+ }, [data, dataKey, getOption]);
78
+
79
+ const optionMap = React.useMemo(() => {
80
+ const map = new Map<string, (typeof options)[number]>();
81
+ for (const o of options) {
82
+ map.set(o.id, o);
83
+ }
84
+ return map;
85
+ }, [options]);
86
+
87
+ return (
88
+ <FormField {...props}>
89
+ {(field) => {
90
+ const fieldValue = field.value;
91
+ const selectedIds = multiple
92
+ ? Array.isArray(fieldValue)
93
+ ? fieldValue
94
+ : []
95
+ : [fieldValue];
96
+
97
+ const selectedOptions = selectedIds
98
+ .map((id) => optionMap.get(id))
99
+ .filter(Boolean);
100
+
101
+ const displayLabel = multiple
102
+ ? selectedOptions.map((o) => o?.label).join(", ")
103
+ : selectedOptions[0]?.label;
104
+
105
+ return (
106
+ <Popover open={open} onOpenChange={setOpen}>
107
+ <PopoverTrigger asChild>
108
+ <Button
109
+ variant="secondary"
110
+ role="combobox"
111
+ disabled={disabled}
112
+ className={cn(
113
+ "truncate flex justify-between bg-transparent dark:bg-input/30 border border-input",
114
+ selectedIds.length === 0 && "text-muted-foreground",
115
+ open && "border-ring ring-ring/50 ring-[3px]",
116
+ )}
117
+ >
118
+ <span
119
+ className={cn(
120
+ "truncate min-w-0",
121
+ !displayLabel && "text-muted-foreground",
122
+ )}
123
+ >
124
+ {!displayLabel ? field.placeholder : displayLabel}
125
+ </span>
126
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
127
+ </Button>
128
+ </PopoverTrigger>
129
+
130
+ <PopoverContent className="p-0 w-(--radix-popover-trigger-width)">
131
+ <Command>
132
+ <CommandInput placeholder={field.placeholder} />
133
+ <CommandList>
134
+ <CommandEmpty>
135
+ {isFetching ? "Loading..." : "No results found."}
136
+ </CommandEmpty>
137
+
138
+ <CommandGroup>
139
+ {options.map((option) => {
140
+ const isSelected = selectedIds.includes(option.id);
141
+ return (
142
+ <CommandItem
143
+ key={option.key}
144
+ value={option.key}
145
+ onSelect={() => {
146
+ if (multiple) {
147
+ let newValue;
148
+ if (isSelected) {
149
+ // remove
150
+ newValue = field.value.filter(
151
+ (v: string) => v !== option.value,
152
+ );
153
+ } else {
154
+ // add
155
+ newValue = [
156
+ ...(field.value || []),
157
+ option.value,
158
+ ];
159
+ }
160
+ field.onChange(newValue);
161
+ } else {
162
+ field.onChange(option.value);
163
+ setOpen(false);
164
+ }
165
+ }}
166
+ >
167
+ {option.content}
168
+ {isSelected && <Check className="ml-auto size-4" />}
169
+ </CommandItem>
170
+ );
171
+ })}
172
+ </CommandGroup>
173
+ </CommandList>
174
+ </Command>
175
+ </PopoverContent>
176
+ </Popover>
177
+ );
178
+ }}
179
+ </FormField>
180
+ );
181
+ }