mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-13 10:42:16 +01:00
Updated PDF reports
This commit is contained in:
committed by
Constantin Graf
parent
0860aa9d24
commit
a4d8a02b80
45
resources/pdf-js/echarts.min.js
vendored
Normal file
45
resources/pdf-js/echarts.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,127 +0,0 @@
|
||||
@use('Brick\Math\BigDecimal')
|
||||
@use('PhpOffice\PhpSpreadsheet\Cell\DataType')
|
||||
@use('Carbon\CarbonInterval')
|
||||
@inject('interval', 'App\Service\IntervalService')
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Report</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
}
|
||||
table {
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Report</h1>
|
||||
|
||||
<div>
|
||||
<span>{{ $start->format('Y-m-d') }} - {{ $end->format('Y-m-d') }}</span><br><br>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span>Duration: {{ $interval->format(CarbonInterval::seconds($aggregatedData['seconds'])) }}</span><br>
|
||||
<span>Total cost: {{ round(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->toFloat(), 2) }}</span><br>
|
||||
</div>
|
||||
|
||||
<div id="main-chart" style="width: 800px; height:400px;"></div>
|
||||
|
||||
@foreach($aggregatedData['grouped_data'] as $group1Entry)
|
||||
<h2>{{ $group->description() }}: {{ $group1Entry['description'] ?? $group1Entry['key'] ?? '-' }}</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{{ $subGroup->description() }}
|
||||
</th>
|
||||
<th>
|
||||
Duration
|
||||
</th>
|
||||
<th>
|
||||
Duration (decimal)
|
||||
</th>
|
||||
<th>
|
||||
Amount ({{ Str::upper($currency) }})
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@php
|
||||
$counter = 1;
|
||||
$totalDuration = 0;
|
||||
$totalCost = 0;
|
||||
@endphp
|
||||
@foreach($group1Entry['grouped_data'] as $group2Entry)
|
||||
@php
|
||||
$duration = CarbonInterval::seconds($group2Entry['seconds']);
|
||||
@endphp
|
||||
<tr>
|
||||
<td>
|
||||
{{ $group2Entry['description'] ?? $group2Entry['key'] ?? '-' }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $interval->format($duration) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ round($duration->totalHours, 2) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ round(BigDecimal::ofUnscaledValue($group2Entry['cost'], 2)->toFloat(), 2) }}
|
||||
</td>
|
||||
</tr>
|
||||
@php
|
||||
$totalDuration += $group2Entry['seconds'];
|
||||
$totalCost += $group2Entry['cost'];
|
||||
@endphp
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@endforeach
|
||||
|
||||
<script>
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
let element = document.getElementById('main-chart');
|
||||
let myChart = echarts.init(element, null, {
|
||||
renderer: 'svg'
|
||||
});
|
||||
|
||||
// Specify the configuration items and data for the chart
|
||||
let option = {
|
||||
tooltip: {},
|
||||
xAxis: {
|
||||
data: ['{!! collect($dataHistoryChart['grouped_data'])->pluck('key')->implode("', '") !!}'],
|
||||
rotate: 0
|
||||
},
|
||||
yAxis: {
|
||||
minInterval: 1,
|
||||
axisLabel: {
|
||||
formatter: function (value, index) {
|
||||
let totalSeconds = value;
|
||||
let hours = Math.floor(totalSeconds / 3600);
|
||||
totalSeconds %= 3600;
|
||||
let minutes = Math.floor(totalSeconds / 60);
|
||||
let seconds = totalSeconds % 60;
|
||||
return hours + ':' + minutes + ':' + seconds;
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'time',
|
||||
type: 'bar',
|
||||
data: [{!! collect($dataHistoryChart['grouped_data'])->pluck('seconds')->implode(', ') !!}],
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Display the chart using the configuration items and data just specified.
|
||||
myChart.setOption(option);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-size: 12px;
|
||||
margin-top: 40px;
|
||||
margin-left: 47px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Page <span class="pageNumber"></span> of <span class="totalPages"></span>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
197
resources/views/reports/time-entry-aggregate/pdf.blade.php
Normal file
197
resources/views/reports/time-entry-aggregate/pdf.blade.php
Normal file
@@ -0,0 +1,197 @@
|
||||
@use('Brick\Math\BigDecimal')
|
||||
@use('Brick\Money\Money')
|
||||
@use('PhpOffice\PhpSpreadsheet\Cell\DataType')
|
||||
@use('Carbon\CarbonInterval')
|
||||
@inject('interval', 'App\Service\IntervalService')
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Report</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
table thead {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.range {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
<script src="echarts.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Report</h1>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="range">
|
||||
<span>{{ $start->format('d.m.Y') }} - {{ $end->format('d.m.Y') }}</span><br><br>
|
||||
</div>
|
||||
|
||||
<div class="properties">
|
||||
<span>Duration: {{ $interval->format(CarbonInterval::seconds($aggregatedData['seconds'])) }}</span><br>
|
||||
<span>Total cost: {{ Money::of(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->__toString(), $currency)->formatTo('en_US') }}</span><br>
|
||||
</div>
|
||||
|
||||
<div id="main-chart" style="width: 100%; height:400px; margin-bottom: 50px;"></div>
|
||||
|
||||
@foreach($aggregatedData['grouped_data'] as $group1Entry)
|
||||
<h2>{{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }}</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{{ $subGroup->description() }}
|
||||
</th>
|
||||
<th>
|
||||
Duration
|
||||
</th>
|
||||
<th>
|
||||
Duration (decimal)
|
||||
</th>
|
||||
<th>
|
||||
Cost
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@php
|
||||
$counter = 1;
|
||||
$totalDuration = 0;
|
||||
$totalCost = 0;
|
||||
@endphp
|
||||
@foreach($group1Entry['grouped_data'] as $group2Entry)
|
||||
@php
|
||||
$duration = CarbonInterval::seconds($group2Entry['seconds']);
|
||||
@endphp
|
||||
<tr>
|
||||
<td style="text-align: left;">
|
||||
{{ $group2Entry['description'] ?? $group2Entry['key'] ?? '-' }}
|
||||
</td>
|
||||
<td style="text-align: right;">
|
||||
{{ $interval->format($duration) }}
|
||||
</td>
|
||||
<td style="text-align: right;">
|
||||
{{ round($duration->totalHours, 2) }}
|
||||
</td>
|
||||
<td style="text-align: right;">
|
||||
{{ Money::of(BigDecimal::ofUnscaledValue($group2Entry['cost'], 2)->__toString(), $currency)->formatTo('en_US') }}
|
||||
</td>
|
||||
</tr>
|
||||
@php
|
||||
$totalDuration += $group2Entry['seconds'];
|
||||
$totalCost += $group2Entry['cost'];
|
||||
@endphp
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@endforeach
|
||||
|
||||
<script>
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
let element = document.getElementById('main-chart');
|
||||
let myChart = echarts.init(element, null, {
|
||||
renderer: 'svg'
|
||||
});
|
||||
|
||||
// Specify the configuration items and data for the chart
|
||||
let option = {
|
||||
tooltip: {},
|
||||
xAxis: {
|
||||
data: ['{!! collect($dataHistoryChart['grouped_data'])->pluck('key')->implode("', '") !!}'],
|
||||
axisLabel: {
|
||||
show: true,
|
||||
interval: 0,
|
||||
rotate: 90,
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
containLabel: true
|
||||
},
|
||||
yAxis: {
|
||||
minInterval: 1,
|
||||
axisLabel: {
|
||||
show: false,
|
||||
inside: true,
|
||||
formatter: function (value, index) {
|
||||
let totalSeconds = value;
|
||||
let hours = Math.floor(totalSeconds / 3600);
|
||||
if (hours < 10) {
|
||||
hours = '0' + hours;
|
||||
}
|
||||
totalSeconds %= 3600;
|
||||
let minutes = Math.floor(totalSeconds / 60);
|
||||
if (minutes < 10) {
|
||||
minutes = '0' + minutes;
|
||||
}
|
||||
let seconds = totalSeconds % 60;
|
||||
if (seconds < 10) {
|
||||
seconds = '0' + seconds;
|
||||
}
|
||||
return hours + ':' + minutes + ':' + seconds;
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'time',
|
||||
type: 'bar',
|
||||
data: [{!! collect($dataHistoryChart['grouped_data'])->pluck('seconds')->implode(', ') !!}],
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter: function (params) {
|
||||
let value = params.value;
|
||||
if (value === 0) {
|
||||
return '';
|
||||
}
|
||||
let totalSeconds = value;
|
||||
let hours = Math.floor(totalSeconds / 3600);
|
||||
if (hours < 10) {
|
||||
hours = '0' + hours;
|
||||
}
|
||||
totalSeconds %= 3600;
|
||||
let minutes = Math.floor(totalSeconds / 60);
|
||||
if (minutes < 10) {
|
||||
minutes = '0' + minutes;
|
||||
}
|
||||
let seconds = totalSeconds % 60;
|
||||
if (seconds < 10) {
|
||||
seconds = '0' + seconds;
|
||||
}
|
||||
return hours + ':' + minutes + ':' + seconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Display the chart using the configuration items and data just specified.
|
||||
myChart.setOption(option);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-size: 12px;
|
||||
margin: auto 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<span class="pageNumber"></span> of <span class="totalPages"></span>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,60 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Report</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
}
|
||||
table {
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Detailed Report</h1>
|
||||
|
||||
<div>
|
||||
<span>01.01.2020 - 01.01.2024</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span>Duration: 20:10:10</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>Task</th>
|
||||
<th>Project</th>
|
||||
<th>Client</th>
|
||||
<th>User</th>
|
||||
<th>Duration</th>
|
||||
<th>Billable</th>
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($timeEntries as $timeEntry)
|
||||
<tr>
|
||||
<td>{{ $timeEntry->description }}</td>
|
||||
<td>{{ $timeEntry->task?->name ?? '-' }}</td>
|
||||
<td>{{ $timeEntry->project?->name ?? '-' }}</td>
|
||||
<td>{{ $timeEntry->client?->name ?? '-' }}</td>
|
||||
<td>{{ $timeEntry->user->name }}</td>
|
||||
<td>
|
||||
00:00:01
|
||||
{{ $timeEntry->start->format('Y-m-d H:i:s') }} - {{ $timeEntry->end->format('Y-m-d H:i:s') }}
|
||||
</td>
|
||||
<td>{{ $timeEntry->billable ? 'Yes' : 'no' }}</td>
|
||||
<td>{{ $timeEntry->tagsRelation->implode('name', ', ') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-size: 12px;
|
||||
margin-top: 40px;
|
||||
margin-left: 47px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Page <span class="pageNumber"></span> of <span class="totalPages"></span>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
92
resources/views/reports/time-entry-index/pdf.blade.php
Normal file
92
resources/views/reports/time-entry-index/pdf.blade.php
Normal file
@@ -0,0 +1,92 @@
|
||||
@use('Brick\Math\BigDecimal')
|
||||
@use('Brick\Money\Money')
|
||||
@use('PhpOffice\PhpSpreadsheet\Cell\DataType')
|
||||
@use('Carbon\CarbonInterval')
|
||||
@inject('interval', 'App\Service\IntervalService')
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Report</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
table thead {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.range {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.data {
|
||||
margin-top: 40px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Detailed Report</h1>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="range">
|
||||
<span>{{ $start->format('d.m.Y') }} - {{ $end->format('d.m.Y') }}</span><br><br>
|
||||
</div>
|
||||
|
||||
<div class="properties">
|
||||
<span>Duration: {{ $interval->format(CarbonInterval::seconds($aggregatedData['seconds'])) }}</span><br>
|
||||
<span>Total cost: {{ Money::of(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->__toString(), $currency)->formatTo('en_US') }}</span><br>
|
||||
</div>
|
||||
|
||||
<div class="data">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>Task</th>
|
||||
<th>Project</th>
|
||||
<th>Client</th>
|
||||
<th>User</th>
|
||||
<th>Duration</th>
|
||||
<th>Billable</th>
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($timeEntries as $timeEntry)
|
||||
<tr>
|
||||
<td>{{ $timeEntry->description === '' ? '-' : $timeEntry->description }}</td>
|
||||
<td>{{ $timeEntry->task?->name ?? '-' }}</td>
|
||||
<td>{{ $timeEntry->project?->name ?? '-' }}</td>
|
||||
<td>{{ $timeEntry->client?->name ?? '-' }}</td>
|
||||
<td>{{ $timeEntry->user->name }}</td>
|
||||
<td>
|
||||
{{ $timeEntry->start->format('Y-m-d H:i:s') }} - {{ $timeEntry->end->format('Y-m-d H:i:s') }}
|
||||
</td>
|
||||
<td>{{ $timeEntry->billable ? 'Yes' : 'No' }}</td>
|
||||
<td>{{ count($timeEntry->tagsRelation) === 0 ? '-' : $timeEntry->tagsRelation->implode('name', ', ') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user