/** * UserAvatar — displays a user's avatar image or a generated initials fallback. * * Usage: * * // shows initials "B" * * ADDITIVE ONLY — new component, does not modify existing code. */ import React, { useState } from 'react'; import { resolveBackendUrl } from '../lib/backendUrl'; /** Deterministic color from a string (name-based, visually distinct). */ function nameToColor(name) { const colors = [ '#3b82f6', '#8b5cf6', '#ec4899', '#06b6d4', '#f59e0b', '#10b981', '#ef4444', '#6366f1', '#14b8a6', '#f97316', ]; let hash = 0; for (let i = 0; i < name.length; i++) { hash = name.charCodeAt(i) + ((hash << 5) - hash); } return colors[Math.abs(hash) % colors.length]; } function getInitials(name) { const parts = (name || '').trim().split(/\s+/); if (parts.length >= 2) { return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); } return (name || '?')[0].toUpperCase(); } export default function UserAvatar({ displayName, avatarUrl, size = 40, onClick, style, }) { const [imgError, setImgError] = useState(false); const hasImage = avatarUrl && !imgError; const baseStyle = { width: size, height: size, borderRadius: '50%', overflow: 'hidden', flexShrink: 0, cursor: onClick ? 'pointer' : 'default', ...style, }; if (hasImage) { // Resolve relative URLs against the backend, append auth token for /files/ paths const backendUrl = resolveBackendUrl(); let fullUrl = avatarUrl.startsWith('http') ? avatarUrl : `${backendUrl}${avatarUrl}`; if (fullUrl.includes('/files/')) { const tok = localStorage.getItem('homepilot_auth_token') || ''; if (tok) { const sep = fullUrl.includes('?') ? '&' : '?'; fullUrl = `${fullUrl}${sep}token=${encodeURIComponent(tok)}`; } } return (
{displayName} setImgError(true)} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}/>
); } // Initials fallback const bg = nameToColor(displayName); return (
{getInitials(displayName)}
); }