From 30c2c7afb014f981e66df80f59e3991872257d74 Mon Sep 17 00:00:00 2001 From: Daniele Pintore Date: Wed, 23 Jul 2025 20:12:21 +0200 Subject: [PATCH 1/3] feat(dashboard): generate user fallback avatar using user email. Allow user to select the default avatar. --- .../dashboard/settings/profile/profile-form.tsx | 17 ++++++++++++++++- apps/dokploy/components/layouts/user-nav.tsx | 3 ++- apps/dokploy/lib/utils.ts | 12 ++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index 49fdfd2dd..eaa48a2a6 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -1,3 +1,4 @@ +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -19,7 +20,7 @@ import { import { Input } from "@/components/ui/input"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Switch } from "@/components/ui/switch"; -import { generateSHA256Hash } from "@/lib/utils"; +import { generateSHA256Hash, getFallbackAvatarInitials } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { Loader2, User } from "lucide-react"; @@ -257,6 +258,20 @@ export const ProfileForm = () => { value={field.value} className="flex flex-row flex-wrap gap-2 max-xl:justify-center" > + + + + + + + + {getFallbackAvatarInitials(data?.user?.email)} + + + {availableAvatars.map((image) => ( diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 05c601f6e..a339f05f4 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -17,6 +17,7 @@ import { } from "@/components/ui/select"; import { authClient } from "@/lib/auth-client"; import { Languages } from "@/lib/languages"; +import { getFallbackAvatarInitials } from "@/lib/utils"; import { api } from "@/utils/api"; import useLocale from "@/utils/hooks/use-locale"; import { ChevronsUpDown } from "lucide-react"; @@ -46,7 +47,7 @@ export const UserNav = () => { src={data?.user?.image || ""} alt={data?.user?.image || ""} /> - CN + {getFallbackAvatarInitials(data?.user?.email)}
Account diff --git a/apps/dokploy/lib/utils.ts b/apps/dokploy/lib/utils.ts index b763e9ee9..cf9f2cc94 100644 --- a/apps/dokploy/lib/utils.ts +++ b/apps/dokploy/lib/utils.ts @@ -27,3 +27,15 @@ export function formatTimestamp(timestamp: string | number) { return "Fecha inválida"; } } + +export function getFallbackAvatarInitials(email: string | undefined): string { + if (typeof email === "undefined") return "CN"; + + const [emailUsername = ""] = email.split('@'); + const parts = emailUsername.split(/[\._-]+/).filter(Boolean); + if (parts.length >= 2) { + // @ts-ignore we are sure parts[0] and parts[1] exist + return (parts[0]?.charAt(0) + parts[1].charAt(0)).toUpperCase(); + } + return emailUsername.slice(0, 2).toUpperCase(); +} From f8261b53642e000aa60bc2fc86bc30bceec997aa Mon Sep 17 00:00:00 2001 From: Daniele Pintore Date: Mon, 28 Jul 2025 19:20:05 +0200 Subject: [PATCH 2/3] feat(dashboard): use username instead of email for the generation of the fallback avatar image --- .../dashboard/settings/profile/profile-form.tsx | 2 +- apps/dokploy/components/layouts/user-nav.tsx | 2 +- apps/dokploy/lib/utils.ts | 15 ++++++--------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index eaa48a2a6..794291b7d 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -268,7 +268,7 @@ export const ProfileForm = () => { - {getFallbackAvatarInitials(data?.user?.email)} + {getFallbackAvatarInitials(data?.user?.name)} diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index a339f05f4..aa7f4ddd9 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -47,7 +47,7 @@ export const UserNav = () => { src={data?.user?.image || ""} alt={data?.user?.image || ""} /> - {getFallbackAvatarInitials(data?.user?.email)} + {getFallbackAvatarInitials(data?.user?.name)}
Account diff --git a/apps/dokploy/lib/utils.ts b/apps/dokploy/lib/utils.ts index cf9f2cc94..dc0dfe351 100644 --- a/apps/dokploy/lib/utils.ts +++ b/apps/dokploy/lib/utils.ts @@ -28,14 +28,11 @@ export function formatTimestamp(timestamp: string | number) { } } -export function getFallbackAvatarInitials(email: string | undefined): string { - if (typeof email === "undefined") return "CN"; - - const [emailUsername = ""] = email.split('@'); - const parts = emailUsername.split(/[\._-]+/).filter(Boolean); - if (parts.length >= 2) { - // @ts-ignore we are sure parts[0] and parts[1] exist - return (parts[0]?.charAt(0) + parts[1].charAt(0)).toUpperCase(); +export function getFallbackAvatarInitials(fullName: string | undefined): string { + if (typeof fullName === "undefined" || fullName === "") return "CN"; + const [ name = "", surname = "" ] = fullName.split(" "); + if (surname === "") { + return (name.substring(0,2)).toUpperCase(); } - return emailUsername.slice(0, 2).toUpperCase(); + return (name.charAt(0) + surname.charAt(0)).toUpperCase(); } From f9210d3165a6ac08848ae3ab5a0e4681ad92e9a1 Mon Sep 17 00:00:00 2001 From: Daniele Pintore Date: Mon, 28 Jul 2025 23:39:06 +0200 Subject: [PATCH 3/3] lint: formatted changes using biome --- .../dashboard/settings/profile/profile-form.tsx | 10 +++++++--- apps/dokploy/components/layouts/user-nav.tsx | 4 +++- apps/dokploy/lib/utils.ts | 16 +++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index 794291b7d..89014601e 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -267,9 +267,13 @@ export const ProfileForm = () => { /> - - {getFallbackAvatarInitials(data?.user?.name)} - + + + {getFallbackAvatarInitials( + data?.user?.name, + )} + + {availableAvatars.map((image) => ( diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index aa7f4ddd9..7a906aa6a 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -47,7 +47,9 @@ export const UserNav = () => { src={data?.user?.image || ""} alt={data?.user?.image || ""} /> - {getFallbackAvatarInitials(data?.user?.name)} + + {getFallbackAvatarInitials(data?.user?.name)} +
Account diff --git a/apps/dokploy/lib/utils.ts b/apps/dokploy/lib/utils.ts index dc0dfe351..f01faa4ec 100644 --- a/apps/dokploy/lib/utils.ts +++ b/apps/dokploy/lib/utils.ts @@ -28,11 +28,13 @@ export function formatTimestamp(timestamp: string | number) { } } -export function getFallbackAvatarInitials(fullName: string | undefined): string { - if (typeof fullName === "undefined" || fullName === "") return "CN"; - const [ name = "", surname = "" ] = fullName.split(" "); - if (surname === "") { - return (name.substring(0,2)).toUpperCase(); - } - return (name.charAt(0) + surname.charAt(0)).toUpperCase(); +export function getFallbackAvatarInitials( + fullName: string | undefined, +): string { + if (typeof fullName === "undefined" || fullName === "") return "CN"; + const [name = "", surname = ""] = fullName.split(" "); + if (surname === "") { + return name.substring(0, 2).toUpperCase(); + } + return (name.charAt(0) + surname.charAt(0)).toUpperCase(); }