feat(dashboard): cards colapsables en Analytics Dashboard

- Convertir 9 cards a estructura colapsable con Bootstrap 5 Collapse
- Headers fuera de cards como botones clickeables
- Cards contraídos por defecto para reducir scroll
- Añadir animación de rotación del chevron (180°)
- Ajustar selector JS de loading overlay a .collapsible-section
- CSS para estados hover, focus y bordes redondeados

Cards modificados:
1. CTR 0% (collapse-ctr-zero)
2. Quick Wins (collapse-quick-wins)
3. Contenido Decadencia (collapse-decay)
4. Contenido Estrella (collapse-star)
5. Infraposicionados (collapse-infrapos)
6. Click Distribution (collapse-click-dist)
7. Top Búsquedas (collapse-top-searches)
8. Top Clicks (collapse-top-clicks)
9. Sin Resultados (collapse-zero-results)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
FrankZamora
2025-12-04 19:58:01 -06:00
parent 9acfd0a495
commit 287de596f2
3 changed files with 614 additions and 414 deletions

View File

@@ -212,6 +212,89 @@ body .card {
font-size: 0.875rem;
}
/* =================================================================
Collapsible Cards
================================================================= */
.dashboard-analytics-wrap .collapsible-section {
margin-bottom: 1rem;
}
.dashboard-analytics-wrap .collapsible-header {
cursor: pointer;
transition: all 0.2s ease;
text-align: left;
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
}
.dashboard-analytics-wrap .collapsible-header:hover {
filter: brightness(1.1);
}
.dashboard-analytics-wrap .collapsible-header:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(255, 134, 0, 0.5);
}
/* Chevron rotation */
.dashboard-analytics-wrap .collapse-chevron {
transition: transform 0.3s ease;
font-size: 1rem;
}
.dashboard-analytics-wrap .collapsible-header:not(.collapsed) .collapse-chevron {
transform: rotate(180deg);
}
/* Header bordes */
.dashboard-analytics-wrap .collapsible-header {
border-radius: 0.375rem;
}
.dashboard-analytics-wrap .collapsible-header:not(.collapsed) {
border-radius: 0.375rem 0.375rem 0 0;
}
/* Card sin borde superior cuando está expandido */
.dashboard-analytics-wrap .collapsible-section .card {
border-top-left-radius: 0;
border-top-right-radius: 0;
border-top: none;
}
/* Loading en collapsible section */
.dashboard-analytics-wrap .collapsible-section.is-loading-table {
position: relative;
pointer-events: none;
}
.dashboard-analytics-wrap .collapsible-section.is-loading-table::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.7);
z-index: 10;
border-radius: 0.375rem;
}
.dashboard-analytics-wrap .collapsible-section.is-loading-table::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 24px;
height: 24px;
margin: -12px 0 0 -12px;
border: 3px solid #f3f3f3;
border-top: 3px solid #FF8600;
border-radius: 50%;
z-index: 11;
animation: spin 0.8s linear infinite;
}
/* =================================================================
Responsive
================================================================= */

View File

@@ -99,10 +99,10 @@
return;
}
// Find the card container and add loading state (keeps size, shows overlay)
var card = tbody.closest('.card');
if (card) {
card.classList.add('is-loading-table');
// Find the collapsible section container and add loading state (keeps size, shows overlay)
var section = tbody.closest('.collapsible-section');
if (section) {
section.classList.add('is-loading-table');
}
// Build form data
@@ -157,8 +157,8 @@
tbody.innerHTML = html;
// Remove loading state
if (card) {
card.classList.remove('is-loading-table');
if (section) {
section.classList.remove('is-loading-table');
}
// Update pagination
@@ -167,16 +167,16 @@
bindPaginationEvents(paginationDiv, tableId);
}
} else {
if (card) {
card.classList.remove('is-loading-table');
if (section) {
section.classList.remove('is-loading-table');
}
showErrorMessage('Error al cargar datos');
console.error('AJAX error:', result);
}
})
.catch(function(error) {
if (card) {
card.classList.remove('is-loading-table');
if (section) {
section.classList.remove('is-loading-table');
}
console.error('Fetch error:', error);
showErrorMessage('Error de conexión');