diff --git a/app/Http/Controllers/Api/V1/TimeEntryController.php b/app/Http/Controllers/Api/V1/TimeEntryController.php index 5e2e6d0b..6f8afb58 100644 --- a/app/Http/Controllers/Api/V1/TimeEntryController.php +++ b/app/Http/Controllers/Api/V1/TimeEntryController.php @@ -241,7 +241,9 @@ class TimeEntryController extends Controller ]); $request = Gotenberg::chromium(config('services.gotenberg.url')) ->pdf() - ->pdfa('PDF/A-3b') + ->assets( + Stream::path(resource_path('pdf/Outfit-VariableFont_wght.ttf'), 'outfit.ttf'), + ) ->margins(0.39, 0.78, 0.39, 0.39) ->paperSize('8.27', '11.7') // A4 ->footer(Stream::string('footer', $footerHtml)) diff --git a/app/Service/ColorService.php b/app/Service/ColorService.php index 2708a35a..3b0a79ee 100644 --- a/app/Service/ColorService.php +++ b/app/Service/ColorService.php @@ -33,7 +33,7 @@ class ColorService private const string VALID_REGEX = '/^#[0-9a-f]{6}$/'; - public function getRandomColor(string $seed = null): string + public function getRandomColor(?string $seed = null): string { if ($seed !== null) { srand(crc32($seed)); diff --git a/resources/js/Pages/Reporting.vue b/resources/js/Pages/Reporting.vue index f67b69b4..c9e5797d 100644 --- a/resources/js/Pages/Reporting.vue +++ b/resources/js/Pages/Reporting.vue @@ -198,7 +198,9 @@ async function downloadExport(format: ExportFormat) { 'Export successful', 'Export failed' ); - window.open(response.download_url, '_blank')?.focus(); + if (response?.download_url) { + window.open(response.download_url as string, '_blank')?.focus(); + } } } const { getNameForReportingRowEntry, emptyPlaceholder } = useReportingStore(); diff --git a/resources/js/Pages/ReportingDetailed.vue b/resources/js/Pages/ReportingDetailed.vue index f5e66946..8f49763c 100644 --- a/resources/js/Pages/ReportingDetailed.vue +++ b/resources/js/Pages/ReportingDetailed.vue @@ -233,7 +233,9 @@ async function downloadExport(format: ExportFormat) { 'Export successful', 'Export failed' ); - window.open(response.download_url, '_self')?.focus(); + if (response?.download_url) { + window.open(response.download_url as string, '_blank')?.focus(); + } } } @@ -252,6 +254,7 @@ async function downloadExport(format: ExportFormat) { +
diff --git a/resources/views/reports/time-entry-aggregate/pdf.blade.php b/resources/views/reports/time-entry-aggregate/pdf.blade.php index 39b01708..ba738bf9 100644 --- a/resources/views/reports/time-entry-aggregate/pdf.blade.php +++ b/resources/views/reports/time-entry-aggregate/pdf.blade.php @@ -72,6 +72,10 @@ color: #18181b } + table { + font-size: 14px; + } + thead { border-bottom: 1px #d4d4d8 solid; } @@ -90,8 +94,15 @@ background-color: #fafafa; } + .table-wrapper { + border: 1px solid #d4d4d8; + border-radius: 8px; + overflow: hidden; + width: calc(100% - 2px) + } + table tr { - border-bottom: 1px #e4e4e7 solid + border-bottom: 1px #e4e4e7 solid; } table tr:last-of-type { @@ -104,18 +115,6 @@ padding: 6px 12px; } - .range { - font-size: 24px; - font-weight: bold; - } - - .table-wrapper { - border: 1px solid #d4d4d8; - border-radius: 8px; - overflow: hidden; - width: calc(100% - 2px) - } - .data-table { break-after: auto; } @@ -147,7 +146,6 @@
-
@@ -187,11 +185,18 @@
- {{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }} + + @if($group->is(\App\Enums\TimeEntryAggregationType::Billable)) + {{ $group1Entry['key'] === '1' ? 'Billable' : 'Non-billable' }} + @else + {{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }} + @endif + + @@ -225,18 +230,18 @@ @foreach($aggregatedData['grouped_data'] as $group1Entry)

- @if($group1Entry['key']) + style="padding-top: 16px; padding-bottom: 8px; font-size: 16px; font-weight: 600; padding-left: 6px; color: #3f3f46;"> + @if($group->is(\App\Enums\TimeEntryAggregationType::Billable)) + {{ $group1Entry['key'] === '1' ? 'Billable' : 'Non-billable' }} + @else - {{ $group->description() }}: - + {{ $group->description() }}: + + {{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }} @endif - {{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }}

- - @@ -266,7 +271,11 @@ @endphp + + @foreach($timeEntries as $timeEntry) + + + + + + + + + + + + @endforeach + +
- {{ $group2Entry['description'] ?? $group2Entry['key'] ?? '-' }} + @if($subGroup->is(\App\Enums\TimeEntryAggregationType::Billable)) + {{ $group2Entry['key'] === '1' ? 'Billable' : 'Non-billable' }} + @else + {{ $group2Entry['description'] ?? $group2Entry['key'] ?? '-' }} + @endif {{ $interval->format($duration) }} diff --git a/resources/views/reports/time-entry-index/pdf.blade.php b/resources/views/reports/time-entry-index/pdf.blade.php index b069ff82..9463604b 100644 --- a/resources/views/reports/time-entry-index/pdf.blade.php +++ b/resources/views/reports/time-entry-index/pdf.blade.php @@ -9,9 +9,61 @@ Report -

Detailed Report

- -
- -
- {{ $start->format('d.m.Y') }} - {{ $end->format('d.m.Y') }}

+
+

Detailed Report

+
+ {{ $start->format('d.m.Y') }} - {{ $end->format('d.m.Y') }}

+
+
+
+
+
Duration
+
{{ $interval->format(CarbonInterval::seconds($aggregatedData['seconds'])) }}
+
+
+
Total cost
+
{{ Money::of(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->__toString(), $currency)->formatTo('en_US') }}
+
-
- Duration: {{ $interval->format(CarbonInterval::seconds($aggregatedData['seconds'])) }}
- Total cost: {{ Money::of(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->__toString(), $currency)->formatTo('en_US') }}
-
- -
- - - - - - - - - - - - - - - @foreach($timeEntries as $timeEntry) + +
+
DescriptionTaskProjectClientUserDurationBillableTags
+ - - - - - - - - + + + + + + + + + - @endforeach - -
{{ $timeEntry->description === '' ? '-' : $timeEntry->description }}{{ $timeEntry->task?->name ?? '-' }}{{ $timeEntry->project?->name ?? '-' }}{{ $timeEntry->client?->name ?? '-' }}{{ $timeEntry->user->name }} - {{ $timeEntry->start->format('Y-m-d H:i:s') }} - {{ $timeEntry->end->format('Y-m-d H:i:s') }} - {{ $timeEntry->billable ? 'Yes' : 'No' }}{{ count($timeEntry->tagsRelation) === 0 ? '-' : $timeEntry->tagsRelation->implode('name', ', ') }}DescriptionTaskProjectClientUserTimeDurationBillableTags
+
{{ $timeEntry->description === '' ? '-' : $timeEntry->description }}{{ $timeEntry->task?->name ?? '-' }}{{ $timeEntry->project?->name ?? '-' }}{{ $timeEntry->client?->name ?? '-' }}{{ $timeEntry->user->name }} + {{ $timeEntry->start->format('Y-m-d H:i:s') }} -
{{ $timeEntry->end->format('Y-m-d H:i:s') }} +
+ {{ $interval->format($timeEntry->getDuration()) }} + {{ $timeEntry->billable ? 'Yes' : 'No' }}{{ count($timeEntry->tagsRelation) === 0 ? '-' : $timeEntry->tagsRelation->implode('name', ', ') }}
+
+ +