Skip to content

Commit e308ca7

Browse files
Onatcerkorridor
authored andcommitted
improve design for time entries index export
1 parent 4281736 commit e308ca7

File tree

6 files changed

+196
-81
lines changed

6 files changed

+196
-81
lines changed

app/Http/Controllers/Api/V1/TimeEntryController.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,9 @@ public function indexExport(Organization $organization, TimeEntryIndexExportRequ
241241
]);
242242
$request = Gotenberg::chromium(config('services.gotenberg.url'))
243243
->pdf()
244-
->pdfa('PDF/A-3b')
244+
->assets(
245+
Stream::path(resource_path('pdf/Outfit-VariableFont_wght.ttf'), 'outfit.ttf'),
246+
)
245247
->margins(0.39, 0.78, 0.39, 0.39)
246248
->paperSize('8.27', '11.7') // A4
247249
->footer(Stream::string('footer', $footerHtml))

app/Service/ColorService.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ColorService
3333

3434
private const string VALID_REGEX = '/^#[0-9a-f]{6}$/';
3535

36-
public function getRandomColor(string $seed = null): string
36+
public function getRandomColor(?string $seed = null): string
3737
{
3838
if ($seed !== null) {
3939
srand(crc32($seed));

resources/js/Pages/Reporting.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,9 @@ async function downloadExport(format: ExportFormat) {
198198
'Export successful',
199199
'Export failed'
200200
);
201-
window.open(response.download_url, '_blank')?.focus();
201+
if (response?.download_url) {
202+
window.open(response.download_url as string, '_blank')?.focus();
203+
}
202204
}
203205
}
204206
const { getNameForReportingRowEntry, emptyPlaceholder } = useReportingStore();

resources/js/Pages/ReportingDetailed.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,9 @@ async function downloadExport(format: ExportFormat) {
233233
'Export successful',
234234
'Export failed'
235235
);
236-
window.open(response.download_url, '_self')?.focus();
236+
if (response?.download_url) {
237+
window.open(response.download_url as string, '_blank')?.focus();
238+
}
237239
}
238240
}
239241
</script>
@@ -252,6 +254,7 @@ async function downloadExport(format: ExportFormat) {
252254
<ReportingExportButton
253255
:download="downloadExport"></ReportingExportButton>
254256
</MainContainer>
257+
255258
<div class="py-2.5 w-full border-b border-default-background-separator">
256259
<MainContainer
257260
class="sm:flex space-y-4 sm:space-y-0 justify-between">

resources/views/reports/time-entry-aggregate/pdf.blade.php

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@
7272
color: #18181b
7373
}
7474
75+
table {
76+
font-size: 14px;
77+
}
78+
7579
thead {
7680
border-bottom: 1px #d4d4d8 solid;
7781
}
@@ -90,8 +94,15 @@
9094
background-color: #fafafa;
9195
}
9296
97+
.table-wrapper {
98+
border: 1px solid #d4d4d8;
99+
border-radius: 8px;
100+
overflow: hidden;
101+
width: calc(100% - 2px)
102+
}
103+
93104
table tr {
94-
border-bottom: 1px #e4e4e7 solid
105+
border-bottom: 1px #e4e4e7 solid;
95106
}
96107
97108
table tr:last-of-type {
@@ -104,18 +115,6 @@
104115
padding: 6px 12px;
105116
}
106117
107-
.range {
108-
font-size: 24px;
109-
font-weight: bold;
110-
}
111-
112-
.table-wrapper {
113-
border: 1px solid #d4d4d8;
114-
border-radius: 8px;
115-
overflow: hidden;
116-
width: calc(100% - 2px)
117-
}
118-
119118
.data-table {
120119
break-after: auto;
121120
}
@@ -147,7 +146,6 @@
147146

148147

149148
<div class="table-wrapper">
150-
151149
<div
152150
style="background-color: #fafafa; padding: 5px 14px; border-bottom: 1px #d4d4d8 solid; display: flex; gap: 20px;">
153151
<div style="padding: 8px 12px; border-radius: 8px;">
@@ -187,11 +185,18 @@
187185
<tr>
188186
<td style="display: flex; align-items: center;">
189187
<div style="width: 12px; height: 12px; border-radius: 50%; background-color: {{
190-
$group1Entry['color'] ?? $group1Entry['key'] ? $colorService->getRandomColor($group1Entry['key']) : '#CCCCCC'
188+
$group1Entry['color'] ?? ($group1Entry['key'] ? $colorService->getRandomColor($group1Entry['key']) : '#CCCCCC')
191189
}};">
192190
</div>
193191
<span style="padding-left: 8px;">
194-
{{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }}
192+
193+
@if($group->is(\App\Enums\TimeEntryAggregationType::Billable))
194+
{{ $group1Entry['key'] === '1' ? 'Billable' : 'Non-billable' }}
195+
@else
196+
{{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }}
197+
@endif
198+
199+
195200
</span>
196201
</td>
197202
<td style="text-align: left;">
@@ -225,18 +230,18 @@
225230
@foreach($aggregatedData['grouped_data'] as $group1Entry)
226231
<div class="data-table">
227232
<h2 class="no-break"
228-
style="padding-top: 16px; padding-bottom: 8px; font-size: 20px; font-weight: 600; padding-left: 6px; color: #3f3f46;">
229-
@if($group1Entry['key'])
233+
style="padding-top: 16px; padding-bottom: 8px; font-size: 16px; font-weight: 600; padding-left: 6px; color: #3f3f46;">
234+
@if($group->is(\App\Enums\TimeEntryAggregationType::Billable))
235+
{{ $group1Entry['key'] === '1' ? 'Billable' : 'Non-billable' }}
236+
@else
230237
<span style="color: #a1a1aa;">
231-
{{ $group->description() }}:
232-
</span>
238+
{{ $group->description() }}:
239+
</span>
240+
{{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }}
233241
@endif
234-
{{ $group1Entry['description'] ?? $group1Entry['key'] ?? 'No '.Str::lower($group->description()) }}
235242
</h2>
236243

237244
<div class="table-wrapper">
238-
239-
240245
<table style="width: 100%;">
241246
<thead>
242247
<tr>
@@ -266,7 +271,11 @@
266271
@endphp
267272
<tr>
268273
<td>
269-
{{ $group2Entry['description'] ?? $group2Entry['key'] ?? '-' }}
274+
@if($subGroup->is(\App\Enums\TimeEntryAggregationType::Billable))
275+
{{ $group2Entry['key'] === '1' ? 'Billable' : 'Non-billable' }}
276+
@else
277+
{{ $group2Entry['description'] ?? $group2Entry['key'] ?? '-' }}
278+
@endif
270279
</td>
271280
<td>
272281
{{ $interval->format($duration) }}

resources/views/reports/time-entry-index/pdf.blade.php

Lines changed: 152 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,61 @@
99
<meta charset="utf-8" />
1010
<title>Report</title>
1111
<style>
12+
13+
html, body, div, span, applet, object, iframe,
14+
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
15+
a, abbr, acronym, address, big, cite, code,
16+
del, dfn, em, img, ins, kbd, q, s, samp,
17+
small, strike, strong, sub, sup, tt, var,
18+
b, u, i, center,
19+
dl, dt, dd, ol, ul, li,
20+
fieldset, form, label, legend,
21+
table, caption, tbody, tfoot, thead, tr, th, td,
22+
article, aside, canvas, details, embed,
23+
figure, figcaption, footer, header, hgroup,
24+
menu, nav, output, ruby, section, summary,
25+
time, mark, audio, video {
26+
margin: 0;
27+
padding: 0;
28+
border: 0;
29+
font-size: 100%;
30+
vertical-align: baseline;
31+
box-sizing: border-box;
32+
}
33+
34+
35+
/* HTML5 display-role reset for older browsers */
36+
article, aside, details, figcaption, figure,
37+
footer, header, hgroup, menu, nav, section {
38+
display: block;
39+
}
40+
1241
body {
13-
font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
14-
color: #555;
42+
line-height: 1;
43+
}
44+
45+
ol, ul {
46+
list-style: none;
47+
}
48+
49+
blockquote, q {
50+
quotes: none;
51+
}
52+
53+
blockquote:before, blockquote:after,
54+
q:before, q:after {
55+
content: '';
56+
content: none;
57+
}
58+
59+
@font-face {
60+
font-family: 'Outfit';
61+
src: url('outfit.ttf');
62+
}
63+
64+
body {
65+
font-family: 'Outfit', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
66+
color: #18181b
1567
}
1668
1769
table {
@@ -22,71 +74,118 @@
2274
background-color: #eee;
2375
}
2476
25-
h1 {
26-
font-size: 35px;
27-
font-weight: bold;
77+
78+
.table-wrapper table th {
79+
background-color: #fafafa;
80+
}
81+
82+
.table-wrapper {
83+
border: 1px solid #d4d4d8;
84+
border-radius: 8px;
85+
overflow: hidden;
86+
width: calc(100% - 2px)
2887
}
2988
30-
h2 {
31-
font-size: 20px;
32-
font-weight: bold;
89+
table {
90+
border-collapse: collapse;
91+
border-spacing: 0;
92+
text-align: left;
3393
}
3494
35-
.range {
36-
font-size: 24px;
37-
font-weight: bold;
95+
thead {
96+
border-bottom: 1px #d4d4d8 solid;
3897
}
3998
40-
.data {
41-
margin-top: 40px;
99+
tfoot {
100+
border-top: 1px #d4d4d8 solid;
42101
}
43-
</style>
44-
</head>
45-
<body>
46-
<h1>Detailed Report</h1>
47102
48-
<hr>
103+
table th, table tfoot td {
104+
font-weight: 500;
105+
padding: 6px 12px;
106+
color: #18181b;
107+
}
49108
50-
<div class="range">
51-
<span>{{ $start->format('d.m.Y') }} - {{ $end->format('d.m.Y') }}</span><br><br>
52-
</div>
109+
table td, table th{
110+
font-size: 12px;
111+
}
112+
113+
table tr {
114+
border-bottom: 1px #e4e4e7 solid;
115+
}
53116
54-
<div class="properties">
55-
<span>Duration: {{ $interval->format(CarbonInterval::seconds($aggregatedData['seconds'])) }}</span><br>
56-
<span>Total cost: {{ Money::of(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->__toString(), $currency)->formatTo('en_US') }}</span><br>
117+
table tr:last-of-type {
118+
border-bottom: none;
119+
}
120+
121+
table tr td {
122+
font-weight: 400;
123+
color: #3f3f46;
124+
padding: 6px 12px;
125+
}
126+
127+
</style>
128+
</head>
129+
<body>
130+
<div>
131+
<p style="font-size: 32px; font-weight: 600; margin-bottom: 5px;">Detailed Report</p>
132+
<div style="font-size: 16px; font-weight: 600; color: #71717a;">
133+
<span>{{ $start->format('d.m.Y') }} - {{ $end->format('d.m.Y') }}</span><br><br>
134+
</div>
57135
</div>
136+
<div class="table-wrapper">
137+
<div
138+
style="background-color: #fafafa; border-bottom: 1px #d4d4d8 solid; padding: 5px 14px; display: flex; gap: 20px;">
139+
<div style="padding: 8px 12px; border-radius: 8px;">
140+
<div style="color: #71717a; font-weight: 600;">Duration</div>
141+
<div
142+
style="font-size: 24px; font-weight: 500; margin-top: 2px;">{{ $interval->format(CarbonInterval::seconds($aggregatedData['seconds'])) }} </div>
143+
</div>
144+
<div style="padding: 8px 12px; border-radius: 8px;">
145+
<div style="color: #71717a; font-weight: 600;">Total cost</div>
146+
<div
147+
style="font-size: 24px; font-weight: 500; margin-top: 2px;">{{ Money::of(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->__toString(), $currency)->formatTo('en_US') }} </div>
148+
</div>
58149

59-
<div class="data">
60-
<table>
61-
<thead>
62-
<tr>
63-
<th>Description</th>
64-
<th>Task</th>
65-
<th>Project</th>
66-
<th>Client</th>
67-
<th>User</th>
68-
<th>Duration</th>
69-
<th>Billable</th>
70-
<th>Tags</th>
71-
</tr>
72-
</thead>
73-
<tbody>
74-
@foreach($timeEntries as $timeEntry)
150+
</div>
151+
<div>
152+
<table style="width: 100%;">
153+
<thead>
75154
<tr>
76-
<td>{{ $timeEntry->description === '' ? '-' : $timeEntry->description }}</td>
77-
<td>{{ $timeEntry->task?->name ?? '-' }}</td>
78-
<td>{{ $timeEntry->project?->name ?? '-' }}</td>
79-
<td>{{ $timeEntry->client?->name ?? '-' }}</td>
80-
<td>{{ $timeEntry->user->name }}</td>
81-
<td>
82-
{{ $timeEntry->start->format('Y-m-d H:i:s') }} - {{ $timeEntry->end->format('Y-m-d H:i:s') }}
83-
</td>
84-
<td>{{ $timeEntry->billable ? 'Yes' : 'No' }}</td>
85-
<td>{{ count($timeEntry->tagsRelation) === 0 ? '-' : $timeEntry->tagsRelation->implode('name', ', ') }}</td>
155+
<th>Description</th>
156+
<th>Task</th>
157+
<th>Project</th>
158+
<th>Client</th>
159+
<th>User</th>
160+
<th>Time</th>
161+
<th>Duration</th>
162+
<th>Billable</th>
163+
<th>Tags</th>
86164
</tr>
87-
@endforeach
88-
</tbody>
89-
</table>
165+
</thead>
166+
<tbody>
167+
@foreach($timeEntries as $timeEntry)
168+
<tr>
169+
<td>{{ $timeEntry->description === '' ? '-' : $timeEntry->description }}</td>
170+
<td>{{ $timeEntry->task?->name ?? '-' }}</td>
171+
<td>{{ $timeEntry->project?->name ?? '-' }}</td>
172+
<td>{{ $timeEntry->client?->name ?? '-' }}</td>
173+
<td>{{ $timeEntry->user->name }}</td>
174+
<td>
175+
{{ $timeEntry->start->format('Y-m-d H:i:s') }} - <br> {{ $timeEntry->end->format('Y-m-d H:i:s') }}
176+
</td>
177+
<td>
178+
{{ $interval->format($timeEntry->getDuration()) }}
179+
</td>
180+
<td>{{ $timeEntry->billable ? 'Yes' : 'No' }}</td>
181+
<td>{{ count($timeEntry->tagsRelation) === 0 ? '-' : $timeEntry->tagsRelation->implode('name', ', ') }}</td>
182+
</tr>
183+
@endforeach
184+
</tbody>
185+
</table>
186+
</div>
90187
</div>
188+
189+
91190
</body>
92191
</html>

0 commit comments

Comments
 (0)