Scrum Dashboard

Total Points
0
Points Completed
0
Points Remaining
0
Progress
0%

Sprint Points Distribution

To Do (0)
In Progress (0)
Done (0)

Sprint Details

Sprint Name
Start Date
End Date

User Stories / Tasks

DescriptionStory PointsStatusAction

Error: Could not load required libraries. The tool cannot function.

'; return; } // --- GLOBAL VARIABLES & CONFIG --- let scrum_chartInstance = null; const TABS = ['scrumDashboardTab', 'dataConfigTab']; let scrum_currentTabIndex = 0; const STATUS_MAP = { 'To Do': { color: '#6c757d', columnId: 'todo-cards' }, 'In Progress': { color: '#ffc107', columnId: 'inprogress-cards' }, 'Done': { color: '#28a745', columnId: 'done-cards' } }; // --- DOM ELEMENT REFERENCES --- const dataInputTableBody = document.getElementById('scrum-data-input-table'); const prevBtn = document.getElementById('scrum-prev-btn'); const nextBtn = document.getElementById('scrum-next-btn'); // USA-relevant sample data const sampleData = { sprintName: "Q3 2025 E-commerce Checkout Revamp", startDate: "2025-07-10", endDate: "2025-07-24", stories: [ { id: 1, title: "Design new checkout flow UI/UX", points: 5, status: "Done" }, { id: 2, title: "Develop guest checkout functionality", points: 8, status: "Done" }, { id: 3, title: "Integrate with new payment gateway (Stripe)", points: 13, status: "In Progress" }, { id: 4, title: "Add support for Apple Pay and Google Pay", points: 8, status: "In Progress" }, { id: 5, title: "Create API endpoints for address validation", points: 5, status: "To Do" }, { id: 6, title: "Write end-to-end tests for the checkout process", points: 8, status: "To Do" }, { id: 7, title: "Update shipping cost calculation logic", points: 3, status: "To Do" } ] }; // --- INITIALIZATION --- function scrum_initialize() { if (!dataInputTableBody || !prevBtn || !nextBtn) { console.error('One or more critical HTML elements are missing.'); return; } scrum_populateConfigForm(sampleData); scrum_processDataAndUpdateDashboard(); scrum_updateNavButtons(); } // --- DATA CONFIGURATION LOGIC --- function scrum_populateConfigForm(data) { document.getElementById('sprint-name-input').value = data.sprintName; document.getElementById('sprint-start-date-input').value = data.startDate; document.getElementById('sprint-end-date-input').value = data.endDate; dataInputTableBody.innerHTML = ''; data.stories.forEach(story => scrum_add_story_row(story)); } window.scrum_add_story_row = function(story = { id: Date.now(), title: '', points: '', status: 'To Do' }) { const row = document.createElement('tr'); row.setAttribute('data-id', story.id); row.innerHTML = ` `; dataInputTableBody.appendChild(row); } // --- DATA PROCESSING & DASHBOARD UPDATE --- window.scrum_processDataAndUpdateDashboard = function() { const data = scrum_collectDataFromConfig(); scrum_updateDashboardUI(data); scrum_changeTab('scrumDashboardTab'); } function scrum_collectDataFromConfig() { const stories = []; dataInputTableBody.querySelectorAll('tr').forEach(row => { const titleEl = row.querySelector('.story-title-input'); const pointsEl = row.querySelector('.story-points-input'); const statusEl = row.querySelector('.story-status-input'); if (titleEl && pointsEl && statusEl) { stories.push({ id: row.getAttribute('data-id'), title: titleEl.value, points: parseInt(pointsEl.value) || 0, status: statusEl.value }); } }); return { sprintName: document.getElementById('sprint-name-input').value, startDate: document.getElementById('sprint-start-date-input').value, endDate: document.getElementById('sprint-end-date-input').value, stories: stories }; } function scrum_updateDashboardUI(data) { // Update Sprint Info & Metrics document.getElementById('sprint-name-display').textContent = data.sprintName; document.getElementById('sprint-dates-display').textContent = `From ${data.startDate} to ${data.endDate}`; const totalPoints = data.stories.reduce((sum, s) => sum + s.points, 0); const completedPoints = data.stories.filter(s => s.status === 'Done').reduce((sum, s) => sum + s.points, 0); const remainingPoints = totalPoints - completedPoints; const progress = totalPoints > 0 ? Math.round((completedPoints / totalPoints) * 100) : 0; document.getElementById('sprint-total-points').textContent = totalPoints; document.getElementById('sprint-completed-points').textContent = completedPoints; document.getElementById('sprint-remaining-points').textContent = remainingPoints; document.getElementById('sprint-progress').textContent = `${progress}%`; // Update Kanban Board scrum_renderKanbanBoard(data.stories); // Update Chart const pointsByStatus = { 'To Do': 0, 'In Progress': 0, 'Done': 0 }; data.stories.forEach(s => { pointsByStatus[s.status] += s.points; }); scrum_updateChart([pointsByStatus['To Do'], pointsByStatus['In Progress'], pointsByStatus['Done']]); } function scrum_renderKanbanBoard(stories) { // Clear columns Object.values(STATUS_MAP).forEach(map => document.getElementById(map.columnId).innerHTML = ''); let counts = { 'To Do': 0, 'In Progress': 0, 'Done': 0 }; stories.forEach(story => { counts[story.status]++; const card = document.createElement('div'); card.className = 'scrum-task-card'; card.style.borderLeftColor = STATUS_MAP[story.status].color; card.innerHTML = `
${story.title}
${story.points} Points `; const columnId = STATUS_MAP[story.status].columnId; document.getElementById(columnId).appendChild(card); }); document.getElementById('todo-count').textContent = counts['To Do']; document.getElementById('inprogress-count').textContent = counts['In Progress']; document.getElementById('done-count').textContent = counts['Done']; } window.scrum_handleStatusChange = function(selectElement) { const storyId = selectElement.getAttribute('data-story-id'); const newStatus = selectElement.value; // Update status in the configuration tab const configRow = dataInputTableBody.querySelector(`tr[data-id="${storyId}"]`); if (configRow) { configRow.querySelector('.story-status-input').value = newStatus; } // Re-calculate and update the whole dashboard const data = scrum_collectDataFromConfig(); scrum_updateDashboardUI(data); } function scrum_updateChart(data) { const ctx = document.getElementById('scrum-progress-chart'); if (!ctx) return; if (scrum_chartInstance) { scrum_chartInstance.destroy(); } scrum_chartInstance = new Chart(ctx, { type: 'doughnut', data: { labels: ['To Do', 'In Progress', 'Done'], datasets: [{ data: data, backgroundColor: [STATUS_MAP['To Do'].color, STATUS_MAP['In Progress'].color, STATUS_MAP['Done'].color], borderColor: '#fff', borderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: false, cutout: '60%', plugins: { legend: { position: 'bottom' }, tooltip: { callbacks: { label: (c) => `${c.label}: ${c.raw} points` } } } } }); } // --- PDF EXPORT --- window.scrum_downloadPDF = function() { try { const { jsPDF } = window.jspdf; const doc = new jsPDF(); const data = scrum_collectDataFromConfig(); const canvas = document.getElementById('scrum-progress-chart'); const canvasImage = canvas.toDataURL('image/png', 1.0); // Title doc.setFontSize(18); doc.setTextColor('#0056b3'); doc.text("Scrum Sprint Summary", 105, 20, { align: 'center' }); doc.setFontSize(12); doc.setTextColor('#212529'); doc.text(data.sprintName, 105, 28, { align: 'center' }); // Summary Metrics const totalPoints = data.stories.reduce((sum, s) => sum + s.points, 0); const completedPoints = data.stories.filter(s => s.status === 'Done').reduce((sum, s) => sum + s.points, 0); const progress = totalPoints > 0 ? Math.round((completedPoints / totalPoints) * 100) : 0; const summaryText = ` Sprint Dates: ${data.startDate} to ${data.endDate} Total Story Points: ${totalPoints} Completed Points: ${completedPoints} Progress: ${progress}% `; doc.setFontSize(11); doc.text(summaryText, 14, 45); // Chart doc.addImage(canvasImage, 'PNG', 14, 75, 80, 80); // Task Table const tableHead = [['Task / User Story', 'Points', 'Status']]; const tableBody = data.stories.map(s => [s.title, s.points, s.status]); doc.autoTable({ head: tableHead, body: tableBody, startY: 70, startX: 105, theme: 'grid', headStyles: { fillColor: [0, 123, 255] }, }); doc.save('Scrum_Dashboard_Summary.pdf'); } catch (e) { console.error("Failed to generate PDF:", e); alert("An error occurred while generating the PDF."); } } // --- TABBING & NAVIGATION LOGIC --- window.scrum_changeTab = function(tabId) { document.querySelectorAll('.scrum-tab-content').forEach(c => c.classList.remove('active')); document.querySelectorAll('.scrum-tab-button').forEach(b => b.classList.remove('active')); document.getElementById(tabId).classList.add('active'); const activeButton = Array.from(document.querySelectorAll('.scrum-tab-button')).find(btn => btn.getAttribute('onclick').includes(tabId)); if (activeButton) activeButton.classList.add('active'); scrum_currentTabIndex = TABS.indexOf(tabId); scrum_updateNavButtons(); } window.scrum_navigateTabs = function(direction) { let newIndex = scrum_currentTabIndex; if (direction === 'next') newIndex = Math.min(newIndex + 1, TABS.length - 1); else if (direction === 'prev') newIndex = Math.max(newIndex - 1, 0); scrum_changeTab(TABS[newIndex]); } function scrum_updateNavButtons() { if (!prevBtn || !nextBtn) return; prevBtn.disabled = scrum_currentTabIndex === 0; nextBtn.disabled = scrum_currentTabIndex === TABS.length - 1; } // --- KICK IT OFF --- scrum_initialize(); });
Scroll to Top