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.
- package/README.md +25 -38
- package/SETUP_CHECKLIST.md +708 -0
- package/dist/app/api/hazo_auth/change_password/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/change_password/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/change_password/route.js +98 -0
- package/dist/app/api/hazo_auth/forgot_password/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/forgot_password/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/forgot_password/route.js +78 -0
- package/dist/app/api/hazo_auth/get_auth/route.d.ts +10 -0
- package/dist/app/api/hazo_auth/get_auth/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/get_auth/route.js +63 -0
- package/dist/app/api/hazo_auth/invalidate_cache/route.d.ts +14 -0
- package/dist/app/api/hazo_auth/invalidate_cache/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/invalidate_cache/route.js +96 -0
- package/dist/app/api/hazo_auth/library_photos/route.d.ts +13 -0
- package/dist/app/api/hazo_auth/library_photos/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/library_photos/route.js +55 -0
- package/dist/app/api/hazo_auth/login/route.d.ts +12 -0
- package/dist/app/api/hazo_auth/login/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/login/route.js +140 -0
- package/dist/app/api/hazo_auth/logout/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/logout/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/logout/route.js +71 -0
- package/dist/app/api/hazo_auth/me/route.d.ts +3 -0
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/me/route.js +34 -0
- package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts +7 -0
- package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/profile_picture/[filename]/route.js +43 -0
- package/dist/app/api/hazo_auth/register/route.d.ts +9 -0
- package/dist/app/api/hazo_auth/register/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/register/route.js +80 -0
- package/dist/app/api/hazo_auth/remove_profile_picture/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/remove_profile_picture/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/remove_profile_picture/route.js +64 -0
- package/dist/app/api/hazo_auth/resend_verification/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/resend_verification/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/resend_verification/route.js +79 -0
- package/dist/app/api/hazo_auth/reset_password/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/reset_password/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/reset_password/route.js +76 -0
- package/dist/app/api/hazo_auth/update_user/route.d.ts +9 -0
- package/dist/app/api/hazo_auth/update_user/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/update_user/route.js +95 -0
- package/dist/app/api/hazo_auth/upload_profile_picture/route.d.ts +9 -0
- package/dist/app/api/hazo_auth/upload_profile_picture/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/upload_profile_picture/route.js +204 -0
- package/dist/app/api/hazo_auth/validate_reset_token/route.d.ts +6 -0
- package/dist/app/api/hazo_auth/validate_reset_token/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/validate_reset_token/route.js +58 -0
- package/dist/app/api/hazo_auth/verify_email/route.d.ts +11 -0
- package/dist/app/api/hazo_auth/verify_email/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/verify_email/route.js +63 -0
- package/dist/cli/generate.d.ts +2 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +117 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +120 -0
- package/dist/cli/validate.d.ts +15 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/cli/validate.js +509 -0
- package/dist/components/layouts/email_verification/config/email_verification_field_config.d.ts +2 -2
- package/dist/components/layouts/email_verification/config/email_verification_field_config.d.ts.map +1 -1
- package/dist/components/layouts/email_verification/config/email_verification_field_config.js +1 -1
- package/dist/components/layouts/email_verification/hooks/use_email_verification.d.ts +2 -2
- package/dist/components/layouts/email_verification/hooks/use_email_verification.d.ts.map +1 -1
- package/dist/components/layouts/email_verification/hooks/use_email_verification.js +2 -2
- package/dist/components/layouts/email_verification/index.d.ts +3 -3
- package/dist/components/layouts/email_verification/index.d.ts.map +1 -1
- package/dist/components/layouts/email_verification/index.js +9 -9
- package/dist/components/layouts/forgot_password/config/forgot_password_field_config.d.ts +2 -2
- package/dist/components/layouts/forgot_password/config/forgot_password_field_config.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/config/forgot_password_field_config.js +1 -1
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts +2 -2
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.js +2 -2
- package/dist/components/layouts/forgot_password/index.d.ts +2 -2
- package/dist/components/layouts/forgot_password/index.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/index.js +8 -8
- package/dist/components/layouts/login/config/login_field_config.d.ts +2 -2
- package/dist/components/layouts/login/config/login_field_config.d.ts.map +1 -1
- package/dist/components/layouts/login/config/login_field_config.js +1 -1
- package/dist/components/layouts/login/hooks/use_login_form.d.ts +2 -2
- package/dist/components/layouts/login/hooks/use_login_form.d.ts.map +1 -1
- package/dist/components/layouts/login/hooks/use_login_form.js +4 -4
- package/dist/components/layouts/login/index.d.ts +2 -2
- package/dist/components/layouts/login/index.d.ts.map +1 -1
- package/dist/components/layouts/login/index.js +9 -9
- package/dist/components/layouts/my_settings/components/editable_field.js +3 -3
- package/dist/components/layouts/my_settings/components/password_change_dialog.d.ts +1 -1
- package/dist/components/layouts/my_settings/components/password_change_dialog.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/components/password_change_dialog.js +4 -4
- package/dist/components/layouts/my_settings/components/profile_picture_dialog.js +7 -7
- package/dist/components/layouts/my_settings/components/profile_picture_display.js +1 -1
- package/dist/components/layouts/my_settings/components/profile_picture_gravatar_tab.js +3 -3
- package/dist/components/layouts/my_settings/components/profile_picture_library_tab.js +5 -5
- package/dist/components/layouts/my_settings/components/profile_picture_upload_tab.js +4 -4
- package/dist/components/layouts/my_settings/config/my_settings_field_config.d.ts +1 -1
- package/dist/components/layouts/my_settings/config/my_settings_field_config.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/config/my_settings_field_config.js +1 -1
- package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts +1 -1
- package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/hooks/use_my_settings.js +1 -1
- package/dist/components/layouts/my_settings/index.d.ts +2 -2
- package/dist/components/layouts/my_settings/index.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/index.js +9 -9
- package/dist/components/layouts/register/config/register_field_config.d.ts +2 -2
- package/dist/components/layouts/register/config/register_field_config.d.ts.map +1 -1
- package/dist/components/layouts/register/config/register_field_config.js +1 -1
- package/dist/components/layouts/register/hooks/use_register_form.d.ts +3 -3
- package/dist/components/layouts/register/hooks/use_register_form.d.ts.map +1 -1
- package/dist/components/layouts/register/hooks/use_register_form.js +2 -2
- package/dist/components/layouts/register/index.d.ts +2 -2
- package/dist/components/layouts/register/index.d.ts.map +1 -1
- package/dist/components/layouts/register/index.js +9 -9
- package/dist/components/layouts/reset_password/config/reset_password_field_config.d.ts +2 -2
- package/dist/components/layouts/reset_password/config/reset_password_field_config.d.ts.map +1 -1
- package/dist/components/layouts/reset_password/config/reset_password_field_config.js +1 -1
- package/dist/components/layouts/reset_password/hooks/use_reset_password_form.d.ts +3 -3
- package/dist/components/layouts/reset_password/hooks/use_reset_password_form.d.ts.map +1 -1
- package/dist/components/layouts/reset_password/hooks/use_reset_password_form.js +2 -2
- package/dist/components/layouts/reset_password/index.d.ts +2 -2
- package/dist/components/layouts/reset_password/index.d.ts.map +1 -1
- package/dist/components/layouts/reset_password/index.js +8 -8
- package/dist/components/layouts/shared/components/already_logged_in_guard.js +4 -4
- package/dist/components/layouts/shared/components/auth_page_shell.js +3 -3
- package/dist/components/layouts/shared/components/form_action_buttons.d.ts +1 -1
- package/dist/components/layouts/shared/components/form_action_buttons.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/form_action_buttons.js +1 -1
- package/dist/components/layouts/shared/components/form_field_wrapper.js +2 -2
- package/dist/components/layouts/shared/components/logout_button.js +2 -2
- package/dist/components/layouts/shared/components/password_field.js +3 -3
- package/dist/components/layouts/shared/components/profile_pic_menu.d.ts +1 -1
- package/dist/components/layouts/shared/components/profile_pic_menu.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/profile_pic_menu.js +4 -4
- package/dist/components/layouts/shared/components/profile_pic_menu_wrapper.js +2 -2
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +3 -3
- package/dist/components/layouts/shared/components/standalone_layout_wrapper.js +1 -1
- package/dist/components/layouts/shared/components/two_column_auth_layout.js +1 -1
- package/dist/components/layouts/shared/components/unauthorized_guard.js +2 -2
- package/dist/components/layouts/shared/hooks/use_hazo_auth.d.ts +1 -1
- package/dist/components/layouts/shared/hooks/use_hazo_auth.d.ts.map +1 -1
- package/dist/components/layouts/shared/utils/validation.d.ts +1 -1
- package/dist/components/layouts/shared/utils/validation.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/roles_matrix.js +7 -7
- package/dist/components/layouts/user_management/index.js +11 -11
- package/dist/components/ui/alert-dialog.js +2 -2
- package/dist/components/ui/avatar.js +1 -1
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +45 -0
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/dialog.js +1 -1
- package/dist/components/ui/dropdown-menu.js +1 -1
- package/dist/components/ui/hazo_ui_tooltip.js +1 -1
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/label.js +1 -1
- package/dist/components/ui/separator.js +1 -1
- package/dist/components/ui/sheet.js +1 -1
- package/dist/components/ui/sidebar.d.ts +2 -2
- package/dist/components/ui/sidebar.d.ts.map +1 -1
- package/dist/components/ui/sidebar.js +8 -8
- package/dist/components/ui/skeleton.js +1 -1
- package/dist/components/ui/switch.js +1 -1
- package/dist/components/ui/table.js +1 -1
- package/dist/components/ui/tabs.js +1 -1
- package/dist/components/ui/tooltip.js +1 -1
- package/dist/components/ui/vertical-tabs.js +1 -1
- package/dist/hooks/use-mobile.d.ts.map +1 -1
- package/dist/hooks/use-mobile.js +17 -3
- package/dist/lib/already_logged_in_config.server.js +1 -1
- package/dist/lib/app_logger.js +1 -1
- package/dist/lib/auth/auth_cache.d.ts +1 -1
- package/dist/lib/auth/auth_cache.d.ts.map +1 -1
- package/dist/lib/auth/auth_utils.server.js +2 -2
- package/dist/lib/auth/hazo_get_auth.server.d.ts +1 -1
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +7 -7
- package/dist/lib/auth/server_auth.js +2 -2
- package/dist/lib/auth_utility_config.server.js +1 -1
- package/dist/lib/config/config_loader.server.js +1 -1
- package/dist/lib/email_verification_config.server.js +1 -1
- package/dist/lib/file_types_config.server.js +1 -1
- package/dist/lib/forgot_password_config.server.js +1 -1
- package/dist/lib/hazo_connect_instance.server.js +2 -2
- package/dist/lib/hazo_connect_setup.server.js +2 -2
- package/dist/lib/login_config.server.js +2 -2
- package/dist/lib/messages_config.server.js +1 -1
- package/dist/lib/my_settings_config.server.js +7 -7
- package/dist/lib/password_requirements_config.server.js +1 -1
- package/dist/lib/profile_pic_menu_config.server.js +1 -1
- package/dist/lib/profile_picture_config.server.js +2 -2
- package/dist/lib/register_config.server.js +4 -4
- package/dist/lib/reset_password_config.server.js +3 -3
- package/dist/lib/services/email_service.js +2 -2
- package/dist/lib/services/email_verification_service.js +3 -3
- package/dist/lib/services/login_service.js +3 -3
- package/dist/lib/services/password_change_service.js +3 -3
- package/dist/lib/services/password_reset_service.js +3 -3
- package/dist/lib/services/profile_picture_remove_service.js +3 -3
- package/dist/lib/services/profile_picture_service.d.ts +1 -1
- package/dist/lib/services/profile_picture_service.d.ts.map +1 -1
- package/dist/lib/services/profile_picture_service.js +5 -5
- package/dist/lib/services/registration_service.js +8 -8
- package/dist/lib/services/token_service.js +2 -2
- package/dist/lib/services/user_profiles_service.js +2 -2
- package/dist/lib/services/user_update_service.d.ts +1 -1
- package/dist/lib/services/user_update_service.d.ts.map +1 -1
- package/dist/lib/services/user_update_service.js +4 -4
- package/dist/lib/ui_shell_config.server.js +1 -1
- package/dist/lib/ui_sizes_config.server.js +1 -1
- package/dist/lib/user_fields_config.server.js +1 -1
- package/dist/lib/user_management_config.server.js +1 -1
- package/dist/lib/utils/error_sanitizer.d.ts +1 -1
- package/dist/lib/utils/error_sanitizer.d.ts.map +1 -1
- package/dist/server/config/config_loader.d.ts +1 -1
- package/dist/server/config/config_loader.d.ts.map +1 -1
- package/dist/server/config/config_loader.js +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/logging/logger_service.d.ts +1 -1
- package/dist/server/logging/logger_service.d.ts.map +1 -1
- package/dist/server/routes/change_password.d.ts +2 -0
- package/dist/server/routes/change_password.d.ts.map +1 -0
- package/dist/server/routes/change_password.js +2 -0
- package/dist/server/routes/forgot_password.d.ts +2 -0
- package/dist/server/routes/forgot_password.d.ts.map +1 -0
- package/dist/server/routes/forgot_password.js +2 -0
- package/dist/server/routes/get_auth.d.ts +2 -0
- package/dist/server/routes/get_auth.d.ts.map +1 -0
- package/dist/server/routes/get_auth.js +2 -0
- package/dist/server/routes/index.d.ts +18 -0
- package/dist/server/routes/index.d.ts.map +1 -0
- package/dist/server/routes/index.js +24 -0
- package/dist/server/routes/invalidate_cache.d.ts +2 -0
- package/dist/server/routes/invalidate_cache.d.ts.map +1 -0
- package/dist/server/routes/invalidate_cache.js +2 -0
- package/dist/server/routes/library_photos.d.ts +2 -0
- package/dist/server/routes/library_photos.d.ts.map +1 -0
- package/dist/server/routes/library_photos.js +2 -0
- package/dist/server/routes/login.d.ts +2 -0
- package/dist/server/routes/login.d.ts.map +1 -0
- package/dist/server/routes/login.js +2 -0
- package/dist/server/routes/logout.d.ts +2 -0
- package/dist/server/routes/logout.d.ts.map +1 -0
- package/dist/server/routes/logout.js +2 -0
- package/dist/server/routes/me.d.ts +2 -0
- package/dist/server/routes/me.d.ts.map +1 -0
- package/dist/server/routes/me.js +2 -0
- package/dist/server/routes/profile_picture_filename.d.ts +2 -0
- package/dist/server/routes/profile_picture_filename.d.ts.map +1 -0
- package/dist/server/routes/profile_picture_filename.js +3 -0
- package/dist/server/routes/register.d.ts +2 -0
- package/dist/server/routes/register.d.ts.map +1 -0
- package/dist/server/routes/register.js +2 -0
- package/dist/server/routes/remove_profile_picture.d.ts +2 -0
- package/dist/server/routes/remove_profile_picture.d.ts.map +1 -0
- package/dist/server/routes/remove_profile_picture.js +2 -0
- package/dist/server/routes/resend_verification.d.ts +2 -0
- package/dist/server/routes/resend_verification.d.ts.map +1 -0
- package/dist/server/routes/resend_verification.js +2 -0
- package/dist/server/routes/reset_password.d.ts +2 -0
- package/dist/server/routes/reset_password.d.ts.map +1 -0
- package/dist/server/routes/reset_password.js +2 -0
- package/dist/server/routes/update_user.d.ts +2 -0
- package/dist/server/routes/update_user.d.ts.map +1 -0
- package/dist/server/routes/update_user.js +2 -0
- package/dist/server/routes/upload_profile_picture.d.ts +2 -0
- package/dist/server/routes/upload_profile_picture.d.ts.map +1 -0
- package/dist/server/routes/upload_profile_picture.js +2 -0
- package/dist/server/routes/validate_reset_token.d.ts +2 -0
- package/dist/server/routes/validate_reset_token.d.ts.map +1 -0
- package/dist/server/routes/validate_reset_token.js +2 -0
- package/dist/server/routes/verify_email.d.ts +2 -0
- package/dist/server/routes/verify_email.d.ts.map +1 -0
- package/dist/server/routes/verify_email.js +2 -0
- package/dist/server/server.js +2 -2
- package/package.json +14 -115
- package/components.json +0 -22
- package/instrumentation.ts +0 -32
- package/migrations/001_add_token_type_to_refresh_tokens.sql +0 -14
- package/migrations/002_add_name_to_hazo_users.sql +0 -7
- package/migrations/003_add_url_on_logon_to_hazo_users.sql +0 -8
- package/next.config.mjs +0 -67
- package/postcss.config.mjs +0 -8
- package/public/file.svg +0 -1
- package/public/globe.svg +0 -1
- package/public/next.svg +0 -1
- package/public/vercel.svg +0 -1
- package/public/window.svg +0 -1
- package/scripts/apply_migration.ts +0 -118
- package/scripts/init_users.ts +0 -378
- package/src/app/api/hazo_auth/auth/upload_profile_picture/route.ts +0 -268
- package/src/app/api/hazo_auth/change_password/route.ts +0 -132
- package/src/app/api/hazo_auth/forgot_password/route.ts +0 -107
- package/src/app/api/hazo_auth/get_auth/route.ts +0 -89
- package/src/app/api/hazo_auth/invalidate_cache/route.ts +0 -139
- package/src/app/api/hazo_auth/library_photos/route.ts +0 -73
- package/src/app/api/hazo_auth/login/route.ts +0 -181
- package/src/app/api/hazo_auth/logout/route.ts +0 -89
- package/src/app/api/hazo_auth/me/route.ts +0 -47
- package/src/app/api/hazo_auth/profile_picture/[filename]/route.ts +0 -67
- package/src/app/api/hazo_auth/register/route.ts +0 -109
- package/src/app/api/hazo_auth/remove_profile_picture/route.ts +0 -86
- package/src/app/api/hazo_auth/resend_verification/route.ts +0 -108
- package/src/app/api/hazo_auth/reset_password/route.ts +0 -107
- package/src/app/api/hazo_auth/update_user/route.ts +0 -126
- package/src/app/api/hazo_auth/upload_profile_picture/route.ts +0 -268
- package/src/app/api/hazo_auth/user_management/permissions/route.ts +0 -367
- package/src/app/api/hazo_auth/user_management/roles/route.ts +0 -442
- package/src/app/api/hazo_auth/user_management/users/roles/route.ts +0 -367
- package/src/app/api/hazo_auth/user_management/users/route.ts +0 -239
- package/src/app/api/hazo_auth/validate_reset_token/route.ts +0 -83
- package/src/app/api/hazo_auth/verify_email/route.ts +0 -88
- package/src/app/api/migrations/apply/route.ts +0 -91
- package/src/app/favicon.ico +0 -0
- package/src/app/fonts/GeistMonoVF.woff +0 -0
- package/src/app/fonts/GeistVF.woff +0 -0
- package/src/app/globals.css +0 -89
- package/src/app/hazo_auth/forgot_password/forgot_password_page_client.tsx +0 -60
- package/src/app/hazo_auth/forgot_password/page.tsx +0 -24
- package/src/app/hazo_auth/login/login_page_client.tsx +0 -86
- package/src/app/hazo_auth/login/page.tsx +0 -38
- package/src/app/hazo_auth/my_settings/my_settings_page_client.tsx +0 -120
- package/src/app/hazo_auth/my_settings/page.tsx +0 -40
- package/src/app/hazo_auth/register/page.tsx +0 -36
- package/src/app/hazo_auth/register/register_page_client.tsx +0 -81
- package/src/app/hazo_auth/reset_password/page.tsx +0 -29
- package/src/app/hazo_auth/reset_password/reset_password_page_client.tsx +0 -81
- package/src/app/hazo_auth/user_management/page.tsx +0 -14
- package/src/app/hazo_auth/user_management/user_management_page_client.tsx +0 -16
- package/src/app/hazo_auth/verify_email/page.tsx +0 -24
- package/src/app/hazo_auth/verify_email/verify_email_page_client.tsx +0 -60
- package/src/app/hazo_connect/api/sqlite/data/route.ts +0 -203
- package/src/app/hazo_connect/api/sqlite/schema/route.ts +0 -45
- package/src/app/hazo_connect/api/sqlite/tables/route.ts +0 -36
- package/src/app/hazo_connect/sqlite_admin/page.tsx +0 -51
- package/src/app/hazo_connect/sqlite_admin/sqlite-admin-client.tsx +0 -984
- package/src/app/layout.tsx +0 -43
- package/src/app/page.tsx +0 -170
- package/src/components/index.ts +0 -7
- package/src/components/layouts/email_verification/config/email_verification_field_config.ts +0 -86
- package/src/components/layouts/email_verification/hooks/use_email_verification.ts +0 -297
- package/src/components/layouts/email_verification/index.tsx +0 -297
- package/src/components/layouts/forgot_password/config/forgot_password_field_config.ts +0 -58
- package/src/components/layouts/forgot_password/hooks/use_forgot_password_form.ts +0 -179
- package/src/components/layouts/forgot_password/index.tsx +0 -168
- package/src/components/layouts/index.ts +0 -26
- package/src/components/layouts/login/config/login_field_config.ts +0 -67
- package/src/components/layouts/login/hooks/use_login_form.ts +0 -286
- package/src/components/layouts/login/index.tsx +0 -252
- package/src/components/layouts/my_settings/components/editable_field.tsx +0 -177
- package/src/components/layouts/my_settings/components/password_change_dialog.tsx +0 -301
- package/src/components/layouts/my_settings/components/profile_picture_dialog.tsx +0 -385
- package/src/components/layouts/my_settings/components/profile_picture_display.tsx +0 -66
- package/src/components/layouts/my_settings/components/profile_picture_gravatar_tab.tsx +0 -143
- package/src/components/layouts/my_settings/components/profile_picture_library_tab.tsx +0 -311
- package/src/components/layouts/my_settings/components/profile_picture_upload_tab.tsx +0 -341
- package/src/components/layouts/my_settings/config/my_settings_field_config.ts +0 -61
- package/src/components/layouts/my_settings/hooks/use_my_settings.ts +0 -458
- package/src/components/layouts/my_settings/index.tsx +0 -351
- package/src/components/layouts/register/config/register_field_config.ts +0 -101
- package/src/components/layouts/register/hooks/use_register_form.ts +0 -275
- package/src/components/layouts/register/index.tsx +0 -226
- package/src/components/layouts/reset_password/config/reset_password_field_config.ts +0 -86
- package/src/components/layouts/reset_password/hooks/use_reset_password_form.ts +0 -276
- package/src/components/layouts/reset_password/index.tsx +0 -294
- package/src/components/layouts/shared/components/already_logged_in_guard.tsx +0 -95
- package/src/components/layouts/shared/components/auth_page_shell.tsx +0 -36
- package/src/components/layouts/shared/components/field_error_message.tsx +0 -29
- package/src/components/layouts/shared/components/form_action_buttons.tsx +0 -64
- package/src/components/layouts/shared/components/form_field_wrapper.tsx +0 -44
- package/src/components/layouts/shared/components/form_header.tsx +0 -36
- package/src/components/layouts/shared/components/logout_button.tsx +0 -76
- package/src/components/layouts/shared/components/password_field.tsx +0 -72
- package/src/components/layouts/shared/components/profile_pic_menu.tsx +0 -321
- package/src/components/layouts/shared/components/profile_pic_menu_wrapper.tsx +0 -40
- package/src/components/layouts/shared/components/sidebar_layout_wrapper.tsx +0 -214
- package/src/components/layouts/shared/components/standalone_layout_wrapper.tsx +0 -53
- package/src/components/layouts/shared/components/two_column_auth_layout.tsx +0 -44
- package/src/components/layouts/shared/components/unauthorized_guard.tsx +0 -78
- package/src/components/layouts/shared/components/visual_panel.tsx +0 -41
- package/src/components/layouts/shared/config/layout_customization.ts +0 -95
- package/src/components/layouts/shared/data/layout_data_client.ts +0 -19
- package/src/components/layouts/shared/hooks/use_auth_status.ts +0 -103
- package/src/components/layouts/shared/hooks/use_hazo_auth.ts +0 -158
- package/src/components/layouts/shared/index.ts +0 -34
- package/src/components/layouts/shared/utils/ip_address.ts +0 -37
- package/src/components/layouts/shared/utils/validation.ts +0 -66
- package/src/components/layouts/user_management/components/roles_matrix.tsx +0 -607
- package/src/components/layouts/user_management/index.tsx +0 -1295
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/avatar.tsx +0 -50
- package/src/components/ui/button.tsx +0 -57
- package/src/components/ui/checkbox.tsx +0 -30
- package/src/components/ui/dialog.tsx +0 -122
- package/src/components/ui/dropdown-menu.tsx +0 -201
- package/src/components/ui/hazo_ui_tooltip.tsx +0 -67
- package/src/components/ui/index.ts +0 -22
- package/src/components/ui/input.tsx +0 -22
- package/src/components/ui/label.tsx +0 -26
- package/src/components/ui/separator.tsx +0 -31
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/sidebar.tsx +0 -773
- package/src/components/ui/skeleton.tsx +0 -15
- package/src/components/ui/sonner.tsx +0 -31
- package/src/components/ui/switch.tsx +0 -29
- package/src/components/ui/table.tsx +0 -120
- package/src/components/ui/tabs.tsx +0 -55
- package/src/components/ui/tooltip.tsx +0 -32
- package/src/components/ui/vertical-tabs.tsx +0 -59
- package/src/hooks/use-mobile.tsx +0 -19
- package/src/index.ts +0 -7
- package/src/lib/already_logged_in_config.server.ts +0 -46
- package/src/lib/app_logger.ts +0 -24
- package/src/lib/auth/auth_cache.ts +0 -220
- package/src/lib/auth/auth_rate_limiter.ts +0 -121
- package/src/lib/auth/auth_types.ts +0 -65
- package/src/lib/auth/auth_utils.server.ts +0 -196
- package/src/lib/auth/hazo_get_auth.server.ts +0 -333
- package/src/lib/auth/index.ts +0 -23
- package/src/lib/auth/server_auth.ts +0 -88
- package/src/lib/auth_utility_config.server.ts +0 -136
- package/src/lib/config/config_loader.server.ts +0 -164
- package/src/lib/email_verification_config.server.ts +0 -32
- package/src/lib/file_types_config.server.ts +0 -25
- package/src/lib/forgot_password_config.server.ts +0 -32
- package/src/lib/hazo_connect_instance.server.ts +0 -101
- package/src/lib/hazo_connect_setup.server.ts +0 -194
- package/src/lib/hazo_connect_setup.ts +0 -54
- package/src/lib/index.ts +0 -44
- package/src/lib/login_config.server.ts +0 -71
- package/src/lib/messages_config.server.ts +0 -45
- package/src/lib/migrations/apply_migration.ts +0 -105
- package/src/lib/my_settings_config.server.ts +0 -135
- package/src/lib/password_requirements_config.server.ts +0 -39
- package/src/lib/profile_pic_menu_config.server.ts +0 -138
- package/src/lib/profile_picture_config.server.ts +0 -56
- package/src/lib/register_config.server.ts +0 -73
- package/src/lib/reset_password_config.server.ts +0 -75
- package/src/lib/services/email_service.ts +0 -581
- package/src/lib/services/email_verification_service.ts +0 -270
- package/src/lib/services/index.ts +0 -15
- package/src/lib/services/login_service.ts +0 -134
- package/src/lib/services/password_change_service.ts +0 -154
- package/src/lib/services/password_reset_service.ts +0 -405
- package/src/lib/services/profile_picture_remove_service.ts +0 -120
- package/src/lib/services/profile_picture_service.ts +0 -215
- package/src/lib/services/profile_picture_source_mapper.ts +0 -62
- package/src/lib/services/registration_service.ts +0 -184
- package/src/lib/services/token_service.ts +0 -240
- package/src/lib/services/user_profiles_service.ts +0 -143
- package/src/lib/services/user_update_service.ts +0 -141
- package/src/lib/ui_shell_config.server.ts +0 -73
- package/src/lib/ui_sizes_config.server.ts +0 -37
- package/src/lib/user_fields_config.server.ts +0 -31
- package/src/lib/user_management_config.server.ts +0 -39
- package/src/lib/utils/api_route_helpers.ts +0 -60
- package/src/lib/utils/error_sanitizer.ts +0 -75
- package/src/lib/utils.ts +0 -11
- package/src/middleware.ts +0 -94
- package/src/routes/index.ts +0 -34
- package/src/server/config/config_loader.ts +0 -496
- package/src/server/index.ts +0 -38
- package/src/server/logging/logger_service.ts +0 -56
- package/src/server/routes/root_router.ts +0 -16
- package/src/server/server.ts +0 -28
- package/src/server/types/app_types.ts +0 -74
- package/src/server/types/express.d.ts +0 -16
- package/src/stories/email_verification_layout.stories.tsx +0 -137
- package/src/stories/forgot_password_layout.stories.tsx +0 -85
- package/src/stories/login_layout.stories.tsx +0 -85
- package/src/stories/project_overview.stories.tsx +0 -33
- package/src/stories/register_layout.stories.tsx +0 -107
- package/tailwind.config.ts +0 -77
- package/tsconfig.build.json +0 -39
- 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
|
-
|