JavaScript 56.7%
TypeScript 42.6%
1"use client";23import Image from "next/image";45interface UserAvatarProps {6 userId: string;7 name?: string | null;8 avatarPath?: string | null;9 size?: "sm" | "md" | "lg";10}1112const sizeMap = {13 sm: 24,14 md: 32,15 lg: 48,16};1718const textSizeMap = {19 sm: "text-[10px]",20 md: "text-xs",21 lg: "text-sm",22};2324function hashColor(str: string): string {25 let hash = 0;26 for (let i = 0; i < str.length; i++) {27 hash = str.charCodeAt(i) + ((hash << 5) - hash);28 }29 const colors = [30 "bg-blue-600",31 "bg-emerald-600",32 "bg-purple-600",33 "bg-rose-600",34 "bg-amber-600",35 "bg-cyan-600",36 "bg-indigo-600",37 "bg-pink-600",38 "bg-teal-600",39 "bg-orange-600",40 ];41 return colors[Math.abs(hash) % colors.length];42}4344function getInitials(name?: string | null): string {45 if (!name) return "?";46 const parts = name.trim().split(/\s+/);47 if (parts.length >= 2) {48 return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();49 }50 return name.slice(0, 2).toUpperCase();51}5253export default function UserAvatar({54 userId,55 name,56 avatarPath,57 size = "md",58}: UserAvatarProps) {59 const px = sizeMap[size];6061 if (avatarPath) {62 return (63 <div64 className="relative rounded-full overflow-hidden flex-shrink-0 ring-2 ring-white/70 dark:ring-white/10"65 style={{ width: px, height: px }}66 >67 <Image68 src={`/api/users/${userId}/avatar`}69 alt={name || "Avatar"}70 width={px}71 height={px}72 className="object-cover w-full h-full"73 unoptimized74 />75 </div>76 );77 }7879 const bgColor = hashColor(userId);80 const initials = getInitials(name);8182 return (83 <div84 className={`${bgColor} rounded-full flex items-center justify-center flex-shrink-0 text-white font-semibold ring-2 ring-white/70 dark:ring-white/10 shadow-inner ${textSizeMap[size]}`}85 style={{ width: px, height: px }}86 >87 {initials}88 </div>89 );90}91