- Added planned/actual/baseline dates to Phase and Feature models
- Created ProgressSnapshot model + migration for historical tracking
- Implemented CaptureDailyProgressSnapshot job (scheduled daily at 06:30)
- Added DeviationCalculator logic (SPI, planned progress, deviation days)
- ReportGenerator service with complete data aggregation
- ReportController with builder, preview, generate (HTML/Excel)
- ReportBuilder Livewire component with date range, entity selection, format
- ProjectReportExport with 10 sheets (Summary, Phases, Features, Inspections, Issues, Tasks, Deviations, Media, Progress Curve, Parameters)
- Blade partials for all sections (header, summary, phases, features, inspections, issues, tasks, media, deviations, progress curve)
- New routes: /projects/{project}/reports/*
- Added 'generate reports' permission
- All 101 tests passing
99 lines
2.8 KiB
PHP
99 lines
2.8 KiB
PHP
<div class="section-title">{{ __('Curva S: Progreso Planificado vs Real') }}</div>
|
|
|
|
<div class="card bg-base-100 shadow p-6 mb-8">
|
|
<canvas id="progressCurveChart" height="100"></canvas>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('livewire:load', function() {
|
|
initializeProgressCurveChart();
|
|
});
|
|
|
|
document.addEventListener('livewire:updated', function() {
|
|
initializeProgressCurveChart();
|
|
});
|
|
|
|
function initializeProgressCurveChart() {
|
|
if (typeof Chart === 'undefined') {
|
|
console.warn('Chart.js not loaded');
|
|
return;
|
|
}
|
|
|
|
const ctx = document.getElementById('progressCurveChart');
|
|
if (!ctx) return;
|
|
|
|
// Destroy existing chart
|
|
if (ctx.chart instanceof Chart) {
|
|
ctx.chart.destroy();
|
|
}
|
|
|
|
const labels = @json($curveData['labels'] ?? []);
|
|
const planned = @json($curveData['planned'] ?? []);
|
|
const actual = @json($curveData['actual'] ?? []);
|
|
|
|
if (!labels || labels.length === 0) return;
|
|
|
|
ctx.chart = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [
|
|
{
|
|
label: '{{ __('Progreso Planificado') }} (%)',
|
|
data: planned,
|
|
borderColor: '#3b82f6',
|
|
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
|
fill: true,
|
|
tension: 0.3,
|
|
pointRadius: 4,
|
|
pointHoverRadius: 6,
|
|
},
|
|
{
|
|
label: '{{ __('Progreso Real') }} (%)',
|
|
data: actual,
|
|
borderColor: '#10b981',
|
|
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
|
fill: true,
|
|
tension: 0.3,
|
|
pointRadius: 4,
|
|
pointHoverRadius: 6,
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
title: {
|
|
display: true,
|
|
text: '{{ __('Evolución del progreso') }}',
|
|
font: { size: 16, weight: 'bold' }
|
|
},
|
|
legend: {
|
|
position: 'bottom',
|
|
},
|
|
tooltip: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
}
|
|
},
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
max: 100,
|
|
title: {
|
|
display: true,
|
|
text: '{{ __('Progreso') }} (%)'
|
|
}
|
|
},
|
|
x: {
|
|
title: {
|
|
display: true,
|
|
text: '{{ __('Fecha') }}'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
</script> |