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,77 @@
1
+ import { Skeleton } from "@workspace/ui/components/skeleton";
2
+ import {
3
+ Table,
4
+ TableBody,
5
+ TableCell,
6
+ TableHead,
7
+ TableHeader,
8
+ TableRow,
9
+ } from "@workspace/ui/components/table";
10
+
11
+ interface TableSkeletonProps {
12
+ columnCount?: number;
13
+ rowCount?: number;
14
+ }
15
+
16
+ const TableSkeleton = ({
17
+ columnCount = 5,
18
+ rowCount = 10,
19
+ }: TableSkeletonProps) => {
20
+ return (
21
+ <section className="space-y-4 animate-pulse">
22
+ <div className="flex items-center justify-between gap-4">
23
+ <div className="flex flex-1 items-center gap-2">
24
+ <Skeleton className="h-10 w-37.5" />
25
+ <Skeleton className="h-10 w-full max-w-75" />{" "}
26
+ </div>
27
+ <div className="flex gap-2">
28
+ <Skeleton className="h-10 w-25" />
29
+ <Skeleton className="h-10 w-30" />
30
+ </div>
31
+ </div>
32
+
33
+ <div className="overflow-hidden rounded-lg border">
34
+ <Table>
35
+ <TableHeader className="bg-muted">
36
+ <TableRow>
37
+ {Array.from({ length: columnCount }).map((_, i) => (
38
+ <TableHead key={i}>
39
+ <Skeleton className="h-4 w-24" />
40
+ </TableHead>
41
+ ))}
42
+ <TableHead className="w-20">
43
+ <Skeleton className="h-4 w-12" />
44
+ </TableHead>
45
+ </TableRow>
46
+ </TableHeader>
47
+ <TableBody>
48
+ {Array.from({ length: rowCount }).map((_, rowIndex) => (
49
+ <TableRow key={rowIndex}>
50
+ {Array.from({ length: columnCount }).map((_, colIndex) => (
51
+ <TableCell key={colIndex}>
52
+ <Skeleton className="h-4 w-full" />
53
+ </TableCell>
54
+ ))}
55
+ <TableCell>
56
+ <Skeleton className="h-8 w-8 rounded-md" />{" "}
57
+ </TableCell>
58
+ </TableRow>
59
+ ))}
60
+ </TableBody>
61
+ </Table>
62
+ </div>
63
+
64
+ <div className="flex items-center justify-between px-2">
65
+ <Skeleton className="h-4 w-40" />
66
+ <div className="flex gap-2">
67
+ <Skeleton className="h-8 w-8" />
68
+ <Skeleton className="h-8 w-8" />
69
+ <Skeleton className="h-8 w-8" />
70
+ <Skeleton className="h-8 w-8" />
71
+ </div>
72
+ </div>
73
+ </section>
74
+ );
75
+ };
76
+
77
+ export default TableSkeleton;
@@ -0,0 +1,530 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ "use client";
3
+
4
+ import z from "zod";
5
+ import { toast } from "sonner";
6
+ import { useEffect, useState } from "react";
7
+ import { useForm } from "@tanstack/react-form";
8
+ import { Mail, Shield, Phone, Smartphone, Loader2 } from "lucide-react";
9
+
10
+ import {
11
+ identifierSchema,
12
+ passwordSchema,
13
+ MfaMethodEnum,
14
+ } from "@workspace/contracts";
15
+
16
+ import {
17
+ requestOtp,
18
+ resetPassword,
19
+ requestUpdateIdentifier,
20
+ updateMfa,
21
+ } from "@workspace/sdk/auth";
22
+
23
+ import {
24
+ Card,
25
+ CardContent,
26
+ CardHeader,
27
+ CardTitle,
28
+ } from "@workspace/ui/components/card";
29
+ import { Form } from "@workspace/ui/components/form";
30
+
31
+ import { Button } from "@workspace/ui/components/button";
32
+ import { Badge } from "@workspace/ui/components/badge";
33
+ import { InputField } from "@workspace/ui/components/input-field";
34
+ import { SelectField } from "@workspace/ui/components/select-field";
35
+ import OtpModal, { type OtpMeta } from "@workspace/ui/components/otp-modal";
36
+
37
+ import UserSessions from "./UserSessions";
38
+ import useUser from "@/hooks/user";
39
+
40
+ type IdentifierType = "email" | "phone";
41
+
42
+ interface AccountSectionProps {
43
+ user: UserResponse;
44
+ }
45
+
46
+ const AccountSection = ({ user }: AccountSectionProps) => {
47
+ const { refetchUser } = useUser();
48
+ const [isOpen, setIsOpen] = useState(false);
49
+ const [otpPurpose, setOtpPurpose] = useState<OtpPurpose>();
50
+ const [identifierType, setIdentifierType] = useState<IdentifierType>();
51
+ const [otpMeta, setOtpMeta] = useState<OtpMeta>();
52
+ const [isLoading, setIsLoading] = useState(false);
53
+
54
+ const primaryIdentifier = user.email || user.phone;
55
+ const primaryIdentifierType = user.email ? "email" : "phone";
56
+
57
+ const schema = z.object({
58
+ newIdentifier:
59
+ otpPurpose === "authUpdateIdentifier"
60
+ ? identifierSchema
61
+ : z.string().optional(),
62
+ newPassword: otpPurpose?.includes("Password")
63
+ ? passwordSchema
64
+ : z.string().optional(),
65
+ confirmPassword: otpPurpose?.includes("Password")
66
+ ? passwordSchema
67
+ : z.string().optional(),
68
+ preferredMfa:
69
+ otpPurpose === "authUpdateMfa" ? MfaMethodEnum : MfaMethodEnum.optional(),
70
+ });
71
+
72
+ const form = useForm({
73
+ defaultValues: {
74
+ newIdentifier: otpPurpose === "authUpdateIdentifier" ? "" : undefined,
75
+ newPassword: otpPurpose?.includes("Password") ? "" : undefined,
76
+ confirmPassword: otpPurpose?.includes("Password") ? "" : undefined,
77
+ preferredMfa: user.preferredMfa ?? "email",
78
+ },
79
+ validators: {
80
+ onSubmit: schema as any,
81
+ },
82
+ onSubmit: async ({ value }) => {
83
+ let message = "";
84
+ try {
85
+ setIsLoading(true);
86
+ if (!otpMeta?.token) throw new Error("OTP token is missing");
87
+
88
+ if (otpPurpose === "authUpdateIdentifier") {
89
+ const res = await requestUpdateIdentifier({
90
+ identifier: primaryIdentifier!,
91
+ newIdentifier: value.newIdentifier!,
92
+ purpose: otpPurpose,
93
+ secret: otpMeta.token,
94
+ });
95
+
96
+ message = res.message;
97
+ } else if (otpPurpose === "authResetPassword") {
98
+ const res = await resetPassword({
99
+ identifier: primaryIdentifier!,
100
+ purpose: otpPurpose,
101
+ newPassword: value.newPassword!,
102
+ secret: otpMeta.token,
103
+ });
104
+
105
+ message = res.message;
106
+ } else if (otpPurpose === "authUpdateMfa") {
107
+ const res = await updateMfa({
108
+ identifier: primaryIdentifier!,
109
+ purpose: otpPurpose,
110
+ preferredMfa: value.preferredMfa,
111
+ secret: otpMeta.token,
112
+ });
113
+ message = res.message;
114
+ }
115
+
116
+ toast.success(message);
117
+ form.reset();
118
+ refetchUser();
119
+ } catch (error: any) {
120
+ toast.error(error.message || "Operation failed");
121
+ } finally {
122
+ setOtpPurpose(undefined);
123
+ setOtpMeta(undefined);
124
+ setIdentifierType(undefined);
125
+ setIsLoading(false);
126
+ }
127
+ },
128
+ });
129
+
130
+ const handleOpen = (purpose: OtpPurpose, type?: IdentifierType) => {
131
+ if (type) setIdentifierType(type);
132
+ setOtpMeta(undefined);
133
+ setOtpPurpose(purpose);
134
+ setIsOpen(true);
135
+ };
136
+
137
+ useEffect(() => {
138
+ if (!otpPurpose || !primaryIdentifier) return;
139
+
140
+ if (otpMeta?.valid) return;
141
+
142
+ const otpRequest = async () => {
143
+ try {
144
+ const res = await requestOtp({
145
+ identifier: primaryIdentifier,
146
+ purpose: otpPurpose,
147
+ });
148
+ toast.success(res.message);
149
+ } catch (err: any) {
150
+ toast.error("Failed to request OTP", {
151
+ description: err?.message,
152
+ });
153
+ }
154
+ };
155
+
156
+ otpRequest();
157
+ }, [isOpen, otpPurpose, primaryIdentifier, otpMeta?.valid]);
158
+
159
+ useEffect(() => {
160
+ if (otpMeta?.valid && !otpMeta.token) {
161
+ refetchUser();
162
+ }
163
+ }, [otpMeta?.token, otpMeta?.valid, refetchUser]);
164
+
165
+ return (
166
+ <Form form={form} className="space-y-6">
167
+ {/* Account Identifiers Section */}
168
+ <Card>
169
+ <CardHeader>
170
+ <CardTitle className="flex items-center gap-2">
171
+ <Smartphone className="size-5" />
172
+ Account Identifiers
173
+ </CardTitle>
174
+ </CardHeader>
175
+ <CardContent className="space-y-4">
176
+ {/* Email Section */}
177
+ <div className="space-y-4 p-4 rounded-lg border">
178
+ <div className="flex items-center justify-between">
179
+ <div className="flex items-center gap-3">
180
+ <Mail className="size-5 text-muted-foreground" />
181
+ <div>
182
+ <p className="font-medium">Email Address</p>
183
+ {user.email ? (
184
+ <>
185
+ <p className="text-muted-foreground text-sm">
186
+ {user.email}
187
+ </p>
188
+ <p className="text-xs mt-1">
189
+ Status:{" "}
190
+ <span
191
+ className={
192
+ user.isEmailVerified
193
+ ? "text-green-600"
194
+ : "text-amber-600"
195
+ }
196
+ >
197
+ {user.isEmailVerified ? "Verified" : "Unverified"}
198
+ </span>
199
+ </p>
200
+ </>
201
+ ) : (
202
+ <p className="text-muted-foreground text-sm">
203
+ No email added
204
+ </p>
205
+ )}
206
+ </div>
207
+ </div>
208
+ <Button
209
+ variant="secondary"
210
+ size="sm"
211
+ onClick={() => handleOpen("authUpdateIdentifier", "email")}
212
+ disabled={isLoading}
213
+ >
214
+ {user.email ? "Change Email" : "Add Email"}
215
+ </Button>
216
+ </div>
217
+
218
+ {otpMeta?.valid &&
219
+ otpPurpose === "authUpdateIdentifier" &&
220
+ identifierType === "email" && (
221
+ <div className="space-y-4 p-4 border rounded-lg bg-muted/50">
222
+ <InputField
223
+ form={form}
224
+ name="newIdentifier"
225
+ label={user.email ? "New Email" : "Email Address"}
226
+ type="email"
227
+ placeholder="Enter email address"
228
+ />
229
+ <div className="flex gap-2">
230
+ <Button
231
+ type="submit"
232
+ className="w-full sm:w-auto"
233
+ disabled={isLoading}
234
+ >
235
+ {user.email ? "Change Email" : "Verify Email"}
236
+ {isLoading && <Loader2 className="animate-spin" />}
237
+ </Button>
238
+ <Button
239
+ type="button"
240
+ variant="outline"
241
+ onClick={() => {
242
+ setOtpMeta(undefined);
243
+ }}
244
+ >
245
+ Cancel
246
+ </Button>
247
+ </div>
248
+ </div>
249
+ )}
250
+ </div>
251
+
252
+ {/* Phone Section */}
253
+ <div className="space-y-4 p-4 rounded-lg border">
254
+ <div className="flex items-center justify-between">
255
+ <div className="flex items-center gap-3">
256
+ <Phone className="size-5 text-muted-foreground" />
257
+ <div>
258
+ <p className="font-medium">Phone Number</p>
259
+ {user.phone ? (
260
+ <>
261
+ <p className="text-muted-foreground text-sm">
262
+ {user.phone}
263
+ </p>
264
+ <p className="text-xs mt-1">
265
+ Status:{" "}
266
+ <span
267
+ className={
268
+ user.isPhoneVerified
269
+ ? "text-green-600"
270
+ : "text-amber-600"
271
+ }
272
+ >
273
+ {user.isPhoneVerified ? "Verified" : "Unverified"}
274
+ </span>
275
+ </p>
276
+ </>
277
+ ) : (
278
+ <p className="text-muted-foreground text-sm">
279
+ No phone number added
280
+ </p>
281
+ )}
282
+ </div>
283
+ </div>
284
+ <Button
285
+ variant="secondary"
286
+ size="sm"
287
+ onClick={() => handleOpen("authUpdateIdentifier", "phone")}
288
+ disabled={isLoading}
289
+ >
290
+ {user.phone ? "Change Phone" : "Add Phone"}
291
+ </Button>
292
+ </div>
293
+
294
+ {otpMeta?.valid &&
295
+ otpPurpose === "authUpdateIdentifier" &&
296
+ identifierType === "phone" && (
297
+ <div className="space-y-4 p-4 border rounded-lg bg-muted/50">
298
+ <InputField
299
+ form={form}
300
+ name="newIdentifier"
301
+ label={user.phone ? "New Phone Number" : "Phone Number"}
302
+ type="tel"
303
+ placeholder="Enter phone number"
304
+ />
305
+ <div className="flex gap-2">
306
+ <Button
307
+ type="submit"
308
+ className="w-full sm:w-auto"
309
+ disabled={isLoading}
310
+ >
311
+ {user.phone ? "Change Phone" : "Verify Phone"}
312
+ {isLoading && <Loader2 className="animate-spin" />}
313
+ </Button>
314
+ <Button
315
+ type="button"
316
+ variant="outline"
317
+ onClick={() => {
318
+ setOtpMeta(undefined);
319
+ }}
320
+ >
321
+ Cancel
322
+ </Button>
323
+ </div>
324
+ </div>
325
+ )}
326
+ </div>
327
+
328
+ {/* Primary Identifier Info */}
329
+ <div className="p-3 rounded-lg bg-blue-50 dark:bg-blue-900/20">
330
+ <p className="text-sm text-blue-700 dark:text-blue-300">
331
+ <strong>Current login method:</strong>{" "}
332
+ {primaryIdentifierType === "email" ? "Email" : "Phone number"}
333
+ <br />
334
+ <span className="text-xs">
335
+ You can add both email and phone for additional security and
336
+ recovery options.
337
+ </span>
338
+ </p>
339
+ </div>
340
+ </CardContent>
341
+ </Card>
342
+
343
+ {/* Security Settings Section */}
344
+ <Card>
345
+ <CardHeader>
346
+ <CardTitle className="flex items-center gap-2">
347
+ <Shield className="size-5" />
348
+ Security Settings
349
+ </CardTitle>
350
+ </CardHeader>
351
+ <CardContent className="space-y-6">
352
+ {/* Multi-Factor Authentication */}
353
+ <div className="space-y-4">
354
+ {/* MFA Status */}
355
+ <div className="flex items-center justify-between p-4 rounded-lg border">
356
+ <div className="space-y-1">
357
+ <p className="font-medium">Multi-Factor Authentication</p>
358
+ <div className="text-sm text-muted-foreground">
359
+ {user.preferredMfa ? (
360
+ <div className="flex items-center gap-2">
361
+ <Badge
362
+ variant="secondary"
363
+ className="text-green-700 bg-green-700/30"
364
+ >
365
+ Enabled
366
+ </Badge>
367
+ <Badge variant="secondary">
368
+ {user.preferredMfa === "email" && <Mail />}
369
+ {user.preferredMfa === "sms" && <Phone />}
370
+ {user.preferredMfa === "whatsapp" && <Phone />}
371
+ {user.preferredMfa === "authApp" && <Shield />}
372
+ {user.preferredMfa}
373
+ </Badge>
374
+ </div>
375
+ ) : (
376
+ <span>Disabled</span>
377
+ )}
378
+ </div>
379
+ </div>
380
+
381
+ <div className="flex gap-2">
382
+ {user.preferredMfa ? (
383
+ <>
384
+ <Button
385
+ size="sm"
386
+ variant="secondary"
387
+ onClick={() => handleOpen("authUpdateMfa")}
388
+ >
389
+ Change Method
390
+ </Button>
391
+
392
+ <Button
393
+ size="sm"
394
+ variant="destructive"
395
+ onClick={() => handleOpen("authDisableMfa")}
396
+ >
397
+ Disable
398
+ </Button>
399
+ </>
400
+ ) : (
401
+ <Button
402
+ size="sm"
403
+ variant="secondary"
404
+ onClick={() => handleOpen("authUpdateMfa")}
405
+ disabled={isLoading}
406
+ >
407
+ Enable MFA
408
+ </Button>
409
+ )}
410
+ </div>
411
+ </div>
412
+
413
+ {otpMeta?.valid && otpPurpose === "authUpdateMfa" && (
414
+ <div className="space-y-4 p-4 border rounded-lg">
415
+ <SelectField
416
+ form={form}
417
+ name="preferredMfa"
418
+ label="Select MFA Method"
419
+ options={MfaMethodEnum.options.filter(
420
+ (m) => m !== user.preferredMfa,
421
+ )}
422
+ />
423
+
424
+ <div className="flex gap-2">
425
+ <Button type="submit" disabled={isLoading}>
426
+ Save MFA Settings
427
+ {isLoading && <Loader2 className="animate-spin" />}
428
+ </Button>
429
+
430
+ <Button
431
+ type="button"
432
+ variant="outline"
433
+ onClick={() => {
434
+ setOtpMeta(undefined);
435
+ setOtpPurpose(undefined);
436
+ }}
437
+ >
438
+ Cancel
439
+ </Button>
440
+ </div>
441
+ </div>
442
+ )}
443
+ </div>
444
+
445
+ {/* Password Section */}
446
+ <div className="space-y-4">
447
+ <div className="flex items-center justify-between p-4 rounded-lg border bg-card">
448
+ <div>
449
+ <p className="font-medium">Password</p>
450
+ <p className="text-muted-foreground text-sm">
451
+ Last changed: {new Date(user.updatedAt).toLocaleDateString()}
452
+ </p>
453
+ </div>
454
+ <Button
455
+ variant="secondary"
456
+ size="sm"
457
+ onClick={() => handleOpen("authResetPassword")}
458
+ disabled={isLoading}
459
+ >
460
+ Change Password
461
+ </Button>
462
+ </div>
463
+
464
+ {otpMeta?.valid && otpPurpose === "authResetPassword" && (
465
+ <div className="space-y-4 p-4 border rounded-lg">
466
+ <InputField
467
+ form={form}
468
+ name="newPassword"
469
+ label="New Password"
470
+ type="password"
471
+ placeholder="Enter new password"
472
+ />
473
+ <InputField
474
+ form={form}
475
+ name="confirmPassword"
476
+ label="Confirm Password"
477
+ type="password"
478
+ placeholder="Confirm new password"
479
+ validators={{
480
+ onChangeListenTo: ["newPassword"],
481
+ onChange: ({ value, fieldApi }) =>
482
+ value !== fieldApi.form.getFieldValue("newPassword")
483
+ ? { message: "Passwords do not match" }
484
+ : undefined,
485
+ }}
486
+ />
487
+ <div className="flex gap-2">
488
+ <Button
489
+ type="submit"
490
+ className="w-full sm:w-auto"
491
+ disabled={isLoading}
492
+ >
493
+ Update Password
494
+ {isLoading && <Loader2 className="animate-spin" />}
495
+ </Button>
496
+ <Button
497
+ type="button"
498
+ variant="outline"
499
+ onClick={() => {
500
+ setOtpMeta(undefined);
501
+ setOtpPurpose(undefined);
502
+ }}
503
+ >
504
+ Cancel
505
+ </Button>
506
+ </div>
507
+ </div>
508
+ )}
509
+ </div>
510
+ </CardContent>
511
+ </Card>
512
+
513
+ {/* User Sessions */}
514
+ <UserSessions />
515
+
516
+ {/* OTP Modal */}
517
+ {otpPurpose && (
518
+ <OtpModal
519
+ open={isOpen}
520
+ setOpen={setIsOpen}
521
+ setOtpMeta={setOtpMeta}
522
+ identifier={primaryIdentifier!}
523
+ purpose={otpPurpose}
524
+ />
525
+ )}
526
+ </Form>
527
+ );
528
+ };
529
+
530
+ export default AccountSection;
@@ -0,0 +1,54 @@
1
+ import { cn } from "@workspace/ui/lib/utils";
2
+ import {
3
+ Avatar,
4
+ AvatarFallback,
5
+ AvatarImage,
6
+ } from "@workspace/ui/components/avatar";
7
+
8
+ interface UserCardProps {
9
+ currentUser?: UserResponse;
10
+ isLoading?: boolean;
11
+ variant?: "default" | "avatar";
12
+ }
13
+
14
+ const UserCard = ({
15
+ currentUser,
16
+ isLoading,
17
+ variant = "default",
18
+ }: UserCardProps) => {
19
+ if (isLoading || !currentUser) {
20
+ return <div className="size-8 rounded-lg bg-muted animate-pulse" />;
21
+ }
22
+
23
+ return (
24
+ <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
25
+ <Avatar className="size-8 rounded-lg">
26
+ <AvatarImage
27
+ src={currentUser.image?.url}
28
+ alt={currentUser.displayName}
29
+ />
30
+ <AvatarFallback
31
+ className={cn(
32
+ "uppercase",
33
+ variant === "avatar" ? "rounded-full" : "rounded-lg",
34
+ )}
35
+ >
36
+ {currentUser.firstName.charAt(0)}
37
+ {currentUser.lastName?.charAt(0)}
38
+ </AvatarFallback>
39
+ </Avatar>
40
+ {variant === "default" && (
41
+ <div className="grid flex-1 text-left text-sm leading-tight">
42
+ <span className="truncate font-medium">
43
+ {currentUser?.displayName}
44
+ </span>
45
+ <span className="text-muted-foreground truncate text-xs">
46
+ {currentUser?.email}
47
+ </span>
48
+ </div>
49
+ )}
50
+ </div>
51
+ );
52
+ };
53
+
54
+ export default UserCard;