Loading...
Loading...
An avatar component with a camera upload overlay, supporting preview of the selected image.
import { useState, useRef } from "react";
interface AvatarUploadProps {
currentSrc?: string;
name?: string;
onFileSelect?: (file: File) => void;
}
export function AvatarUpload({ currentSrc, name = "User", onFileSelect }: AvatarUploadProps) {
const [preview, setPreview] = useState<string | null>(currentSrc || null);
const inputRef = useRef<HTMLInputElement>(null);
const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => setPreview(reader.result as string);
reader.readAsDataURL(file);
onFileSelect?.(file);
};
const initials = name.split(" ").map(n => n[0]).join("").toUpperCase().slice(0, 2);
return (
<div className="flex flex-col items-center gap-4 p-8">
<div className="relative group cursor-pointer" onClick={() => inputRef.current?.click()}>
{preview ? (
<img src={preview} alt="Preview" className="w-24 h-24 rounded-full object-cover ring-4 ring-white shadow-lg" />
) : (
<div className="w-24 h-24 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center ring-4 ring-white shadow-lg">
<span className="text-2xl font-bold text-white">{initials}</span>
</div>
)}
<div className="absolute inset-0 rounded-full bg-black/40 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
<svg className="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</div>
<input ref={inputRef} type="file" accept="image/*" className="hidden" onChange={handleFile} />
</div>
<span className="text-sm text-gray-500">Click avatar to upload photo</span>
</div>
);
}