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
| Description | Story Points | Status | Action |
|---|
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 = `${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();
});
