${house.username}
${house.departamento || 'Medellín'}, ${house.sector || 'Antioquia'}
${modelsCount} modelo${modelsCount !== 1 ? 's' : ''}
${house.premium ? '
⭐ Premium
' : ''}
`;
// Resolver imagen de la casa
const houseImg = houseCard.querySelector('.house-image');
resolveAndSetImage(houseImg, house.foto_perfil, PH_HOUSE);
houseCard.addEventListener('click', function() {
viewHouseProfile(house.id);
});
housesGrid.appendChild(houseCard);
});
// Diagnostic: list models that didn't match any house
try {
const unmatched = allModels.filter(m => {
const casaId = Number(m.casa_id || 0);
if (casaId && allUsers.some(h => Number(h.id) === casaId)) return false;
// if any house matched by previous logic
const anyMatch = allUsers.some(h => {
const mid = Number(m.creador_id || 0);
const muid = Number(m.user_id || 0);
if (m.casa_id && Number(m.casa_id) === Number(h.id)) return true;
if (mid && (mid === Number(h.id) || mid === Number(h.meta_user_id || 0) || mid === Number(h.author || 0))) return true;
if (muid && (muid === Number(h.meta_user_id || 0) || muid === Number(h.author || 0))) return true;
return false;
});
return !anyMatch;
});
if (unmatched.length > 0) {
console.group('DEBUG: modelos SIN match con casas cargadas');
unmatched.forEach(m => console.log(`modelo ${m.id} -> casa_id:${m.casa_id} creador_id:${m.creador_id} user_id:${m.user_id}`));
console.groupEnd();
} else {
console.log('DEBUG: todos los modelos están asociados a alguna casa (según reglas actuales)');
}
} catch (e) { console.warn('DEBUG unmatched check falló', e); }
console.log('✅ Casas cargadas en la interfaz:', houses.length);
}
function loadModels() {
console.log('💃 Cargando modelos en la interfaz...');
premiumModelsGrid.innerHTML = '';
regularModelsGrid.innerHTML = '';
// MOSTRAR TODOS LOS MODELOS - SIN FILTROS
const modelsToShow = allModels.filter(model => {
// Solo excluir explícitamente eliminados
return model.estado !== 'eliminado';
});
console.log('💃 Modelos a mostrar:', modelsToShow.length);
const premiumModels = modelsToShow.filter(model => model.premium);
const regularModels = modelsToShow.filter(model => !model.premium);
console.log('⭐ Modelos premium:', premiumModels.length);
console.log('🔵 Modelos regulares:', regularModels.length);
// Cargar modelos premium
if (premiumModels.length > 0) {
premiumModels.forEach(model => {
const modelCard = createModelCard(model);
premiumModelsGrid.appendChild(modelCard);
});
} else {
premiumModelsGrid.innerHTML = `
No hay modelos premium
Los modelos premium aparecerán aquí cuando sean asignados.
`;
}
// Cargar modelos regulares
if (regularModels.length > 0) {
regularModels.forEach(model => {
const modelCard = createModelCard(model);
regularModelsGrid.appendChild(modelCard);
});
} else {
regularModelsGrid.innerHTML = `
No hay modelos regulares
Los modelos regulares aparecerán aquí cuando se registren.
`;
}
console.log('✅ Modelos cargados en la interfaz:', modelsToShow.length);
}
function createModelCard(model) {
const modelCard = document.createElement('div');
modelCard.className = `model-card ${model.premium ? 'premium' : ''}`;
// Nombre a mostrar en la tarjeta: priorizar user_login, luego username, luego email, luego fallback
const displayName = model.user_login || model.username || model.user_email || model.email || `Modelo sin nombre`;
modelCard.innerHTML = `
${displayName}
${model.edad || 'N/A'} años
${model.sector || 'Medellín'}
${model.estado === 'inactivo' ? '
⚠️ No disponible
' : ''}
${model.premium ? '
⭐ Premium
' : ''}
`;
// Resolver imagen del modelo en card
const modelImg = modelCard.querySelector('.model-image');
resolveAndSetImage(modelImg, model.foto_perfil, PH_MODEL);
modelCard.addEventListener('click', function() {
viewModelProfile(model.id);
});
return modelCard;
}
// =============================================
// SISTEMA DE PERFILES - VISIBLE PARA TODOS
// =============================================
function viewHouseProfile(houseId) {
const house = allUsers.find(user => user.id === houseId && user.role === 'house');
if (!house) {
showError('Casa no encontrada');
return;
}
// Actualiza la URL y el historial para permitir volver con el navegador
if (location.hash !== `#casa-${houseId}`) {
history.pushState({ page: 'houseProfile', id: houseId }, '', `#casa-${houseId}`);
}
document.getElementById('houseProfileName').textContent = house.username;
loadHouseModels(houseId);
showPage('houseProfile');
}
function loadHouseModels(houseId) {
houseModelsGrid.innerHTML = '';
// Include models whose explicit casa_id matches the house OR whose creador_id matches (historical)
const houseModels = allModels.filter(model => {
const modelCasa = Number(model.casa_id || 0);
const creador = Number(model.creador_id || 0);
const matches = modelCasa === Number(houseId) || creador === Number(houseId);
return matches;
});
console.log(`🏠 Modelos de la casa ${houseId}:`, houseModels.length);
if (houseModels.length === 0) {
houseModelsGrid.innerHTML = `
No hay modelos en esta casa
Los modelos aparecerán aquí cuando sean asignados a esta casa.
`;
return;
}
houseModels.forEach(model => {
const modelCard = createModelCard(model);
houseModelsGrid.appendChild(modelCard);
});
}
async function viewModelProfile(modelId) {
// Intentamos obtener el detalle completo desde el endpoint del plugin
// (mismo origen, y es la fuente que usan los formularios de crear/editar)
const url = `/wp-json/chimbitasnow/v1/trabajadores/${encodeURIComponent(modelId)}`;
let model = allModels.find(m => m.id === modelId) || { id: modelId };
// Actualiza la URL y el historial para permitir volver con el navegador
if (location.hash !== `#modelo-${modelId}`) {
history.pushState({ page: 'modelProfile', id: modelId }, '', `#modelo-${modelId}`);
}
console.log('📋 Solicitando detalle del modelo desde:', url);
try {
const res = await fetch(url, { credentials: 'same-origin' });
if (res.ok) {
const full = await res.json();
// full may contain top-level fields and meta; normalize to our model shape
const meta = full.meta || full || {};
// Normalize possible array values using pickFirstFromList (handles arrays/strings)
const metaUserLogin = pickFirstFromList(meta.user_login || meta.username || '');
const metaUserEmail = pickFirstFromList(meta.user_email || meta.email || '');
const metaUsername = pickFirstFromList(meta.username || full.title || '');
model = Object.assign({}, model, {
username: metaUsername || model.username,
foto_perfil: pickFirstFromList(meta.foto_perfil) || model.foto_perfil,
user_login: metaUserLogin || model.user_login || '',
user_email: metaUserEmail || model.user_email || ''
});
// also copy any other useful fields present in meta
if (meta.identificacion_como) model.identificacion_como = meta.identificacion_como;
if (meta.edad) model.edad = meta.edad;
if (meta.lugar_atiende) model.lugar_atiende = meta.lugar_atiende;
if (meta.tarifa) model.tarifa = meta.tarifa;
if (meta.mostrar_tarifa !== undefined) model.mostrar_tarifa = Number(meta.mostrar_tarifa);
if (meta.horario_llamadas) model.horario_llamadas = meta.horario_llamadas;
if (meta.departamento) model.departamento = meta.departamento;
if (meta.sector) model.sector = meta.sector;
if (meta.descripcion) model.descripcion = meta.descripcion;
if (meta.galeria) model.galeria = meta.galeria;
} else {
console.warn('Fallo en fetch detalle modelo:', res.status, await res.text());
}
} catch (e) {
console.warn('Error obteniendo detalle del modelo:', e);
}
// Compute a display name prioritizing user_login, then username, then email, fallback to id
const displayName = model.user_login || model.username || model.user_email || model.email || `Modelo #${model.id}`;
console.log('📋 Cargando perfil del modelo:', displayName);
// Llenar información del perfil
document.getElementById('modelProfileName').textContent = displayName;
resolveAndSetImage(document.getElementById('modelProfileImage'), model.foto_perfil, PH_MODEL);
document.getElementById('modelUserLogin').textContent = model.user_login || '';
document.getElementById('modelUserEmail').textContent = model.user_email || '';
document.getElementById('modelIdentification').textContent = model.identificacion_como || 'No especificado';
document.getElementById('modelAge').textContent = model.edad ? `${model.edad} años` : 'No especificado';
document.getElementById('modelLocation').textContent = `${model.sector || ''}, ${model.departamento || ''}`.trim() || 'No especificado';
document.getElementById('modelPlace').textContent = model.lugar_atiende || 'No especificado';
document.getElementById('modelRate').textContent = model.tarifa && model.mostrar_tarifa ? `$${model.tarifa}` : 'Consultar';
document.getElementById('modelSchedule').textContent = model.horario_llamadas || 'No especificado';
document.getElementById('modelDescription').textContent = model.descripcion || 'No hay descripción disponible';
// Configurar estado
const statusBadge = document.getElementById('modelStatusBadge');
statusBadge.textContent = (model.estado === 'activo' || !model.estado) ? 'Disponible' : 'No disponible';
statusBadge.className = `status-badge ${(model.estado === 'activo' || !model.estado) ? '' : 'inactive'}`;
// Configurar WhatsApp
const whatsappBtn = document.getElementById('whatsappBtn');
if (model.whatsapp && model.numero_whatsapp) {
whatsappBtn.style.display = 'block';
whatsappBtn.onclick = function() {
const phone = model.numero_whatsapp.replace(/\D/g, '');
const message = `Hola ${displayName}, vi tu perfil en Chimbitas.now y estoy interesado en tus servicios`;
window.open(`https://wa.me/${phone}?text=${encodeURIComponent(message)}`, '_blank');
};
} else {
whatsappBtn.style.display = 'none';
}
// Cargar galería
loadModelGallery(model.galeria);
showPage('modelProfile');
}
function loadModelGallery(galeriaString) {
const galleryGrid = document.getElementById('modelGallery');
galleryGrid.innerHTML = '';
console.log('🖼️ DEBUG GALERÍA - String recibido:', galeriaString);
console.log('🔍 Tipo de datos:', typeof galeriaString);
console.log('📏 Longitud:', galeriaString ? galeriaString.length : 'null/undefined');
// Asegurar que galeriaString sea string
let galeriaVal = galeriaString;
if (Array.isArray(galeriaVal)) {
galeriaVal = galeriaVal.join(',');
} else if (typeof galeriaVal !== 'string') {
galeriaVal = galeriaVal ? String(galeriaVal) : '';
}
if (!galeriaVal || galeriaVal.trim() === '' || galeriaVal === 'null' || galeriaVal === 'undefined') {
console.log('❌ Galería vacía o inválida');
galleryGrid.innerHTML = `
No hay galería disponible
Este modelo no ha subido imágenes a su galería.
Debug: "${galeriaVal}"
`;
return;
}
let galleryImages = [];
// Manejar diferentes separadores y formatos
const cleanString = galeriaVal.trim();
if (cleanString.includes('|||')) {
galleryImages = cleanString.split('|||').map(img => img.trim()).filter(img => img && img !== '' && img !== 'null');
console.log('🔗 Procesado con separador |||:', galleryImages);
} else if (cleanString.includes(',')) {
galleryImages = cleanString.split(',').map(img => img.trim()).filter(img => img && img !== '' && img !== 'null');
console.log('🔗 Procesado con separador ,:', galleryImages);
} else if (cleanString.length > 0) {
galleryImages = [cleanString];
console.log('🔗 Procesado como imagen única:', galleryImages);
}
// Filtrar URLs válidas
galleryImages = galleryImages.filter(img => {
const isValid = img && img.length > 0 && (img.startsWith('http') || img.startsWith('/') || img.startsWith('data:'));
if (!isValid) {
console.warn('⚠️ Imagen inválida filtrada:', img);
}
return isValid;
});
console.log('✅ Imágenes válidas procesadas:', galleryImages.length, galleryImages);
currentGalleryImages = galleryImages;
if (galleryImages.length === 0) {
galleryGrid.innerHTML = `
No hay imágenes válidas
Las imágenes no tienen un formato válido.
Debug original: "${galeriaString}"
`;
return;
}
galleryImages.forEach((imageUrl, index) => {
const galleryItem = document.createElement('img');
galleryItem.alt = `Imagen ${index + 1}`;
galleryItem.className = 'gallery-item';
galleryItem.loading = 'lazy';
resolveAndSetImage(galleryItem, imageUrl, PH_GAL);
galleryItem.addEventListener('click', function() {
openGallery(index);
});
galleryGrid.appendChild(galleryItem);
});
}
// =============================================
// FUNCIONES AUXILIARES
// Devuelve una URL válida para una imagen, usando el valor original o el fallback
function getImageUrl(imagePath, fallback) {
if (!imagePath) return fallback;
if (typeof imagePath === 'string' && (imagePath.startsWith('http') || imagePath.startsWith('data:') || imagePath.startsWith('/'))) {
return imagePath;
}
return fallback;
}
// =============================================
// Placeholders seguros (inline SVG) para evitar bucles de onerror/cambios de dominio
const PH_MODEL = '/assets/img/default-model.svg';
const PH_HOUSE = '/assets/img/default-house.svg';
const PH_GAL = 'data:image/svg+xml;utf8,