50 lines
1.1 KiB
TypeScript
50 lines
1.1 KiB
TypeScript
import { cn } from "@/lib/utils";
|
|
|
|
function initials(name: string): string {
|
|
return (
|
|
name
|
|
.split(/\s+/)
|
|
.filter(Boolean)
|
|
.map((p) => p[0])
|
|
.slice(0, 2)
|
|
.join("")
|
|
.toUpperCase() || "?"
|
|
);
|
|
}
|
|
|
|
/** Small round avatar — the user's image, or their initials as a fallback. */
|
|
export function Avatar({
|
|
src,
|
|
name,
|
|
className,
|
|
}: {
|
|
src: string | null;
|
|
name: string;
|
|
className?: string;
|
|
}) {
|
|
if (src) {
|
|
return (
|
|
// eslint-disable-next-line @next/next/no-img-element -- arbitrary R2/CDN
|
|
// hosts; next/image optimization isn't worth it for tiny admin avatars.
|
|
<img
|
|
src={src}
|
|
alt={name}
|
|
className={cn(
|
|
"size-9 shrink-0 rounded-full object-cover ring-1 ring-border",
|
|
className,
|
|
)}
|
|
/>
|
|
);
|
|
}
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"flex size-9 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium text-muted-foreground ring-1 ring-border",
|
|
className,
|
|
)}
|
|
>
|
|
{initials(name)}
|
|
</div>
|
|
);
|
|
}
|