// Activity Tracker Dashboard - Main JavaScript let categoryChart = null; let timeChart = null; // Category colors const CATEGORY_COLORS = { 'Development': '#3b82f6', 'Meeting': '#8b5cf6', 'Research': '#10b981', 'Design': '#f59e0b', 'Other': '#6b7280' }; // Format time in minutes to human readable function formatTime(minutes) { const hours = Math.floor(minutes / 60); const mins = minutes % 60; if (hours > 0) { return `${hours}h ${mins}m`; } return `${mins}m`; } // Format date function formatDate(dateString) { if (!dateString) return 'N/A'; const date = new Date(dateString); return date.toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric' }); } // Format datetime function formatDateTime(dateString) { if (!dateString) return 'N/A'; const date = new Date(dateString); return date.toLocaleString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } // Update stats cards function updateStatsCards(stats) { document.getElementById('total-captures').textContent = stats.total_captures.toLocaleString(); document.getElementById('storage-size').textContent = stats.total_size_mb.toFixed(2) + ' MB'; document.getElementById('oldest-capture').textContent = formatDateTime(stats.oldest_capture); document.getElementById('newest-capture').textContent = formatDateTime(stats.newest_capture); } // Create category pie chart function createCategoryChart(categories) { const ctx = document.getElementById('category-chart').getContext('2d'); if (categoryChart) { categoryChart.destroy(); } const labels = Object.keys(categories); const data = Object.values(categories); const colors = labels.map(label => CATEGORY_COLORS[label] || '#6b7280'); categoryChart = new Chart(ctx, { type: 'doughnut', data: { labels: labels, datasets: [{ data: data, backgroundColor: colors, borderColor: '#1f2937', borderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { position: 'bottom', labels: { color: '#9ca3af', padding: 15, font: { size: 12 } } }, tooltip: { callbacks: { label: function(context) { const label = context.label || ''; const value = context.parsed || 0; const total = context.dataset.data.reduce((a, b) => a + b, 0); const percentage = ((value / total) * 100).toFixed(1); return `${label}: ${value} captures (${percentage}%)`; } } } } } }); } // Create time trend chart function createTimeChart(activities) { const ctx = document.getElementById('time-chart').getContext('2d'); if (timeChart) { timeChart.destroy(); } // Sort activities by date activities.sort((a, b) => a.date.localeCompare(b.date)); const labels = activities.map(a => formatDate(a.date)); const datasets = []; // Get all unique categories const allCategories = new Set(); activities.forEach(activity => { Object.keys(activity.categories).forEach(cat => allCategories.add(cat)); }); // Create dataset for each category allCategories.forEach(category => { const data = activities.map(activity => activity.categories[category] || 0); datasets.push({ label: category, data: data, backgroundColor: CATEGORY_COLORS[category] || '#6b7280', borderColor: CATEGORY_COLORS[category] || '#6b7280', borderWidth: 2, tension: 0.4 }); }); timeChart = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: datasets }, options: { responsive: true, maintainAspectRatio: true, scales: { y: { beginAtZero: true, ticks: { color: '#9ca3af', callback: function(value) { return formatTime(value); } }, grid: { color: '#374151' } }, x: { ticks: { color: '#9ca3af' }, grid: { color: '#374151' } } }, plugins: { legend: { position: 'bottom', labels: { color: '#9ca3af', padding: 15, font: { size: 12 } } }, tooltip: { callbacks: { label: function(context) { const label = context.dataset.label || ''; const value = context.parsed.y || 0; return `${label}: ${formatTime(value)}`; } } } } } }); } // Update recent activities table function updateActivitiesTable(activities) { const tbody = document.getElementById('activities-tbody'); tbody.innerHTML = ''; if (activities.length === 0) { tbody.innerHTML = 'No activities found'; return; } // Sort by date descending activities.sort((a, b) => b.date.localeCompare(a.date)); activities.forEach(activity => { const row = document.createElement('tr'); row.className = 'hover:bg-gray-700'; const categories = activity.categories; row.innerHTML = ` ${formatDate(activity.date)} ${formatTime(activity.total_time_minutes)} ${formatTime(categories.Development || 0)} ${formatTime(categories.Meeting || 0)} ${formatTime(categories.Research || 0)} ${formatTime(categories.Other || 0)} `; tbody.appendChild(row); }); } // Load dashboard data async function loadDashboard() { try { const response = await fetch('/api/dashboard?days=7'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); // Update stats updateStatsCards(data.stats); // Update charts createCategoryChart(data.stats.captures_by_category); createTimeChart(data.recent_activities); // Update table updateActivitiesTable(data.recent_activities); // Update last update time const now = new Date(); document.getElementById('last-update').textContent = `Last update: ${now.toLocaleTimeString('fr-FR')}`; } catch (error) { console.error('Error loading dashboard:', error); document.getElementById('activities-tbody').innerHTML = `Error loading data: ${error.message}`; } } // Initialize dashboard document.addEventListener('DOMContentLoaded', () => { loadDashboard(); // Auto-refresh every 5 minutes setInterval(loadDashboard, 5 * 60 * 1000); });