Skip to content

Commit 6b518fd

Browse files
authored
Add events list to log detail (#43)
1 parent df66d35 commit 6b518fd

File tree

9 files changed

+218
-113
lines changed

9 files changed

+218
-113
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
}
5656
},
5757
"scripts": {
58-
"analyse": "vendor/bin/phpstan analyse",
58+
"analyse": "vendor/bin/phpstan analyse --memory-limit=-1",
5959
"test": "vendor/bin/pest",
6060
"test-coverage": "vendor/bin/pest --coverage",
6161
"format": "vendor/bin/pint -v"

resources/dist/css/theme.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/views/modals/details.blade.php

Lines changed: 184 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,200 @@
11
<div
22
class="text-sm"
3-
x-init="
4-
if (localStorage.getItem('theme') === 'light') {
5-
$refs.variables.classList.add('theme-light');
6-
} else {
7-
$refs.variables.classList.add('theme-dark');
8-
}
3+
x-data="{
4+
activeTab: null,
5+
highlighted: false,
96
10-
hljs.highlightAll()">
11-
<div>
12-
<p class="font-bold mb-1">Sender</p>
13-
@if ($log->sender->name)
14-
<p>{{ $log->sender->name }} &lt;{{ $log->sender->email }}&gt;</p>
15-
@else
16-
<p>{{ $log->sender->email }}</p>
17-
@endif
18-
</div>
7+
init() {
8+
this.setTab('data');
199
20-
<div class="mt-4">
21-
<p class="font-bold mb-1">Recipient</p>
22-
<p>{{ $log->recipient }}</p>
23-
</div>
10+
if (localStorage.getItem('theme') === 'light') {
11+
document.getElementById('log-variables').classList.add('theme-light');
12+
} else {
13+
document.getElementById('log-variables').classList.add('theme-dark');
14+
}
15+
},
16+
setTab(tabName) {
17+
this.activeTab = tabName;
2418
25-
<div class="mt-4">
26-
<p class="font-bold mb-1">Subject</p>
27-
<p>{{ $log->subject }}</p>
28-
</div>
19+
if (tabName === 'data' && !this.highlighted) {
20+
this.highlighted = true;
21+
hljs.highlightAll();
22+
}
23+
},
24+
}"
25+
x-init="init()">
26+
<x-filament::tabs class="mb-6" style="width: fit-content">
27+
<x-filament::tabs.item
28+
icon="heroicon-o-at-symbol"
29+
alpine-active="activeTab === 'data'"
30+
x-on:click="setTab('data')">
31+
Data
32+
</x-filament::tabs.item>
2933

30-
<div class="mt-4">
31-
<p class="font-bold mb-1">Cc</p>
32-
@forelse (($log->cc ?: []) as $cc)
33-
@if ($cc->name)
34-
<p>{{ $cc->name }} &lt;{{ $cc->email }}&gt;</p>
35-
@else
36-
<p>{{ $cc->email }}</p>
37-
@endif
38-
@empty
39-
<p class="italic opacity-80 text-xs">No data available</p>
40-
@endforelse
41-
</div>
34+
<x-filament::tabs.item
35+
icon="heroicon-o-paper-clip"
36+
alpine-active="activeTab === 'attachments'"
37+
x-on:click="setTab('attachments')">
38+
Attachments ({{ count($log->attachments) }})
39+
</x-filament::tabs.item>
4240

43-
<div class="mt-4">
44-
<p class="font-bold mb-1">Bcc</p>
45-
@forelse (($log->bcc ?: []) as $bcc)
46-
@if ($bcc->name)
47-
<p>{{ $bcc->name }} &lt;{{ $bcc->email }}&gt;</p>
41+
<x-filament::tabs.item
42+
icon="heroicon-o-rss"
43+
alpine-active="activeTab === 'events'"
44+
x-on:click="setTab('events')">
45+
Events ({{ count($log->events) }})
46+
</x-filament::tabs.item>
47+
</x-filament::tabs>
48+
49+
<!-- Data -->
50+
<x-filament::section x-show="activeTab === 'data'">
51+
<div>
52+
<p class="font-bold mb-1">Sender</p>
53+
@if ($log->sender->name)
54+
<p>{{ $log->sender->name }} &lt;{{ $log->sender->email }}&gt;</p>
4855
@else
49-
<p>{{ $bcc->email }}</p>
56+
<p>{{ $log->sender->email }}</p>
5057
@endif
51-
@empty
52-
<p class="italic opacity-80 text-xs">No data available</p>
53-
@endforelse
54-
</div>
58+
</div>
5559

56-
<div class="mt-4">
57-
<p class="font-bold mb-1">Template</p>
58-
{!! $template !!}
59-
</div>
60+
<div class="mt-4">
61+
<p class="font-bold mb-1">Recipient</p>
62+
<p>{{ $log->recipient }}</p>
63+
</div>
6064

61-
<div class="mt-4">
62-
<p class="font-bold mb-1">Attachments</p>
63-
<table class="w-full table-auto border-collapse text-left rtl:text-right divide-y dark:divide-gray-700 text-sm rounded-lg overflow-hidden">
64-
<thead>
65-
<tr class="bg-gray-50 dark:bg-gray-500/10">
66-
<th>
67-
<span class="p-0 flex items-center w-full px-4 py-2 whitespace-nowrap space-x-1 rtl:space-x-reverse font-medium text-gray-600 dark:text-gray-300 cursor-default">
68-
Name
69-
</span>
70-
</th>
71-
<th>
72-
<span class="p-0 flex items-center w-full px-4 py-2 whitespace-nowrap space-x-1 rtl:space-x-reverse font-medium text-gray-600 dark:text-gray-300 cursor-default">
73-
Size
74-
</span>
75-
</th>
76-
<th>
77-
<span class="p-0 flex items-center w-full px-4 py-2 whitespace-nowrap space-x-1 rtl:space-x-reverse font-medium text-gray-600 dark:text-gray-300 cursor-default">
78-
Disk
79-
</span>
80-
</th>
81-
<th class="w-5"></th>
82-
</tr>
83-
</thead>
84-
<tbody class="divide-y whitespace-nowrap dark:divide-gray-700">
85-
@forelse ($log->attachments as $attachment)
86-
<tr class="dark:bg-gray-700/50 odd:bg-white even:bg-gray-50 even:dark:bg-gray-700/80">
87-
<td class="px-4 py-2 dark:text-white">
88-
{{ $attachment->name }}
89-
</td>
90-
<td class="px-4 py-2 dark:text-white">
91-
{{ $attachment->readableSize() }}
92-
</td>
93-
<td class="px-4 py-2 dark:text-white">
94-
{{ $attachment->disk ?: '-' }}
95-
</td>
96-
<td class="px-4 py-2">
97-
@if ($attachment->canBeDownloaded())
98-
<a
99-
href="{{ URL::route('download.attachment', $attachment) }}"
100-
class="font-medium text-primary-500 hover:text-primary-400"
101-
target="_blank"
102-
download>
103-
Download
104-
</a>
105-
@endif
106-
</td>
107-
</tr>
108-
@empty
109-
<tr class="dark:bg-gray-700/50 odd:bg-white even:bg-gray-50 even:dark:bg-gray-700/80">
110-
<td class="px-4 py-2 italic text-xs opacity-80" colspan="4">No attachment available</td>
65+
<div class="mt-4">
66+
<p class="font-bold mb-1">Subject</p>
67+
<p>{{ $log->subject }}</p>
68+
</div>
69+
70+
<div class="mt-4">
71+
<p class="font-bold mb-1">Cc</p>
72+
@forelse (($log->cc ?: []) as $cc)
73+
@if ($cc->name)
74+
<p>{{ $cc->name }} &lt;{{ $cc->email }}&gt;</p>
75+
@else
76+
<p>{{ $cc->email }}</p>
77+
@endif
78+
@empty
79+
<p class="italic opacity-80 text-xs">No data available</p>
80+
@endforelse
81+
</div>
82+
83+
<div class="mt-4">
84+
<p class="font-bold mb-1">Bcc</p>
85+
@forelse (($log->bcc ?: []) as $bcc)
86+
@if ($bcc->name)
87+
<p>{{ $bcc->name }} &lt;{{ $bcc->email }}&gt;</p>
88+
@else
89+
<p>{{ $bcc->email }}</p>
90+
@endif
91+
@empty
92+
<p class="italic opacity-80 text-xs">No data available</p>
93+
@endforelse
94+
</div>
95+
96+
<div class="mt-4">
97+
<p class="font-bold mb-1">Template</p>
98+
{!! $template !!}
99+
</div>
100+
101+
<div class="mt-4">
102+
<p class="font-bold mb-1">Variables</p>
103+
<pre wire:ignore id="log-variables" class="hljs w-full rounded p-3 shadow-sm focus:border-primary-600 focus:ring-1 focus:ring-inset focus:ring-primary-600 disabled:opacity-70 border border-gray-300/50 dark:border-gray-600/50"><code class="language-json">{!! $variables !!}</code></pre>
104+
</div>
105+
</x-filament::section>
106+
107+
<!-- Attachments -->
108+
<x-filament::section x-show="activeTab === 'attachments'">
109+
@if (count($log->attachments) === 0)
110+
<p class="italic text-xs opacity-80">No attachment available</p>
111+
@else
112+
<table class="w-full table-auto border-collapse text-left rtl:text-right divide-y dark:divide-gray-700 text-sm rounded-lg overflow-hidden">
113+
<thead>
114+
<tr>
115+
<th>
116+
<span class="p-0 flex items-center w-full px-4 py-2 whitespace-nowrap space-x-1 rtl:space-x-reverse font-bold cursor-default">
117+
Name
118+
</span>
119+
</th>
120+
<th>
121+
<span class="p-0 flex items-center w-full px-4 py-2 whitespace-nowrap space-x-1 rtl:space-x-reverse font-bold cursor-default">
122+
Size
123+
</span>
124+
</th>
125+
<th>
126+
<span class="p-0 flex items-center w-full px-4 py-2 whitespace-nowrap space-x-1 rtl:space-x-reverse font-bold cursor-default">
127+
Disk
128+
</span>
129+
</th>
130+
<th class="w-5"></th>
111131
</tr>
112-
@endforelse
113-
</tbody>
114-
</table>
115-
</div>
132+
</thead>
133+
<tbody class="divide-y whitespace-nowrap dark:divide-gray-700">
134+
@foreach ($log->attachments as $attachment)
135+
<tr>
136+
<td class="px-4 py-2 dark:text-white">
137+
{{ $attachment->name }}
138+
</td>
139+
<td class="px-4 py-2 dark:text-white">
140+
{{ $attachment->readableSize() }}
141+
</td>
142+
<td class="px-4 py-2 dark:text-white">
143+
{{ $attachment->disk ?: '-' }}
144+
</td>
145+
<td class="px-4 py-2">
146+
@if ($attachment->canBeDownloaded())
147+
<a
148+
href="{{ URL::route('download.attachment', $attachment) }}"
149+
class="font-medium text-primary-500 hover:text-primary-400"
150+
target="_blank"
151+
download>
152+
Download
153+
</a>
154+
@endif
155+
</td>
156+
</tr>
157+
@endforeach
158+
</tbody>
159+
</table>
160+
@endif
161+
</x-filament::section>
116162

117-
<div class="mt-4">
118-
<p class="font-bold mb-1">Variables</p>
119-
<pre wire:ignore x-ref="variables" class="hljs w-full rounded p-3 shadow-sm focus:border-primary-600 focus:ring-1 focus:ring-inset focus:ring-primary-600 disabled:opacity-70 border border-gray-300/50 dark:border-gray-600/50"><code class="language-json">{!! $variables !!}</code></pre>
120-
</div>
163+
<!-- Events -->
164+
<x-filament::section x-show="activeTab === 'events'">
165+
@if (count($log->events) === 0)
166+
<p class="italic text-xs opacity-80">No events available</p>
167+
@else
168+
<table class="w-full table-auto border-collapse text-left rtl:text-right divide-y dark:divide-gray-700 text-sm rounded-lg overflow-hidden">
169+
<thead>
170+
<tr>
171+
<th>
172+
<span class="p-0 flex items-center w-full px-4 py-2 whitespace-nowrap space-x-1 rtl:space-x-reverse font-bold cursor-default">
173+
Name
174+
</span>
175+
</th>
176+
<th>
177+
<span class="p-0 flex items-center w-full px-4 py-2 whitespace-nowrap space-x-1 rtl:space-x-reverse font-bold cursor-default">
178+
Date
179+
</span>
180+
</th>
181+
</tr>
182+
</thead>
183+
<tbody class="divide-y whitespace-nowrap dark:divide-gray-700">
184+
@foreach ($log->events as $event)
185+
<tr>
186+
<td class="px-4 py-2 dark:text-white" style="width: 60%">
187+
{{ $event->name }}
188+
</td>
189+
<td class="px-4 py-2 dark:text-white">
190+
{{ $event->created_at->toDateTimeString() }}
191+
</td>
192+
</tr>
193+
@endforeach
194+
</tbody>
195+
</table>
196+
@endif
197+
</x-filament::section>
121198
</div>
122199

123200
@assets

src/Jobs/SendMailJob.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ protected function send(): void
8282
$sent = Mail::send(new GenericMail($override ?: $this->genericMailDto));
8383

8484
if ($sent instanceof SentMessage) {
85-
$this->log->message_id = $sent->getMessageId();
85+
// MailGun for example sends <[email protected]> as value, but then in the Webhooks there are no < >
86+
$this->log->message_id = str_replace(['<', '>'], '', $sent->getMessageId());
8687
$this->log->save();
8788
}
8889
};

src/Models/Log.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public function attachments(): HasMany
127127
*/
128128
public function events(): HasMany
129129
{
130-
return $this->hasMany(LogEvent::class);
130+
return $this->hasMany(LogEvent::class)->latest();
131131
}
132132

133133
/**

src/Models/LogEvent.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class LogEvent extends Model
3838
protected $fillable = [
3939
'log_id',
4040
'name',
41+
'created_at',
4142
];
4243

4344
/**

src/Resources/LogResource.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Filament\Support\Enums\Alignment;
1111
use Filament\Tables;
1212
use Filament\Tables\Actions\Action as TablesAction;
13+
use Illuminate\Support\Facades\Blade;
1314
use Illuminate\Support\Facades\Config;
1415
use Illuminate\Support\Facades\View;
1516
use Illuminate\Support\HtmlString;
@@ -69,8 +70,33 @@ public static function table(Tables\Table $table): Tables\Table
6970
->icon(fn (Log $record) => $record->attachments->isNotEmpty() ? 'heroicon-o-paper-clip' : '')
7071
->iconColor('primary'),
7172

73+
Tables\Columns\TextColumn::make('events_count')
74+
->counts('events')
75+
->label('Events')
76+
->formatStateUsing(function (Log $record): HtmlString {
77+
if ($record->events->isEmpty()) {
78+
return new HtmlString('<span class="text-xs italic opacity-70">No events</span>');
79+
}
80+
81+
// We don't use latestOfMany because it's not compatible with Postgresql UUIDs
82+
$lastEvent = ucfirst($record->events->first()->name);
83+
$hasMoreEvents = $record->events->count() > 1
84+
? '<span style="font-size: 0.65rem" class="opacity-85 text-center">+ ' . ($record->events->count() - 1) . ' more</span>'
85+
: '';
86+
87+
return new HtmlString(Blade::render(<<<HTML
88+
<div class="flex gap-1">
89+
<x-filament::badge icon="heroicon-o-rss">
90+
$lastEvent
91+
</x-filament::badge>
92+
$hasMoreEvents
93+
</div>
94+
HTML));
95+
}),
96+
7297
Tables\Columns\TextColumn::make('tries')
7398
->badge()
99+
->icon('heroicon-o-arrow-path')
74100
->tooltip(function (Log $record) {
75101
if ($record->status !== LogStatus::Failed || is_null($record->last_try_at)) {
76102
return null;

src/Resources/LogResource/Pages/ListLogs.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ListLogs extends ListRecords
1919
protected function getTableQuery(): EloquentBuilder
2020
{
2121
return Log::query()
22-
->with(['template', 'attachments'])
22+
->with(['template', 'attachments', 'events'])
2323
->latest();
2424
}
2525

0 commit comments

Comments
 (0)