Files
otrs-turbo/public/js/views/dashboard.js
T
2026-07-05 10:55:40 +02:00

152 lines
6.4 KiB
JavaScript

/**
* Dashboard View
* Shows stats overview, distribution charts, and recent tickets.
*/
const DashboardView = {
async render() {
const container = document.getElementById('view-container');
container.innerHTML = '<div class="loading-screen"><div class="spinner"></div><p>Caricamento dashboard...</p></div>';
try {
const stats = await App.api('/api/dashboard/stats');
const maxByState = Math.max(...(stats.by_state || []).map(s => parseInt(s.count)), 1);
const maxByPriority = Math.max(...(stats.by_priority || []).map(s => parseInt(s.count)), 1);
const maxByQueue = Math.max(...(stats.by_queue || []).map(s => parseInt(s.count)), 1);
container.innerHTML = `
<!-- Stats Cards -->
<div class="stats-grid">
<div class="stat-card accent">
<div class="stat-label">Ticket Aperti</div>
<div class="stat-value">${stats.total_open}</div>
</div>
<div class="stat-card">
<div class="stat-label">Creati Oggi</div>
<div class="stat-value">${stats.created_today}</div>
</div>
<div class="stat-card">
<div class="stat-label">Creati Settimana</div>
<div class="stat-value">${stats.created_this_week}</div>
</div>
<div class="stat-card ${stats.escalated > 0 ? 'danger' : ''}">
<div class="stat-label">Escalated</div>
<div class="stat-value">${stats.escalated}</div>
</div>
</div>
<!-- Distributions -->
<div class="distribution-section">
<div class="card">
<div class="card-title">Per Stato</div>
${(stats.by_state || []).map(s => `
<div class="dist-bar-container">
<div class="dist-bar-header">
<span class="dist-bar-label">${s.state}</span>
<span class="dist-bar-value">${s.count}</span>
</div>
<div class="dist-bar-track">
<div class="dist-bar-fill" style="width: ${(parseInt(s.count) / maxByState * 100).toFixed(1)}%"></div>
</div>
</div>
`).join('')}
${(stats.by_state || []).length === 0 ? '<p style="color:var(--text-tertiary);font-size:0.85rem;">Nessun dato</p>' : ''}
</div>
<div class="card">
<div class="card-title">Per Priorità</div>
${(stats.by_priority || []).map((p, idx) => `
<div class="dist-bar-container">
<div class="dist-bar-header">
<span class="dist-bar-label">${p.priority}</span>
<span class="dist-bar-value">${p.count}</span>
</div>
<div class="dist-bar-track">
<div class="dist-bar-fill" style="width: ${(parseInt(p.count) / maxByPriority * 100).toFixed(1)}%; background: linear-gradient(90deg, var(--priority-${idx + 1}-text, var(--accent-primary)), var(--accent-secondary));"></div>
</div>
</div>
`).join('')}
${(stats.by_priority || []).length === 0 ? '<p style="color:var(--text-tertiary);font-size:0.85rem;">Nessun dato</p>' : ''}
</div>
<div class="card">
<div class="card-title">Per Coda (Top 10)</div>
${(stats.by_queue || []).map(q => `
<div class="dist-bar-container">
<div class="dist-bar-header">
<span class="dist-bar-label">${q.queue}</span>
<span class="dist-bar-value">${q.count}</span>
</div>
<div class="dist-bar-track">
<div class="dist-bar-fill" style="width: ${(parseInt(q.count) / maxByQueue * 100).toFixed(1)}%"></div>
</div>
</div>
`).join('')}
${(stats.by_queue || []).length === 0 ? '<p style="color:var(--text-tertiary);font-size:0.85rem;">Nessun dato</p>' : ''}
</div>
</div>
<!-- Recent Tickets -->
<div class="card">
<div class="card-title">Ticket Recenti</div>
${(stats.recent_tickets || []).length > 0 ? `
<table class="recent-tickets-table">
<thead>
<tr>
<th>Numero</th>
<th>Titolo</th>
<th>Stato</th>
<th>Priorità</th>
<th>Coda</th>
<th>Data</th>
</tr>
</thead>
<tbody>
${stats.recent_tickets.map(t => `
<tr onclick="window.location.hash='#/tickets/${t.id}'">
<td><span class="ticket-tn">${t.tn}</span></td>
<td class="ticket-title-cell">${App.escapeHtml(t.title || '')}</td>
<td><span class="badge badge-state" data-state-type="${(t.state_name || '').toLowerCase()}">${t.state_name}</span></td>
<td><span class="badge badge-priority" data-priority="${t.priority_name ? App.priorityIndex(t.priority_name) : 3}">${t.priority_name}</span></td>
<td><span class="badge badge-queue">${t.queue_name}</span></td>
<td style="color:var(--text-tertiary);font-size:0.78rem;">${App.formatDate(t.create_time)}</td>
</tr>
`).join('')}
</tbody>
</table>
` : `
<div class="empty-state">
<div class="empty-state-icon">📭</div>
<div class="empty-state-text">Nessun ticket recente</div>
</div>
`}
</div>
`;
// Animate bars after render
requestAnimationFrame(() => {
document.querySelectorAll('.dist-bar-fill').forEach(bar => {
const w = bar.style.width;
bar.style.width = '0%';
requestAnimationFrame(() => { bar.style.width = w; });
});
});
// Update open ticket count in sidebar badge
const badge = document.getElementById('open-ticket-count');
if (badge && stats.total_open > 0) {
badge.textContent = stats.total_open;
}
} catch (err) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">⚠️</div>
<div class="empty-state-text">Errore caricamento dashboard</div>
<div class="empty-state-sub">${App.escapeHtml(err.message)}</div>
</div>
`;
}
},
};