hazo_auth 1.4.2 → 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/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/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/hooks/use-mobile.d.ts.map +1 -1
- package/dist/hooks/use-mobile.js +17 -3
- 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/package.json +12 -17
- 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 -36
- package/tsconfig.json +0 -28
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
// file_description: profile picture dialog component with tabs for Upload, Library, and Gravatar
|
|
2
|
-
// section: client_directive
|
|
3
|
-
"use client";
|
|
4
|
-
|
|
5
|
-
// section: imports
|
|
6
|
-
import { useState, useEffect } from "react";
|
|
7
|
-
import {
|
|
8
|
-
Dialog,
|
|
9
|
-
DialogContent,
|
|
10
|
-
DialogHeader,
|
|
11
|
-
DialogTitle,
|
|
12
|
-
DialogDescription,
|
|
13
|
-
} from "../../../ui/dialog";
|
|
14
|
-
import { Button } from "../../../ui/button";
|
|
15
|
-
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/tabs";
|
|
16
|
-
import { ProfilePictureUploadTab } from "./profile_picture_upload_tab";
|
|
17
|
-
import { ProfilePictureLibraryTab } from "./profile_picture_library_tab";
|
|
18
|
-
import { ProfilePictureGravatarTab } from "./profile_picture_gravatar_tab";
|
|
19
|
-
import { toast } from "sonner";
|
|
20
|
-
import { cn } from "../../../../lib/utils";
|
|
21
|
-
|
|
22
|
-
// section: types
|
|
23
|
-
export type ProfilePictureDialogProps = {
|
|
24
|
-
open: boolean;
|
|
25
|
-
onOpenChange: (open: boolean) => void;
|
|
26
|
-
onSave: (profilePictureUrl: string, profileSource: "upload" | "library" | "gravatar") => Promise<void>;
|
|
27
|
-
email: string;
|
|
28
|
-
allowPhotoUpload: boolean;
|
|
29
|
-
maxPhotoSize: number; // in bytes
|
|
30
|
-
libraryPhotoPath: string;
|
|
31
|
-
currentProfilePictureUrl?: string;
|
|
32
|
-
currentProfileSource?: "upload" | "library" | "gravatar" | "custom";
|
|
33
|
-
saveButtonLabel?: string;
|
|
34
|
-
cancelButtonLabel?: string;
|
|
35
|
-
disabled?: boolean;
|
|
36
|
-
messages: {
|
|
37
|
-
photo_upload_disabled_message: string;
|
|
38
|
-
gravatar_setup_message: string;
|
|
39
|
-
gravatar_no_account_message: string;
|
|
40
|
-
library_tooltip_message: string;
|
|
41
|
-
};
|
|
42
|
-
uiSizes: {
|
|
43
|
-
gravatar_size: number;
|
|
44
|
-
profile_picture_size: number;
|
|
45
|
-
tooltip_icon_size_default: number;
|
|
46
|
-
tooltip_icon_size_small: number;
|
|
47
|
-
library_photo_grid_columns: number;
|
|
48
|
-
library_photo_preview_size: number;
|
|
49
|
-
image_compression_max_dimension: number;
|
|
50
|
-
upload_file_hard_limit_bytes: number;
|
|
51
|
-
};
|
|
52
|
-
fileTypes: {
|
|
53
|
-
allowed_image_extensions: string[];
|
|
54
|
-
allowed_image_mime_types: string[];
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// section: component
|
|
59
|
-
/**
|
|
60
|
-
* Profile picture dialog component with tabs for Upload, Library, and Gravatar
|
|
61
|
-
* Only one switch can be active at a time
|
|
62
|
-
* @param props - Component props including open state, save handler, and configuration
|
|
63
|
-
* @returns Profile picture dialog component
|
|
64
|
-
*/
|
|
65
|
-
export function ProfilePictureDialog({
|
|
66
|
-
open,
|
|
67
|
-
onOpenChange,
|
|
68
|
-
onSave,
|
|
69
|
-
email,
|
|
70
|
-
allowPhotoUpload,
|
|
71
|
-
maxPhotoSize,
|
|
72
|
-
libraryPhotoPath,
|
|
73
|
-
currentProfilePictureUrl,
|
|
74
|
-
currentProfileSource,
|
|
75
|
-
saveButtonLabel = "Save",
|
|
76
|
-
cancelButtonLabel = "Cancel",
|
|
77
|
-
disabled = false,
|
|
78
|
-
messages,
|
|
79
|
-
uiSizes,
|
|
80
|
-
fileTypes,
|
|
81
|
-
}: ProfilePictureDialogProps) {
|
|
82
|
-
const [useUpload, setUseUpload] = useState(false);
|
|
83
|
-
const [useLibrary, setUseLibrary] = useState(false);
|
|
84
|
-
const [useGravatar, setUseGravatar] = useState(false);
|
|
85
|
-
const [selectedPhotoUrl, setSelectedPhotoUrl] = useState<string | null>(null);
|
|
86
|
-
const [selectedSource, setSelectedSource] = useState<"upload" | "library" | "gravatar" | null>(null);
|
|
87
|
-
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
|
88
|
-
const [uploading, setUploading] = useState(false);
|
|
89
|
-
const [activeTab, setActiveTab] = useState(allowPhotoUpload ? "upload" : "library");
|
|
90
|
-
|
|
91
|
-
// Initialize state based on current profile picture
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
if (open) {
|
|
94
|
-
// Determine default active tab (skip upload if not enabled)
|
|
95
|
-
const defaultTab = allowPhotoUpload ? "upload" : "library";
|
|
96
|
-
|
|
97
|
-
// Handle current profile source (may be "upload", "library", "gravatar", or "custom")
|
|
98
|
-
// "custom" should be treated as "upload" for backwards compatibility
|
|
99
|
-
const normalizedSource = currentProfileSource === "custom" ? "upload" : currentProfileSource;
|
|
100
|
-
|
|
101
|
-
if (normalizedSource === "upload" && allowPhotoUpload) {
|
|
102
|
-
setUseUpload(true);
|
|
103
|
-
setUseLibrary(false);
|
|
104
|
-
setUseGravatar(false);
|
|
105
|
-
setSelectedSource("upload");
|
|
106
|
-
setSelectedPhotoUrl(currentProfilePictureUrl || null); // Set current image for upload tab
|
|
107
|
-
setActiveTab("upload");
|
|
108
|
-
} else if (normalizedSource === "library") {
|
|
109
|
-
setUseUpload(false);
|
|
110
|
-
setUseLibrary(true);
|
|
111
|
-
setUseGravatar(false);
|
|
112
|
-
setSelectedSource("library");
|
|
113
|
-
setSelectedPhotoUrl(currentProfilePictureUrl || null);
|
|
114
|
-
setActiveTab("library");
|
|
115
|
-
} else if (normalizedSource === "gravatar") {
|
|
116
|
-
setUseUpload(false);
|
|
117
|
-
setUseLibrary(false);
|
|
118
|
-
setUseGravatar(true);
|
|
119
|
-
setSelectedSource("gravatar");
|
|
120
|
-
setActiveTab("gravatar");
|
|
121
|
-
} else {
|
|
122
|
-
// Reset to defaults
|
|
123
|
-
setUseUpload(false);
|
|
124
|
-
setUseLibrary(false);
|
|
125
|
-
setUseGravatar(false);
|
|
126
|
-
setSelectedSource(null);
|
|
127
|
-
setSelectedPhotoUrl(null);
|
|
128
|
-
setUploadedFile(null);
|
|
129
|
-
setActiveTab(defaultTab);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}, [open, currentProfileSource, currentProfilePictureUrl, allowPhotoUpload]);
|
|
133
|
-
|
|
134
|
-
const handleUseUploadChange = (use: boolean) => {
|
|
135
|
-
if (!allowPhotoUpload) {
|
|
136
|
-
return; // Prevent changes if upload is not enabled
|
|
137
|
-
}
|
|
138
|
-
setUseUpload(use);
|
|
139
|
-
if (use) {
|
|
140
|
-
setUseLibrary(false);
|
|
141
|
-
setUseGravatar(false);
|
|
142
|
-
setSelectedPhotoUrl(null); // Clear library photo URL when upload is selected
|
|
143
|
-
setSelectedSource("upload");
|
|
144
|
-
} else {
|
|
145
|
-
setSelectedSource(null);
|
|
146
|
-
setUploadedFile(null);
|
|
147
|
-
setSelectedPhotoUrl(null);
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
const handleUseLibraryChange = (use: boolean) => {
|
|
152
|
-
setUseLibrary(use);
|
|
153
|
-
if (use) {
|
|
154
|
-
if (allowPhotoUpload) {
|
|
155
|
-
setUseUpload(false);
|
|
156
|
-
setUploadedFile(null);
|
|
157
|
-
}
|
|
158
|
-
setUseGravatar(false);
|
|
159
|
-
setSelectedSource("library");
|
|
160
|
-
// Note: selectedPhotoUrl should already be set when a photo is selected
|
|
161
|
-
// If no photo is selected yet, the first photo in the category will be auto-selected
|
|
162
|
-
} else {
|
|
163
|
-
setSelectedSource(null);
|
|
164
|
-
setSelectedPhotoUrl(null);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const handleUseGravatarChange = (use: boolean) => {
|
|
169
|
-
setUseGravatar(use);
|
|
170
|
-
if (use) {
|
|
171
|
-
if (allowPhotoUpload) {
|
|
172
|
-
setUseUpload(false);
|
|
173
|
-
setUploadedFile(null);
|
|
174
|
-
}
|
|
175
|
-
setUseLibrary(false);
|
|
176
|
-
setSelectedPhotoUrl(null); // Clear library photo URL when Gravatar is selected
|
|
177
|
-
setSelectedSource("gravatar");
|
|
178
|
-
} else {
|
|
179
|
-
setSelectedSource(null);
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const handleFileSelect = async (file: File) => {
|
|
184
|
-
setUploadedFile(file);
|
|
185
|
-
setUploading(true);
|
|
186
|
-
|
|
187
|
-
try {
|
|
188
|
-
const formData = new FormData();
|
|
189
|
-
formData.append("file", file);
|
|
190
|
-
|
|
191
|
-
const response = await fetch("/api/hazo_auth/upload_profile_picture", {
|
|
192
|
-
method: "POST",
|
|
193
|
-
credentials: "include",
|
|
194
|
-
body: formData,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
const data = await response.json();
|
|
198
|
-
|
|
199
|
-
if (!response.ok || !data.success) {
|
|
200
|
-
const errorMessage = data.error || "Failed to upload photo";
|
|
201
|
-
toast.error(errorMessage);
|
|
202
|
-
setUploadedFile(null);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
setSelectedPhotoUrl(data.profile_picture_url);
|
|
207
|
-
toast.success("Photo uploaded successfully");
|
|
208
|
-
} catch (error) {
|
|
209
|
-
const errorMessage = error instanceof Error ? error.message : "Failed to upload photo";
|
|
210
|
-
toast.error(errorMessage);
|
|
211
|
-
setUploadedFile(null);
|
|
212
|
-
} finally {
|
|
213
|
-
setUploading(false);
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
const handlePhotoSelect = (photoUrl: string) => {
|
|
218
|
-
setSelectedPhotoUrl(photoUrl);
|
|
219
|
-
// Automatically enable library photo when a photo is selected
|
|
220
|
-
if (!useLibrary) {
|
|
221
|
-
handleUseLibraryChange(true);
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
const handleSave = async () => {
|
|
226
|
-
if (!selectedSource) {
|
|
227
|
-
toast.error("Please select a profile picture source");
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (selectedSource === "upload" && !selectedPhotoUrl) {
|
|
232
|
-
toast.error("Please upload a photo first");
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (selectedSource === "library" && !selectedPhotoUrl) {
|
|
237
|
-
toast.error("Please select a photo from the library");
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (selectedSource === "gravatar") {
|
|
242
|
-
// For Gravatar, generate the URL client-side using gravatar-url
|
|
243
|
-
// The onSave handler will use this URL directly
|
|
244
|
-
const gravatarUrlModule = await import("gravatar-url");
|
|
245
|
-
const gravatarUrl = gravatarUrlModule.default(email, {
|
|
246
|
-
size: 200,
|
|
247
|
-
default: "identicon",
|
|
248
|
-
});
|
|
249
|
-
await onSave(gravatarUrl, "gravatar");
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (selectedPhotoUrl) {
|
|
254
|
-
await onSave(selectedPhotoUrl, selectedSource);
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
const handleCancel = () => {
|
|
259
|
-
setUseUpload(false);
|
|
260
|
-
setUseLibrary(false);
|
|
261
|
-
setUseGravatar(false);
|
|
262
|
-
setSelectedPhotoUrl(null);
|
|
263
|
-
setSelectedSource(null);
|
|
264
|
-
setUploadedFile(null);
|
|
265
|
-
onOpenChange(false);
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
return (
|
|
269
|
-
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
270
|
-
<DialogContent className="cls_profile_picture_dialog max-w-4xl max-h-[90vh] overflow-y-auto">
|
|
271
|
-
<DialogHeader className="cls_profile_picture_dialog_header">
|
|
272
|
-
<DialogTitle className="cls_profile_picture_dialog_title">
|
|
273
|
-
Change Profile Picture
|
|
274
|
-
</DialogTitle>
|
|
275
|
-
<DialogDescription className="cls_profile_picture_dialog_description">
|
|
276
|
-
Choose how you want to set your profile picture
|
|
277
|
-
</DialogDescription>
|
|
278
|
-
</DialogHeader>
|
|
279
|
-
|
|
280
|
-
<Tabs value={activeTab} onValueChange={setActiveTab} className="cls_profile_picture_dialog_tabs">
|
|
281
|
-
<TabsList className="cls_profile_picture_dialog_tabs_list">
|
|
282
|
-
{allowPhotoUpload && (
|
|
283
|
-
<TabsTrigger
|
|
284
|
-
value="upload"
|
|
285
|
-
className={cn(
|
|
286
|
-
"cls_profile_picture_dialog_tabs_trigger",
|
|
287
|
-
useUpload && "!text-blue-600 font-semibold data-[state=active]:!text-blue-600"
|
|
288
|
-
)}
|
|
289
|
-
>
|
|
290
|
-
Upload
|
|
291
|
-
</TabsTrigger>
|
|
292
|
-
)}
|
|
293
|
-
<TabsTrigger
|
|
294
|
-
value="library"
|
|
295
|
-
className={cn(
|
|
296
|
-
"cls_profile_picture_dialog_tabs_trigger",
|
|
297
|
-
useLibrary && "!text-blue-600 font-semibold data-[state=active]:!text-blue-600"
|
|
298
|
-
)}
|
|
299
|
-
>
|
|
300
|
-
Library
|
|
301
|
-
</TabsTrigger>
|
|
302
|
-
<TabsTrigger
|
|
303
|
-
value="gravatar"
|
|
304
|
-
className={cn(
|
|
305
|
-
"cls_profile_picture_dialog_tabs_trigger",
|
|
306
|
-
useGravatar && "!text-blue-600 font-semibold data-[state=active]:!text-blue-600"
|
|
307
|
-
)}
|
|
308
|
-
>
|
|
309
|
-
Gravatar
|
|
310
|
-
</TabsTrigger>
|
|
311
|
-
</TabsList>
|
|
312
|
-
|
|
313
|
-
{allowPhotoUpload && (
|
|
314
|
-
<TabsContent value="upload" className="cls_profile_picture_dialog_tabs_content">
|
|
315
|
-
<ProfilePictureUploadTab
|
|
316
|
-
useUpload={useUpload}
|
|
317
|
-
onUseUploadChange={handleUseUploadChange}
|
|
318
|
-
onFileSelect={handleFileSelect}
|
|
319
|
-
maxSize={maxPhotoSize}
|
|
320
|
-
uploadEnabled={allowPhotoUpload}
|
|
321
|
-
disabled={disabled || uploading}
|
|
322
|
-
currentPreview={currentProfilePictureUrl || selectedPhotoUrl || undefined}
|
|
323
|
-
photoUploadDisabledMessage={messages.photo_upload_disabled_message}
|
|
324
|
-
imageCompressionMaxDimension={uiSizes.image_compression_max_dimension}
|
|
325
|
-
uploadFileHardLimitBytes={uiSizes.upload_file_hard_limit_bytes}
|
|
326
|
-
allowedImageMimeTypes={fileTypes.allowed_image_mime_types}
|
|
327
|
-
/>
|
|
328
|
-
</TabsContent>
|
|
329
|
-
)}
|
|
330
|
-
|
|
331
|
-
<TabsContent value="library" className="cls_profile_picture_dialog_tabs_content">
|
|
332
|
-
<ProfilePictureLibraryTab
|
|
333
|
-
useLibrary={useLibrary}
|
|
334
|
-
onUseLibraryChange={handleUseLibraryChange}
|
|
335
|
-
onPhotoSelect={handlePhotoSelect}
|
|
336
|
-
disabled={disabled}
|
|
337
|
-
libraryPhotoPath={libraryPhotoPath}
|
|
338
|
-
currentPhotoUrl={selectedPhotoUrl || undefined}
|
|
339
|
-
libraryTooltipMessage={messages.library_tooltip_message}
|
|
340
|
-
tooltipIconSizeSmall={uiSizes.tooltip_icon_size_small}
|
|
341
|
-
libraryPhotoGridColumns={uiSizes.library_photo_grid_columns}
|
|
342
|
-
libraryPhotoPreviewSize={uiSizes.library_photo_preview_size}
|
|
343
|
-
/>
|
|
344
|
-
</TabsContent>
|
|
345
|
-
|
|
346
|
-
<TabsContent value="gravatar" className="cls_profile_picture_dialog_tabs_content">
|
|
347
|
-
<ProfilePictureGravatarTab
|
|
348
|
-
email={email}
|
|
349
|
-
useGravatar={useGravatar}
|
|
350
|
-
onUseGravatarChange={handleUseGravatarChange}
|
|
351
|
-
disabled={disabled}
|
|
352
|
-
gravatarSetupMessage={messages.gravatar_setup_message}
|
|
353
|
-
gravatarNoAccountMessage={messages.gravatar_no_account_message}
|
|
354
|
-
gravatarSize={uiSizes.gravatar_size}
|
|
355
|
-
/>
|
|
356
|
-
</TabsContent>
|
|
357
|
-
</Tabs>
|
|
358
|
-
|
|
359
|
-
{/* Actions */}
|
|
360
|
-
<div className="cls_profile_picture_dialog_actions flex justify-end gap-3 pt-4 border-t">
|
|
361
|
-
<Button
|
|
362
|
-
type="button"
|
|
363
|
-
onClick={handleSave}
|
|
364
|
-
disabled={disabled || uploading || !selectedSource}
|
|
365
|
-
className="cls_profile_picture_dialog_save_button"
|
|
366
|
-
aria-label={saveButtonLabel}
|
|
367
|
-
>
|
|
368
|
-
{saveButtonLabel}
|
|
369
|
-
</Button>
|
|
370
|
-
<Button
|
|
371
|
-
type="button"
|
|
372
|
-
onClick={handleCancel}
|
|
373
|
-
disabled={disabled || uploading}
|
|
374
|
-
variant="outline"
|
|
375
|
-
className="cls_profile_picture_dialog_cancel_button"
|
|
376
|
-
aria-label={cancelButtonLabel}
|
|
377
|
-
>
|
|
378
|
-
{cancelButtonLabel}
|
|
379
|
-
</Button>
|
|
380
|
-
</div>
|
|
381
|
-
</DialogContent>
|
|
382
|
-
</Dialog>
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// file_description: component for displaying and editing profile picture (edit functionality to be implemented later)
|
|
2
|
-
// section: client_directive
|
|
3
|
-
"use client";
|
|
4
|
-
|
|
5
|
-
// section: imports
|
|
6
|
-
import { Avatar, AvatarImage, AvatarFallback } from "../../../ui/avatar";
|
|
7
|
-
import { Button } from "../../../ui/button";
|
|
8
|
-
import { Pencil } from "lucide-react";
|
|
9
|
-
|
|
10
|
-
// section: types
|
|
11
|
-
export type ProfilePictureDisplayProps = {
|
|
12
|
-
profilePictureUrl?: string;
|
|
13
|
-
name?: string;
|
|
14
|
-
email?: string;
|
|
15
|
-
onEdit?: () => void;
|
|
16
|
-
disabled?: boolean;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// section: component
|
|
20
|
-
/**
|
|
21
|
-
* Profile picture display component
|
|
22
|
-
* Shows profile picture with fallback to initials or default icon
|
|
23
|
-
* Displays pencil icon for editing (functionality to be implemented later)
|
|
24
|
-
* @param props - Component props including profile picture URL, name, email, onEdit callback
|
|
25
|
-
* @returns Profile picture display component
|
|
26
|
-
*/
|
|
27
|
-
export function ProfilePictureDisplay({
|
|
28
|
-
profilePictureUrl,
|
|
29
|
-
name,
|
|
30
|
-
email,
|
|
31
|
-
onEdit,
|
|
32
|
-
disabled = false,
|
|
33
|
-
}: ProfilePictureDisplayProps) {
|
|
34
|
-
// Get initials from name or email
|
|
35
|
-
const getInitials = (): string => {
|
|
36
|
-
if (name) {
|
|
37
|
-
const parts = name.trim().split(" ");
|
|
38
|
-
if (parts.length >= 2) {
|
|
39
|
-
return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
|
|
40
|
-
}
|
|
41
|
-
return name[0]?.toUpperCase() || "";
|
|
42
|
-
}
|
|
43
|
-
if (email) {
|
|
44
|
-
return email[0]?.toUpperCase() || "";
|
|
45
|
-
}
|
|
46
|
-
return "?";
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const initials = getInitials();
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<div className="cls_profile_picture_display">
|
|
53
|
-
<Avatar className="cls_profile_picture_display_avatar h-32 w-32">
|
|
54
|
-
<AvatarImage
|
|
55
|
-
src={profilePictureUrl}
|
|
56
|
-
alt={name ? `Profile picture of ${name}` : "Profile picture"}
|
|
57
|
-
className="cls_profile_picture_display_image"
|
|
58
|
-
/>
|
|
59
|
-
<AvatarFallback className="cls_profile_picture_display_fallback bg-slate-200 text-slate-600 text-3xl">
|
|
60
|
-
{initials}
|
|
61
|
-
</AvatarFallback>
|
|
62
|
-
</Avatar>
|
|
63
|
-
</div>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
// file_description: Gravatar tab component for profile picture dialog
|
|
2
|
-
// section: client_directive
|
|
3
|
-
"use client";
|
|
4
|
-
|
|
5
|
-
// section: imports
|
|
6
|
-
import { useState, useEffect } from "react";
|
|
7
|
-
import { Switch } from "../../../ui/switch";
|
|
8
|
-
import { Label } from "../../../ui/label";
|
|
9
|
-
import { Avatar, AvatarImage, AvatarFallback } from "../../../ui/avatar";
|
|
10
|
-
import { Info } from "lucide-react";
|
|
11
|
-
import gravatarUrl from "gravatar-url";
|
|
12
|
-
|
|
13
|
-
// section: types
|
|
14
|
-
export type ProfilePictureGravatarTabProps = {
|
|
15
|
-
email: string;
|
|
16
|
-
useGravatar: boolean;
|
|
17
|
-
onUseGravatarChange: (use: boolean) => void;
|
|
18
|
-
disabled?: boolean;
|
|
19
|
-
gravatarSetupMessage: string;
|
|
20
|
-
gravatarNoAccountMessage: string;
|
|
21
|
-
gravatarSize: number;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// section: component
|
|
25
|
-
/**
|
|
26
|
-
* Gravatar tab component for profile picture dialog
|
|
27
|
-
* Shows Gravatar preview and setup instructions
|
|
28
|
-
* @param props - Component props including email, useGravatar state, and change handler
|
|
29
|
-
* @returns Gravatar tab component
|
|
30
|
-
*/
|
|
31
|
-
export function ProfilePictureGravatarTab({
|
|
32
|
-
email,
|
|
33
|
-
useGravatar,
|
|
34
|
-
onUseGravatarChange,
|
|
35
|
-
disabled = false,
|
|
36
|
-
gravatarSetupMessage,
|
|
37
|
-
gravatarNoAccountMessage,
|
|
38
|
-
gravatarSize,
|
|
39
|
-
}: ProfilePictureGravatarTabProps) {
|
|
40
|
-
const [gravatarUrlState, setGravatarUrlState] = useState<string>("");
|
|
41
|
-
const [gravatarExists, setGravatarExists] = useState<boolean | null>(null);
|
|
42
|
-
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
if (email) {
|
|
45
|
-
const url = gravatarUrl(email, {
|
|
46
|
-
size: gravatarSize,
|
|
47
|
-
default: "404", // Return 404 if Gravatar doesn't exist
|
|
48
|
-
});
|
|
49
|
-
setGravatarUrlState(url);
|
|
50
|
-
|
|
51
|
-
// Check if Gravatar exists by trying to load the image
|
|
52
|
-
const img = new Image();
|
|
53
|
-
img.onload = () => {
|
|
54
|
-
setGravatarExists(true);
|
|
55
|
-
};
|
|
56
|
-
img.onerror = () => {
|
|
57
|
-
setGravatarExists(false);
|
|
58
|
-
};
|
|
59
|
-
img.src = url;
|
|
60
|
-
}
|
|
61
|
-
}, [email]);
|
|
62
|
-
|
|
63
|
-
const getInitials = (): string => {
|
|
64
|
-
if (email) {
|
|
65
|
-
return email[0]?.toUpperCase() || "?";
|
|
66
|
-
}
|
|
67
|
-
return "?";
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<div className="cls_profile_picture_gravatar_tab flex flex-col gap-4">
|
|
72
|
-
{/* Switch */}
|
|
73
|
-
<div className="cls_profile_picture_gravatar_tab_switch flex items-center gap-3">
|
|
74
|
-
<Switch
|
|
75
|
-
id="use-gravatar"
|
|
76
|
-
checked={useGravatar}
|
|
77
|
-
onCheckedChange={onUseGravatarChange}
|
|
78
|
-
disabled={disabled}
|
|
79
|
-
className="cls_profile_picture_gravatar_tab_switch_input"
|
|
80
|
-
aria-label="Use Gravatar photo"
|
|
81
|
-
/>
|
|
82
|
-
<Label
|
|
83
|
-
htmlFor="use-gravatar"
|
|
84
|
-
className="cls_profile_picture_gravatar_tab_switch_label text-sm font-medium text-slate-700 cursor-pointer"
|
|
85
|
-
>
|
|
86
|
-
Use Gravatar photo
|
|
87
|
-
</Label>
|
|
88
|
-
</div>
|
|
89
|
-
|
|
90
|
-
{/* Preview */}
|
|
91
|
-
<div className="cls_profile_picture_gravatar_tab_preview flex flex-col items-center gap-4 p-6 border border-slate-200 rounded-lg bg-slate-50">
|
|
92
|
-
{gravatarExists === true ? (
|
|
93
|
-
<>
|
|
94
|
-
<Avatar className="cls_profile_picture_gravatar_tab_avatar h-32 w-32">
|
|
95
|
-
<AvatarImage
|
|
96
|
-
src={gravatarUrlState}
|
|
97
|
-
alt="Gravatar profile picture"
|
|
98
|
-
className="cls_profile_picture_gravatar_tab_avatar_image"
|
|
99
|
-
/>
|
|
100
|
-
<AvatarFallback className="cls_profile_picture_gravatar_tab_avatar_fallback bg-slate-200 text-slate-600 text-3xl">
|
|
101
|
-
{getInitials()}
|
|
102
|
-
</AvatarFallback>
|
|
103
|
-
</Avatar>
|
|
104
|
-
<p className="cls_profile_picture_gravatar_tab_success_text text-sm text-slate-600 text-center">
|
|
105
|
-
Your Gravatar is available and will be used as your profile picture.
|
|
106
|
-
</p>
|
|
107
|
-
</>
|
|
108
|
-
) : gravatarExists === false ? (
|
|
109
|
-
<>
|
|
110
|
-
<div className="cls_profile_picture_gravatar_tab_no_gravatar flex flex-col items-center gap-4">
|
|
111
|
-
<div className="cls_profile_picture_gravatar_tab_no_gravatar_icon flex items-center justify-center w-16 h-16 rounded-full bg-slate-100">
|
|
112
|
-
<Info className="h-8 w-8 text-slate-400" aria-hidden="true" />
|
|
113
|
-
</div>
|
|
114
|
-
<div className="cls_profile_picture_gravatar_tab_no_gravatar_content flex flex-col gap-2 text-center">
|
|
115
|
-
<p className="cls_profile_picture_gravatar_tab_no_gravatar_title text-sm font-medium text-slate-900">
|
|
116
|
-
No Gravatar found
|
|
117
|
-
</p>
|
|
118
|
-
<p className="cls_profile_picture_gravatar_tab_no_gravatar_message text-sm text-slate-600">
|
|
119
|
-
{gravatarSetupMessage} <span className="font-semibold">{email}</span>:
|
|
120
|
-
</p>
|
|
121
|
-
<ol className="cls_profile_picture_gravatar_tab_no_gravatar_steps text-sm text-slate-600 list-decimal list-inside space-y-1 mt-2">
|
|
122
|
-
<li>Visit <a href="https://gravatar.com" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:text-blue-700 underline">gravatar.com</a></li>
|
|
123
|
-
<li>Sign up or log in with your email: <span className="font-mono text-xs">{email}</span></li>
|
|
124
|
-
<li>Upload a profile picture</li>
|
|
125
|
-
<li>Return here and refresh to see your Gravatar</li>
|
|
126
|
-
</ol>
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
|
-
</>
|
|
130
|
-
) : (
|
|
131
|
-
<>
|
|
132
|
-
<div className="cls_profile_picture_gravatar_tab_loading flex items-center justify-center">
|
|
133
|
-
<p className="cls_profile_picture_gravatar_tab_loading_text text-sm text-slate-600">
|
|
134
|
-
Checking Gravatar...
|
|
135
|
-
</p>
|
|
136
|
-
</div>
|
|
137
|
-
</>
|
|
138
|
-
)}
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|