Skip to content

Commit 2e14e7a

Browse files
authored
[Feature] User role (alexjustesen#762)
1 parent 705e2f2 commit 2e14e7a

File tree

16 files changed

+340
-66
lines changed

16 files changed

+340
-66
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use App\Models\User;
6+
use Illuminate\Console\Command;
7+
8+
use function Laravel\Prompts\confirm;
9+
use function Laravel\Prompts\info;
10+
use function Laravel\Prompts\select;
11+
use function Laravel\Prompts\text;
12+
13+
class UpdateUserRole extends Command
14+
{
15+
/**
16+
* The name and signature of the console command.
17+
*
18+
* @var string
19+
*/
20+
protected $signature = 'app:update-user-role';
21+
22+
/**
23+
* The console command description.
24+
*
25+
* @var string
26+
*/
27+
protected $description = 'Change the role for a given user.';
28+
29+
/**
30+
* Execute the console command.
31+
*/
32+
public function handle()
33+
{
34+
$email = text(
35+
label: 'What is the email address?',
36+
required: true,
37+
validate: fn (string $value) => match (true) {
38+
! User::firstWhere('email', $value) => 'User not found.',
39+
default => null
40+
}
41+
);
42+
43+
$role = select(
44+
label: 'What role should the user have?',
45+
options: [
46+
'admin' => 'Admin',
47+
'guest' => 'Guest',
48+
'user' => 'User',
49+
],
50+
default: 'guest'
51+
);
52+
53+
$confirmed = confirm(
54+
label: 'Are you sure?',
55+
required: true
56+
);
57+
58+
if ($confirmed) {
59+
User::firstWhere('email', $email)
60+
->update([
61+
'role' => $role,
62+
]);
63+
64+
info('User role updated.');
65+
}
66+
}
67+
}

app/Filament/Pages/Dashboard.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,13 @@ class Dashboard extends BasePage
1818

1919
protected static string $view = 'filament.pages.dashboard';
2020

21-
protected function getPollingInterval(): ?string
22-
{
23-
return null;
24-
}
25-
2621
protected function getHeaderActions(): array
2722
{
2823
return [
2924
Action::make('speedtest')
3025
->label('Queue Speedtest')
31-
->action('queueSpeedtest'),
26+
->action('queueSpeedtest')
27+
->hidden(fn (): bool => ! auth()->user()->is_admin && ! auth()->user()->is_user),
3228
];
3329
}
3430

app/Filament/Pages/DeleteData.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ class DeleteData extends Page
2121

2222
protected ?string $maxContentWidth = '3xl';
2323

24+
public function mount(): void
25+
{
26+
abort_unless(auth()->user()->is_admin, 403);
27+
}
28+
29+
public static function shouldRegisterNavigation(): bool
30+
{
31+
return auth()->user()->is_admin;
32+
}
33+
2434
public function getHeaderActions(): array
2535
{
2636
return [

app/Filament/Pages/Settings/GeneralPage.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ class GeneralPage extends SettingsPage
2525

2626
protected static string $settings = GeneralSettings::class;
2727

28+
public function mount(): void
29+
{
30+
abort_unless(auth()->user()->is_admin, 403);
31+
}
32+
33+
public static function shouldRegisterNavigation(): bool
34+
{
35+
return auth()->user()->is_admin;
36+
}
37+
2838
public function form(Form $form): Form
2939
{
3040
return $form

app/Filament/Pages/Settings/InfluxDbPage.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ class InfluxDbPage extends SettingsPage
2121

2222
protected static string $settings = InfluxDbSettings::class;
2323

24+
public function mount(): void
25+
{
26+
abort_unless(auth()->user()->is_admin, 403);
27+
}
28+
29+
public static function shouldRegisterNavigation(): bool
30+
{
31+
return auth()->user()->is_admin;
32+
}
33+
2434
public function form(Form $form): Form
2535
{
2636
return $form

app/Filament/Pages/Settings/NotificationPage.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ class NotificationPage extends SettingsPage
2929

3030
protected static string $settings = NotificationSettings::class;
3131

32+
public function mount(): void
33+
{
34+
abort_unless(auth()->user()->is_admin, 403);
35+
}
36+
37+
public static function shouldRegisterNavigation(): bool
38+
{
39+
return auth()->user()->is_admin;
40+
}
41+
3242
public function form(Form $form): Form
3343
{
3444
return $form

app/Filament/Pages/Settings/ThresholdsPage.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ class ThresholdsPage extends SettingsPage
2121

2222
protected static string $settings = ThresholdSettings::class;
2323

24+
public function mount(): void
25+
{
26+
abort_unless(auth()->user()->is_admin, 403);
27+
}
28+
29+
public static function shouldRegisterNavigation(): bool
30+
{
31+
return auth()->user()->is_admin;
32+
}
33+
2434
public function form(Form $form): Form
2535
{
2636
return $form

app/Filament/Resources/ResultResource.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ class ResultResource extends Resource
2626

2727
protected static ?string $navigationIcon = 'heroicon-o-table-cells';
2828

29-
protected static ?string $navigationLabel = 'Results';
30-
3129
public static function form(Form $form): Form
3230
{
3331
$settings = new GeneralSettings();
@@ -168,6 +166,7 @@ public static function table(Table $table): Table
168166
Tables\Actions\ViewAction::make(),
169167
Tables\Actions\Action::make('updateComments')
170168
->icon('heroicon-o-chat-bubble-bottom-center-text')
169+
->hidden(fn (): bool => ! auth()->user()->is_admin && ! auth()->user()->is_user)
171170
->mountUsing(fn (Forms\ComponentContainer $form, Result $record) => $form->fill([
172171
'comments' => $record->comments,
173172
]))
@@ -188,6 +187,7 @@ public static function table(Table $table): Table
188187
Tables\Actions\BulkAction::make('export')
189188
->label('Export selected')
190189
->icon('heroicon-o-arrow-down-tray')
190+
->hidden(fn (): bool => ! auth()->user()->is_admin)
191191
->action(function (Collection $records) {
192192
$export = new ResultsSelectedBulkExport($records->toArray());
193193

app/Filament/Resources/UserResource.php

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,7 @@ class UserResource extends Resource
1919
{
2020
protected static ?string $model = User::class;
2121

22-
protected static ?string $navigationGroup = 'System';
23-
24-
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
25-
26-
protected static ?int $navigationSort = 0;
27-
28-
protected static ?string $slug = 'system/users';
22+
protected static ?string $navigationIcon = 'heroicon-o-users';
2923

3024
public static function form(Form $form): Form
3125
{
@@ -65,20 +59,48 @@ public static function form(Form $form): Form
6559
->visible(fn ($livewire) => $livewire instanceof EditUser)
6660
->dehydrated(false),
6761
])
68-
->columns('full')
62+
->columns(1)
6963
->columnSpan([
7064
'md' => 2,
7165
]),
7266

73-
Forms\Components\Section::make()
67+
Forms\Components\Grid::make([
68+
'default' => 1,
69+
])
7470
->schema([
75-
Forms\Components\Placeholder::make('created_at')
76-
->content(fn ($record) => $record?->created_at?->diffForHumans() ?? new HtmlString('&mdash;')),
77-
Forms\Components\Placeholder::make('updated_at')
78-
->content(fn ($record) => $record?->updated_at?->diffForHumans() ?? new HtmlString('&mdash;')),
71+
Forms\Components\Section::make()
72+
->schema([
73+
Forms\Components\Select::make('role')
74+
->options([
75+
'admin' => 'Admin',
76+
'guest' => 'Guest',
77+
'user' => 'User',
78+
])
79+
->default('guest')
80+
->disabled(fn (): bool => ! auth()->user()->is_admin || auth()->user()->is_user)
81+
->required(),
82+
])
83+
->columns(1)
84+
->columnSpan([
85+
'md' => 1,
86+
]),
87+
88+
Forms\Components\Section::make()
89+
->schema([
90+
Forms\Components\Placeholder::make('created_at')
91+
->content(fn ($record) => $record?->created_at?->diffForHumans() ?? new HtmlString('&mdash;')),
92+
Forms\Components\Placeholder::make('updated_at')
93+
->content(fn ($record) => $record?->updated_at?->diffForHumans() ?? new HtmlString('&mdash;')),
94+
])
95+
->columns(1)
96+
->columnSpan([
97+
'md' => 1,
98+
]),
7999
])
80-
->columns('full')
81-
->columnSpan(1),
100+
->columns(1)
101+
->columnSpan([
102+
'md' => 1,
103+
]),
82104
]),
83105
]);
84106
}
@@ -87,27 +109,36 @@ public static function table(Table $table): Table
87109
{
88110
return $table
89111
->columns([
112+
Tables\Columns\TextColumn::make('id')
113+
->label('ID'),
90114
Tables\Columns\TextColumn::make('name')
91115
->searchable(),
92116
Tables\Columns\TextColumn::make('email')
93117
->searchable(),
94-
Tables\Columns\TextColumn::make('email_verified_at')
95-
->dateTime(),
96-
Tables\Columns\TextColumn::make('created_at')
97-
->dateTime(),
118+
Tables\Columns\TextColumn::make('role')
119+
->badge()
120+
->color(fn (string $state): string => match ($state) {
121+
'admin' => 'success',
122+
'guest' => 'gray',
123+
'user' => 'info',
124+
}),
98125
Tables\Columns\TextColumn::make('updated_at')
126+
->label('Last updated')
99127
->dateTime(),
100128
])
101129
->filters([
102-
//
130+
Tables\Filters\SelectFilter::make('role')
131+
->options([
132+
'admin' => 'Admin',
133+
'guest' => 'Guest',
134+
'user' => 'User',
135+
]),
103136
])
104137
->actions([
105-
Tables\Actions\EditAction::make(),
106-
])
107-
->bulkActions([
108-
Tables\Actions\BulkActionGroup::make([
109-
Tables\Actions\DeleteBulkAction::make()
110-
->requiresConfirmation(),
138+
Tables\Actions\ActionGroup::make([
139+
Tables\Actions\ViewAction::make(),
140+
Tables\Actions\EditAction::make(),
141+
Tables\Actions\DeleteAction::make(),
111142
]),
112143
]);
113144
}

app/Models/User.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Filament\Models\Contracts\FilamentUser;
88
use Filament\Panel;
9+
use Illuminate\Database\Eloquent\Casts\Attribute;
910
use Illuminate\Database\Eloquent\Factories\HasFactory;
1011
use Illuminate\Foundation\Auth\User as Authenticatable;
1112
use Illuminate\Notifications\Notifiable;
@@ -25,6 +26,7 @@ class User extends Authenticatable implements FilamentUser
2526
'email',
2627
'email_verified_at',
2728
'password',
29+
'role',
2830
];
2931

3032
/**
@@ -53,4 +55,34 @@ public function canAccessPanel(Panel $panel): bool
5355
{
5456
return true;
5557
}
58+
59+
/**
60+
* Determine if the user has an admin role.
61+
*/
62+
protected function isAdmin(): Attribute
63+
{
64+
return Attribute::make(
65+
get: fn (mixed $value, array $attributes): bool => $attributes['role'] == 'admin',
66+
);
67+
}
68+
69+
/**
70+
* Determine if the user has a guest role.
71+
*/
72+
protected function isGuest(): Attribute
73+
{
74+
return Attribute::make(
75+
get: fn (mixed $value, array $attributes): bool => $attributes['role'] == 'guest' || blank($attributes['role']),
76+
);
77+
}
78+
79+
/**
80+
* Determine if the user has a user role.
81+
*/
82+
protected function isUser(): Attribute
83+
{
84+
return Attribute::make(
85+
get: fn (mixed $value, array $attributes): bool => $attributes['role'] == 'user',
86+
);
87+
}
5688
}

0 commit comments

Comments
 (0)