Skip to content

Commit 9a9a124

Browse files
committed
Add UserResource
1 parent e05ea9d commit 9a9a124

File tree

6 files changed

+269
-3
lines changed

6 files changed

+269
-3
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace App\Filament\Resources\Users\Pages;
4+
5+
use App\Filament\Resources\Users\UserResource;
6+
use Filament\Actions\CreateAction;
7+
use Filament\Resources\Pages\ListRecords;
8+
9+
class ListUsers extends ListRecords
10+
{
11+
protected static string $resource = UserResource::class;
12+
13+
protected function getHeaderActions(): array
14+
{
15+
return [
16+
// CreateAction::make(),
17+
];
18+
}
19+
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
<?php
2+
3+
namespace App\Filament\Resources\Users\Tables;
4+
5+
use App\Jobs\BanUser;
6+
use App\Jobs\DeleteUserThreads;
7+
use App\Jobs\UnbanUser;
8+
use App\Jobs\UnVerifyAuthor;
9+
use App\Jobs\VerifyAuthor;
10+
use App\Models\User;
11+
use App\Policies\UserPolicy;
12+
use Filament\Actions\Action;
13+
use Filament\Actions\ActionGroup;
14+
use Filament\Actions\BulkActionGroup;
15+
use Filament\Actions\DeleteAction;
16+
use Filament\Actions\DeleteBulkAction;
17+
use Filament\Forms\Components\Checkbox;
18+
use Filament\Forms\Components\TextInput;
19+
use Filament\Notifications\Notification;
20+
use Filament\Tables\Columns\IconColumn;
21+
use Filament\Tables\Columns\ImageColumn;
22+
use Filament\Tables\Columns\TextColumn;
23+
use Filament\Tables\Filters\SelectFilter;
24+
use Filament\Tables\Filters\TernaryFilter;
25+
use Filament\Tables\Table;
26+
27+
class UsersTable
28+
{
29+
public static function configure(Table $table): Table
30+
{
31+
return $table
32+
->defaultSort('created_at', 'desc')
33+
->openRecordUrlInNewTab()
34+
->columns([
35+
ImageColumn::make('github_id')
36+
->label('Name')
37+
->circular()
38+
->width('0%')
39+
->defaultImageUrl(fn(?string $state): string => $state ? sprintf('https://avatars.githubusercontent.com/u/%s', $state) : asset('images/laravelio-icon-gray.svg')),
40+
41+
TextColumn::make('username')
42+
->label('')
43+
->searchable()
44+
->formatStateUsing(fn(User $user): ?string => $user->name)
45+
->description(fn(User $user): ?string => $user->username),
46+
47+
TextColumn::make('email')
48+
->searchable()
49+
->toggleable(isToggledHiddenByDefault: true),
50+
51+
TextColumn::make('type')
52+
->label('Role')
53+
->badge()
54+
->formatStateUsing(fn(string $state): string => match ($state) {
55+
'1' => 'User',
56+
'2' => 'Moderator',
57+
'3' => 'Admin',
58+
}),
59+
60+
IconColumn::make('banned_at')
61+
->label('Banned')
62+
->boolean()
63+
->default(false),
64+
65+
TextColumn::make('created_at')
66+
->label('Joined on')
67+
->dateTime()
68+
->sortable()
69+
])
70+
->filters([
71+
SelectFilter::make('type')
72+
->options([
73+
'1' => 'User',
74+
'2' => 'Moderator',
75+
'3' => 'Admin',
76+
]),
77+
78+
TernaryFilter::make('banned_at')
79+
->label('Banned')
80+
->nullable()
81+
])
82+
->recordActions([
83+
Action::make('view')
84+
->url(fn(User $user): string => route('profile', $user->username))
85+
->openUrlInNewTab()
86+
->icon('heroicon-s-eye'),
87+
88+
ActionGroup::make([
89+
Action::make('verify_author')
90+
->action(function (User $user) {
91+
VerifyAuthor::dispatchSync($user);
92+
93+
Notification::make()
94+
->title($user->name . ' is now a verified author.')
95+
->success()
96+
->send();
97+
})
98+
->openUrlInNewTab()
99+
->color('primary')
100+
->icon('heroicon-s-check-circle')
101+
->requiresConfirmation()
102+
->visible(fn(User $user): bool => auth()->user()->can(UserPolicy::ADMIN, $user) && ! $user->isVerifiedAuthor()),
103+
104+
Action::make('unverify_author')
105+
->action(function (User $user) {
106+
UnVerifyAuthor::dispatchSync($user);
107+
108+
Notification::make()
109+
->title($user->name . '\'s threads have been deleted.')
110+
->success()
111+
->send();
112+
})
113+
->openUrlInNewTab()
114+
->color('danger')
115+
->icon('heroicon-s-x-circle')
116+
->requiresConfirmation()
117+
->visible(fn(User $user): bool => auth()->user()->can(UserPolicy::ADMIN, $user) && $user->isVerifiedAuthor()),
118+
119+
Action::make('ban_author')
120+
->schema([
121+
TextInput::make('reason')
122+
->label('Reason')
123+
->required()
124+
->placeholder('Provide a reason for banning this user...'),
125+
126+
Checkbox::make('delete_threads')
127+
->label('Delete all threads')
128+
->default(false),
129+
])
130+
->action(function (User $user, array $data) {
131+
BanUser::dispatchSync($user, $data['reason']);
132+
133+
if ($data['delete_threads']) {
134+
DeleteUserThreads::dispatchSync($user);
135+
}
136+
137+
Notification::make()
138+
->title(
139+
$user->name . ' is now banned.' . ($data['delete_threads'] ? ' And all his threads are now deleted.' : '')
140+
)
141+
->success()
142+
->send();
143+
})
144+
->modalDescription('Are you sure you\'d like to ban this user? This will prevent him from logging in, posting threads and replying to threads.')
145+
->openUrlInNewTab()
146+
->color('danger')
147+
->icon('heroicon-s-check-circle')
148+
->requiresConfirmation()
149+
->visible(fn(User $user): bool => auth()->user()->can(UserPolicy::BAN, $user) && ! $user->isBanned()),
150+
151+
Action::make('unban_author')
152+
->action(function (User $user) {
153+
154+
UnbanUser::dispatchSync($user);
155+
156+
Notification::make()
157+
->title($user->name . ' is no longer a banned user.')
158+
->success()
159+
->send();
160+
})
161+
->openUrlInNewTab()
162+
->color('primary')
163+
->icon('heroicon-s-x-circle')
164+
->requiresConfirmation()
165+
->visible(fn(User $user): bool => auth()->user()->can(UserPolicy::BAN, $user) && $user->isBanned()),
166+
167+
Action::make('delete_threads')
168+
->action(function (User $user) {
169+
DeleteUserThreads::dispatchSync($user);
170+
171+
Notification::make()
172+
->title($user->name . '\'s threads have been deleted.')
173+
->success()
174+
->send();
175+
})
176+
->openUrlInNewTab()
177+
->color('danger')
178+
->icon('heroicon-s-archive-box-x-mark')
179+
->requiresConfirmation()
180+
->visible(fn(User $user): bool => auth()->user()->can(UserPolicy::DELETE, $user)),
181+
182+
DeleteAction::make()
183+
->visible(fn(User $user): bool => auth()->user()->can(UserPolicy::DELETE, $user)),
184+
]),
185+
])
186+
->toolbarActions([
187+
BulkActionGroup::make([
188+
DeleteBulkAction::make(),
189+
]),
190+
]);
191+
}
192+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace App\Filament\Resources\Users;
4+
5+
use App\Filament\Resources\Users\Pages\CreateUser;
6+
use App\Filament\Resources\Users\Pages\EditUser;
7+
use App\Filament\Resources\Users\Pages\ListUsers;
8+
use App\Filament\Resources\Users\Schemas\UserForm;
9+
use App\Filament\Resources\Users\Tables\UsersTable;
10+
use App\Models\User;
11+
use BackedEnum;
12+
use Filament\Resources\Resource;
13+
use Filament\Schemas\Schema;
14+
use Filament\Support\Icons\Heroicon;
15+
use Filament\Tables\Table;
16+
17+
class UserResource extends Resource
18+
{
19+
protected static ?string $model = User::class;
20+
21+
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedUsers;
22+
23+
protected static ?string $recordTitleAttribute = 'name';
24+
25+
public static function table(Table $table): Table
26+
{
27+
return UsersTable::configure($table);
28+
}
29+
30+
public static function getRelations(): array
31+
{
32+
return [
33+
//
34+
];
35+
}
36+
37+
public static function getPages(): array
38+
{
39+
return [
40+
'index' => ListUsers::route('/'),
41+
];
42+
}
43+
}

app/Jobs/BanUser.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44

55
use App\Models\User;
66
use Carbon\Carbon;
7+
use Illuminate\Contracts\Queue\ShouldQueue;
8+
use Illuminate\Foundation\Queue\Queueable;
79

8-
final class BanUser
10+
final class BanUser implements ShouldQueue
911
{
12+
use Queueable;
13+
1014
public function __construct(private User $user, private $reason) {}
1115

1216
public function handle(): void

app/Jobs/DeleteUserThreads.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
namespace App\Jobs;
44

55
use App\Models\User;
6+
use Illuminate\Contracts\Queue\ShouldQueue;
7+
use Illuminate\Foundation\Queue\Queueable;
68

7-
final class DeleteUserThreads
9+
final class DeleteUserThreads implements ShouldQueue
810
{
11+
use Queueable;
12+
913
public function __construct(private User $user) {}
1014

1115
public function handle(): void

app/Jobs/UnbanUser.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
namespace App\Jobs;
44

55
use App\Models\User;
6+
use Illuminate\Contracts\Queue\ShouldQueue;
7+
use Illuminate\Foundation\Queue\Queueable;
68

7-
final class UnbanUser
9+
final class UnbanUser implements ShouldQueue
810
{
11+
use Queueable;
12+
913
public function __construct(private User $user) {}
1014

1115
public function handle(): void

0 commit comments

Comments
 (0)