profile-image.tsx 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. "use client"
  2. import { useState } from "react"
  3. import { cn } from "@/lib/utils"
  4. interface ProfileImageProps {
  5. src?: string | null
  6. alt: string
  7. fallback: string
  8. size?: "sm" | "md" | "lg"
  9. className?: string
  10. }
  11. export function ProfileImage({
  12. src,
  13. alt,
  14. fallback,
  15. size = "md",
  16. className
  17. }: ProfileImageProps) {
  18. const [imageError, setImageError] = useState(false)
  19. const [imageLoaded, setImageLoaded] = useState(false)
  20. const sizeClasses = {
  21. sm: "w-8 h-8 text-xs",
  22. md: "w-12 h-12 text-sm",
  23. lg: "w-16 h-16 text-lg"
  24. }
  25. const hasValidImage = src && src.trim() !== '' && !imageError
  26. return (
  27. <div className={cn(
  28. "rounded-full overflow-hidden flex-shrink-0 relative",
  29. sizeClasses[size],
  30. className
  31. )}>
  32. {hasValidImage ? (
  33. <>
  34. <img
  35. src={src}
  36. alt={alt}
  37. className={cn(
  38. "w-full h-full object-cover transition-opacity duration-200",
  39. imageLoaded ? "opacity-100" : "opacity-0"
  40. )}
  41. onLoad={() => setImageLoaded(true)}
  42. onError={() => setImageError(true)}
  43. />
  44. {/* Fallback que se muestra mientras carga */}
  45. {!imageLoaded && (
  46. <div className="absolute inset-0 bg-primary rounded-full flex items-center justify-center text-primary-foreground font-semibold">
  47. {fallback.charAt(0).toUpperCase()}
  48. </div>
  49. )}
  50. </>
  51. ) : (
  52. <div className="w-full h-full bg-primary rounded-full flex items-center justify-center text-primary-foreground font-semibold">
  53. {fallback.charAt(0).toUpperCase()}
  54. </div>
  55. )}
  56. </div>
  57. )
  58. }