feat(dashboard): generate user fallback avatar using user email. Allow

user to select the default avatar.
This commit is contained in:
2025-07-23 20:12:21 +02:00
parent 17e9154887
commit 30c2c7afb0
3 changed files with 30 additions and 2 deletions

View File

@@ -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"
>
<FormItem key="no-avatar">
<FormLabel className="[&:has([data-state=checked])>.default-avatar]:border-primary [&:has([data-state=checked])>.default-avatar]:border-1 [&:has([data-state=checked])>.default-avatar]:p-px cursor-pointer">
<FormControl>
<RadioGroupItem
value=""
className="sr-only"
/>
</FormControl>
<Avatar className="default-avatar h-12 w-12 rounded-full border hover:p-px hover:border-primary transition-transform">
<AvatarFallback className="rounded-lg">{getFallbackAvatarInitials(data?.user?.email)}</AvatarFallback>
</Avatar>
</FormLabel>
</FormItem>
{availableAvatars.map((image) => (
<FormItem key={image}>
<FormLabel className="[&:has([data-state=checked])>img]:border-primary [&:has([data-state=checked])>img]:border-1 [&:has([data-state=checked])>img]:p-px cursor-pointer">

View File

@@ -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 || ""}
/>
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
<AvatarFallback className="rounded-lg">{getFallbackAvatarInitials(data?.user?.email)}</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">Account</span>

View File

@@ -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();
}