From 39ce9dcb0f9a81af3309a332096fb86ee6e1542a Mon Sep 17 00:00:00 2001 From: MathewEm Date: Tue, 24 Dec 2024 11:10:15 -0330 Subject: [PATCH] feat(librarium): implement password reset action closes #816 --- app/Filament/Resources/UserResource.php | 146 +++++++++--------- .../Resources/UserResource/Pages/EditUser.php | 12 ++ resources/src/auto-imports.d.ts | 3 - 3 files changed, 85 insertions(+), 76 deletions(-) diff --git a/app/Filament/Resources/UserResource.php b/app/Filament/Resources/UserResource.php index 581a1631..53ce0614 100644 --- a/app/Filament/Resources/UserResource.php +++ b/app/Filament/Resources/UserResource.php @@ -24,93 +24,93 @@ public static function form(Form $form): Form return $form ->schema([ Forms\Components\TextInput::make('first_name') - ->disabledOn('edit') - ->filled() - ->required(), + ->disabledOn('edit') + ->filled() + ->required(), Forms\Components\TextInput::make('last_name') - ->disabledOn('edit') - ->filled(), + ->disabledOn('edit') + ->filled(), Forms\Components\Section::make('User Information') - ->schema([ - Forms\Components\TextInput::make('email') - ->filled() - ->rules(['bail', 'required', 'string', 'email', new AuthorizedEmailDomain]), - Forms\Components\CheckboxList::make('roles') - ->relationship(titleAttribute: 'name') - ->getOptionLabelFromRecordUsing(fn (Role $record) => UserRole::from($record->name)->label()) - ->label('Roles'), - ]), + ->schema([ + Forms\Components\TextInput::make('email') + ->filled() + ->rules(['bail', 'required', 'string', 'email', new AuthorizedEmailDomain]), + Forms\Components\CheckboxList::make('roles') + ->relationship(titleAttribute: 'name') + ->getOptionLabelFromRecordUsing(fn (Role $record) => UserRole::from($record->name)->label()) + ->label('Roles'), + ]), Forms\Components\Section::make('Available Actions') - ->description('Actions are instantaneous and may disappear once applied!') - ->schema([ - Forms\Components\Toggle::make('active') - ->label('Activate User') - ->dehydrated(false) - ->hidden(fn ($record) => $record && ! $record->email_verified_at) - ->onColor('success'), - Forms\Components\Actions::make([ - Forms\Components\Actions\Action::make('verify_email') - ->label('Activate & Verify') - ->hidden(fn ($record) => $record && $record->email_verified_at) - ->action('setVerifiedEmail'), - Forms\Components\Actions\Action::make('reset_password') - ->label('Send Password Reset Email') - ->action('sendPasswordReset'), - ]), - ]), - ]); + ->description('Actions are instantaneous and may disappear once applied!') + ->schema([ + Forms\Components\Toggle::make('active') + ->label('Activate User') + ->dehydrated(false) + ->hidden(fn ($record) => $record && ! $record->email_verified_at) + ->onColor('success'), + Forms\Components\Actions::make([ + Forms\Components\Actions\Action::make('reset_password') + ->label('Send Password Reset Email') + ->action('sendPasswordReset'), + Forms\Components\Actions\Action::make('verify_email') + ->label('Activate User & Verify Email') + ->disabled(fn ($record) => isset($record->email_verified_at)) + ->action('setVerifiedEmail'), + ]), + ]), + ]); } public static function table(Table $table): Table { - return $table + return $table ->columns([ Tables\Columns\TextColumn::make('first_name') - ->searchable() - ->sortable(), + ->searchable() + ->sortable(), Tables\Columns\TextColumn::make('last_name') - ->searchable() - ->sortable(), + ->searchable() + ->sortable(), Tables\Columns\TextColumn::make('email') - ->searchable(), + ->searchable(), Tables\Columns\IconColumn::make('email_verified_at') - ->label('Email Verified') - ->default('heroicon-o-x-circle') - ->icon(fn ($state) => $state instanceof \DateTime ? 'heroicon-o-check-circle' : 'heroicon-o-x-circle') - ->color(fn ($state) => $state instanceof \DateTime ? 'success' : 'danger') - ->sortable(), + ->label('Email Verified') + ->default('heroicon-o-x-circle') + ->icon(fn ($state) => $state instanceof \DateTime ? 'heroicon-o-check-circle' : 'heroicon-o-x-circle') + ->color(fn ($state) => $state instanceof \DateTime ? 'success' : 'danger') + ->sortable(), Tables\Columns\IconColumn::make('active') - ->boolean() - ->sortable(), + ->boolean() + ->sortable(), Tables\Columns\TextColumn::make('roles.name') - ->badge() - ->formatStateUsing(fn (string $state) => UserRole::from($state)->label()) - ->color(fn (string $state): string => match (UserRole::from($state)) { - UserRole::AUTHOR => 'success', - UserRole::DIRECTOR, UserRole::EDITOR, UserRole::CHIEF_EDITOR => 'warning', - UserRole::ADMIN => 'danger', - }) - ->searchable(), + ->badge() + ->formatStateUsing(fn (string $state) => UserRole::from($state)->label()) + ->color(fn (string $state): string => match (UserRole::from($state)) { + UserRole::AUTHOR => 'success', + UserRole::DIRECTOR, UserRole::EDITOR, UserRole::CHIEF_EDITOR => 'warning', + UserRole::ADMIN => 'danger', + }) + ->searchable(), ]) ->filters([ Tables\Filters\TernaryFilter::make('email_verified_at') - ->label('Verified') - ->nullable() - ->placeholder('All'), + ->label('Verified') + ->nullable() + ->placeholder('All'), Tables\Filters\SelectFilter::make('active') - ->options([ - true => 'Active', - false => 'Inactive', - ]), + ->options([ + true => 'Active', + false => 'Inactive', + ]), Tables\Filters\Filter::make('no_roles') - ->label('No Roles') - ->query(fn ($query) => $query->whereDoesntHave('roles')), + ->label('No Roles') + ->query(fn ($query) => $query->whereDoesntHave('roles')), Tables\Filters\SelectFilter::make('roles') - ->relationship('roles', 'name') - ->getOptionLabelFromRecordUsing(fn (Role $record) => UserRole::from($record->name)->label()) - ->label('Role'), + ->relationship('roles', 'name') + ->getOptionLabelFromRecordUsing(fn (Role $record) => UserRole::from($record->name)->label()) + ->label('Role'), ]) ->actions([ Tables\Actions\EditAction::make(), @@ -125,17 +125,17 @@ public static function table(Table $table): Table public static function getRelations(): array { - return [ - // - ]; + return [ + // + ]; } public static function getPages(): array { - return [ - 'index' => Pages\ListUsers::route('/'), - 'create' => Pages\CreateUser::route('/create'), - 'edit' => Pages\EditUser::route('/{record}/edit'), - ]; + return [ + 'index' => Pages\ListUsers::route('/'), + 'create' => Pages\CreateUser::route('/create'), + 'edit' => Pages\EditUser::route('/{record}/edit'), + ]; } } diff --git a/app/Filament/Resources/UserResource/Pages/EditUser.php b/app/Filament/Resources/UserResource/Pages/EditUser.php index 31adf802..c39e379e 100644 --- a/app/Filament/Resources/UserResource/Pages/EditUser.php +++ b/app/Filament/Resources/UserResource/Pages/EditUser.php @@ -4,6 +4,7 @@ use App\Filament\Resources\UserResource; use Filament\Actions; +use Filament\Notifications\Notification; use Filament\Resources\Pages\EditRecord; use Illuminate\Support\Facades\Password; @@ -62,10 +63,21 @@ public function setVerifiedEmail(): void } $this->record->markEmailAsVerified(); $this->refreshFormData(['active']); + Notification::make() + ->title('User\'s email is verified!') + ->success() + ->send(); } + /** + * Call the Reset Password Email process + */ public function sendPasswordReset(): void { Password::sendResetLink(['email' => $this->data['email']]); + Notification::make() + ->title('Reset password email sent!') + ->success() + ->send(); } } diff --git a/resources/src/auto-imports.d.ts b/resources/src/auto-imports.d.ts index 447fad7a..1c5e02e7 100644 --- a/resources/src/auto-imports.d.ts +++ b/resources/src/auto-imports.d.ts @@ -332,9 +332,6 @@ declare global { // @ts-ignore export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' import('vue') - // @ts-ignore - export type { Locale } from './stores/LocaleStore' - import('./stores/LocaleStore') } // for vue template auto import