// Componente Header del directorio de vendedores
function Header() {
return (
Directorio de Vendedores
Encuentra vendedores de confianza en tu provincia. Explora sus perfiles y descubre sus productos.
);
}
// Componente de búsqueda y filtros
function SearchFilters({ searchTerm, setSearchTerm, selectedProvince, setSelectedProvince, showFavoritesOnly, setShowFavoritesOnly }) {
const provinces = [
"Todas",
"Álava",
"Albacete",
"Alicante",
"Almería",
"Asturias",
"Ávila",
"Badajoz",
"Barcelona",
"Burgos",
"Cáceres",
"Cádiz",
"Cantabria",
"Castellón",
"Ciudad Real",
"Córdoba",
"Cuenca",
"Girona",
"Granada",
"Guadalajara",
"Guipúzcoa",
"Huelva",
"Huesca",
"Islas Baleares",
"Jaén",
"La Coruña",
"La Rioja",
"Las Palmas",
"León",
"Lleida",
"Lugo",
"Madrid",
"Málaga",
"Murcia",
"Navarra",
"Ourense",
"Palencia",
"Pontevedra",
"Salamanca",
"Santa Cruz de Tenerife",
"Segovia",
"Sevilla",
"Soria",
"Tarragona",
"Teruel",
"Toledo",
"Valencia",
"Valladolid",
"Vizcaya",
"Zamora",
"Zaragoza"
];
return (
{/* Búsqueda */}
{/* Filtro Provincia */}
{/* Filtro Favoritos */}
);
}
// Componente para mostrar el contador de resultados
function ResultsCount({ count, loading }) {
if (loading) {
return null;
}
return (
Mostrando {count} vendedor{count !== 1 ? 'es' : ''}
);
}
// Componente de tarjeta individual de vendedor
function VendorCard({ vendor, isFavorite, onToggleFavorite }) {
// Función para generar slug
const generateSlug = (text) => {
if (!text) return 'vendedor';
return text
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '') // Remover acentos
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
};
const vendorSlug = generateSlug(vendor.businessName);
const profileUrl = `/vendedores/${vendorSlug}`;
return (
{/* Header de la tarjeta */}
{/* Avatar */}
{vendor.image ? (

{
e.target.style.display = 'none';
e.target.nextElementSibling.style.display = 'flex';
}}
/>
) : null}
{/* Información del vendedor */}
{vendor.businessName}
{/* Botón de favoritos */}
{/* Descripción */}
{vendor.description}
{/* Información de contacto */}
{/* Ubicación */}
{/* Teléfono */}
{/* Email */}
{/* Botón Ver Perfil */}
);
}
// Componente de grid de vendedores
function VendorGrid({ vendors, loading, favorites, onToggleFavorite }) {
if (loading) {
return (
);
}
if (vendors.length === 0) {
return (
No se encontraron vendedores
Intenta ajustar los filtros de búsqueda
);
}
return (
{vendors.map((vendor, index) => (
))}
);
}
// Componente principal de la aplicación Vendedores
function VendedoresApp() {
const [searchTerm, setSearchTerm] = React.useState('');
const [selectedProvince, setSelectedProvince] = React.useState('Todas');
const [showFavoritesOnly, setShowFavoritesOnly] = React.useState(false);
const [vendors, setVendors] = React.useState([]);
const [loading, setLoading] = React.useState(false);
const [allVendors, setAllVendors] = React.useState([]);
const [favorites, setFavorites] = React.useState([]);
// Cargar favoritos al inicializar
React.useEffect(() => {
loadFavorites();
}, []);
// Función para cargar favoritos
const loadFavorites = async () => {
try {
const form = new URLSearchParams();
form.append('action', 'vendedores_get_favorites');
form.append('nonce', (window.vendedoresPlugin && window.vendedoresPlugin.nonce) || '');
const res = await fetch((window.vendedoresPlugin && window.vendedoresPlugin.ajax_url) || '/wp-admin/admin-ajax.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: form.toString()
});
const json = await res.json();
if (json && json.success && Array.isArray(json.data.favorites)) {
// Convertir a strings para asegurar consistencia
const favoritesAsStrings = json.data.favorites.map(id => String(id));
setFavorites(favoritesAsStrings);
}
} catch (e) {
console.error('Error loading favorites:', e);
}
};
// Función para toggle favoritos con actualización optimista
const handleToggleFavorite = async (vendorId) => {
const isFavorite = favorites.includes(vendorId);
// Actualización optimista: cambiar el estado inmediatamente
if (isFavorite) {
setFavorites(prev => prev.filter(id => id !== vendorId));
} else {
setFavorites(prev => [...prev, vendorId]);
}
const action = isFavorite ? 'vendedores_remove_favorite' : 'vendedores_add_favorite';
try {
const form = new URLSearchParams();
form.append('action', action);
form.append('vendor_id', vendorId);
form.append('nonce', (window.vendedoresPlugin && window.vendedoresPlugin.nonce) || '');
const res = await fetch((window.vendedoresPlugin && window.vendedoresPlugin.ajax_url) || '/wp-admin/admin-ajax.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: form.toString()
});
const json = await res.json();
if (json && json.success && Array.isArray(json.data.favorites)) {
// Sincronizar con el servidor
const favoritesAsStrings = json.data.favorites.map(id => String(id));
setFavorites(favoritesAsStrings);
} else if (json && !json.success) {
// Revertir cambio optimista si hay error
if (isFavorite) {
setFavorites(prev => [...prev, vendorId]);
} else {
setFavorites(prev => prev.filter(id => id !== vendorId));
}
if (json.data && json.data.message) {
alert(json.data.message);
}
}
} catch (e) {
// Revertir cambio optimista si hay error de red
if (isFavorite) {
setFavorites(prev => [...prev, vendorId]);
} else {
setFavorites(prev => prev.filter(id => id !== vendorId));
}
console.error('Error toggling favorite:', e);
alert('Error al procesar favoritos. Por favor, inténtalo de nuevo.');
}
};
// Cargar vendedores desde AJAX de WP (que proxy a la API)
React.useEffect(() => {
let abort = false;
async function loadVendors() {
try {
setLoading(true);
const form = new URLSearchParams();
form.append('action', 'vendedores_list');
form.append('nonce', (window.vendedoresPlugin && window.vendedoresPlugin.nonce) || '');
const res = await fetch((window.vendedoresPlugin && window.vendedoresPlugin.ajax_url) || '/wp-admin/admin-ajax.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: form.toString()
});
const json = await res.json();
if (!abort) {
if (json && json.success && json.data && Array.isArray(json.data.items)) {
// Mapear a formato de UI
const mapped = json.data.items.map((item) => ({
id: String(item.id), // Asegurar que sea string
businessName: item.nombre || 'Sin nombre',
vendorName: item.nombre || 'Sin nombre',
category: 'General',
description: `${item.ciudad || ''}${item.ciudad && item.provincia ? ', ' : ''}${item.provincia || ''}`.trim() || 'Sin descripción',
location: `${item.ciudad || ''}${item.ciudad && item.provincia ? ', ' : ''}${item.provincia || ''}`.trim(),
phone: item.telefono || item.whatsapp || '',
email: item.email || '',
rating: '-',
views: '-',
image: item.imagen || ''
}));
setAllVendors(mapped);
} else {
setAllVendors([]);
}
}
} catch (e) {
if (!abort) setAllVendors([]);
} finally {
if (!abort) setLoading(false);
}
}
loadVendors();
return () => { abort = true; };
}, []);
// Función para filtrar vendedores
const filterVendors = React.useCallback(() => {
return allVendors.filter(vendor => {
const matchesSearch = !searchTerm ||
vendor.businessName.toLowerCase().includes(searchTerm.toLowerCase()) ||
vendor.vendorName.toLowerCase().includes(searchTerm.toLowerCase());
const matchesProvince = selectedProvince === 'Todas' ||
vendor.location.includes(selectedProvince);
const matchesFavorites = !showFavoritesOnly || favorites.includes(vendor.id);
return matchesSearch && matchesProvince && matchesFavorites;
});
}, [searchTerm, selectedProvince, showFavoritesOnly, allVendors, favorites]);
// Efecto para aplicar filtros
React.useEffect(() => {
const filteredVendors = filterVendors();
setVendors(filteredVendors);
}, [filterVendors]);
return (
{/* Header */}
{/* Búsqueda y filtros */}
{/* Contador de resultados */}
{/* Grid de vendedores */}
);
}
// Renderizar la aplicación
function initReactApp() {
const container = document.getElementById('vendedores-container');
if (container) {
const root = ReactDOM.createRoot(container);
root.render(React.createElement(VendedoresApp));
}
}
// Inicializar aplicación
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initReactApp);
} else {
initReactApp();
}