Updated PDF footer and added pie chart to aggregate report

This commit is contained in:
Constantin Graf
2024-11-29 11:55:35 +01:00
committed by Constantin Graf
parent 05da595470
commit 620c4c97dc
4 changed files with 170 additions and 92 deletions

View File

@@ -234,6 +234,7 @@ class TimeEntryController extends Controller
$request = Gotenberg::chromium(config('services.gotenberg.url')) $request = Gotenberg::chromium(config('services.gotenberg.url'))
->pdf() ->pdf()
->pdfa('PDF/A-3b') ->pdfa('PDF/A-3b')
->margins(0.39, 0.78, 0.39, 0.39)
->paperSize('8.27', '11.7') // A4 ->paperSize('8.27', '11.7') // A4
->footer(Stream::string('footer', $footerHtml)) ->footer(Stream::string('footer', $footerHtml))
->html(Stream::string('body', $html)); ->html(Stream::string('body', $html));
@@ -411,6 +412,7 @@ class TimeEntryController extends Controller
->pdf() ->pdf()
->waitForExpression("window.status === 'ready'") ->waitForExpression("window.status === 'ready'")
->pdfa('PDF/A-3b') ->pdfa('PDF/A-3b')
->margins(0.39, 0.78, 0.39, 0.39)
->paperSize('8.27', '11.7') // A4 ->paperSize('8.27', '11.7') // A4
->footer(Stream::string('footer', $footerHtml)) ->footer(Stream::string('footer', $footerHtml))
->assets(Stream::path(resource_path('pdf-js/echarts.min.js'), 'echarts.min.js')) ->assets(Stream::path(resource_path('pdf-js/echarts.min.js'), 'echarts.min.js'))

View File

@@ -1,17 +1,20 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<style> <style>
body { .page-number {
position: absolute;
bottom: 0;
left: 0;
font-size: 12px; font-size: 12px;
margin-top: 40px; margin-left: 46px;
margin-left: 47px; margin-bottom: 40px;
} }
</style> </style>
</head> </head>
<body> <body>
<p> <div class="page-number">
Page <span class="pageNumber"></span> of <span class="totalPages"></span> Page <span class="pageNumber"></span> of <span class="totalPages"></span>
</p> </div>
</body> </body>
</html> </html>

View File

@@ -3,7 +3,8 @@
@use('PhpOffice\PhpSpreadsheet\Cell\DataType') @use('PhpOffice\PhpSpreadsheet\Cell\DataType')
@use('Carbon\CarbonInterval') @use('Carbon\CarbonInterval')
@inject('interval', 'App\Service\IntervalService') @inject('interval', 'App\Service\IntervalService')
<!DOCTYPE html> @inject('colorService', 'App\Service\ColorService')
<!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
@@ -36,6 +37,13 @@
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
} }
.data-table {
break-after: auto;
}
.no-break {
break-after: avoid-page;
}
</style> </style>
<script> <script>
window.status = 'processing'; window.status = 'processing';
@@ -43,6 +51,7 @@
<script src="echarts.min.js"></script> <script src="echarts.min.js"></script>
</head> </head>
<body> <body>
<h1>Report</h1> <h1>Report</h1>
<hr> <hr>
@@ -56,10 +65,13 @@
<span>Total cost: {{ Money::of(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->__toString(), $currency)->formatTo('en_US') }}</span><br> <span>Total cost: {{ Money::of(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->__toString(), $currency)->formatTo('en_US') }}</span><br>
</div> </div>
<div id="main-chart" style="width: 100%; height:400px; margin-bottom: 50px;"></div> <div id="main-chart" style="width: 100%; height:400px;"></div>
<div id="pie-chart" style="width: 100%; height: 150px; margin-bottom: 50px;"></div>
@foreach($aggregatedData['grouped_data'] as $group1Entry) @foreach($aggregatedData['grouped_data'] as $group1Entry)
<h2>{{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }}</h2> <div class="data-table">
<h2 class="no-break">{{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }}</h2>
<table> <table>
<thead> <thead>
@@ -109,30 +121,85 @@
@endforeach @endforeach
</tbody> </tbody>
</table> </table>
</div>
@endforeach @endforeach
<script> <script>
// Initialize the echarts instance based on the prepared dom let elementPieChart = document.getElementById('pie-chart');
let element = document.getElementById('main-chart'); let pieChart = echarts.init(elementPieChart, null, {
let myChart = echarts.init(element, null, {
renderer: 'svg' renderer: 'svg'
}); });
let pieChartOptions = {
legend: {
left: '25%',
align: 'left',
top: 'middle',
orient: 'vertical',
},
backgroundColor: 'transparent',
series: [
{
label: {
show: false,
},
data: {!! json_encode(collect($aggregatedData['grouped_data'])->map(function (array $data) use (&$colorService, $group): object {
$color = $data['color'];
if ($color === null) {
$color = $colorService->getRandomColor();
}
if ($data['key'] === null) {
$color = '#CCCCCC';
}
return (object)[
'value' => $data['seconds'],
'name' => $data['description'] ?? $data['key'] ?? 'No '.Str::lower($group->description()),
'color' => $color,
'itemStyle' => (object) [
'color' => $color.'BB',
],
'emphasis' => (object) [
'itemStyle' => (object) [
'color' => $color,
],
],
];
})->toArray()) !!},
center: ['10%', '50%'],
radius: ['30%', '60%'],
left: 'left',
type: 'pie',
},
],
};
pieChart.on('finished', () => {
window.pieChartFinished = true;
if (window.mainChartFinished && window.pieChartFinished) {
window.status = 'ready';
}
})
pieChart.setOption(pieChartOptions);
// Specify the configuration items and data for the chart let elementMainChart = document.getElementById('main-chart');
let option = { let mainChart = echarts.init(elementMainChart, null, {
renderer: 'svg'
});
let mainChartOptions = {
tooltip: {}, tooltip: {},
xAxis: { xAxis: {
data: ['{!! collect($dataHistoryChart['grouped_data'])->pluck('key')->implode("', '") !!}'], data: ['{!! collect($dataHistoryChart['grouped_data'])->pluck('key')->implode("', '") !!}'],
axisLabel: { axisLabel: {
show: true, fontSize: 12,
fontWeight: 600,
color: 'rgb(120, 120, 120)',
margin: 16,
fontFamily: 'Outfit, sans-serif',
},
axisTick: {
interval: 0, interval: 0,
rotate: 90, alignWithLabel: true,
} },
}, },
grid: { grid: {
left: 0,
right: 0,
bottom: 0,
containLabel: true containLabel: true
}, },
yAxis: { yAxis: {
@@ -164,6 +231,9 @@
name: 'time', name: 'time',
type: 'bar', type: 'bar',
data: [{!! collect($dataHistoryChart['grouped_data'])->pluck('seconds')->implode(', ') !!}], data: [{!! collect($dataHistoryChart['grouped_data'])->pluck('seconds')->implode(', ') !!}],
itemStyle: {
borderColor: '#5470c6',
},
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
@@ -192,13 +262,13 @@
} }
] ]
}; };
mainChart.on('finished', () => {
myChart.on('finished', () => { window.mainChartFinished = true;
if (window.mainChartFinished && window.pieChartFinished) {
window.status = 'ready'; window.status = 'ready';
}
}) })
mainChart.setOption(mainChartOptions);
// Display the chart using the configuration items and data just specified.
myChart.setOption(option);
</script> </script>
</body> </body>
</html> </html>

View File

@@ -1,17 +1,20 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<style> <style>
body { .page-number {
position: absolute;
bottom: 0;
left: 0;
font-size: 12px; font-size: 12px;
margin-top: 40px; margin-left: 46px;
margin-left: 47px; margin-bottom: 40px;
} }
</style> </style>
</head> </head>
<body> <body>
<p> <div class="page-number">
Page <span class="pageNumber"></span> of <span class="totalPages"></span> Page <span class="pageNumber"></span> of <span class="totalPages"></span>
</p> </div>
</body> </body>
</html> </html>