Add real-time search: filter experts by name, skills, modules, languages, summary. Works with filters.

This commit is contained in:
Hermes Agent
2026-08-07 17:04:45 +00:00
parent d4fa069935
commit e029dabeb2
+43 -35
View File
@@ -224,14 +224,21 @@
</div>
<!-- Filters -->
<div class="flex flex-wrap gap-3 mb-10" id="filters">
<div class="flex flex-wrap items-center justify-between gap-6 mb-12">
<div class="flex-1 max-w-lg">
<div style="position:relative;">
<svg style="position:absolute;left:16px;top:50%;transform:translateY(-50%);" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2.5" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="expertSearch" placeholder="Buscar por nombre, habilidades, módulos..." oninput="searchExperts(this.value)" style="width:100%;padding:12px 16px 12px 46px;background:#fff;border:1px solid #e2e8f0;border-radius:12px;font-family:'Inter',sans-serif;font-size:0.9375rem;color:#0f172a;outline:none;transition:border-color 0.2s;" onfocus="this.style.borderColor='#4338ca'" onblur="this.style.borderColor='#e2e8f0'">
</div>
</div>
<div class="flex flex-wrap gap-3" id="filters">
<button class="filter-chip active" data-filter="all" onclick="filterExperts('all')">Todos</button>
<button class="filter-chip" data-filter="certified" onclick="filterExperts('certified')">Certificados</button>
<button class="filter-chip" data-filter="funcional" onclick="filterExperts('funcional')">Consultores funcionales</button>
<button class="filter-chip" data-filter="developer" onclick="filterExperts('developer')">Desarrolladores</button>
</div>
<!-- Expert grid (mock data) -->
<!-- Expert grid -->
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6" id="expertGrid">
<!-- Dynamically loaded from GET endpoint -->
<div id="expertGridLoading" style="grid-column:1/-1;text-align:center;padding:60px 0;color:#64748b;">
@@ -664,26 +671,44 @@
}
});
// ===== EXPERT FILTERS =====
// ===== EXPERT SEARCH =====
let currentFilter = 'all';
function searchExperts(query) {
const q = query.toLowerCase().trim();
let filtered = allExperts;
// Apply current filter
if (currentFilter === 'certified') {
filtered = filtered.filter(ex => ex.certified === 'yes' || ex.certified === true);
}
// Apply search query
if (q.length > 0) {
filtered = filtered.filter(ex => {
const name = (ex.name || '').toLowerCase();
const skills = (ex.skills || '').toLowerCase();
const modules = (ex.modules || '').toLowerCase();
const extra = (ex.extra || '').toLowerCase();
const summary = (ex.summary || '').toLowerCase();
const languages = (ex.languages || '').toLowerCase();
return name.includes(q) || skills.includes(q) || modules.includes(q) ||
extra.includes(q) || summary.includes(q) || languages.includes(q) ||
(ex.certified === 'yes' && 'certificado'.includes(q));
});
}
renderExperts(filtered);
}
// Update filterExperts to store current filter and search together
function filterExperts(filter) {
currentFilter = filter;
document.querySelectorAll('.filter-chip').forEach(chip => {
chip.classList.toggle('active', chip.dataset.filter === filter);
});
if (filter === 'all') {
renderExperts(allExperts);
} else if (filter === 'certified') {
renderExperts(allExperts.filter(ex => ex.certified === 'yes' || ex.certified === true));
} else {
// Filter by skills/modules containing the filter term
renderExperts(allExperts.filter(ex => {
const skills = (ex.skills || '').toLowerCase();
const modules = (ex.modules || '').toLowerCase();
const combined = skills + ' ' + modules;
// No way to programmatically determine funcional vs developer from API data
// Show all for now
return true;
}));
}
const searchInput = document.getElementById('expertSearch');
if (searchInput) searchExperts(searchInput.value);
}
// Load experts on page load
@@ -755,23 +780,6 @@
setTimeout(() => toast.classList.remove('show'), 4000);
}
// ===== EXPERT FILTERS =====
function filterExperts(filter) {
document.querySelectorAll('.filter-chip').forEach(chip => {
chip.classList.toggle('active', chip.dataset.filter === filter);
});
document.querySelectorAll('#expertGrid .expert-card').forEach(card => {
const role = card.dataset.role;
const certified = card.dataset.certified === 'true';
let show = true;
if (filter === 'certified') show = certified;
if (filter === 'funcional') show = role === 'funcional';
if (filter === 'developer') show = role === 'developer';
card.style.display = show ? '' : 'none';
});
}
// ===== SCROLL ANIMATIONS =====
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {