Skip to content

Commit 2227176

Browse files
committed
add user activity widget
1 parent e514b19 commit 2227176

7 files changed

Lines changed: 195 additions & 0 deletions

File tree

config/inspirecms.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@
245245
'guard_widgets' => [
246246
\SolutionForest\InspireCms\Filament\Widgets\CmsInfoWidget::class,
247247
\SolutionForest\InspireCms\Filament\Widgets\TemplateInfo::class,
248+
\SolutionForest\InspireCms\Filament\Widgets\UserActivity::class,
248249
],
249250
],
250251

resources/lang/en/widgets.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,12 @@
3434
'permission_display_name' => 'View Template Info',
3535
],
3636

37+
'user_activity' => [
38+
'title' => 'User Activity',
39+
'empty_state' => [
40+
'heading' => 'No recent activity found.',
41+
],
42+
'permission_display_name' => 'View User Activity',
43+
],
44+
3745
];

resources/lang/zh_TW/widgets.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,12 @@
3434
'permission_display_name' => '查看模板資訊',
3535
],
3636

37+
'user_activity' => [
38+
'title' => '用戶活動',
39+
'empty_state' => [
40+
'heading' => '沒有最近的活動。',
41+
],
42+
'permission_display_name' => '查看用戶活動',
43+
],
44+
3745
];
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<x-filament::widget class="filament-widgets-user-activity-widget">
2+
<x-filament::section>
3+
<x-slot name="heading">
4+
{{ __('inspirecms::widgets.user_activity.title') }}
5+
</x-slot>
6+
7+
@if($activities && $activities->count())
8+
<div class="space-y-4">
9+
@foreach($activities as $activity)
10+
<div class="flex items-center justify-between p-2 rounded-lg bg-gray-50 dark:bg-gray-800">
11+
<div class="flex items-center space-x-3">
12+
<div class="flex-shrink-0">
13+
@if($activity->causer && $activity->causer->avatar)
14+
<img src="{{ $activity->causer->avatar }}" alt="{{ $activity->causer->name }}" class="w-10 h-10 rounded-full">
15+
@else
16+
<div class="flex items-center justify-center w-10 h-10 rounded-full bg-primary-500">
17+
<span class="text-white text-sm font-medium">
18+
{{ $activity->causer ? substr($activity->causer->name ?? '', 0, 2) : 'SYS' }}
19+
</span>
20+
</div>
21+
@endif
22+
</div>
23+
<div>
24+
<p class="font-medium text-sm">
25+
{{ $activity->causer->name ?? 'System' }}
26+
</p>
27+
<p class="text-xs text-gray-500 dark:text-gray-400">
28+
{{ $activity->description }}
29+
@if($activity->subject)
30+
<span class="font-medium">{{ get_class($activity->subject) == 'App\Models\User' ? $activity->subject->name : class_basename($activity->subject) }}</span>
31+
@endif
32+
</p>
33+
</div>
34+
</div>
35+
<div class="text-xs text-gray-500 dark:text-gray-400 flex flex-col items-end">
36+
@if($activity->last_logged_in_at_local)
37+
<span>Login: {{ $activity->last_logged_in_at_local->diffForHumans() }}</span>
38+
@endif
39+
@if($activity->last_logged_out_at_local)
40+
<span>Logout: {{ $activity->last_logged_out_at_local->diffForHumans() }}</span>
41+
@endif
42+
</div>
43+
</div>
44+
@endforeach
45+
</div>
46+
47+
@if($activities->hasPages())
48+
<div class="mt-2">
49+
{{ $activities->links() }}
50+
</div>
51+
@endif
52+
@else
53+
<div class="flex items-center justify-center h-32 text-gray-500 dark:text-gray-400">
54+
{{ __('inspirecms::widgets.user_activity.empty_state.heading') }}
55+
</div>
56+
@endif
57+
</x-filament::section>
58+
</x-filament::widget>

src/CmsPanelProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ protected function configureCmsPanel(Panel $panel)
7777
->widgets([
7878
Widgets\CmsInfoWidget::class,
7979
Widgets\PageActivity::class,
80+
Widgets\UserActivity::class,
8081
Widgets\AlertOverview::class,
8182
Widgets\TemplateInfo::class,
8283
TreeNavigation::class,

src/Filament/Pages/Dashboard.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ protected function getHeaderWidgets(): array
4242
return [
4343
Widgets\CmsInfoWidget::class,
4444
Widgets\PageActivity::class,
45+
Widgets\UserActivity::class,
4546
];
4647
}
4748
public function getHeaderWidgetsColumns(): int | string | array
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
namespace SolutionForest\InspireCms\Filament\Widgets;
4+
5+
use Carbon\Carbon;
6+
use Filament\Widgets\Widget;
7+
use Illuminate\Pagination\LengthAwarePaginator;
8+
use Livewire\WithPagination;
9+
use SolutionForest\InspireCms\Filament\Contracts\GuardWidget;
10+
use SolutionForest\InspireCms\Filament\Widgets\Conceners\GuardWidgetTrait;
11+
12+
class UserActivity extends Widget implements GuardWidget
13+
{
14+
use GuardWidgetTrait;
15+
use WithPagination;
16+
17+
protected static string $view = 'inspirecms::filament.widgets.user-activity';
18+
19+
protected int | string | array $columnSpan = 'full';
20+
21+
protected static ?string $pollingInterval = '5s';
22+
23+
public static function getPermissionName(): string
24+
{
25+
return 'widgets_view-user-activity';
26+
}
27+
28+
public static function getPermissionDisplayName(): string
29+
{
30+
return strval(__('inspirecms::widgets.user_activity.permission_display_name'));
31+
}
32+
33+
public function getViewData(): array
34+
{
35+
return [
36+
'activities' => $this->getUserActivities(),
37+
];
38+
}
39+
40+
protected function getUserActivities()
41+
{
42+
$pageName = 'user-activity';
43+
44+
$user = auth()->user();
45+
46+
try {
47+
48+
if ($user && is_inspirecms_user($user)) {
49+
50+
$activities = $user->userActivities()
51+
->latest('last_logged_in_at_utc')
52+
->simplePaginate(perPage: 5, pageName: $pageName, page: $this->getPage($pageName));
53+
54+
$activities->tap(function ($activities) use ($user) {
55+
$activities->setCollection($activities->getCollection()->map(function ($activity) use ($user) {
56+
57+
$activity->causer = $user;
58+
$activity->subject = $user;
59+
60+
$activity->description = $activity->ip_address;
61+
62+
$dtFormat = 'Y-m-d H:i:s';
63+
if ($activity->last_logged_in_at_utc != null) {
64+
$ts = $activity->last_logged_in_at_utc->format($dtFormat);
65+
$activity->last_logged_in_at_utc = Carbon::createFromFormat(
66+
$dtFormat,
67+
$ts,
68+
'UTC'
69+
);
70+
$activity->last_logged_in_at_local = Carbon::createFromFormat(
71+
$dtFormat,
72+
$ts,
73+
'UTC'
74+
)->setTimezone(config('app.timezone'));
75+
} else {
76+
$activity->last_logged_in_at_local = null;
77+
}
78+
79+
if ($activity->last_logged_out_at_utc != null) {
80+
$ts = $activity->last_logged_out_at_utc->format($dtFormat);
81+
$activity->last_logged_out_at_utc = Carbon::createFromFormat(
82+
$dtFormat,
83+
$ts,
84+
'UTC'
85+
);
86+
$activity->last_logged_out_at_local = Carbon::createFromFormat(
87+
$dtFormat,
88+
$ts,
89+
'UTC'
90+
)->setTimezone(config('app.timezone'));
91+
} else {
92+
$activity->last_logged_out_at_local = null;
93+
}
94+
$activity->last_logged_out_at_utc = \Carbon\Carbon::createFromFormat(
95+
$dtFormat,
96+
$activity->last_logged_out_at_utc->format($dtFormat),
97+
'UTC'
98+
);
99+
100+
return $activity;
101+
}));
102+
});
103+
104+
return $activities;
105+
106+
}
107+
108+
} catch (\Throwable $th) {
109+
//
110+
}
111+
112+
// empty pagination
113+
114+
return \Illuminate\Pagination\Paginator::currentPageResolver(function ($pageName) {
115+
return $this->getPage($pageName);
116+
});
117+
}
118+
}

0 commit comments

Comments
 (0)