import { useState, useRef } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { useAuthStore } from '@/stores/auth'; import { useTranslation } from '@/hooks/useTranslation'; import { EmailVerificationBadge } from '@/features/auth/components/EmailVerificationBadge'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Camera, Save, X } from 'lucide-react'; import { useUIStore } from '@/stores/ui'; import { apiService } from '@/services/api'; // Schéma de validation pour le profil const profileSchema = z.object({ first_name: z.string().min(1, 'Le prénom est requis').optional(), last_name: z.string().min(1, 'Le nom est requis').optional(), username: z.string().min(3, 'Le nom d\'utilisateur doit contenir au moins 3 caractères'), email: z.string().email('Email invalide'), bio: z.string().max(500, 'La bio ne peut pas dépasser 500 caractères').optional(), }); type ProfileFormData = z.infer; /** * Composant formulaire de profil utilisateur avec validation et upload d'avatar. */ export function ProfileForm() { const { user, refreshUser } = useAuthStore(); const { t } = useTranslation(); const { addNotification } = useUIStore(); const [isEditing, setIsEditing] = useState(false); const [avatarPreview, setAvatarPreview] = useState(null); const [isUploading, setIsUploading] = useState(false); const fileInputRef = useRef(null); const { register, handleSubmit, formState: { errors, isSubmitting }, reset, setValue, } = useForm({ resolver: zodResolver(profileSchema), defaultValues: { first_name: user?.first_name || '', last_name: user?.last_name || '', username: user?.username || '', email: user?.email || '', bio: user?.bio || '', }, }); const handleEdit = () => { setIsEditing(true); reset({ first_name: user?.first_name || '', last_name: user?.last_name || '', username: user?.username || '', email: user?.email || '', bio: user?.bio || '', }); }; const handleCancel = () => { setIsEditing(false); setAvatarPreview(null); reset({ first_name: user?.first_name || '', last_name: user?.last_name || '', username: user?.username || '', email: user?.email || '', bio: user?.bio || '', }); }; const handleAvatarClick = () => { if (isEditing && fileInputRef.current) { fileInputRef.current.click(); } }; const handleAvatarChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; // Validation du type de fichier const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (!allowedTypes.includes(file.type)) { addNotification({ type: 'error', title: 'Type de fichier invalide', message: 'Veuillez sélectionner une image (JPEG, PNG, GIF, WebP)', }); return; } // Validation de la taille (max 5MB) const maxSize = 5 * 1024 * 1024; // 5MB if (file.size > maxSize) { addNotification({ type: 'error', title: 'Fichier trop volumineux', message: 'L\'image ne peut pas dépasser 5MB', }); return; } // Créer une preview const reader = new FileReader(); reader.onloadend = () => { setAvatarPreview(reader.result as string); }; reader.readAsDataURL(file); // Upload de l'avatar setIsUploading(true); try { const formData = new FormData(); formData.append('avatar', file); // TODO: Implémenter l'endpoint API pour l'upload d'avatar // const response = await apiService.uploadAvatar(formData); // await refreshUser(); addNotification({ type: 'success', title: 'Avatar mis à jour', message: 'Votre photo de profil a été mise à jour avec succès', }); } catch (error) { console.error('Error uploading avatar:', error); addNotification({ type: 'error', title: 'Erreur', message: 'Impossible de mettre à jour l\'avatar', }); } finally { setIsUploading(false); } }; const onSubmit = async (data: ProfileFormData) => { try { if (!user?.id) return; // Mettre à jour le profil via l'API await apiService.updateUser(user.id, { first_name: data.first_name, last_name: data.last_name, username: data.username, email: data.email, bio: data.bio, }); // Rafraîchir les données utilisateur await refreshUser(); setIsEditing(false); setAvatarPreview(null); addNotification({ type: 'success', title: 'Profil mis à jour', message: 'Vos informations ont été mises à jour avec succès', }); } catch (error) { console.error('Error updating profile:', error); addNotification({ type: 'error', title: 'Erreur', message: 'Impossible de mettre à jour le profil', }); } }; return (

{t('profile.title')}

{t('profile.subtitle')}

{t('profile.personalInfo')} {t('profile.updateProfile')}
{!isEditing && ( )}
{errors.first_name && (

{errors.first_name.message}

)}
{errors.last_name && (

{errors.last_name.message}

)}
{errors.username && (

{errors.username.message}

)}
{/* T0190: Afficher badge de vérification d'email */} {user && ( )}
{errors.email && (

{errors.email.message}

)}