- 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
126 lines
3.8 KiB
PHP
126 lines
3.8 KiB
PHP
@php
|
|
$data = $data ?? [];
|
|
$project = $data['project'] ?? null;
|
|
$filters = $data['filters'] ?? null;
|
|
$summary = $data['summary'] ?? null;
|
|
$phases = $data['phases'] ?? null;
|
|
$features = $data['features'] ?? null;
|
|
$inspections = $data['inspections'] ?? null;
|
|
$issues = $data['issues'] ?? null;
|
|
$tasks = $data['tasks'] ?? null;
|
|
$deviations = $data['deviations'] ?? null;
|
|
$progress_curve = $data['progress_curve'] ?? null;
|
|
$media = $data['media'] ?? null;
|
|
@endphp
|
|
|
|
<div class="page-wrapper" style="max-width: 100%; padding: 20px; font-size: 12px;">
|
|
|
|
{{-- Header --}}
|
|
@include('reports.partials._header', ['project' => $project, 'filters' => $filters])
|
|
|
|
{{-- Summary --}}
|
|
@if($summary)
|
|
@include('reports.partials._summary', ['summary' => $summary])
|
|
@endif
|
|
|
|
{{-- Progress Curve --}}
|
|
@if($progress_curve && !empty($progress_curve['labels']))
|
|
<div class="section-title">{{ __('Curva S: Progreso Planificado vs Real') }}</div>
|
|
<div class="card bg-base-100 shadow p-4 mb-8" style="height: 300px;">
|
|
<canvas id="previewProgressCurveChart"></canvas>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Deviations --}}
|
|
@if($deviations)
|
|
@include('reports.partials._deviations', ['deviations' => $deviations])
|
|
@endif
|
|
|
|
{{-- Phases --}}
|
|
@if($phases && !empty($phases))
|
|
@include('reports.partials._phases', ['phases' => $phases])
|
|
@endif
|
|
|
|
{{-- Features --}}
|
|
@if($features && !empty($features))
|
|
@include('reports.partials._features', ['features' => $features])
|
|
@endif
|
|
|
|
{{-- Inspections --}}
|
|
@if($inspections && !empty($inspections))
|
|
@include('reports.partials._inspections', ['inspections' => $inspections])
|
|
@endif
|
|
|
|
{{-- Issues --}}
|
|
@if($issues && !empty($issues))
|
|
@include('reports.partials._issues', ['issues' => $issues])
|
|
@endif
|
|
|
|
{{-- Tasks --}}
|
|
@if($tasks && !empty($tasks))
|
|
@include('reports.partials._tasks', ['tasks' => $tasks])
|
|
@endif
|
|
|
|
{{-- Media --}}
|
|
@if($media && !empty($media) && ($filters['include_photos'] ?? false))
|
|
@include('reports.partials._media', ['media' => $media])
|
|
@endif
|
|
|
|
</div>
|
|
|
|
<script>
|
|
function initializePreviewProgressCurveChart() {
|
|
if (typeof Chart === 'undefined') {
|
|
setTimeout(initializePreviewProgressCurveChart, 100);
|
|
return;
|
|
}
|
|
|
|
const ctx = document.getElementById('previewProgressCurveChart');
|
|
if (!ctx) return;
|
|
|
|
if (ctx.chart instanceof Chart) {
|
|
ctx.chart.destroy();
|
|
}
|
|
|
|
const labels = @json($progress_curve['labels'] ?? []);
|
|
const planned = @json($progress_curve['planned'] ?? []);
|
|
const actual = @json($progress_curve['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,
|
|
},
|
|
{
|
|
label: '{{ __('Progreso Real') }} (%)',
|
|
data: actual,
|
|
borderColor: '#10b981',
|
|
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
|
fill: true,
|
|
tension: 0.3,
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { position: 'bottom' } },
|
|
scales: {
|
|
y: { beginAtZero: true, max: 100 },
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', initializePreviewProgressCurveChart);
|
|
</script> |