hazo_auth 1.4.1 → 1.6.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 (478) hide show
  1. package/README.md +25 -38
  2. package/SETUP_CHECKLIST.md +708 -0
  3. package/dist/app/api/hazo_auth/change_password/route.d.ts +8 -0
  4. package/dist/app/api/hazo_auth/change_password/route.d.ts.map +1 -0
  5. package/dist/app/api/hazo_auth/change_password/route.js +98 -0
  6. package/dist/app/api/hazo_auth/forgot_password/route.d.ts +8 -0
  7. package/dist/app/api/hazo_auth/forgot_password/route.d.ts.map +1 -0
  8. package/dist/app/api/hazo_auth/forgot_password/route.js +78 -0
  9. package/dist/app/api/hazo_auth/get_auth/route.d.ts +10 -0
  10. package/dist/app/api/hazo_auth/get_auth/route.d.ts.map +1 -0
  11. package/dist/app/api/hazo_auth/get_auth/route.js +63 -0
  12. package/dist/app/api/hazo_auth/invalidate_cache/route.d.ts +14 -0
  13. package/dist/app/api/hazo_auth/invalidate_cache/route.d.ts.map +1 -0
  14. package/dist/app/api/hazo_auth/invalidate_cache/route.js +96 -0
  15. package/dist/app/api/hazo_auth/library_photos/route.d.ts +13 -0
  16. package/dist/app/api/hazo_auth/library_photos/route.d.ts.map +1 -0
  17. package/dist/app/api/hazo_auth/library_photos/route.js +55 -0
  18. package/dist/app/api/hazo_auth/login/route.d.ts +12 -0
  19. package/dist/app/api/hazo_auth/login/route.d.ts.map +1 -0
  20. package/dist/app/api/hazo_auth/login/route.js +140 -0
  21. package/dist/app/api/hazo_auth/logout/route.d.ts +8 -0
  22. package/dist/app/api/hazo_auth/logout/route.d.ts.map +1 -0
  23. package/dist/app/api/hazo_auth/logout/route.js +71 -0
  24. package/dist/app/api/hazo_auth/me/route.d.ts +3 -0
  25. package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -0
  26. package/dist/app/api/hazo_auth/me/route.js +34 -0
  27. package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts +7 -0
  28. package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts.map +1 -0
  29. package/dist/app/api/hazo_auth/profile_picture/[filename]/route.js +43 -0
  30. package/dist/app/api/hazo_auth/register/route.d.ts +9 -0
  31. package/dist/app/api/hazo_auth/register/route.d.ts.map +1 -0
  32. package/dist/app/api/hazo_auth/register/route.js +80 -0
  33. package/dist/app/api/hazo_auth/remove_profile_picture/route.d.ts +8 -0
  34. package/dist/app/api/hazo_auth/remove_profile_picture/route.d.ts.map +1 -0
  35. package/dist/app/api/hazo_auth/remove_profile_picture/route.js +64 -0
  36. package/dist/app/api/hazo_auth/resend_verification/route.d.ts +8 -0
  37. package/dist/app/api/hazo_auth/resend_verification/route.d.ts.map +1 -0
  38. package/dist/app/api/hazo_auth/resend_verification/route.js +79 -0
  39. package/dist/app/api/hazo_auth/reset_password/route.d.ts +8 -0
  40. package/dist/app/api/hazo_auth/reset_password/route.d.ts.map +1 -0
  41. package/dist/app/api/hazo_auth/reset_password/route.js +76 -0
  42. package/dist/app/api/hazo_auth/update_user/route.d.ts +9 -0
  43. package/dist/app/api/hazo_auth/update_user/route.d.ts.map +1 -0
  44. package/dist/app/api/hazo_auth/update_user/route.js +95 -0
  45. package/dist/app/api/hazo_auth/upload_profile_picture/route.d.ts +9 -0
  46. package/dist/app/api/hazo_auth/upload_profile_picture/route.d.ts.map +1 -0
  47. package/dist/app/api/hazo_auth/upload_profile_picture/route.js +204 -0
  48. package/dist/app/api/hazo_auth/validate_reset_token/route.d.ts +6 -0
  49. package/dist/app/api/hazo_auth/validate_reset_token/route.d.ts.map +1 -0
  50. package/dist/app/api/hazo_auth/validate_reset_token/route.js +58 -0
  51. package/dist/app/api/hazo_auth/verify_email/route.d.ts +11 -0
  52. package/dist/app/api/hazo_auth/verify_email/route.d.ts.map +1 -0
  53. package/dist/app/api/hazo_auth/verify_email/route.js +63 -0
  54. package/dist/cli/generate.d.ts +2 -0
  55. package/dist/cli/generate.d.ts.map +1 -0
  56. package/dist/cli/generate.js +117 -0
  57. package/dist/cli/index.d.ts +3 -0
  58. package/dist/cli/index.d.ts.map +1 -0
  59. package/dist/cli/index.js +120 -0
  60. package/dist/cli/validate.d.ts +15 -0
  61. package/dist/cli/validate.d.ts.map +1 -0
  62. package/dist/cli/validate.js +509 -0
  63. package/dist/components/layouts/email_verification/config/email_verification_field_config.d.ts +2 -2
  64. package/dist/components/layouts/email_verification/config/email_verification_field_config.d.ts.map +1 -1
  65. package/dist/components/layouts/email_verification/config/email_verification_field_config.js +1 -1
  66. package/dist/components/layouts/email_verification/hooks/use_email_verification.d.ts +2 -2
  67. package/dist/components/layouts/email_verification/hooks/use_email_verification.d.ts.map +1 -1
  68. package/dist/components/layouts/email_verification/hooks/use_email_verification.js +2 -2
  69. package/dist/components/layouts/email_verification/index.d.ts +3 -3
  70. package/dist/components/layouts/email_verification/index.d.ts.map +1 -1
  71. package/dist/components/layouts/email_verification/index.js +9 -9
  72. package/dist/components/layouts/forgot_password/config/forgot_password_field_config.d.ts +2 -2
  73. package/dist/components/layouts/forgot_password/config/forgot_password_field_config.d.ts.map +1 -1
  74. package/dist/components/layouts/forgot_password/config/forgot_password_field_config.js +1 -1
  75. package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts +2 -2
  76. package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts.map +1 -1
  77. package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.js +2 -2
  78. package/dist/components/layouts/forgot_password/index.d.ts +2 -2
  79. package/dist/components/layouts/forgot_password/index.d.ts.map +1 -1
  80. package/dist/components/layouts/forgot_password/index.js +8 -8
  81. package/dist/components/layouts/login/config/login_field_config.d.ts +2 -2
  82. package/dist/components/layouts/login/config/login_field_config.d.ts.map +1 -1
  83. package/dist/components/layouts/login/config/login_field_config.js +1 -1
  84. package/dist/components/layouts/login/hooks/use_login_form.d.ts +2 -2
  85. package/dist/components/layouts/login/hooks/use_login_form.d.ts.map +1 -1
  86. package/dist/components/layouts/login/hooks/use_login_form.js +4 -4
  87. package/dist/components/layouts/login/index.d.ts +2 -2
  88. package/dist/components/layouts/login/index.d.ts.map +1 -1
  89. package/dist/components/layouts/login/index.js +9 -9
  90. package/dist/components/layouts/my_settings/components/editable_field.js +3 -3
  91. package/dist/components/layouts/my_settings/components/password_change_dialog.d.ts +1 -1
  92. package/dist/components/layouts/my_settings/components/password_change_dialog.d.ts.map +1 -1
  93. package/dist/components/layouts/my_settings/components/password_change_dialog.js +4 -4
  94. package/dist/components/layouts/my_settings/components/profile_picture_dialog.js +7 -7
  95. package/dist/components/layouts/my_settings/components/profile_picture_display.js +1 -1
  96. package/dist/components/layouts/my_settings/components/profile_picture_gravatar_tab.js +3 -3
  97. package/dist/components/layouts/my_settings/components/profile_picture_library_tab.js +5 -5
  98. package/dist/components/layouts/my_settings/components/profile_picture_upload_tab.js +4 -4
  99. package/dist/components/layouts/my_settings/config/my_settings_field_config.d.ts +1 -1
  100. package/dist/components/layouts/my_settings/config/my_settings_field_config.d.ts.map +1 -1
  101. package/dist/components/layouts/my_settings/config/my_settings_field_config.js +1 -1
  102. package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts +1 -1
  103. package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts.map +1 -1
  104. package/dist/components/layouts/my_settings/hooks/use_my_settings.js +1 -1
  105. package/dist/components/layouts/my_settings/index.d.ts +2 -2
  106. package/dist/components/layouts/my_settings/index.d.ts.map +1 -1
  107. package/dist/components/layouts/my_settings/index.js +9 -9
  108. package/dist/components/layouts/register/config/register_field_config.d.ts +2 -2
  109. package/dist/components/layouts/register/config/register_field_config.d.ts.map +1 -1
  110. package/dist/components/layouts/register/config/register_field_config.js +1 -1
  111. package/dist/components/layouts/register/hooks/use_register_form.d.ts +3 -3
  112. package/dist/components/layouts/register/hooks/use_register_form.d.ts.map +1 -1
  113. package/dist/components/layouts/register/hooks/use_register_form.js +2 -2
  114. package/dist/components/layouts/register/index.d.ts +2 -2
  115. package/dist/components/layouts/register/index.d.ts.map +1 -1
  116. package/dist/components/layouts/register/index.js +9 -9
  117. package/dist/components/layouts/reset_password/config/reset_password_field_config.d.ts +2 -2
  118. package/dist/components/layouts/reset_password/config/reset_password_field_config.d.ts.map +1 -1
  119. package/dist/components/layouts/reset_password/config/reset_password_field_config.js +1 -1
  120. package/dist/components/layouts/reset_password/hooks/use_reset_password_form.d.ts +3 -3
  121. package/dist/components/layouts/reset_password/hooks/use_reset_password_form.d.ts.map +1 -1
  122. package/dist/components/layouts/reset_password/hooks/use_reset_password_form.js +2 -2
  123. package/dist/components/layouts/reset_password/index.d.ts +2 -2
  124. package/dist/components/layouts/reset_password/index.d.ts.map +1 -1
  125. package/dist/components/layouts/reset_password/index.js +8 -8
  126. package/dist/components/layouts/shared/components/already_logged_in_guard.js +4 -4
  127. package/dist/components/layouts/shared/components/auth_page_shell.js +3 -3
  128. package/dist/components/layouts/shared/components/form_action_buttons.d.ts +1 -1
  129. package/dist/components/layouts/shared/components/form_action_buttons.d.ts.map +1 -1
  130. package/dist/components/layouts/shared/components/form_action_buttons.js +1 -1
  131. package/dist/components/layouts/shared/components/form_field_wrapper.js +2 -2
  132. package/dist/components/layouts/shared/components/logout_button.js +2 -2
  133. package/dist/components/layouts/shared/components/password_field.js +3 -3
  134. package/dist/components/layouts/shared/components/profile_pic_menu.d.ts +1 -1
  135. package/dist/components/layouts/shared/components/profile_pic_menu.d.ts.map +1 -1
  136. package/dist/components/layouts/shared/components/profile_pic_menu.js +4 -4
  137. package/dist/components/layouts/shared/components/profile_pic_menu_wrapper.js +2 -2
  138. package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +3 -3
  139. package/dist/components/layouts/shared/components/standalone_layout_wrapper.js +1 -1
  140. package/dist/components/layouts/shared/components/two_column_auth_layout.js +1 -1
  141. package/dist/components/layouts/shared/components/unauthorized_guard.js +2 -2
  142. package/dist/components/layouts/shared/hooks/use_hazo_auth.d.ts +1 -1
  143. package/dist/components/layouts/shared/hooks/use_hazo_auth.d.ts.map +1 -1
  144. package/dist/components/layouts/shared/utils/validation.d.ts +1 -1
  145. package/dist/components/layouts/shared/utils/validation.d.ts.map +1 -1
  146. package/dist/components/layouts/user_management/components/roles_matrix.js +7 -7
  147. package/dist/components/layouts/user_management/index.js +11 -11
  148. package/dist/components/ui/alert-dialog.js +2 -2
  149. package/dist/components/ui/avatar.js +1 -1
  150. package/dist/components/ui/button.js +1 -1
  151. package/dist/components/ui/card.d.ts +9 -0
  152. package/dist/components/ui/card.d.ts.map +1 -0
  153. package/dist/components/ui/card.js +45 -0
  154. package/dist/components/ui/checkbox.js +1 -1
  155. package/dist/components/ui/dialog.js +1 -1
  156. package/dist/components/ui/dropdown-menu.js +1 -1
  157. package/dist/components/ui/hazo_ui_tooltip.js +1 -1
  158. package/dist/components/ui/input.js +1 -1
  159. package/dist/components/ui/label.js +1 -1
  160. package/dist/components/ui/separator.js +1 -1
  161. package/dist/components/ui/sheet.js +1 -1
  162. package/dist/components/ui/sidebar.d.ts +2 -2
  163. package/dist/components/ui/sidebar.d.ts.map +1 -1
  164. package/dist/components/ui/sidebar.js +8 -8
  165. package/dist/components/ui/skeleton.js +1 -1
  166. package/dist/components/ui/switch.js +1 -1
  167. package/dist/components/ui/table.js +1 -1
  168. package/dist/components/ui/tabs.js +1 -1
  169. package/dist/components/ui/tooltip.js +1 -1
  170. package/dist/components/ui/vertical-tabs.js +1 -1
  171. package/dist/hooks/use-mobile.d.ts.map +1 -1
  172. package/dist/hooks/use-mobile.js +17 -3
  173. package/dist/lib/already_logged_in_config.server.js +1 -1
  174. package/dist/lib/app_logger.js +1 -1
  175. package/dist/lib/auth/auth_cache.d.ts +1 -1
  176. package/dist/lib/auth/auth_cache.d.ts.map +1 -1
  177. package/dist/lib/auth/auth_utils.server.js +2 -2
  178. package/dist/lib/auth/hazo_get_auth.server.d.ts +1 -1
  179. package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
  180. package/dist/lib/auth/hazo_get_auth.server.js +7 -7
  181. package/dist/lib/auth/server_auth.js +2 -2
  182. package/dist/lib/auth_utility_config.server.js +1 -1
  183. package/dist/lib/config/config_loader.server.js +1 -1
  184. package/dist/lib/email_verification_config.server.js +1 -1
  185. package/dist/lib/file_types_config.server.js +1 -1
  186. package/dist/lib/forgot_password_config.server.js +1 -1
  187. package/dist/lib/hazo_connect_instance.server.js +2 -2
  188. package/dist/lib/hazo_connect_setup.server.js +2 -2
  189. package/dist/lib/login_config.server.js +2 -2
  190. package/dist/lib/messages_config.server.js +1 -1
  191. package/dist/lib/my_settings_config.server.js +7 -7
  192. package/dist/lib/password_requirements_config.server.js +1 -1
  193. package/dist/lib/profile_pic_menu_config.server.js +1 -1
  194. package/dist/lib/profile_picture_config.server.js +2 -2
  195. package/dist/lib/register_config.server.js +4 -4
  196. package/dist/lib/reset_password_config.server.js +3 -3
  197. package/dist/lib/services/email_service.js +2 -2
  198. package/dist/lib/services/email_verification_service.js +3 -3
  199. package/dist/lib/services/login_service.js +3 -3
  200. package/dist/lib/services/password_change_service.js +3 -3
  201. package/dist/lib/services/password_reset_service.js +3 -3
  202. package/dist/lib/services/profile_picture_remove_service.js +3 -3
  203. package/dist/lib/services/profile_picture_service.d.ts +1 -1
  204. package/dist/lib/services/profile_picture_service.d.ts.map +1 -1
  205. package/dist/lib/services/profile_picture_service.js +5 -5
  206. package/dist/lib/services/registration_service.js +8 -8
  207. package/dist/lib/services/token_service.js +2 -2
  208. package/dist/lib/services/user_profiles_service.js +2 -2
  209. package/dist/lib/services/user_update_service.d.ts +1 -1
  210. package/dist/lib/services/user_update_service.d.ts.map +1 -1
  211. package/dist/lib/services/user_update_service.js +4 -4
  212. package/dist/lib/ui_shell_config.server.js +1 -1
  213. package/dist/lib/ui_sizes_config.server.js +1 -1
  214. package/dist/lib/user_fields_config.server.js +1 -1
  215. package/dist/lib/user_management_config.server.js +1 -1
  216. package/dist/lib/utils/error_sanitizer.d.ts +1 -1
  217. package/dist/lib/utils/error_sanitizer.d.ts.map +1 -1
  218. package/dist/server/config/config_loader.d.ts +1 -1
  219. package/dist/server/config/config_loader.d.ts.map +1 -1
  220. package/dist/server/config/config_loader.js +1 -1
  221. package/dist/server/index.js +2 -2
  222. package/dist/server/logging/logger_service.d.ts +1 -1
  223. package/dist/server/logging/logger_service.d.ts.map +1 -1
  224. package/dist/server/routes/change_password.d.ts +2 -0
  225. package/dist/server/routes/change_password.d.ts.map +1 -0
  226. package/dist/server/routes/change_password.js +2 -0
  227. package/dist/server/routes/forgot_password.d.ts +2 -0
  228. package/dist/server/routes/forgot_password.d.ts.map +1 -0
  229. package/dist/server/routes/forgot_password.js +2 -0
  230. package/dist/server/routes/get_auth.d.ts +2 -0
  231. package/dist/server/routes/get_auth.d.ts.map +1 -0
  232. package/dist/server/routes/get_auth.js +2 -0
  233. package/dist/server/routes/index.d.ts +18 -0
  234. package/dist/server/routes/index.d.ts.map +1 -0
  235. package/dist/server/routes/index.js +24 -0
  236. package/dist/server/routes/invalidate_cache.d.ts +2 -0
  237. package/dist/server/routes/invalidate_cache.d.ts.map +1 -0
  238. package/dist/server/routes/invalidate_cache.js +2 -0
  239. package/dist/server/routes/library_photos.d.ts +2 -0
  240. package/dist/server/routes/library_photos.d.ts.map +1 -0
  241. package/dist/server/routes/library_photos.js +2 -0
  242. package/dist/server/routes/login.d.ts +2 -0
  243. package/dist/server/routes/login.d.ts.map +1 -0
  244. package/dist/server/routes/login.js +2 -0
  245. package/dist/server/routes/logout.d.ts +2 -0
  246. package/dist/server/routes/logout.d.ts.map +1 -0
  247. package/dist/server/routes/logout.js +2 -0
  248. package/dist/server/routes/me.d.ts +2 -0
  249. package/dist/server/routes/me.d.ts.map +1 -0
  250. package/dist/server/routes/me.js +2 -0
  251. package/dist/server/routes/profile_picture_filename.d.ts +2 -0
  252. package/dist/server/routes/profile_picture_filename.d.ts.map +1 -0
  253. package/dist/server/routes/profile_picture_filename.js +3 -0
  254. package/dist/server/routes/register.d.ts +2 -0
  255. package/dist/server/routes/register.d.ts.map +1 -0
  256. package/dist/server/routes/register.js +2 -0
  257. package/dist/server/routes/remove_profile_picture.d.ts +2 -0
  258. package/dist/server/routes/remove_profile_picture.d.ts.map +1 -0
  259. package/dist/server/routes/remove_profile_picture.js +2 -0
  260. package/dist/server/routes/resend_verification.d.ts +2 -0
  261. package/dist/server/routes/resend_verification.d.ts.map +1 -0
  262. package/dist/server/routes/resend_verification.js +2 -0
  263. package/dist/server/routes/reset_password.d.ts +2 -0
  264. package/dist/server/routes/reset_password.d.ts.map +1 -0
  265. package/dist/server/routes/reset_password.js +2 -0
  266. package/dist/server/routes/update_user.d.ts +2 -0
  267. package/dist/server/routes/update_user.d.ts.map +1 -0
  268. package/dist/server/routes/update_user.js +2 -0
  269. package/dist/server/routes/upload_profile_picture.d.ts +2 -0
  270. package/dist/server/routes/upload_profile_picture.d.ts.map +1 -0
  271. package/dist/server/routes/upload_profile_picture.js +2 -0
  272. package/dist/server/routes/validate_reset_token.d.ts +2 -0
  273. package/dist/server/routes/validate_reset_token.d.ts.map +1 -0
  274. package/dist/server/routes/validate_reset_token.js +2 -0
  275. package/dist/server/routes/verify_email.d.ts +2 -0
  276. package/dist/server/routes/verify_email.d.ts.map +1 -0
  277. package/dist/server/routes/verify_email.js +2 -0
  278. package/dist/server/server.js +2 -2
  279. package/package.json +14 -115
  280. package/components.json +0 -22
  281. package/instrumentation.ts +0 -32
  282. package/migrations/001_add_token_type_to_refresh_tokens.sql +0 -14
  283. package/migrations/002_add_name_to_hazo_users.sql +0 -7
  284. package/migrations/003_add_url_on_logon_to_hazo_users.sql +0 -8
  285. package/next.config.mjs +0 -67
  286. package/postcss.config.mjs +0 -8
  287. package/public/file.svg +0 -1
  288. package/public/globe.svg +0 -1
  289. package/public/next.svg +0 -1
  290. package/public/vercel.svg +0 -1
  291. package/public/window.svg +0 -1
  292. package/scripts/apply_migration.ts +0 -118
  293. package/scripts/init_users.ts +0 -378
  294. package/src/app/api/hazo_auth/auth/upload_profile_picture/route.ts +0 -268
  295. package/src/app/api/hazo_auth/change_password/route.ts +0 -132
  296. package/src/app/api/hazo_auth/forgot_password/route.ts +0 -107
  297. package/src/app/api/hazo_auth/get_auth/route.ts +0 -89
  298. package/src/app/api/hazo_auth/invalidate_cache/route.ts +0 -139
  299. package/src/app/api/hazo_auth/library_photos/route.ts +0 -73
  300. package/src/app/api/hazo_auth/login/route.ts +0 -181
  301. package/src/app/api/hazo_auth/logout/route.ts +0 -89
  302. package/src/app/api/hazo_auth/me/route.ts +0 -47
  303. package/src/app/api/hazo_auth/profile_picture/[filename]/route.ts +0 -67
  304. package/src/app/api/hazo_auth/register/route.ts +0 -109
  305. package/src/app/api/hazo_auth/remove_profile_picture/route.ts +0 -86
  306. package/src/app/api/hazo_auth/resend_verification/route.ts +0 -108
  307. package/src/app/api/hazo_auth/reset_password/route.ts +0 -107
  308. package/src/app/api/hazo_auth/update_user/route.ts +0 -126
  309. package/src/app/api/hazo_auth/upload_profile_picture/route.ts +0 -268
  310. package/src/app/api/hazo_auth/user_management/permissions/route.ts +0 -367
  311. package/src/app/api/hazo_auth/user_management/roles/route.ts +0 -442
  312. package/src/app/api/hazo_auth/user_management/users/roles/route.ts +0 -367
  313. package/src/app/api/hazo_auth/user_management/users/route.ts +0 -239
  314. package/src/app/api/hazo_auth/validate_reset_token/route.ts +0 -83
  315. package/src/app/api/hazo_auth/verify_email/route.ts +0 -88
  316. package/src/app/api/migrations/apply/route.ts +0 -91
  317. package/src/app/favicon.ico +0 -0
  318. package/src/app/fonts/GeistMonoVF.woff +0 -0
  319. package/src/app/fonts/GeistVF.woff +0 -0
  320. package/src/app/globals.css +0 -89
  321. package/src/app/hazo_auth/forgot_password/forgot_password_page_client.tsx +0 -60
  322. package/src/app/hazo_auth/forgot_password/page.tsx +0 -24
  323. package/src/app/hazo_auth/login/login_page_client.tsx +0 -86
  324. package/src/app/hazo_auth/login/page.tsx +0 -38
  325. package/src/app/hazo_auth/my_settings/my_settings_page_client.tsx +0 -120
  326. package/src/app/hazo_auth/my_settings/page.tsx +0 -40
  327. package/src/app/hazo_auth/register/page.tsx +0 -36
  328. package/src/app/hazo_auth/register/register_page_client.tsx +0 -81
  329. package/src/app/hazo_auth/reset_password/page.tsx +0 -29
  330. package/src/app/hazo_auth/reset_password/reset_password_page_client.tsx +0 -81
  331. package/src/app/hazo_auth/user_management/page.tsx +0 -14
  332. package/src/app/hazo_auth/user_management/user_management_page_client.tsx +0 -16
  333. package/src/app/hazo_auth/verify_email/page.tsx +0 -24
  334. package/src/app/hazo_auth/verify_email/verify_email_page_client.tsx +0 -60
  335. package/src/app/hazo_connect/api/sqlite/data/route.ts +0 -203
  336. package/src/app/hazo_connect/api/sqlite/schema/route.ts +0 -45
  337. package/src/app/hazo_connect/api/sqlite/tables/route.ts +0 -36
  338. package/src/app/hazo_connect/sqlite_admin/page.tsx +0 -51
  339. package/src/app/hazo_connect/sqlite_admin/sqlite-admin-client.tsx +0 -984
  340. package/src/app/layout.tsx +0 -43
  341. package/src/app/page.tsx +0 -170
  342. package/src/components/index.ts +0 -7
  343. package/src/components/layouts/email_verification/config/email_verification_field_config.ts +0 -86
  344. package/src/components/layouts/email_verification/hooks/use_email_verification.ts +0 -297
  345. package/src/components/layouts/email_verification/index.tsx +0 -297
  346. package/src/components/layouts/forgot_password/config/forgot_password_field_config.ts +0 -58
  347. package/src/components/layouts/forgot_password/hooks/use_forgot_password_form.ts +0 -179
  348. package/src/components/layouts/forgot_password/index.tsx +0 -168
  349. package/src/components/layouts/index.ts +0 -26
  350. package/src/components/layouts/login/config/login_field_config.ts +0 -67
  351. package/src/components/layouts/login/hooks/use_login_form.ts +0 -286
  352. package/src/components/layouts/login/index.tsx +0 -252
  353. package/src/components/layouts/my_settings/components/editable_field.tsx +0 -177
  354. package/src/components/layouts/my_settings/components/password_change_dialog.tsx +0 -301
  355. package/src/components/layouts/my_settings/components/profile_picture_dialog.tsx +0 -385
  356. package/src/components/layouts/my_settings/components/profile_picture_display.tsx +0 -66
  357. package/src/components/layouts/my_settings/components/profile_picture_gravatar_tab.tsx +0 -143
  358. package/src/components/layouts/my_settings/components/profile_picture_library_tab.tsx +0 -311
  359. package/src/components/layouts/my_settings/components/profile_picture_upload_tab.tsx +0 -341
  360. package/src/components/layouts/my_settings/config/my_settings_field_config.ts +0 -61
  361. package/src/components/layouts/my_settings/hooks/use_my_settings.ts +0 -458
  362. package/src/components/layouts/my_settings/index.tsx +0 -351
  363. package/src/components/layouts/register/config/register_field_config.ts +0 -101
  364. package/src/components/layouts/register/hooks/use_register_form.ts +0 -275
  365. package/src/components/layouts/register/index.tsx +0 -226
  366. package/src/components/layouts/reset_password/config/reset_password_field_config.ts +0 -86
  367. package/src/components/layouts/reset_password/hooks/use_reset_password_form.ts +0 -276
  368. package/src/components/layouts/reset_password/index.tsx +0 -294
  369. package/src/components/layouts/shared/components/already_logged_in_guard.tsx +0 -95
  370. package/src/components/layouts/shared/components/auth_page_shell.tsx +0 -36
  371. package/src/components/layouts/shared/components/field_error_message.tsx +0 -29
  372. package/src/components/layouts/shared/components/form_action_buttons.tsx +0 -64
  373. package/src/components/layouts/shared/components/form_field_wrapper.tsx +0 -44
  374. package/src/components/layouts/shared/components/form_header.tsx +0 -36
  375. package/src/components/layouts/shared/components/logout_button.tsx +0 -76
  376. package/src/components/layouts/shared/components/password_field.tsx +0 -72
  377. package/src/components/layouts/shared/components/profile_pic_menu.tsx +0 -321
  378. package/src/components/layouts/shared/components/profile_pic_menu_wrapper.tsx +0 -40
  379. package/src/components/layouts/shared/components/sidebar_layout_wrapper.tsx +0 -214
  380. package/src/components/layouts/shared/components/standalone_layout_wrapper.tsx +0 -53
  381. package/src/components/layouts/shared/components/two_column_auth_layout.tsx +0 -44
  382. package/src/components/layouts/shared/components/unauthorized_guard.tsx +0 -78
  383. package/src/components/layouts/shared/components/visual_panel.tsx +0 -41
  384. package/src/components/layouts/shared/config/layout_customization.ts +0 -95
  385. package/src/components/layouts/shared/data/layout_data_client.ts +0 -19
  386. package/src/components/layouts/shared/hooks/use_auth_status.ts +0 -103
  387. package/src/components/layouts/shared/hooks/use_hazo_auth.ts +0 -158
  388. package/src/components/layouts/shared/index.ts +0 -34
  389. package/src/components/layouts/shared/utils/ip_address.ts +0 -37
  390. package/src/components/layouts/shared/utils/validation.ts +0 -66
  391. package/src/components/layouts/user_management/components/roles_matrix.tsx +0 -607
  392. package/src/components/layouts/user_management/index.tsx +0 -1295
  393. package/src/components/ui/alert-dialog.tsx +0 -141
  394. package/src/components/ui/avatar.tsx +0 -50
  395. package/src/components/ui/button.tsx +0 -57
  396. package/src/components/ui/checkbox.tsx +0 -30
  397. package/src/components/ui/dialog.tsx +0 -122
  398. package/src/components/ui/dropdown-menu.tsx +0 -201
  399. package/src/components/ui/hazo_ui_tooltip.tsx +0 -67
  400. package/src/components/ui/index.ts +0 -22
  401. package/src/components/ui/input.tsx +0 -22
  402. package/src/components/ui/label.tsx +0 -26
  403. package/src/components/ui/separator.tsx +0 -31
  404. package/src/components/ui/sheet.tsx +0 -139
  405. package/src/components/ui/sidebar.tsx +0 -773
  406. package/src/components/ui/skeleton.tsx +0 -15
  407. package/src/components/ui/sonner.tsx +0 -31
  408. package/src/components/ui/switch.tsx +0 -29
  409. package/src/components/ui/table.tsx +0 -120
  410. package/src/components/ui/tabs.tsx +0 -55
  411. package/src/components/ui/tooltip.tsx +0 -32
  412. package/src/components/ui/vertical-tabs.tsx +0 -59
  413. package/src/hooks/use-mobile.tsx +0 -19
  414. package/src/index.ts +0 -7
  415. package/src/lib/already_logged_in_config.server.ts +0 -46
  416. package/src/lib/app_logger.ts +0 -24
  417. package/src/lib/auth/auth_cache.ts +0 -220
  418. package/src/lib/auth/auth_rate_limiter.ts +0 -121
  419. package/src/lib/auth/auth_types.ts +0 -65
  420. package/src/lib/auth/auth_utils.server.ts +0 -196
  421. package/src/lib/auth/hazo_get_auth.server.ts +0 -333
  422. package/src/lib/auth/index.ts +0 -23
  423. package/src/lib/auth/server_auth.ts +0 -88
  424. package/src/lib/auth_utility_config.server.ts +0 -136
  425. package/src/lib/config/config_loader.server.ts +0 -164
  426. package/src/lib/email_verification_config.server.ts +0 -32
  427. package/src/lib/file_types_config.server.ts +0 -25
  428. package/src/lib/forgot_password_config.server.ts +0 -32
  429. package/src/lib/hazo_connect_instance.server.ts +0 -101
  430. package/src/lib/hazo_connect_setup.server.ts +0 -194
  431. package/src/lib/hazo_connect_setup.ts +0 -54
  432. package/src/lib/index.ts +0 -44
  433. package/src/lib/login_config.server.ts +0 -71
  434. package/src/lib/messages_config.server.ts +0 -45
  435. package/src/lib/migrations/apply_migration.ts +0 -105
  436. package/src/lib/my_settings_config.server.ts +0 -135
  437. package/src/lib/password_requirements_config.server.ts +0 -39
  438. package/src/lib/profile_pic_menu_config.server.ts +0 -138
  439. package/src/lib/profile_picture_config.server.ts +0 -56
  440. package/src/lib/register_config.server.ts +0 -73
  441. package/src/lib/reset_password_config.server.ts +0 -75
  442. package/src/lib/services/email_service.ts +0 -581
  443. package/src/lib/services/email_verification_service.ts +0 -270
  444. package/src/lib/services/index.ts +0 -15
  445. package/src/lib/services/login_service.ts +0 -134
  446. package/src/lib/services/password_change_service.ts +0 -154
  447. package/src/lib/services/password_reset_service.ts +0 -405
  448. package/src/lib/services/profile_picture_remove_service.ts +0 -120
  449. package/src/lib/services/profile_picture_service.ts +0 -215
  450. package/src/lib/services/profile_picture_source_mapper.ts +0 -62
  451. package/src/lib/services/registration_service.ts +0 -184
  452. package/src/lib/services/token_service.ts +0 -240
  453. package/src/lib/services/user_profiles_service.ts +0 -143
  454. package/src/lib/services/user_update_service.ts +0 -141
  455. package/src/lib/ui_shell_config.server.ts +0 -73
  456. package/src/lib/ui_sizes_config.server.ts +0 -37
  457. package/src/lib/user_fields_config.server.ts +0 -31
  458. package/src/lib/user_management_config.server.ts +0 -39
  459. package/src/lib/utils/api_route_helpers.ts +0 -60
  460. package/src/lib/utils/error_sanitizer.ts +0 -75
  461. package/src/lib/utils.ts +0 -11
  462. package/src/middleware.ts +0 -94
  463. package/src/routes/index.ts +0 -34
  464. package/src/server/config/config_loader.ts +0 -496
  465. package/src/server/index.ts +0 -38
  466. package/src/server/logging/logger_service.ts +0 -56
  467. package/src/server/routes/root_router.ts +0 -16
  468. package/src/server/server.ts +0 -28
  469. package/src/server/types/app_types.ts +0 -74
  470. package/src/server/types/express.d.ts +0 -16
  471. package/src/stories/email_verification_layout.stories.tsx +0 -137
  472. package/src/stories/forgot_password_layout.stories.tsx +0 -85
  473. package/src/stories/login_layout.stories.tsx +0 -85
  474. package/src/stories/project_overview.stories.tsx +0 -33
  475. package/src/stories/register_layout.stories.tsx +0 -107
  476. package/tailwind.config.ts +0 -77
  477. package/tsconfig.build.json +0 -39
  478. package/tsconfig.json +0 -28
@@ -1,311 +0,0 @@
1
- // file_description: Library tab component for profile picture dialog with category tabs and image grid
2
- // section: client_directive
3
- "use client";
4
-
5
- // section: imports
6
- import { useState, useEffect } from "react";
7
- import { Switch } from "hazo_auth/components/ui/switch";
8
- import { Label } from "hazo_auth/components/ui/label";
9
- import { Avatar, AvatarFallback } from "hazo_auth/components/ui/avatar";
10
- import { VerticalTabs, VerticalTabsList, VerticalTabsTrigger, VerticalTabsContent } from "hazo_auth/components/ui/vertical-tabs";
11
- import { Loader2 } from "lucide-react";
12
- import { HazoUITooltip } from "hazo_auth/components/ui/hazo_ui_tooltip";
13
-
14
- // section: types
15
- export type ProfilePictureLibraryTabProps = {
16
- useLibrary: boolean;
17
- onUseLibraryChange: (use: boolean) => void;
18
- onPhotoSelect: (photoUrl: string) => void;
19
- disabled?: boolean;
20
- libraryPhotoPath: string;
21
- currentPhotoUrl?: string;
22
- libraryTooltipMessage: string;
23
- tooltipIconSizeSmall: number;
24
- libraryPhotoGridColumns: number;
25
- libraryPhotoPreviewSize: number;
26
- };
27
-
28
- // section: component
29
- /**
30
- * Library tab component for profile picture dialog
31
- * Two columns: left = vertical category tabs, right = image grid + preview
32
- * Lazy loads thumbnails when category is selected
33
- * @param props - Component props including library state, photo selection handler, and configuration
34
- * @returns Library tab component
35
- */
36
- export function ProfilePictureLibraryTab({
37
- useLibrary,
38
- onUseLibraryChange,
39
- onPhotoSelect,
40
- disabled = false,
41
- libraryPhotoPath,
42
- currentPhotoUrl,
43
- libraryTooltipMessage,
44
- tooltipIconSizeSmall,
45
- libraryPhotoGridColumns,
46
- libraryPhotoPreviewSize,
47
- }: ProfilePictureLibraryTabProps) {
48
- const [categories, setCategories] = useState<string[]>([]);
49
- const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
50
- const [photos, setPhotos] = useState<string[]>([]);
51
- const [selectedPhoto, setSelectedPhoto] = useState<string | null>(currentPhotoUrl || null);
52
- const [loadingCategories, setLoadingCategories] = useState(false);
53
- const [loadingPhotos, setLoadingPhotos] = useState(false);
54
-
55
- // Load categories on mount
56
- useEffect(() => {
57
- const loadCategories = async () => {
58
- setLoadingCategories(true);
59
- try {
60
- const response = await fetch("/api/hazo_auth/library_photos");
61
- const data = await response.json();
62
- if (data.success && data.categories) {
63
- setCategories(data.categories);
64
- // Select first category if available
65
- if (data.categories.length > 0) {
66
- setSelectedCategory(data.categories[0]);
67
- }
68
- }
69
- } catch (error) {
70
- // Client-side error handling - show toast notification
71
- import("sonner").then(({ toast }) => {
72
- toast.error("Failed to load photo categories. Please try again.");
73
- });
74
- } finally {
75
- setLoadingCategories(false);
76
- }
77
- };
78
-
79
- void loadCategories();
80
- }, []);
81
-
82
- // Sync selectedPhoto with currentPhotoUrl when it changes
83
- useEffect(() => {
84
- if (currentPhotoUrl && currentPhotoUrl !== selectedPhoto) {
85
- setSelectedPhoto(currentPhotoUrl);
86
- }
87
- }, [currentPhotoUrl]);
88
-
89
- // Load photos when category is selected
90
- useEffect(() => {
91
- if (!selectedCategory) {
92
- setPhotos([]);
93
- if (!currentPhotoUrl) {
94
- setSelectedPhoto(null);
95
- }
96
- return;
97
- }
98
-
99
- const loadPhotos = async () => {
100
- setLoadingPhotos(true);
101
- try {
102
- const response = await fetch(`/api/hazo_auth/library_photos?category=${encodeURIComponent(selectedCategory)}`);
103
- const data = await response.json();
104
- if (data.success && data.photos) {
105
- setPhotos(data.photos);
106
-
107
- // If we have a current photo URL and it's in this category, select it
108
- if (currentPhotoUrl && data.photos.includes(currentPhotoUrl)) {
109
- setSelectedPhoto(currentPhotoUrl);
110
- onPhotoSelect(currentPhotoUrl);
111
- } else if (data.photos.length > 0) {
112
- // Otherwise, select first photo if available and notify parent
113
- const firstPhoto = data.photos[0];
114
- setSelectedPhoto(firstPhoto);
115
- onPhotoSelect(firstPhoto);
116
- } else if (!currentPhotoUrl) {
117
- // Clear selection if no photos and no current photo
118
- setSelectedPhoto(null);
119
- }
120
- }
121
- } catch (error) {
122
- // Client-side error handling - show toast notification
123
- import("sonner").then(({ toast }) => {
124
- toast.error("Failed to load photos. Please try again.");
125
- });
126
- } finally {
127
- setLoadingPhotos(false);
128
- }
129
- };
130
-
131
- void loadPhotos();
132
- }, [selectedCategory, onPhotoSelect, currentPhotoUrl]);
133
-
134
- const handlePhotoClick = (photoUrl: string) => {
135
- setSelectedPhoto(photoUrl);
136
- onPhotoSelect(photoUrl);
137
- };
138
-
139
- const getInitials = (): string => {
140
- return "L";
141
- };
142
-
143
- // Map column count to Tailwind grid class
144
- const getGridColumnsClass = (columns: number): string => {
145
- const columnMap: Record<number, string> = {
146
- 1: "grid-cols-1",
147
- 2: "grid-cols-2",
148
- 3: "grid-cols-3",
149
- 4: "grid-cols-4",
150
- 5: "grid-cols-5",
151
- 6: "grid-cols-6",
152
- 7: "grid-cols-7",
153
- 8: "grid-cols-8",
154
- };
155
- return columnMap[columns] || "grid-cols-4";
156
- };
157
-
158
- return (
159
- <div className="cls_profile_picture_library_tab flex flex-col gap-4">
160
- {/* Switch */}
161
- <div className="cls_profile_picture_library_tab_switch flex items-center gap-3">
162
- <Switch
163
- id="use-library"
164
- checked={useLibrary}
165
- onCheckedChange={onUseLibraryChange}
166
- disabled={disabled}
167
- className="cls_profile_picture_library_tab_switch_input"
168
- aria-label="Use library photo"
169
- />
170
- <Label
171
- htmlFor="use-library"
172
- className="cls_profile_picture_library_tab_switch_label text-sm font-medium text-slate-700 cursor-pointer"
173
- >
174
- Use library photo
175
- <HazoUITooltip
176
- message={libraryTooltipMessage}
177
- iconSize={tooltipIconSizeSmall}
178
- side="top"
179
- />
180
- </Label>
181
- </div>
182
-
183
- {/* Three columns: category tabs (25%), photo grid (50%), preview (25%) */}
184
- <div className="cls_profile_picture_library_tab_content grid grid-cols-12 gap-4">
185
- {/* Left column: Category tabs (25% - 3 columns) */}
186
- <div className="cls_profile_picture_library_tab_categories_container flex flex-col gap-2 col-span-3">
187
- <Label className="cls_profile_picture_library_tab_categories_label text-sm font-medium text-slate-700">
188
- Categories
189
- </Label>
190
- {loadingCategories ? (
191
- <div className="cls_profile_picture_library_tab_loading flex items-center justify-center p-8 border border-slate-200 rounded-lg bg-slate-50 min-h-[400px]">
192
- <Loader2 className="h-6 w-6 text-slate-400 animate-spin" aria-hidden="true" />
193
- </div>
194
- ) : categories.length > 0 ? (
195
- <VerticalTabs
196
- value={selectedCategory || categories[0]}
197
- onValueChange={setSelectedCategory}
198
- className="cls_profile_picture_library_tab_vertical_tabs"
199
- >
200
- <VerticalTabsList className="cls_profile_picture_library_tab_vertical_tabs_list w-full">
201
- {categories.map((category) => (
202
- <VerticalTabsTrigger
203
- key={category}
204
- value={category}
205
- className="cls_profile_picture_library_tab_vertical_tabs_trigger w-full justify-start"
206
- >
207
- {category}
208
- </VerticalTabsTrigger>
209
- ))}
210
- </VerticalTabsList>
211
- </VerticalTabs>
212
- ) : (
213
- <div className="cls_profile_picture_library_tab_no_categories flex items-center justify-center p-8 border border-slate-200 rounded-lg bg-slate-50 min-h-[400px]">
214
- <p className="cls_profile_picture_library_tab_no_categories_text text-sm text-slate-600">
215
- No categories available
216
- </p>
217
- </div>
218
- )}
219
- </div>
220
-
221
- {/* Middle column: Photo grid (50% - 6 columns) */}
222
- <div className="cls_profile_picture_library_tab_photos_container flex flex-col gap-2 col-span-6">
223
- <Label className="cls_profile_picture_library_tab_photos_label text-sm font-medium text-slate-700">
224
- Photos
225
- </Label>
226
- {loadingPhotos ? (
227
- <div className="cls_profile_picture_library_tab_photos_loading flex items-center justify-center p-8 border border-slate-200 rounded-lg bg-slate-50 min-h-[400px]">
228
- <Loader2 className="h-6 w-6 text-slate-400 animate-spin" aria-hidden="true" />
229
- </div>
230
- ) : photos.length > 0 ? (
231
- <div className={`cls_profile_picture_library_tab_photos_grid grid ${getGridColumnsClass(libraryPhotoGridColumns)} gap-3 overflow-y-auto p-4 border border-slate-200 rounded-lg bg-slate-50 min-h-[400px] max-h-[400px]`}>
232
- {photos.map((photoUrl) => (
233
- <button
234
- key={photoUrl}
235
- type="button"
236
- onClick={() => handlePhotoClick(photoUrl)}
237
- className={`
238
- cls_profile_picture_library_tab_photo_thumbnail
239
- aspect-square rounded-lg overflow-hidden border-2 transition-colors cursor-pointer
240
- ${selectedPhoto === photoUrl ? "border-blue-500 ring-2 ring-blue-200" : "border-slate-200 hover:border-slate-300"}
241
- `}
242
- aria-label={`Select photo ${photoUrl.split('/').pop()}`}
243
- >
244
- <img
245
- src={photoUrl}
246
- alt={`Library photo ${photoUrl.split('/').pop()}`}
247
- className="cls_profile_picture_library_tab_photo_thumbnail_image w-full h-full object-cover"
248
- loading="lazy"
249
- onError={(e) => {
250
- // Fallback if image fails to load
251
- const target = e.target as HTMLImageElement;
252
- target.style.display = 'none';
253
- }}
254
- />
255
- </button>
256
- ))}
257
- </div>
258
- ) : (
259
- <div className="cls_profile_picture_library_tab_no_photos flex items-center justify-center p-8 border border-slate-200 rounded-lg bg-slate-50 min-h-[400px]">
260
- <p className="cls_profile_picture_library_tab_no_photos_text text-sm text-slate-600">
261
- No photos in this category
262
- </p>
263
- </div>
264
- )}
265
- </div>
266
-
267
- {/* Right column: Preview (25% - 3 columns) */}
268
- <div className="cls_profile_picture_library_tab_preview_container flex flex-col gap-2 col-span-3">
269
- <Label className="cls_profile_picture_library_tab_preview_label text-sm font-medium text-slate-700">
270
- Preview
271
- </Label>
272
- {selectedPhoto ? (
273
- <div className="cls_profile_picture_library_tab_preview flex flex-col items-center gap-4 p-4 border border-slate-200 rounded-lg bg-slate-50 min-h-[400px] justify-center">
274
- <div className="cls_profile_picture_library_tab_preview_image_wrapper w-full flex items-center justify-center">
275
- <img
276
- src={selectedPhoto}
277
- alt="Selected library photo preview"
278
- className="cls_profile_picture_library_tab_preview_image max-w-full max-h-[350px] rounded-lg object-contain"
279
- onError={(e) => {
280
- // Fallback if preview image fails to load
281
- const target = e.target as HTMLImageElement;
282
- target.style.display = 'none';
283
- const wrapper = target.parentElement;
284
- if (wrapper) {
285
- wrapper.innerHTML = '<p class="text-sm text-red-500">Failed to load preview</p>';
286
- }
287
- }}
288
- />
289
- </div>
290
- <p className="cls_profile_picture_library_tab_preview_text text-sm text-slate-600 text-center">
291
- Selected photo preview
292
- </p>
293
- </div>
294
- ) : (
295
- <div className="cls_profile_picture_library_tab_preview_empty flex flex-col items-center justify-center gap-2 p-8 border border-slate-200 rounded-lg bg-slate-50 min-h-[400px]">
296
- <Avatar className="cls_profile_picture_library_tab_preview_empty_avatar h-32 w-32">
297
- <AvatarFallback className="cls_profile_picture_library_tab_preview_empty_avatar_fallback bg-slate-200 text-slate-600 text-3xl">
298
- {getInitials()}
299
- </AvatarFallback>
300
- </Avatar>
301
- <p className="cls_profile_picture_library_tab_preview_empty_text text-sm text-slate-500 text-center">
302
- Select a photo to see preview
303
- </p>
304
- </div>
305
- )}
306
- </div>
307
- </div>
308
- </div>
309
- );
310
- }
311
-
@@ -1,341 +0,0 @@
1
- // file_description: Upload tab component for profile picture dialog with dropzone and preview
2
- // section: client_directive
3
- "use client";
4
-
5
- // section: imports
6
- import { useState, useCallback, useEffect } from "react";
7
- import { Switch } from "hazo_auth/components/ui/switch";
8
- import { Label } from "hazo_auth/components/ui/label";
9
- import { Avatar, AvatarImage, AvatarFallback } from "hazo_auth/components/ui/avatar";
10
- import { Upload, X, Loader2, Info } from "lucide-react";
11
- import { Button } from "hazo_auth/components/ui/button";
12
- import imageCompression from "browser-image-compression";
13
-
14
- // section: types
15
- export type ProfilePictureUploadTabProps = {
16
- useUpload: boolean;
17
- onUseUploadChange: (use: boolean) => void;
18
- onFileSelect: (file: File) => Promise<void>;
19
- maxSize: number; // in bytes
20
- uploadEnabled: boolean;
21
- disabled?: boolean;
22
- currentPreview?: string;
23
- photoUploadDisabledMessage?: string;
24
- imageCompressionMaxDimension?: number;
25
- uploadFileHardLimitBytes?: number;
26
- allowedImageMimeTypes?: string[];
27
- };
28
-
29
- // section: component
30
- /**
31
- * Upload tab component for profile picture dialog
32
- * Two columns: left = dropzone, right = preview
33
- * Uses browser-image-compression for client-side compression
34
- * @param props - Component props including upload state, file handler, and configuration
35
- * @returns Upload tab component
36
- */
37
- export function ProfilePictureUploadTab({
38
- useUpload,
39
- onUseUploadChange,
40
- onFileSelect,
41
- maxSize,
42
- uploadEnabled,
43
- disabled = false,
44
- currentPreview,
45
- photoUploadDisabledMessage,
46
- imageCompressionMaxDimension = 200,
47
- uploadFileHardLimitBytes = 10485760, // 10MB default
48
- allowedImageMimeTypes = ["image/jpeg", "image/jpg", "image/png"],
49
- }: ProfilePictureUploadTabProps) {
50
- const [dragActive, setDragActive] = useState(false);
51
- const [preview, setPreview] = useState<string | null>(currentPreview || null);
52
- const [isNewImage, setIsNewImage] = useState(false); // Track if preview is showing a newly uploaded image
53
- const [uploading, setUploading] = useState(false);
54
- const [compressing, setCompressing] = useState(false);
55
- const [error, setError] = useState<string | null>(null);
56
-
57
- // Update preview when currentPreview changes (e.g., when dialog opens)
58
- useEffect(() => {
59
- if (currentPreview) {
60
- setPreview(currentPreview);
61
- setIsNewImage(false); // Reset to current when dialog opens or currentPreview changes
62
- } else {
63
- // If no current preview, only clear if we're not showing a new image
64
- if (!isNewImage) {
65
- setPreview(null);
66
- }
67
- }
68
- // eslint-disable-next-line react-hooks/exhaustive-deps
69
- }, [currentPreview]); // Only depend on currentPreview to avoid loops, isNewImage check is intentional
70
-
71
- const handleFile = useCallback(async (file: File) => {
72
- // Validate file type
73
- if (!allowedImageMimeTypes.includes(file.type)) {
74
- setError(`Invalid file type. Only ${allowedImageMimeTypes.map(t => t.split("/")[1].toUpperCase()).join(", ")} files are allowed.`);
75
- return;
76
- }
77
-
78
- // Hard limit: reject files larger than configured limit (too large to process efficiently)
79
- if (file.size > uploadFileHardLimitBytes) {
80
- setError(`File is too large. Maximum size is ${Math.round(maxSize / 1024)}KB.`);
81
- return;
82
- }
83
-
84
- setError(null);
85
- setCompressing(false);
86
- setUploading(false);
87
-
88
- // If file is larger than maxSize, compress it
89
- if (file.size > maxSize) {
90
- setCompressing(true);
91
- try {
92
- // Compress image
93
- const options = {
94
- maxSizeMB: maxSize / (1024 * 1024), // Convert bytes to MB
95
- maxWidthOrHeight: imageCompressionMaxDimension,
96
- useWebWorker: true,
97
- fileType: file.type,
98
- };
99
-
100
- const compressedFile = await imageCompression(file, options);
101
- setCompressing(false);
102
-
103
- // Check if compressed file is still too large
104
- if (compressedFile.size > maxSize) {
105
- setError(`File is too large. Maximum size is ${Math.round(maxSize / 1024)}KB. After compression, file is ${Math.round(compressedFile.size / 1024)}KB.`);
106
- return;
107
- }
108
-
109
- // Create preview
110
- const reader = new FileReader();
111
- reader.onloadend = () => {
112
- setPreview(reader.result as string);
113
- setIsNewImage(true); // Mark as new image
114
- };
115
- reader.readAsDataURL(compressedFile);
116
-
117
- // Upload the compressed file
118
- setUploading(true);
119
- await onFileSelect(compressedFile);
120
- setUploading(false);
121
- } catch (error) {
122
- setCompressing(false);
123
- const errorMessage = error instanceof Error ? error.message : "Failed to compress image";
124
- setError(errorMessage);
125
- }
126
- } else {
127
- // File is already small enough, just upload it
128
- setUploading(true);
129
- try {
130
- // Create preview
131
- const reader = new FileReader();
132
- reader.onloadend = () => {
133
- setPreview(reader.result as string);
134
- setIsNewImage(true); // Mark as new image
135
- };
136
- reader.readAsDataURL(file);
137
-
138
- // Call onFileSelect with original file
139
- await onFileSelect(file);
140
- } catch (error) {
141
- const errorMessage = error instanceof Error ? error.message : "Failed to process image";
142
- setError(errorMessage);
143
- } finally {
144
- setUploading(false);
145
- }
146
- }
147
- }, [maxSize, onFileSelect]);
148
-
149
- const handleDrag = useCallback((e: React.DragEvent<HTMLDivElement>) => {
150
- e.preventDefault();
151
- e.stopPropagation();
152
- if (e.type === "dragenter" || e.type === "dragover") {
153
- setDragActive(true);
154
- } else if (e.type === "dragleave") {
155
- setDragActive(false);
156
- }
157
- }, []);
158
-
159
- const handleDrop = useCallback((e: React.DragEvent<HTMLDivElement>) => {
160
- e.preventDefault();
161
- e.stopPropagation();
162
- setDragActive(false);
163
-
164
- if (!uploadEnabled || disabled) {
165
- setError("Photo upload is not enabled");
166
- return;
167
- }
168
-
169
- if (e.dataTransfer.files && e.dataTransfer.files[0]) {
170
- void handleFile(e.dataTransfer.files[0]);
171
- }
172
- }, [uploadEnabled, disabled, handleFile]);
173
-
174
- const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
175
- e.preventDefault();
176
- if (!uploadEnabled || disabled) {
177
- setError("Photo upload is not enabled");
178
- return;
179
- }
180
-
181
- if (e.target.files && e.target.files[0]) {
182
- void handleFile(e.target.files[0]);
183
- }
184
- }, [uploadEnabled, disabled, handleFile]);
185
-
186
- const handleRemove = useCallback(() => {
187
- setPreview(currentPreview || null); // Reset to current preview if available
188
- setIsNewImage(false); // Reset to showing current image
189
- setError(null);
190
- }, [currentPreview]);
191
-
192
- const getInitials = (): string => {
193
- return "U";
194
- };
195
-
196
- return (
197
- <div className="cls_profile_picture_upload_tab flex flex-col gap-4">
198
- {/* Switch */}
199
- <div className="cls_profile_picture_upload_tab_switch flex items-center gap-3">
200
- <Switch
201
- id="use-upload"
202
- checked={useUpload}
203
- onCheckedChange={onUseUploadChange}
204
- disabled={disabled || !uploadEnabled}
205
- className="cls_profile_picture_upload_tab_switch_input"
206
- aria-label="Use uploaded photo"
207
- />
208
- <Label
209
- htmlFor="use-upload"
210
- className="cls_profile_picture_upload_tab_switch_label text-sm font-medium text-slate-700 cursor-pointer"
211
- >
212
- Use uploaded photo
213
- </Label>
214
- </div>
215
-
216
- {!uploadEnabled && (
217
- <div className="cls_profile_picture_upload_tab_disabled flex items-center gap-2 p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
218
- <Info className="h-4 w-4 text-yellow-600" aria-hidden="true" />
219
- <p className="cls_profile_picture_upload_tab_disabled_text text-sm text-yellow-800">
220
- {photoUploadDisabledMessage}
221
- </p>
222
- </div>
223
- )}
224
-
225
- {/* Two columns: dropzone and preview */}
226
- <div className="cls_profile_picture_upload_tab_content grid grid-cols-1 md:grid-cols-2 gap-6">
227
- {/* Left column: Dropzone */}
228
- <div className="cls_profile_picture_upload_tab_dropzone_container flex flex-col gap-2">
229
- <Label className="cls_profile_picture_upload_tab_dropzone_label text-sm font-medium text-slate-700">
230
- Upload Photo
231
- </Label>
232
- <div
233
- className={`
234
- cls_profile_picture_upload_tab_dropzone
235
- flex flex-col items-center justify-center
236
- border-2 border-dashed rounded-lg p-8
237
- transition-colors
238
- ${dragActive ? "border-blue-500 bg-blue-50" : "border-slate-300 bg-slate-50"}
239
- ${disabled || !uploadEnabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:border-slate-400"}
240
- `}
241
- onDragEnter={handleDrag}
242
- onDragLeave={handleDrag}
243
- onDragOver={handleDrag}
244
- onDrop={handleDrop}
245
- onClick={() => {
246
- if (!disabled && uploadEnabled) {
247
- document.getElementById("file-upload-input")?.click();
248
- }
249
- }}
250
- >
251
- <input
252
- id="file-upload-input"
253
- type="file"
254
- accept={allowedImageMimeTypes.join(",")}
255
- onChange={handleChange}
256
- disabled={disabled || !uploadEnabled}
257
- className="hidden"
258
- aria-label="Upload profile picture"
259
- />
260
- <Upload className="h-8 w-8 text-slate-400 mb-2" aria-hidden="true" />
261
- <p className="cls_profile_picture_upload_tab_dropzone_text text-sm text-slate-600 text-center">
262
- Drag and drop an image here, or click to select
263
- </p>
264
- <p className="cls_profile_picture_upload_tab_dropzone_hint text-xs text-slate-500 text-center mt-1">
265
- JPG or PNG, max {Math.round(maxSize / 1024)}KB
266
- </p>
267
- </div>
268
- {error && (
269
- <p className="cls_profile_picture_upload_tab_error text-sm text-red-600" role="alert">
270
- {error}
271
- </p>
272
- )}
273
- </div>
274
-
275
- {/* Right column: Preview */}
276
- <div className="cls_profile_picture_upload_tab_preview_container flex flex-col gap-2">
277
- <Label className="cls_profile_picture_upload_tab_preview_label text-sm font-medium text-slate-700">
278
- {isNewImage ? "Preview (new)" : "Preview (current)"}
279
- </Label>
280
- <div className="cls_profile_picture_upload_tab_preview_content flex flex-col items-center justify-center border border-slate-200 rounded-lg p-6 bg-slate-50 min-h-[200px]">
281
- {compressing ? (
282
- <div className="cls_profile_picture_upload_tab_compressing flex flex-col items-center gap-2">
283
- <Loader2 className="h-8 w-8 text-slate-400 animate-spin" aria-hidden="true" />
284
- <p className="cls_profile_picture_upload_tab_compressing_text text-sm text-slate-600">
285
- Compressing image...
286
- </p>
287
- </div>
288
- ) : uploading ? (
289
- <div className="cls_profile_picture_upload_tab_uploading flex flex-col items-center gap-2">
290
- <Loader2 className="h-8 w-8 text-slate-400 animate-spin" aria-hidden="true" />
291
- <p className="cls_profile_picture_upload_tab_uploading_text text-sm text-slate-600">
292
- Uploading...
293
- </p>
294
- </div>
295
- ) : preview ? (
296
- <div className="cls_profile_picture_upload_tab_preview_image_container flex flex-col items-center gap-4">
297
- <div className="cls_profile_picture_upload_tab_preview_image_wrapper relative">
298
- <Avatar className="cls_profile_picture_upload_tab_preview_avatar h-32 w-32">
299
- <AvatarImage
300
- src={preview}
301
- alt="Uploaded profile picture preview"
302
- className="cls_profile_picture_upload_tab_preview_avatar_image"
303
- />
304
- <AvatarFallback className="cls_profile_picture_upload_tab_preview_avatar_fallback bg-slate-200 text-slate-600 text-3xl">
305
- {getInitials()}
306
- </AvatarFallback>
307
- </Avatar>
308
- <Button
309
- type="button"
310
- onClick={handleRemove}
311
- variant="ghost"
312
- size="icon"
313
- className="cls_profile_picture_upload_tab_preview_remove absolute -top-2 -right-2 rounded-full h-6 w-6 border border-slate-300 bg-white hover:bg-slate-50"
314
- aria-label="Remove preview"
315
- >
316
- <X className="h-4 w-4" aria-hidden="true" />
317
- </Button>
318
- </div>
319
- <p className="cls_profile_picture_upload_tab_preview_success_text text-sm text-slate-600 text-center">
320
- Preview of your uploaded photo
321
- </p>
322
- </div>
323
- ) : (
324
- <div className="cls_profile_picture_upload_tab_preview_empty flex flex-col items-center gap-2">
325
- <Avatar className="cls_profile_picture_upload_tab_preview_empty_avatar h-32 w-32">
326
- <AvatarFallback className="cls_profile_picture_upload_tab_preview_empty_avatar_fallback bg-slate-200 text-slate-600 text-3xl">
327
- {getInitials()}
328
- </AvatarFallback>
329
- </Avatar>
330
- <p className="cls_profile_picture_upload_tab_preview_empty_text text-sm text-slate-500 text-center">
331
- Upload an image to see preview
332
- </p>
333
- </div>
334
- )}
335
- </div>
336
- </div>
337
- </div>
338
- </div>
339
- );
340
- }
341
-