Add Sprint Management feature.

This commit is contained in:
2025-07-04 20:03:30 +02:00
committed by Jens Luedicke
parent 49cc0c94d2
commit 9f4190a29b
8 changed files with 2667 additions and 25 deletions

View File

@@ -106,6 +106,7 @@
<select id="chart-type">
<option value="timeSeries">Time Series</option>
<option value="projectDistribution">Project Distribution</option>
<option value="burndown">Burndown Chart</option>
</select>
<div class="export-buttons">
<button class="btn btn-secondary" onclick="exportChart('png')">Export PNG</button>
@@ -268,7 +269,12 @@ class TimeAnalyticsController {
const chartTypeSelect = document.getElementById('chart-type');
if (chartTypeSelect) {
chartTypeSelect.addEventListener('change', () => {
this.updateChart();
// For burndown chart, we need to reload data from the server
if (chartTypeSelect.value === 'burndown') {
this.loadData();
} else {
this.updateChart();
}
});
}
@@ -309,6 +315,12 @@ class TimeAnalyticsController {
params.append('project_id', this.state.selectedProject);
}
// Add chart_type parameter for graph view
if (this.state.activeView === 'graph') {
const chartType = document.getElementById('chart-type')?.value || 'timeSeries';
params.append('chart_type', chartType);
}
const response = await fetch(`/api/analytics/data?${params}`);
const data = await response.json();
@@ -396,11 +408,29 @@ class TimeAnalyticsController {
const data = this.state.data;
if (!data) return;
// Update stats
document.getElementById('total-hours').textContent = data.totalHours?.toFixed(1) || '0';
document.getElementById('total-days').textContent = data.totalDays || '0';
document.getElementById('avg-hours').textContent =
data.totalDays > 0 ? (data.totalHours / data.totalDays).toFixed(1) : '0';
const chartType = document.getElementById('chart-type').value;
// Update stats based on chart type
if (chartType === 'burndown' && data.burndown) {
document.getElementById('total-hours').textContent = data.burndown.total_tasks || '0';
document.getElementById('total-days').textContent = data.burndown.dates?.length || '0';
document.getElementById('avg-hours').textContent = data.burndown.tasks_completed || '0';
// Update stat labels for burndown
document.querySelector('.stat-card:nth-child(1) h4').textContent = 'Total Tasks';
document.querySelector('.stat-card:nth-child(2) h4').textContent = 'Timeline Days';
document.querySelector('.stat-card:nth-child(3) h4').textContent = 'Completed Tasks';
} else {
document.getElementById('total-hours').textContent = data.totalHours?.toFixed(1) || '0';
document.getElementById('total-days').textContent = data.totalDays || '0';
document.getElementById('avg-hours').textContent =
data.totalDays > 0 ? (data.totalHours / data.totalDays).toFixed(1) : '0';
// Restore original stat labels
document.querySelector('.stat-card:nth-child(1) h4').textContent = 'Total Hours';
document.querySelector('.stat-card:nth-child(2) h4').textContent = 'Total Days';
document.querySelector('.stat-card:nth-child(3) h4').textContent = 'Average Hours/Day';
}
this.updateChart();
}
@@ -483,6 +513,68 @@ class TimeAnalyticsController {
}
}
});
} else if (chartType === 'burndown') {
this.charts.main = new Chart(ctx, {
type: 'line',
data: {
labels: data.burndown?.dates || [],
datasets: [{
label: 'Remaining Tasks',
data: data.burndown?.remaining || [],
borderColor: '#FF5722',
backgroundColor: 'rgba(255, 87, 34, 0.1)',
fill: true,
tension: 0.1,
pointBackgroundColor: '#FF5722',
pointBorderColor: '#FF5722',
pointRadius: 4
}, {
label: 'Ideal Burndown',
data: data.burndown?.ideal || [],
borderColor: '#4CAF50',
backgroundColor: 'transparent',
borderDash: [5, 5],
fill: false,
tension: 0,
pointRadius: 0
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Project Burndown Chart'
},
legend: {
display: true,
position: 'top'
}
},
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Remaining Tasks'
},
ticks: {
stepSize: 1
}
},
x: {
title: {
display: true,
text: 'Date'
}
}
},
interaction: {
intersect: false,
mode: 'index'
}
}
});
}
}
@@ -554,7 +646,9 @@ function exportChart(format) {
} else if (format === 'pdf') {
// Get chart title for PDF
const chartType = document.getElementById('chart-type').value;
const title = chartType === 'timeSeries' ? 'Daily Hours Worked' : 'Time Distribution by Project';
const title = chartType === 'timeSeries' ? 'Daily Hours Worked' :
chartType === 'projectDistribution' ? 'Time Distribution by Project' :
'Project Burndown Chart';
// Create PDF using jsPDF
const { jsPDF } = window.jspdf;