import React, { useEffect, useState, useRef } from 'react'; import './ProfileView.css'; const AVATARS = [ '/avatars/avatar1.png', '/avatars/avatar2.png', '/avatars/avatar3.png', '/avatars/avatar4.png', '/avatars/avatar5.png', '/avatars/avatar6.png', ]; const COLOR_CIRCLES = [ '/avatars/color1.png', '/avatars/color2.png', '/avatars/color3.png', '/avatars/color4.png', '/avatars/color5.png', '/avatars/color6.png', ]; function resolveAvatarUrl(url) { if (!url) return ''; const matchPreset = url.match(/^preset_avatar_(\d+)$/); if (matchPreset) { const idx = parseInt(matchPreset[1], 10); if (idx >= 1 && idx <= 6) return AVATARS[idx - 1]; } const matchColor = url.match(/^color_circle_(\d+)$/); if (matchColor) { const idx = parseInt(matchColor[1], 10); if (idx >= 1 && idx <= 6) return COLOR_CIRCLES[idx - 1]; } return url; } function ProfileView({ currentUser, onProfileSaved, onLogout, onAdminClick }) { const [firstName, setFirstName] = useState(currentUser?.first_name || ''); const [lastName, setLastName] = useState(currentUser?.last_name || ''); const [avatarUrl, setAvatarUrl] = useState(resolveAvatarUrl(currentUser?.avatar_url)); const [searchHistory, setSearchHistory] = useState([]); const [uploading, setUploading] = useState(false); const [uploadError, setUploadError] = useState(''); const fileInputRef = useRef(null); // Avatar modal state const [showAvatarModal, setShowAvatarModal] = useState(false); const [avatarTab, setAvatarTab] = useState('presets'); // Config modal state const [showConfig, setShowConfig] = useState(false); const [configFirstName, setConfigFirstName] = useState(''); const [configLastName, setConfigLastName] = useState(''); const [configEmail, setConfigEmail] = useState(''); const [configCity, setConfigCity] = useState(''); const [configAddress, setConfigAddress] = useState(''); const [configSaving, setConfigSaving] = useState(false); const [configFeedback, setConfigFeedback] = useState(null); // Addresses modal state const [showAddresses, setShowAddresses] = useState(false); const [addresses, setAddresses] = useState([]); const [addressesLoading, setAddressesLoading] = useState(false); const [showAddressForm, setShowAddressForm] = useState(false); const [editingAddressId, setEditingAddressId] = useState(null); const [formAddress, setFormAddress] = useState(''); const [formLabel, setFormLabel] = useState(''); const [formDefault, setFormDefault] = useState(false); const [formSaving, setFormSaving] = useState(false); const [formError, setFormError] = useState(''); useEffect(() => { setFirstName(currentUser?.first_name || ''); setLastName(currentUser?.last_name || ''); setAvatarUrl(resolveAvatarUrl(currentUser?.avatar_url)); loadSearchHistory(); }, [currentUser?.id]); async function loadSearchHistory() { try { const res = await fetch('/api/search-history', { credentials: 'include' }); if (res.ok) { const data = await res.json(); setSearchHistory(data); } } catch (err) { console.error('Error loading search history:', err); } } function openConfig() { setConfigFirstName(currentUser?.first_name || ''); setConfigLastName(currentUser?.last_name || ''); setConfigEmail(currentUser?.email || ''); setConfigCity(currentUser?.city || ''); setConfigAddress(currentUser?.address || ''); setConfigFeedback(null); setShowConfig(true); } async function loadAddresses() { setAddressesLoading(true); try { const res = await fetch('/api/addresses', { credentials: 'include' }); if (res.ok) { const data = await res.json(); setAddresses(data); } } catch (err) { console.error('Error loading addresses:', err); } finally { setAddressesLoading(false); } } function openAddresses() { setShowAddresses(true); loadAddresses(); } function openAddAddressForm() { setEditingAddressId(null); setFormAddress(''); setFormLabel(''); setFormDefault(addresses.length === 0); setFormError(''); setShowAddressForm(true); } function openEditAddressForm(addr) { setEditingAddressId(addr.id); setFormAddress(addr.address); setFormLabel(addr.label || ''); setFormDefault(Boolean(addr.is_default)); setFormError(''); setShowAddressForm(true); } async function handleAddressSave(e) { e?.preventDefault(); const addr = formAddress.trim(); if (!addr) { setFormError('La dirección es obligatoria'); return; } setFormSaving(true); setFormError(''); try { const url = editingAddressId ? `/api/addresses/${editingAddressId}` : '/api/addresses'; const method = editingAddressId ? 'PUT' : 'POST'; const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ address: addr, label: formLabel.trim(), is_default: formDefault, }), }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || 'Error al guardar la dirección'); } setShowAddressForm(false); setEditingAddressId(null); loadAddresses(); } catch (err) { setFormError(err.message); } finally { setFormSaving(false); } } async function handleDeleteAddress(id) { try { const res = await fetch(`/api/addresses/${id}`, { method: 'DELETE', credentials: 'include', }); if (res.ok) { setAddresses(prev => prev.filter(a => a.id !== id)); } } catch (err) { console.error('Error deleting address:', err); } } async function handleSetDefault(id) { try { const res = await fetch(`/api/addresses/${id}/default`, { method: 'PUT', credentials: 'include', }); if (res.ok) { loadAddresses(); } } catch (err) { console.error('Error setting default address:', err); } } async function handleConfigSave(e) { e?.preventDefault(); setConfigSaving(true); setConfigFeedback(null); try { const res = await fetch('/api/users/me', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ first_name: configFirstName.trim() || null, last_name: configLastName.trim() || null, email: configEmail.trim() || null, city: configCity.trim() || null, address: configAddress.trim() || null, }), }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || `Error al guardar (HTTP ${res.status})`); } const updated = await res.json(); onProfileSaved?.(updated); setFirstName(updated.first_name || ''); setLastName(updated.last_name || ''); setConfigFeedback({ type: 'ok', text: 'Perfil guardado.' }); setTimeout(() => setShowConfig(false), 1200); } catch (err) { setConfigFeedback({ type: 'err', text: err.message || 'Error al guardar' }); } finally { setConfigSaving(false); } } async function handleAvatarUpload(e) { const file = e.target.files?.[0]; if (!file) return; setUploadError(''); setShowAvatarModal(false); setUploading(true); if (file.size > 5 * 1024 * 1024) { setUploadError('La imagen no puede superar los 5 MB'); setUploading(false); return; } try { const reader = new FileReader(); reader.onload = async () => { const base64 = reader.result; setAvatarUrl(base64); try { const res = await fetch('/api/users/me', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ avatar_url: base64 }), }); if (res.ok) { const updated = await res.json(); onProfileSaved?.(updated); } else { setAvatarUrl(resolveAvatarUrl(currentUser?.avatar_url)); setUploadError('Error al guardar la imagen. Intenta con otra foto.'); } } catch (err) { setAvatarUrl(resolveAvatarUrl(currentUser?.avatar_url)); setUploadError('Error de conexión al guardar la imagen.'); } setUploading(false); }; reader.readAsDataURL(file); } catch (err) { setUploadError('Error al procesar la imagen.'); setUploading(false); } } async function handleSelectPresetAvatar(index) { const url = AVATARS[index]; setAvatarUrl(url); setShowAvatarModal(false); try { const res = await fetch('/api/users/me', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ avatar_url: `preset_avatar_${index + 1}` }), }); if (res.ok) { const updated = await res.json(); onProfileSaved?.(updated); } } catch (err) { console.error('Error saving avatar:', err); } } async function handleSelectColor(index) { const url = COLOR_CIRCLES[index]; setAvatarUrl(url); setShowAvatarModal(false); try { const res = await fetch('/api/users/me', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ avatar_url: `color_circle_${index + 1}` }), }); if (res.ok) { const updated = await res.json(); onProfileSaved?.(updated); } } catch (err) { console.error('Error saving avatar:', err); } } function handleGalleryUpload() { fileInputRef.current?.click(); } async function handleDeleteSearch(id) { try { const res = await fetch(`/api/search-history/${id}`, { method: 'DELETE', credentials: 'include', }); if (res.ok) { setSearchHistory(prev => prev.filter(item => item.id !== id)); } } catch (err) { console.error('Error deleting search:', err); } } const displayName = [firstName, lastName].filter(Boolean).join(' ') || currentUser?.username || 'Usuario'; return (
Subiendo foto...
} {uploadError &&{uploadError}
}Nombre
{firstName || '—'}
Apellidos
{lastName || '—'}
Tus búsquedas recientes:
{searchHistory.map((item) => (Cargando direcciones...
) : (