feat(ui): dashboard skeletons, empty states, Cmd+K registration, improved loading states
This commit is contained in:
@@ -208,7 +208,7 @@ const Dashboard = (() => {
|
||||
function setKpiError(valueId, metaId) {
|
||||
const v = document.getElementById(valueId);
|
||||
const m = document.getElementById(metaId);
|
||||
if (v) v.textContent = '--';
|
||||
if (v) v.innerHTML = '<span style="color:var(--color-error)">--</span>';
|
||||
if (m) m.innerHTML = '<span class="kpi-meta-text" style="color:var(--color-error)">Error al cargar</span>';
|
||||
}
|
||||
|
||||
@@ -225,7 +225,12 @@ const Dashboard = (() => {
|
||||
if (!container) return;
|
||||
|
||||
if (!registers || registers.length === 0) {
|
||||
container.innerHTML = '<div class="rank-item" style="justify-content:center;color:var(--color-text-muted);font-size:var(--text-caption);">Sin cajas registradas hoy</div>';
|
||||
container.innerHTML = renderEmptyState({
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><path d="M8 21h8M12 17v4"/></svg>',
|
||||
title: 'Sin cajas hoy',
|
||||
subtitle: 'Ninguna caja ha sido abierta el día de hoy.',
|
||||
action: '<a href="/pos/sale" class="btn btn--primary btn--sm">Abrir POS</a>'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -326,7 +331,12 @@ const Dashboard = (() => {
|
||||
if (!container) return;
|
||||
|
||||
if (!data || !data.data || data.data.length === 0) {
|
||||
container.innerHTML = '<div class="rank-item" style="justify-content:center;color:var(--color-text-muted);font-size:var(--text-caption);">Sin ventas hoy</div>';
|
||||
container.innerHTML = renderEmptyState({
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><path d="M8 21h8M12 17v4"/></svg>',
|
||||
title: 'Sin ventas hoy',
|
||||
subtitle: 'Aún no hay transacciones registradas el día de hoy.',
|
||||
action: '<a href="/pos/sale" class="btn btn--primary btn--sm">Nueva venta</a>'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -353,7 +363,11 @@ const Dashboard = (() => {
|
||||
const sorted = Object.values(productMap).sort((a, b) => b.revenue - a.revenue).slice(0, 5);
|
||||
|
||||
if (sorted.length === 0) {
|
||||
container.innerHTML = '<div class="rank-item" style="justify-content:center;color:var(--color-text-muted);font-size:var(--text-caption);">Sin productos vendidos</div>';
|
||||
container.innerHTML = renderEmptyState({
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 01-8 0"/></svg>',
|
||||
title: 'Sin productos vendidos',
|
||||
subtitle: 'No hay suficiente información para mostrar el ranking.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -444,7 +458,12 @@ const Dashboard = (() => {
|
||||
if (!tbody) return;
|
||||
|
||||
if (!data || !data.data || data.data.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--color-text-muted);font-size:var(--text-caption);">Sin ventas hoy</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="5">' + renderEmptyState({
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><path d="M8 21h8M12 17v4"/></svg>',
|
||||
title: 'Sin ventas hoy',
|
||||
subtitle: 'Aún no hay transacciones registradas.',
|
||||
action: '<a href="/pos/sale" class="btn btn--primary btn--sm">Nueva venta</a>'
|
||||
}) + '</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -526,6 +545,17 @@ const Dashboard = (() => {
|
||||
}, 120000);
|
||||
}
|
||||
|
||||
// Register Cmd+K items
|
||||
if (typeof registerCmdKItem === 'function') {
|
||||
registerCmdKItem({ group: 'Principal', label: 'Dashboard', href: '/pos/dashboard', icon: '📊' });
|
||||
registerCmdKItem({ group: 'Principal', label: 'POS Ventas', href: '/pos/sale', icon: '🛒' });
|
||||
registerCmdKItem({ group: 'Principal', label: 'Catálogo', href: '/pos/catalog', icon: '📁' });
|
||||
registerCmdKItem({ group: 'Principal', label: 'Clientes', href: '/pos/customers', icon: '👤' });
|
||||
registerCmdKItem({ group: 'Principal', label: 'Facturación', href: '/pos/invoicing', icon: '📄' });
|
||||
registerCmdKItem({ group: 'Principal', label: 'Reportes', href: '/pos/reports', icon: '📈' });
|
||||
registerCmdKItem({ group: 'Principal', label: 'Configuración', href: '/pos/config', icon: '⚙️' });
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
|
||||
return { init, setTheme };
|
||||
|
||||
@@ -255,9 +255,9 @@
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="kpi-card__value" id="kpi-ventas-total">--</div>
|
||||
<div class="kpi-card__value" id="kpi-ventas-total"><div class="skeleton skeleton--text" style="width:60%;"></div></div>
|
||||
<div class="kpi-card__meta" id="kpi-ventas-meta">
|
||||
<span class="kpi-tag kpi-tag--neutral">Cargando...</span>
|
||||
<div class="skeleton skeleton--text-sm" style="width:80%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -273,9 +273,9 @@
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="kpi-card__value" id="kpi-tickets-count">--</div>
|
||||
<div class="kpi-card__value" id="kpi-tickets-count"><div class="skeleton skeleton--text" style="width:40%;"></div></div>
|
||||
<div class="kpi-card__meta" id="kpi-tickets-meta">
|
||||
<span class="kpi-tag kpi-tag--neutral">Cargando...</span>
|
||||
<div class="skeleton skeleton--text-sm" style="width:70%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -291,9 +291,9 @@
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="kpi-card__value" id="kpi-promedio-value">--</div>
|
||||
<div class="kpi-card__value" id="kpi-promedio-value"><div class="skeleton skeleton--text" style="width:60%;"></div></div>
|
||||
<div class="kpi-card__meta" id="kpi-promedio-meta">
|
||||
<span class="kpi-tag kpi-tag--neutral">Cargando...</span>
|
||||
<div class="skeleton skeleton--text-sm" style="width:75%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -308,9 +308,9 @@
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="kpi-card__value" id="kpi-cajas-count">--</div>
|
||||
<div class="kpi-card__value" id="kpi-cajas-count"><div class="skeleton skeleton--text" style="width:30%;"></div></div>
|
||||
<div class="kpi-card__meta" id="kpi-cajas-meta">
|
||||
<span class="kpi-tag kpi-tag--neutral">Cargando...</span>
|
||||
<div class="skeleton skeleton--text-sm" style="width:80%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -399,7 +399,9 @@
|
||||
<a href="/pos/inventory" style="font-size:var(--text-caption);color:var(--color-primary);font-weight:var(--font-weight-semibold);cursor:pointer;text-decoration:none;">Ver todos</a>
|
||||
</div>
|
||||
<div class="rank-list" id="top-products-list">
|
||||
<div class="rank-item" style="justify-content:center;color:var(--color-text-muted);font-size:var(--text-caption);">Cargando...</div>
|
||||
<div class="rank-item"><div class="skeleton skeleton--text" style="width:90%;"></div></div>
|
||||
<div class="rank-item"><div class="skeleton skeleton--text" style="width:70%;"></div></div>
|
||||
<div class="rank-item"><div class="skeleton skeleton--text" style="width:80%;"></div></div>
|
||||
</div>
|
||||
</div><!-- end Top Productos -->
|
||||
|
||||
@@ -412,7 +414,8 @@
|
||||
<a href="/pos/config" style="font-size:var(--text-caption);color:var(--color-primary);font-weight:var(--font-weight-semibold);cursor:pointer;text-decoration:none;">Gestionar</a>
|
||||
</div>
|
||||
<div class="rank-list" id="registers-list">
|
||||
<div class="rank-item" style="justify-content:center;color:var(--color-text-muted);font-size:var(--text-caption);">Cargando...</div>
|
||||
<div class="rank-item"><div class="skeleton skeleton--text" style="width:85%;"></div></div>
|
||||
<div class="rank-item"><div class="skeleton skeleton--text" style="width:65%;"></div></div>
|
||||
</div>
|
||||
</div><!-- end Cajas del Día -->
|
||||
|
||||
@@ -429,7 +432,8 @@
|
||||
<span class="section-action">Gestionar todo →</span>
|
||||
</div>
|
||||
<div class="alerts-grid" id="alerts-grid">
|
||||
<div class="alert-item" style="justify-content:center;color:var(--color-text-muted);font-size:var(--text-caption);border-left:none;">Cargando alertas...</div>
|
||||
<div class="alert-item" style="border-left:none;"><div class="skeleton skeleton--text" style="width:100%;"></div></div>
|
||||
<div class="alert-item" style="border-left:none;"><div class="skeleton skeleton--text" style="width:80%;"></div></div>
|
||||
</div><!-- end alerts-grid -->
|
||||
</section>
|
||||
|
||||
@@ -467,7 +471,9 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="recent-sales-tbody">
|
||||
<tr><td colspan="5" style="text-align:center;color:var(--color-text-muted);font-size:var(--text-caption);">Cargando ventas recientes...</td></tr>
|
||||
<tr><td colspan="5"><div class="skeleton skeleton--text" style="width:100%;"></div></td></tr>
|
||||
<tr><td colspan="5"><div class="skeleton skeleton--text" style="width:80%;"></div></td></tr>
|
||||
<tr><td colspan="5"><div class="skeleton skeleton--text" style="width:90%;"></div></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -487,7 +493,7 @@
|
||||
<script src="/pos/static/js/pos-utils.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js" defer></script>
|
||||
<script src="/pos/static/js/dashboard-stats.js" defer></script>
|
||||
<script src="/pos/static/js/dashboard.js" defer></script>
|
||||
<script src="/pos/static/js/dashboard.js?v=2" defer></script>
|
||||
<script src="/pos/static/js/sync-engine.js" defer></script>
|
||||
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
|
||||
<script src="/pos/static/js/pwa-install.js" defer></script>
|
||||
|
||||
Reference in New Issue
Block a user