diff --git a/.github/workflows/test-runner.yml b/.github/workflows/test-runner.yml index b78f75f..fd9ce47 100644 --- a/.github/workflows/test-runner.yml +++ b/.github/workflows/test-runner.yml @@ -1,15 +1,13 @@ name: Tests on: - # Run testing on all push and pull requests for the main branch that have committed changes in PHP files + # Run testing on all push and pull requests that have committed changes in PHP files push: - branches: [ "main" ] paths: - - '**.php' + - '**/*.php' pull_request: - branches: [ "main" ] paths: - - '**.php' + - '**/*.php' # Make it possible to run the workflow manually workflow_dispatch: diff --git a/composer.json b/composer.json index 95cc046..752bbbe 100644 --- a/composer.json +++ b/composer.json @@ -42,35 +42,33 @@ }, "require": { "php": "^8.3", - "bezhansalleh/filament-language-switch": "^3.1", - "bezhansalleh/filament-panel-switch": "^1.1", - "bezhansalleh/filament-shield": "^3.3", + "bezhansalleh/filament-language-switch": "^4.0", + "bezhansalleh/filament-panel-switch": "^2.0", + "bezhansalleh/filament-shield": "^4.0", "datalinx/php-utils": "^2.5", - "dutchcodingcompany/filament-developer-logins": "^1.6", + "dutchcodingcompany/filament-developer-logins": "^2.0", "eclipsephp/common": "dev-main", "eclipsephp/world-plugin": "dev-main", - "filament/filament": "^3.3", - "filament/spatie-laravel-media-library-plugin": "^3.2", - "filament/spatie-laravel-translatable-plugin": "^3.2", - "hasnayeen/themes": "^3.0", + "filament/filament": "^4.0", + "filament/spatie-laravel-media-library-plugin": "^4.0", + "lara-zeus/spatie-translatable": "^1.0", "laravel/framework": "^12.0", "laravel/horizon": "^5.31", "laravel/telescope": "^5.5", "laravel/tinker": "^2.10", + "nben/filament-record-nav": "^1.0", "opcodesio/log-viewer": "^3.17", - "pxlrbt/filament-environment-indicator": "^2.1", - "pxlrbt/filament-excel": "^2.4", - "pxlrbt/filament-spotlight": "^1.3", - "shuvroroy/filament-spatie-laravel-health": "^2.3", + "pxlrbt/filament-environment-indicator": "^3.0", + "pxlrbt/filament-excel": "^3.0", + "pxlrbt/filament-spotlight": "^2.0", + "shuvroroy/filament-spatie-laravel-health": "^3.0", "spatie/laravel-package-tools": "^1.18", "spatie/laravel-translatable": "^6.11", "spatie/security-advisories-health-check": "^1.2", - "stechstudio/filament-impersonate": "^3.16", + "stechstudio/filament-impersonate": "^4.0", "symfony/http-client": "^7.3", "symfony/mailgun-mailer": "^7.3", - "tangodev-it/filament-emoji-picker": "^1.0", - "typesense/typesense-php": "^5.0", - "nben/filament-record-nav": "^1.0" + "typesense/typesense-php": "^5.0" }, "require-dev": { "laravel/pint": "^1.21", diff --git a/config/filament-shield.php b/config/filament-shield.php index 65c1230..2d8bbb8 100644 --- a/config/filament-shield.php +++ b/config/filament-shield.php @@ -1,29 +1,31 @@ [ - 'should_register_navigation' => true, 'slug' => 'shield/roles', - 'navigation_sort' => -1, - 'navigation_badge' => true, - 'navigation_group' => true, - 'is_globally_searchable' => false, 'show_model_path' => true, - 'is_scoped_to_tenant' => false, 'cluster' => null, + 'tabs' => [ + 'pages' => true, + 'widgets' => true, + 'resources' => true, + 'custom_permissions' => true, + ], ], 'tenant_model' => \Eclipse\Core\Models\Site::class, - 'auth_provider_model' => [ - 'fqcn' => \Eclipse\Core\Models\User::class, - ], + 'auth_provider_model' => \Eclipse\Core\Models\User::class, 'super_admin' => [ 'enabled' => true, 'name' => 'super_admin', 'define_via_gate' => false, - 'intercept_gate' => 'before', // after + 'intercept_gate' => 'before', ], 'panel_user' => [ @@ -31,51 +33,56 @@ 'name' => 'panel_user', ], - 'permission_prefixes' => [ - 'resource' => [ - 'view_any', - 'view', - 'create', - 'update', - 'restore', - 'restore_any', - 'replicate', - 'reorder', - 'delete', - 'delete_any', - 'force_delete', - 'force_delete_any', - ], - - 'page' => 'page', - 'widget' => 'widget', + 'permissions' => [ + 'separator' => '_', + 'case' => 'lower_snake', + 'generate' => true, ], - 'entities' => [ - 'pages' => true, - 'widgets' => true, - 'resources' => true, - 'custom_permissions' => false, + 'policies' => [ + 'path' => app_path('Policies'), + 'merge' => false, + 'generate' => true, + 'methods' => [ + 'viewAny', 'view', 'create', 'update', 'restore', 'restoreAny', + 'replicate', 'reorder', 'delete', 'deleteAny', 'forceDelete', 'forceDeleteAny', + ], + 'single_parameter_methods' => [ + 'viewAny', 'create', 'deleteAny', 'forceDeleteAny', 'restoreAny', 'reorder', + ], ], - 'generator' => [ - 'option' => 'policies_and_permissions', - 'policy_directory' => 'Policies', - 'policy_namespace' => 'Policies', + 'localization' => [ + 'enabled' => false, + 'key' => 'filament-shield::filament-shield', ], - 'exclude' => [ - 'enabled' => true, + 'resources' => [ + 'subject' => 'model', + 'manage' => [], + 'exclude' => [], + ], - 'pages' => [ - 'Dashboard', + 'pages' => [ + 'subject' => 'class', + 'prefix' => 'view', + 'exclude' => [ + Dashboard::class, ], + ], - 'widgets' => [ - 'AccountWidget', 'FilamentInfoWidget', + 'widgets' => [ + 'subject' => 'class', + 'prefix' => 'view', + 'exclude' => [ + AccountWidget::class, + FilamentInfoWidget::class, ], + ], - 'resources' => [], + 'custom_permissions' => [ + 'impersonate_user', + 'send_email_user', ], 'discovery' => [ @@ -84,8 +91,5 @@ 'discover_all_pages' => false, ], - 'register_role_policy' => [ - 'enabled' => true, - ], - + 'register_role_policy' => true, ]; diff --git a/config/themes.php b/config/themes.php deleted file mode 100644 index 03fae51..0000000 --- a/config/themes.php +++ /dev/null @@ -1,36 +0,0 @@ - 'user', - - /* - |-------------------------------------------------------------------------- - | Theme Icon - |-------------------------------------------------------------------------- - */ - - 'icon' => 'heroicon-o-swatch', - - /* - |-------------------------------------------------------------------------- - | Default Theme - |-------------------------------------------------------------------------- - */ - - 'default' => [ - 'theme' => 'default', - 'theme_color' => 'blue', - ], -]; diff --git a/database/migrations/2025_05_27_071139_add_themes_settings_to_users_table.php b/database/migrations/2025_05_27_071139_add_themes_settings_to_users_table.php deleted file mode 100644 index 310d8cb..0000000 --- a/database/migrations/2025_05_27_071139_add_themes_settings_to_users_table.php +++ /dev/null @@ -1,23 +0,0 @@ -string('theme')->nullable()->default('default'); - $table->string('theme_color')->nullable(); - }); - } - - public function down() - { - Schema::table('users', function (Blueprint $table) { - $table->dropColumn(['theme', 'theme_color']); - }); - } -}; diff --git a/database/seeders/CoreSeeder.php b/database/seeders/CoreSeeder.php index ec37038..6644ddb 100644 --- a/database/seeders/CoreSeeder.php +++ b/database/seeders/CoreSeeder.php @@ -2,6 +2,9 @@ namespace Eclipse\Core\Database\Seeders; +use Eclipse\Core\Models\Site; +use Eclipse\Core\Models\User\Permission; +use Eclipse\Core\Models\User\Role; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\Artisan; @@ -16,7 +19,6 @@ public function run(): void '--all' => null, '--panel' => 'admin', '--option' => 'permissions', - '--minimal' => null, ]); // Seed additional roles @@ -25,7 +27,37 @@ public function run(): void // Sites $this->call(SiteSeeder::class); + // Assign permissions to roles + $this->assignPermissionsToRoles(); + // Users $this->call(UserSeeder::class); } + + private function assignPermissionsToRoles(): void + { + $allPermissions = Permission::all(); + + foreach (Site::all() as $site) { + foreach (['super_admin', 'admin'] as $roleName) { + $role = Role::firstOrCreate([ + 'name' => $roleName, + 'guard_name' => 'web', + 'site_id' => $site->id, + ]); + + $role->syncPermissions($allPermissions); + } + } + + $this->assignCustomPermissionsToRoles(); + } + + private function assignCustomPermissionsToRoles(): void + { + app(\Spatie\Permission\PermissionRegistrar::class)->forgetCachedPermissions(); + $guard = config('auth.defaults.guard', 'web'); + Permission::findOrCreate('impersonate_user', $guard); + Permission::findOrCreate('send_email_user', $guard); + } } diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php index 2d82fde..5cbd981 100644 --- a/database/seeders/RoleSeeder.php +++ b/database/seeders/RoleSeeder.php @@ -14,7 +14,11 @@ public function run(): void { if (is_array(config('eclipse.seed.roles.presets'))) { foreach (config('eclipse.seed.roles.presets') as $preset) { - Role::create($preset['data']); + $data = $preset['data']; + Role::firstOrCreate([ + 'name' => $data['name'], + 'guard_name' => $data['guard_name'] ?? 'web', + ], $data); } } diff --git a/resources/views/filament/components/my-settings.blade.php b/resources/views/filament/components/my-settings.blade.php index 7805951..3537def 100644 --- a/resources/views/filament/components/my-settings.blade.php +++ b/resources/views/filament/components/my-settings.blade.php @@ -9,8 +9,9 @@ @endphp
- + {{ __($navigationLabel) }} diff --git a/src/Console/Commands/PostComposerUpdate.php b/src/Console/Commands/PostComposerUpdate.php index 4e55529..a0e322b 100644 --- a/src/Console/Commands/PostComposerUpdate.php +++ b/src/Console/Commands/PostComposerUpdate.php @@ -31,7 +31,6 @@ public function handle(): void // ------------------ $this->call('vendor:publish', ['--tag' => 'laravel-assets', '--force' => true]); $this->call('log-viewer:publish'); - $this->call('themes:upgrade'); // ------------------ diff --git a/src/EclipseServiceProvider.php b/src/EclipseServiceProvider.php index 2fd2e29..13a2f18 100644 --- a/src/EclipseServiceProvider.php +++ b/src/EclipseServiceProvider.php @@ -2,13 +2,18 @@ namespace Eclipse\Core; -use BezhanSalleh\FilamentLanguageSwitch\LanguageSwitch; +use BezhanSalleh\FilamentShield\Resources\Roles\RoleResource; +use BezhanSalleh\LanguageSwitch\LanguageSwitch; use Eclipse\Common\Foundation\Providers\PackageServiceProvider; use Eclipse\Common\Package; use Eclipse\Core\Console\Commands\ClearCommand; use Eclipse\Core\Console\Commands\DeployCommand; use Eclipse\Core\Console\Commands\PostComposerUpdate; use Eclipse\Core\Console\Commands\SetupReverb; +use Eclipse\Core\Filament\Resources\LocaleResource; +use Eclipse\Core\Filament\Resources\MailLogResource; +use Eclipse\Core\Filament\Resources\SiteResource; +use Eclipse\Core\Filament\Resources\UserResource; use Eclipse\Core\Health\Checks\ReverbCheck; use Eclipse\Core\Listeners\LogEmailToDatabase; use Eclipse\Core\Listeners\SendEmailSuccessNotification; @@ -69,7 +74,6 @@ public function configurePackage(SpatiePackage|Package $package): void 'permission', 'settings', 'telescope', - 'themes', 'health', ]) ->hasViews() @@ -119,6 +123,59 @@ public function boot(): void { parent::boot(); + // Merge per-resource abilities into the effective config + $this->app->booted(function () { + $manage = config('filament-shield.resources.manage', []); + + $pluginManage = [ + + RoleResource::class => [ + 'viewAny', + 'view', + 'create', + 'update', + 'delete', + ], + MailLogResource::class => [ + 'viewAny', + 'view', + ], + LocaleResource::class => [ + 'viewAny', + 'create', + 'update', + 'delete', + 'deleteAny', + ], + SiteResource::class => [ + 'viewAny', + 'create', + 'update', + 'delete', + 'deleteAny', + ], + UserResource::class => [ + 'viewAny', + 'view', + 'create', + 'update', + 'delete', + 'deleteAny', + 'restore', + 'restoreAny', + 'forceDelete', + 'forceDeleteAny', + 'impersonate', + 'sendEmail', + ], + ]; + + config()->set('filament-shield.resources.manage', array_replace_recursive( + $manage, + $pluginManage, + )); + }); + // For unit tests... if (app()->runningUnitTests()) { // Set the correct user model in auth config diff --git a/src/Filament/Actions/SendEmailAction.php b/src/Filament/Actions/SendEmailAction.php index ab95bb0..8b5fe66 100644 --- a/src/Filament/Actions/SendEmailAction.php +++ b/src/Filament/Actions/SendEmailAction.php @@ -2,18 +2,21 @@ namespace Eclipse\Core\Filament\Actions; +use Closure; use Eclipse\Core\Mail\SendEmailToUser; use Eclipse\Core\Models\User; +use Exception; use Filament\Actions\Action; -use Filament\Forms\Components\Grid; use Filament\Forms\Components\Hidden; use Filament\Forms\Components\RichEditor; -use Filament\Forms\Components\Section; use Filament\Forms\Components\TagsInput; use Filament\Forms\Components\TextInput; use Filament\Notifications\Notification; +use Filament\Schemas\Components\Grid; +use Filament\Schemas\Components\Section; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\ValidationException; class SendEmailAction { @@ -96,7 +99,7 @@ protected static function getEmailFormSchema(): array ]; } - protected static function getEmailActionClosure(): \Closure + protected static function getEmailActionClosure(): Closure { return function (array $data) { try { @@ -130,7 +133,7 @@ protected static function getEmailActionClosure(): \Closure ->success() ->sendToDatabase(auth()->user()) ->broadcast([auth()->user()]); - } catch (\Illuminate\Validation\ValidationException $e) { + } catch (ValidationException $e) { $errors = collect($e->errors())->flatten()->implode(' '); Notification::make() @@ -139,7 +142,7 @@ protected static function getEmailActionClosure(): \Closure ->danger() ->sendToDatabase(auth()->user()) ->broadcast([auth()->user()]); - } catch (\Exception $e) { + } catch (Exception $e) { Notification::make() ->title(__('eclipse::email.error')) ->body(__('eclipse::email.send_error_message', ['error' => $e->getMessage()])) diff --git a/src/Filament/Actions/SendEmailTableAction.php b/src/Filament/Actions/SendEmailTableAction.php index fac0170..619dac4 100644 --- a/src/Filament/Actions/SendEmailTableAction.php +++ b/src/Filament/Actions/SendEmailTableAction.php @@ -3,7 +3,7 @@ namespace Eclipse\Core\Filament\Actions; use Eclipse\Core\Models\User; -use Filament\Tables\Actions\Action; +use Filament\Actions\Action; class SendEmailTableAction extends SendEmailAction { diff --git a/src/Filament/Pages/EditProfile.php b/src/Filament/Pages/EditProfile.php index 9658b02..ec559bc 100644 --- a/src/Filament/Pages/EditProfile.php +++ b/src/Filament/Pages/EditProfile.php @@ -3,15 +3,14 @@ namespace Eclipse\Core\Filament\Pages; use Eclipse\Core\Filament\Resources\UserResource; -use Filament\Forms\Form; -use Filament\Pages\Auth\EditProfile as BaseEditProfile; +use Filament\Schemas\Schema; -class EditProfile extends BaseEditProfile +class EditProfile extends \Filament\Auth\Pages\EditProfile { - public function form(Form $form): Form + public function form(Schema $schema): Schema { - return $form - ->schema([ + return $schema + ->components([ UserResource::getFirstNameFormComponent(), UserResource::getLastNameFormComponent(), UserResource::getEmailFormComponent(), diff --git a/src/Filament/Pages/ManageEclipse.php b/src/Filament/Pages/ManageEclipse.php index 417795d..2c5abf7 100644 --- a/src/Filament/Pages/ManageEclipse.php +++ b/src/Filament/Pages/ManageEclipse.php @@ -5,26 +5,26 @@ use BezhanSalleh\FilamentShield\Traits\HasPageShield; use Eclipse\Common\CommonPlugin; use Eclipse\Core\Settings\EclipseSettings; -use Filament\Forms; -use Filament\Forms\Form; +use Filament\Forms\Components\Toggle; use Filament\Pages\SettingsPage; +use Filament\Schemas\Schema; use Illuminate\Contracts\Support\Htmlable; class ManageEclipse extends SettingsPage { use HasPageShield; - protected static ?string $navigationIcon = 'heroicon-o-cog-6-tooth'; + protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-cog-6-tooth'; protected static string $settings = EclipseSettings::class; - public function form(Form $form): Form + public function form(Schema $schema): Schema { - return $form - ->schema([ - Forms\Components\Toggle::make('email_verification') + return $schema + ->components([ + Toggle::make('email_verification') ->label('Enable user email verification'), - Forms\Components\Toggle::make('address_book') + Toggle::make('address_book') ->label('Enable address book'), ]); } diff --git a/src/Filament/Pages/ManageUserSettings.php b/src/Filament/Pages/ManageUserSettings.php index 7bd059d..53641bc 100644 --- a/src/Filament/Pages/ManageUserSettings.php +++ b/src/Filament/Pages/ManageUserSettings.php @@ -3,30 +3,33 @@ namespace Eclipse\Core\Filament\Pages; use Eclipse\Core\Settings\UserSettings; -use Filament\Forms\Components; -use Filament\Forms\Form; +use Filament\Forms\Components\RichEditor; +use Filament\Forms\Components\TextInput; use Filament\Pages\SettingsPage; +use Filament\Schemas\Components\Section; +use Filament\Schemas\Schema; class ManageUserSettings extends SettingsPage { - protected static ?string $navigationIcon = 'heroicon-o-cog-6-tooth'; + protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-cog-6-tooth'; protected static string $settings = UserSettings::class; protected static bool $shouldRegisterNavigation = false; - public function form(Form $form): Form + public function form(Schema $schema): Schema { - return $form - ->schema([ - Components\Section::make('Email settings') + return $schema + ->components([ + Section::make('Email settings') ->schema([ - Components\TextInput::make('outgoing_email_address') + TextInput::make('outgoing_email_address') ->email() ->label('Outgoing email address'), - Components\RichEditor::make('outgoing_email_signature') + RichEditor::make('outgoing_email_signature') ->label('Outgoing email signature'), - ]), + ]) + ->columnSpanFull(), ]); } diff --git a/src/Filament/Resources/LocaleResource.php b/src/Filament/Resources/LocaleResource.php index ef1140c..ad30a5a 100644 --- a/src/Filament/Resources/LocaleResource.php +++ b/src/Filament/Resources/LocaleResource.php @@ -2,29 +2,34 @@ namespace Eclipse\Core\Filament\Resources; -use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions; use Eclipse\Common\Foundation\Models\Scopes\ActiveScope; -use Eclipse\Core\Filament\Resources; +use Eclipse\Core\Filament\Resources\LocaleResource\Pages\CreateLocale; +use Eclipse\Core\Filament\Resources\LocaleResource\Pages\EditLocale; +use Eclipse\Core\Filament\Resources\LocaleResource\Pages\ListLocales; use Eclipse\Core\Models\Locale; -use Filament\Forms\Components\Section; +use Filament\Actions\ActionGroup; +use Filament\Actions\BulkActionGroup; +use Filament\Actions\DeleteAction; +use Filament\Actions\DeleteBulkAction; +use Filament\Actions\EditAction; use Filament\Forms\Components\Select; use Filament\Forms\Components\TextInput; -use Filament\Forms\Form; use Filament\Resources\Resource; -use Filament\Tables; -use Filament\Tables\Actions\ActionGroup; -use Filament\Tables\Actions\DeleteAction; +use Filament\Schemas\Components\Section; +use Filament\Schemas\Schema; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Columns\ToggleColumn; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\HtmlString; -class LocaleResource extends Resource implements HasShieldPermissions +class LocaleResource extends Resource { protected static ?string $model = Locale::class; - protected static ?string $navigationIcon = 'heroicon-o-language'; + protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-language'; - public static function form(Form $form): Form + public static function form(Schema $schema): Schema { $basic = [ TextInput::make('id') @@ -63,7 +68,7 @@ public static function form(Form $form): Form ->label(__('eclipse::locale.system_locale')); } - return $form->schema([ + return $schema->components([ Section::make(__('eclipse::locale.sections.basic')) ->schema($basic), Section::make(__('eclipse::locale.sections.datetime_formats')) @@ -91,36 +96,36 @@ public static function table(Table $table): Table { return $table ->columns([ - Tables\Columns\TextColumn::make('id') + TextColumn::make('id') ->label('ID') ->searchable() ->sortable(), - Tables\Columns\TextColumn::make('name') + TextColumn::make('name') ->label(__('eclipse::locale.name')) ->searchable() ->sortable(), - Tables\Columns\TextColumn::make('native_name') + TextColumn::make('native_name') ->label(__('eclipse::locale.native_name')) ->searchable() ->sortable(), - Tables\Columns\TextColumn::make('system_locale') + TextColumn::make('system_locale') ->label(__('eclipse::locale.system_locale')) ->sortable() ->formatStateUsing(fn (?string $state = null): HtmlString => new HtmlString("$state")), - Tables\Columns\TextColumn::make('datetime_format') + TextColumn::make('datetime_format') ->formatStateUsing(fn (?string $state = null): HtmlString => new HtmlString("$state")) ->label(__('eclipse::locale.datetime_format')), - Tables\Columns\TextColumn::make('date_format') + TextColumn::make('date_format') ->formatStateUsing(fn (?string $state = null): HtmlString => new HtmlString("$state")) ->label(__('eclipse::locale.date_format')), - Tables\Columns\TextColumn::make('time_format') + TextColumn::make('time_format') ->formatStateUsing(fn (?string $state = null): HtmlString => new HtmlString("$state")) ->label(__('eclipse::locale.time_format')), - Tables\Columns\ToggleColumn::make('is_active') + ToggleColumn::make('is_active') ->label(__('eclipse::locale.is_active')) ->sortable() ->disabled(fn (Locale $record): bool => ! auth()->user()->can('update', $record)), - Tables\Columns\ToggleColumn::make('is_available_in_panel') + ToggleColumn::make('is_available_in_panel') ->label(__('eclipse::locale.is_available_in_panel')) ->disabled(function (Locale $record): bool { return ! $record->is_active or ! auth()->user()->can('update', $record); @@ -129,17 +134,17 @@ public static function table(Table $table): Table ->filters([ // ]) - ->actions([ - Tables\Actions\EditAction::make()->label(__('eclipse::locale.actions.edit.label')), + ->recordActions([ + EditAction::make()->label(__('eclipse::locale.actions.edit.label')), ActionGroup::make([ DeleteAction::make() ->label(__('eclipse-world::countries.actions.delete.label')) ->modalHeading(__('eclipse-world::countries.actions.delete.heading')), ]), ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), ]), ]); } @@ -154,9 +159,9 @@ public static function getRelations(): array public static function getPages(): array { return [ - 'index' => Resources\LocaleResource\Pages\ListLocales::route('/'), - 'create' => Resources\LocaleResource\Pages\CreateLocale::route('/create'), - 'edit' => Resources\LocaleResource\Pages\EditLocale::route('/{record}/edit'), + 'index' => ListLocales::route('/'), + 'create' => CreateLocale::route('/create'), + 'edit' => EditLocale::route('/{record}/edit'), ]; } @@ -182,15 +187,4 @@ public static function getEloquentQuery(): Builder ActiveScope::class, ]); } - - public static function getPermissionPrefixes(): array - { - return [ - 'view_any', - 'create', - 'update', - 'delete', - 'delete_any', - ]; - } } diff --git a/src/Filament/Resources/LocaleResource/Pages/EditLocale.php b/src/Filament/Resources/LocaleResource/Pages/EditLocale.php index 5feb9ad..9022922 100644 --- a/src/Filament/Resources/LocaleResource/Pages/EditLocale.php +++ b/src/Filament/Resources/LocaleResource/Pages/EditLocale.php @@ -3,7 +3,7 @@ namespace Eclipse\Core\Filament\Resources\LocaleResource\Pages; use Eclipse\Core\Filament\Resources\LocaleResource; -use Filament\Actions; +use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; use Illuminate\Contracts\Support\Htmlable; @@ -19,7 +19,7 @@ public function getHeading(): string|Htmlable protected function getHeaderActions(): array { return [ - Actions\DeleteAction::make() + DeleteAction::make() ->modalHeading(__('eclipse::locale.actions.delete.heading')), ]; } diff --git a/src/Filament/Resources/LocaleResource/Pages/ListLocales.php b/src/Filament/Resources/LocaleResource/Pages/ListLocales.php index c61e801..f3e3368 100644 --- a/src/Filament/Resources/LocaleResource/Pages/ListLocales.php +++ b/src/Filament/Resources/LocaleResource/Pages/ListLocales.php @@ -3,20 +3,20 @@ namespace Eclipse\Core\Filament\Resources\LocaleResource\Pages; use Eclipse\Core\Filament\Resources\LocaleResource; -use Filament\Actions; +use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use Filament\Support\Enums\MaxWidth; +use Filament\Support\Enums\Width; class ListLocales extends ListRecords { protected static string $resource = LocaleResource::class; - protected ?string $maxContentWidth = MaxWidth::Full->value; + protected Width|string|null $maxContentWidth = Width::Full->value; protected function getHeaderActions(): array { return [ - Actions\CreateAction::make()->label(__('eclipse::locale.actions.create.label')), + CreateAction::make()->label(__('eclipse::locale.actions.create.label')), ]; } } diff --git a/src/Filament/Resources/MailLogResource.php b/src/Filament/Resources/MailLogResource.php index afafc13..6a279ba 100644 --- a/src/Filament/Resources/MailLogResource.php +++ b/src/Filament/Resources/MailLogResource.php @@ -2,24 +2,33 @@ namespace Eclipse\Core\Filament\Resources; -use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions; +use Eclipse\Core\Filament\Resources\MailLogResource\Pages\ListMailLogs; use Eclipse\Core\Models\MailLog; use Eclipse\Core\Services\Registry; -use Filament\Forms; -use Filament\Forms\Form; +use Filament\Actions\ViewAction; +use Filament\Forms\Components\DateTimePicker; +use Filament\Forms\Components\KeyValue; +use Filament\Forms\Components\Placeholder; +use Filament\Forms\Components\Textarea; +use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; -use Filament\Tables; +use Filament\Schemas\Components\Section; +use Filament\Schemas\Schema; +use Filament\Tables\Columns\IconColumn; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Filters\Filter; +use Filament\Tables\Filters\SelectFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\HtmlString; -class MailLogResource extends Resource implements HasShieldPermissions +class MailLogResource extends Resource { protected static ?string $model = MailLog::class; - protected static ?string $navigationIcon = 'heroicon-o-envelope'; + protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-envelope'; - protected static ?string $navigationGroup = 'Tools'; + protected static string|\UnitEnum|null $navigationGroup = 'Tools'; protected static ?int $navigationSort = 2; @@ -38,50 +47,50 @@ public static function getPluralModelLabel(): string return __('eclipse::email.outbox'); } - public static function form(Form $form): Form + public static function form(Schema $schema): Schema { - return $form->schema([ - Forms\Components\Section::make(__('eclipse::email.email_details')) + return $schema->components([ + Section::make(__('eclipse::email.email_details')) ->schema([ - Forms\Components\TextInput::make('from') + TextInput::make('from') ->label(__('eclipse::email.from')) ->disabled(), - Forms\Components\TextInput::make('to') + TextInput::make('to') ->label(__('eclipse::email.to')) ->disabled(), - Forms\Components\TextInput::make('cc') + TextInput::make('cc') ->label(__('eclipse::email.cc')) ->disabled(), - Forms\Components\TextInput::make('bcc') + TextInput::make('bcc') ->label(__('eclipse::email.bcc')) ->disabled(), - Forms\Components\TextInput::make('subject') + TextInput::make('subject') ->label(__('eclipse::email.subject')) ->disabled(), - Forms\Components\Textarea::make('body') + Textarea::make('body') ->label(__('eclipse::email.email_body')) ->rows(10) ->disabled(), ]) ->columns(2), - Forms\Components\Section::make(__('eclipse::email.metadata')) + Section::make(__('eclipse::email.metadata')) ->schema([ - Forms\Components\TextInput::make('status') + TextInput::make('status') ->label(__('eclipse::email.status')) ->disabled(), - Forms\Components\DateTimePicker::make('sent_at') + DateTimePicker::make('sent_at') ->label(__('eclipse::email.sent_at')) ->disabled(), - Forms\Components\TextInput::make('message_id') + TextInput::make('message_id') ->label(__('eclipse::email.message_id')) ->disabled(), ]) ->columns(3), - Forms\Components\Section::make(__('eclipse::email.headers')) + Section::make(__('eclipse::email.headers')) ->schema([ - Forms\Components\KeyValue::make('headers') + KeyValue::make('headers') ->label('') ->disabled(), ]), @@ -92,50 +101,50 @@ public static function table(Table $table): Table { return $table ->columns([ - Tables\Columns\TextColumn::make('id') + TextColumn::make('id') ->label('ID') ->sortable() ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('message_id') + TextColumn::make('message_id') ->label(__('eclipse::email.message_id')) ->limit(20) ->tooltip(fn (MailLog $record): ?string => $record->message_id) ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('sent_at') + TextColumn::make('sent_at') ->label(__('eclipse::email.sent_at')) ->dateTime() ->sortable(), - Tables\Columns\TextColumn::make('from') + TextColumn::make('from') ->label(__('eclipse::email.from')) ->limit(30) ->tooltip(fn (MailLog $record): ?string => $record->from), - Tables\Columns\TextColumn::make('to') + TextColumn::make('to') ->label(__('eclipse::email.to')) ->limit(30) ->tooltip(fn (MailLog $record): ?string => $record->to), - Tables\Columns\TextColumn::make('cc') + TextColumn::make('cc') ->label(__('eclipse::email.cc')) ->limit(30) ->tooltip(fn (MailLog $record): ?string => $record->cc) ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('bcc') + TextColumn::make('bcc') ->label(__('eclipse::email.bcc')) ->limit(30) ->tooltip(fn (MailLog $record): ?string => $record->bcc) ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('subject') + TextColumn::make('subject') ->label(__('eclipse::email.subject')) ->limit(50) ->tooltip(fn (MailLog $record): ?string => $record->subject), - Tables\Columns\TextColumn::make('status') + TextColumn::make('status') ->label(__('eclipse::email.status')) ->badge() ->color(fn (string $state): string => match ($state) { @@ -145,60 +154,60 @@ public static function table(Table $table): Table default => 'gray', }), - Tables\Columns\TextColumn::make('sender.name') + TextColumn::make('sender.name') ->label('Sender') ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('recipient.name') + TextColumn::make('recipient.name') ->label('Recipient') ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\IconColumn::make('opened') + IconColumn::make('opened') ->label('Opened') ->boolean() ->trueIcon('heroicon-o-eye') ->falseIcon('heroicon-o-eye-slash') ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\IconColumn::make('delivered') + IconColumn::make('delivered') ->label('Delivered') ->boolean() ->trueIcon('heroicon-o-check-circle') ->falseIcon('heroicon-o-x-circle') ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\IconColumn::make('complaint') + IconColumn::make('complaint') ->label('Complaint') ->boolean() ->trueIcon('heroicon-o-exclamation-triangle') ->falseIcon('heroicon-o-check') ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\IconColumn::make('bounced') + IconColumn::make('bounced') ->label('Bounced') ->boolean() ->trueIcon('heroicon-o-arrow-uturn-left') ->falseIcon('heroicon-o-check') ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('site.name') + TextColumn::make('site.name') ->label('Site') ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('created_at') + TextColumn::make('created_at') ->label('Created At') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('updated_at') + TextColumn::make('updated_at') ->label('Updated At') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ - Tables\Filters\SelectFilter::make('status') + SelectFilter::make('status') ->label(__('eclipse::email.status')) ->options([ 'sent' => __('eclipse::email.sent'), @@ -206,21 +215,21 @@ public static function table(Table $table): Table 'failed' => __('eclipse::email.failed'), ]), - Tables\Filters\Filter::make('sent_today') + Filter::make('sent_today') ->label(__('eclipse::email.sent_today')) ->query(fn (Builder $query): Builder => $query->whereDate('sent_at', today())), - Tables\Filters\Filter::make('sent_this_week') + Filter::make('sent_this_week') ->label(__('eclipse::email.sent_this_week')) ->query(fn (Builder $query): Builder => $query->whereBetween('sent_at', [now()->startOfWeek(), now()->endOfWeek()])), ]) - ->actions([ - Tables\Actions\ViewAction::make() + ->recordActions([ + ViewAction::make() ->label(__('eclipse::email.view_email')) ->modalHeading(fn (MailLog $record): string => __('eclipse::email.view_email').': '.$record->subject) ->modalWidth('7xl') - ->form([ - Forms\Components\Placeholder::make('email_preview') + ->schema([ + Placeholder::make('email_preview') ->label('') ->content(fn (MailLog $record) => static::buildEmailPreview($record)) ->columnSpanFull(), @@ -267,7 +276,7 @@ protected static function buildEmailPreview(MailLog $record): HtmlString public static function getPages(): array { return [ - 'index' => MailLogResource\Pages\ListMailLogs::route('/'), + 'index' => ListMailLogs::route('/'), ]; } @@ -281,12 +290,4 @@ public static function getEloquentQuery(): Builder return $query; } - - public static function getPermissionPrefixes(): array - { - return [ - 'view_any', - 'view', - ]; - } } diff --git a/src/Filament/Resources/MailLogResource/Pages/ListMailLogs.php b/src/Filament/Resources/MailLogResource/Pages/ListMailLogs.php index f272ee9..d1dfe13 100644 --- a/src/Filament/Resources/MailLogResource/Pages/ListMailLogs.php +++ b/src/Filament/Resources/MailLogResource/Pages/ListMailLogs.php @@ -4,13 +4,13 @@ use Eclipse\Core\Filament\Resources\MailLogResource; use Filament\Resources\Pages\ListRecords; -use Filament\Support\Enums\MaxWidth; +use Filament\Support\Enums\Width; class ListMailLogs extends ListRecords { protected static string $resource = MailLogResource::class; - protected ?string $maxContentWidth = MaxWidth::Full->value; + protected Width|string|null $maxContentWidth = Width::Full->value; protected function getHeaderActions(): array { diff --git a/src/Filament/Resources/SiteResource.php b/src/Filament/Resources/SiteResource.php index c8f42a0..cde7022 100644 --- a/src/Filament/Resources/SiteResource.php +++ b/src/Filament/Resources/SiteResource.php @@ -2,26 +2,31 @@ namespace Eclipse\Core\Filament\Resources; -use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions; -use Eclipse\Core\Filament\Resources; +use Eclipse\Core\Filament\Resources\SiteResource\Pages\CreateSite; +use Eclipse\Core\Filament\Resources\SiteResource\Pages\EditSite; +use Eclipse\Core\Filament\Resources\SiteResource\Pages\ListSites; use Eclipse\Core\Models\Site; -use Filament\Forms; +use Filament\Actions\BulkActionGroup; +use Filament\Actions\DeleteBulkAction; +use Filament\Actions\EditAction; use Filament\Forms\Components\TextInput; -use Filament\Forms\Form; +use Filament\Forms\Components\Toggle; use Filament\Resources\Resource; -use Filament\Tables; +use Filament\Schemas\Schema; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Columns\ToggleColumn; use Filament\Tables\Table; -class SiteResource extends Resource implements HasShieldPermissions +class SiteResource extends Resource { protected static ?string $model = Site::class; - protected static ?string $navigationIcon = 'heroicon-s-globe-alt'; + protected static string|\BackedEnum|null $navigationIcon = 'heroicon-s-globe-alt'; - public static function form(Form $form): Form + public static function form(Schema $schema): Schema { - return $form - ->schema([ + return $schema + ->components([ TextInput::make('domain') ->label('Domain') ->required() @@ -30,9 +35,9 @@ public static function form(Form $form): Form ->label('Site name') ->required() ->maxLength(255), - Forms\Components\Toggle::make('is_active') + Toggle::make('is_active') ->label('Is active'), - Forms\Components\Toggle::make('is_secure') + Toggle::make('is_secure') ->label('Secure site') ->helperText('Use HTTPS for the site by default.'), ]); @@ -42,35 +47,35 @@ public static function table(Table $table): Table { return $table ->columns([ - Tables\Columns\TextColumn::make('id') + TextColumn::make('id') ->label('ID') ->searchable() ->sortable() ->width(50), - Tables\Columns\TextColumn::make('name') + TextColumn::make('name') ->label('Site name') ->searchable() ->sortable(), - Tables\Columns\TextColumn::make('domain') + TextColumn::make('domain') ->label('Domain') ->searchable() ->sortable(), - Tables\Columns\ToggleColumn::make('is_active') + ToggleColumn::make('is_active') ->label('Is active') ->width(100), - Tables\Columns\ToggleColumn::make('is_secure') + ToggleColumn::make('is_secure') ->label('Secure site') ->width(100), ]) ->filters([ // ]) - ->actions([ - Tables\Actions\EditAction::make(), + ->recordActions([ + EditAction::make(), ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), ]), ]); } @@ -85,9 +90,9 @@ public static function getRelations(): array public static function getPages(): array { return [ - 'index' => Resources\SiteResource\Pages\ListSites::route('/'), - 'create' => Resources\SiteResource\Pages\CreateSite::route('/create'), - 'edit' => Resources\SiteResource\Pages\EditSite::route('/{record}/edit'), + 'index' => ListSites::route('/'), + 'create' => CreateSite::route('/create'), + 'edit' => EditSite::route('/{record}/edit'), ]; } @@ -96,17 +101,6 @@ public static function getNavigationGroup(): ?string return __('eclipse-common::nav.configuration'); } - public static function getPermissionPrefixes(): array - { - return [ - 'view_any', - 'create', - 'update', - 'delete', - 'delete_any', - ]; - } - public static function shouldRegisterNavigation(): bool { return config('eclipse.multi_site'); diff --git a/src/Filament/Resources/SiteResource/Pages/EditSite.php b/src/Filament/Resources/SiteResource/Pages/EditSite.php index b9b4233..4a9c801 100644 --- a/src/Filament/Resources/SiteResource/Pages/EditSite.php +++ b/src/Filament/Resources/SiteResource/Pages/EditSite.php @@ -3,7 +3,7 @@ namespace Eclipse\Core\Filament\Resources\SiteResource\Pages; use Eclipse\Core\Filament\Resources\SiteResource; -use Filament\Actions; +use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; class EditSite extends EditRecord @@ -13,7 +13,7 @@ class EditSite extends EditRecord protected function getHeaderActions(): array { return [ - Actions\DeleteAction::make(), + DeleteAction::make(), ]; } } diff --git a/src/Filament/Resources/SiteResource/Pages/ListSites.php b/src/Filament/Resources/SiteResource/Pages/ListSites.php index 5fef80c..4d603d5 100644 --- a/src/Filament/Resources/SiteResource/Pages/ListSites.php +++ b/src/Filament/Resources/SiteResource/Pages/ListSites.php @@ -3,20 +3,20 @@ namespace Eclipse\Core\Filament\Resources\SiteResource\Pages; use Eclipse\Core\Filament\Resources\SiteResource; -use Filament\Actions; +use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use Filament\Support\Enums\MaxWidth; +use Filament\Support\Enums\Width; class ListSites extends ListRecords { protected static string $resource = SiteResource::class; - protected ?string $maxContentWidth = MaxWidth::Full->value; + protected Width|string|null $maxContentWidth = Width::Full->value; protected function getHeaderActions(): array { return [ - Actions\CreateAction::make(), + CreateAction::make(), ]; } } diff --git a/src/Filament/Resources/UserResource.php b/src/Filament/Resources/UserResource.php index 4d38fd9..8aa9206 100644 --- a/src/Filament/Resources/UserResource.php +++ b/src/Filament/Resources/UserResource.php @@ -2,49 +2,65 @@ namespace Eclipse\Core\Filament\Resources; -use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions; use Eclipse\Core\Filament\Actions\SendEmailTableAction; use Eclipse\Core\Filament\Exports\TableExport; -use Eclipse\Core\Filament\Resources; +use Eclipse\Core\Filament\Resources\UserResource\Pages\CreateUser; +use Eclipse\Core\Filament\Resources\UserResource\Pages\EditUser; +use Eclipse\Core\Filament\Resources\UserResource\Pages\ListUsers; +use Eclipse\Core\Filament\Resources\UserResource\Pages\ViewUser; use Eclipse\Core\Filament\Resources\UserResource\RelationManagers\AddressesRelationManager; use Eclipse\Core\Models\User; -use Filament\Forms; -use Filament\Forms\Components\Actions\Action; -use Filament\Forms\Form; -use Filament\Forms\Set; -use Filament\Infolists\Components\Group; -use Filament\Infolists\Components\Section; +use Filament\Actions\Action; +use Filament\Actions\ActionGroup; +use Filament\Actions\BulkActionGroup; +use Filament\Actions\DeleteAction; +use Filament\Actions\DeleteBulkAction; +use Filament\Actions\EditAction; +use Filament\Actions\RestoreAction; +use Filament\Actions\ViewAction; +use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\DateTimePicker; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\SpatieMediaLibraryFileUpload; +use Filament\Forms\Components\TextInput; use Filament\Infolists\Components\SpatieMediaLibraryImageEntry; use Filament\Infolists\Components\TextEntry; -use Filament\Infolists\Infolist; use Filament\Notifications\Notification; use Filament\Resources\Resource; +use Filament\Schemas\Components\Group; +use Filament\Schemas\Components\Section; +use Filament\Schemas\Components\Utilities\Set; +use Filament\Schemas\Schema; use Filament\Support\Colors\Color; -use Filament\Tables; use Filament\Tables\Columns\SpatieMediaLibraryImageColumn; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Filters\QueryBuilder; use Filament\Tables\Filters\QueryBuilder\Constraints\TextConstraint; +use Filament\Tables\Filters\SelectFilter; +use Filament\Tables\Filters\TernaryFilter; +use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; use pxlrbt\FilamentExcel\Actions\Tables\ExportBulkAction; -use STS\FilamentImpersonate\Tables\Actions\Impersonate; +use STS\FilamentImpersonate\Actions\Impersonate; -class UserResource extends Resource implements HasShieldPermissions +class UserResource extends Resource { protected static ?string $model = User::class; - protected static ?string $navigationGroup = 'Users'; + protected static string|\UnitEnum|null $navigationGroup = 'Users'; - protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack'; + protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-rectangle-stack'; protected static ?string $recordTitleAttribute = 'name'; - public static function form(Form $form): Form + public static function form(Schema $schema): Schema { - return $form->schema([ - Forms\Components\SpatieMediaLibraryFileUpload::make('avatar') + return $schema->components([ + SpatieMediaLibraryFileUpload::make('avatar') ->collection('avatars') ->avatar() ->imageEditor() @@ -52,13 +68,13 @@ public static function form(Form $form): Form self::getFirstNameFormComponent(), self::getLastNameFormComponent(), self::getEmailFormComponent(), - Forms\Components\TextInput::make('phone_number') + TextInput::make('phone_number') ->label('Phone') ->tel(), - Forms\Components\DateTimePicker::make('email_verified_at') + DateTimePicker::make('email_verified_at') ->visible(config('eclipse.email_verification')) ->disabled(), - Forms\Components\TextInput::make('password') + TextInput::make('password') ->password() ->revealable() ->dehydrateStateUsing(fn ($state) => Hash::make($state)) @@ -74,16 +90,16 @@ public static function form(Form $form): Form fn (Set $set) => $set('password', Str::password(16)) ) ), - Forms\Components\Select::make('country_id') + Select::make('country_id') ->relationship('country', 'name') ->preload() ->optionsLimit(20) ->searchable(), - Forms\Components\DatePicker::make('date_of_birth') + DatePicker::make('date_of_birth') ->native(false) ->minDate(now()->subYears(80)) ->maxDate(now()), - Forms\Components\Select::make('roles') + Select::make('roles') ->relationship('roles', 'name') ->saveRelationshipsUsing(function (User $record, $state) { $record->roles()->syncWithPivotValues($state, [config('permission.column_names.team_foreign_key') => getPermissionsTeamId()]); @@ -103,25 +119,25 @@ public static function table(Table $table): Table ->size(50) ->circular() ->defaultImageUrl(fn (User $user) => 'https://ui-avatars.com/api/?name='.urlencode($user->name)), - Tables\Columns\TextColumn::make('first_name') + TextColumn::make('first_name') ->searchable() ->sortable() ->toggleable(), - Tables\Columns\TextColumn::make('last_name') + TextColumn::make('last_name') ->searchable() ->sortable() ->toggleable(), - Tables\Columns\TextColumn::make('name') + TextColumn::make('name') ->label('Full name') ->searchable() ->sortable() ->toggleable(), - Tables\Columns\TextColumn::make('last_login_at') + TextColumn::make('last_login_at') ->label('Last login') ->dateTime() ->sortable() ->toggleable(), - Tables\Columns\TextColumn::make('login_count') + TextColumn::make('login_count') ->label('Total Logins') ->sortable() ->numeric() @@ -129,7 +145,7 @@ public static function table(Table $table): Table ]; if (config('eclipse.email_verification')) { - $columns[] = Tables\Columns\TextColumn::make('email') + $columns[] = TextColumn::make('email') ->searchable() ->sortable() ->width(150) @@ -137,16 +153,16 @@ public static function table(Table $table): Table ->iconColor(fn (User $user) => $user->email_verified_at ? Color::Green : Color::Red) ->tooltip(fn (User $user) => $user->email_verified_at ? 'Verified' : 'Not verified'); } else { - $columns[] = Tables\Columns\TextColumn::make('email') + $columns[] = TextColumn::make('email') ->searchable() ->sortable() ->width(150); } - $columns[] = Tables\Columns\TextColumn::make('phone_number') + $columns[] = TextColumn::make('phone_number') ->label('Phone'); - $columns[] = Tables\Columns\TextColumn::make('email_verified_at') + $columns[] = TextColumn::make('email_verified_at') ->label('Verified email') ->placeholder('Not verified') ->dateTime() @@ -155,26 +171,26 @@ public static function table(Table $table): Table ->visible(config('eclipse.email_verification')) ->width(150); - $columns[] = Tables\Columns\TextColumn::make('country.name') + $columns[] = TextColumn::make('country.name') ->badge(); - $columns[] = Tables\Columns\TextColumn::make('date_of_birth') + $columns[] = TextColumn::make('date_of_birth') ->date('M d, Y'); - $columns[] = Tables\Columns\TextColumn::make('created_at') + $columns[] = TextColumn::make('created_at') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true) ->width(150); - $columns[] = Tables\Columns\TextColumn::make('updated_at') + $columns[] = TextColumn::make('updated_at') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true) ->width(150); $filters = [ - Tables\Filters\TernaryFilter::make('email_verified_at') + TernaryFilter::make('email_verified_at') ->label('Email verification') ->nullable() ->placeholder('All users') @@ -186,13 +202,13 @@ public static function table(Table $table): Table blank: fn (Builder $query) => $query, ) ->visible(config('eclipse.email_verification')), - Tables\Filters\SelectFilter::make('country_id') + SelectFilter::make('country_id') ->label('Country') ->multiple() ->relationship('country', 'name', fn (Builder $query): Builder => $query->distinct()) ->preload() ->optionsLimit(20), - Tables\Filters\QueryBuilder::make() + QueryBuilder::make() ->constraints([ TextConstraint::make('first_name') ->label('First name'), @@ -205,31 +221,31 @@ public static function table(Table $table): Table TextConstraint::make('login_count') ->label('Total Logins'), ]), - Tables\Filters\TrashedFilter::make(), + TrashedFilter::make(), ]; return $table ->columns($columns) ->filters($filters) - ->actions([ - Tables\Actions\ActionGroup::make([ - Tables\Actions\ViewAction::make(), - Tables\Actions\EditAction::make(), + ->recordActions([ + ActionGroup::make([ + ViewAction::make(), + EditAction::make(), SendEmailTableAction::makeAction(), Impersonate::make() ->grouped() ->redirectTo(route('filament.admin.tenant')), - Tables\Actions\DeleteAction::make() + DeleteAction::make() ->authorize(fn (User $record) => auth()->user()->can('delete_user') && auth()->id() !== $record->id) ->requiresConfirmation(), - Tables\Actions\RestoreAction::make() + RestoreAction::make() ->visible(fn (User $user) => $user->trashed() && auth()->user()->can('restore_user')) ->requiresConfirmation(), ]), ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make()->before(function ($records, Tables\Actions\DeleteBulkAction $action) { + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make()->before(function ($records, DeleteBulkAction $action) { $user_id = auth()->id(); $ids = $records->pluck('id')->toArray(); if (in_array($user_id, $ids)) { @@ -249,9 +265,9 @@ public static function table(Table $table): Table ]); } - public static function infolist(Infolist $infolist): Infolist + public static function infolist(Schema $schema): Schema { - return $infolist->schema([ + return $schema->components([ Section::make() ->columns(2) ->schema([ @@ -297,32 +313,32 @@ public static function getRelations(): array public static function getPages(): array { return [ - 'index' => Resources\UserResource\Pages\ListUsers::route('/'), - 'create' => Resources\UserResource\Pages\CreateUser::route('/create'), - 'view' => Resources\UserResource\Pages\ViewUser::route('/{record}'), - 'edit' => Resources\UserResource\Pages\EditUser::route('/{record}/edit'), + 'index' => ListUsers::route('/'), + 'create' => CreateUser::route('/create'), + 'view' => ViewUser::route('/{record}'), + 'edit' => EditUser::route('/{record}/edit'), ]; } - public static function getFirstNameFormComponent(): Forms\Components\TextInput + public static function getFirstNameFormComponent(): TextInput { - return Forms\Components\TextInput::make('first_name') + return TextInput::make('first_name') ->label('First name') ->required() ->maxLength(255); } - public static function getLastNameFormComponent(): Forms\Components\TextInput + public static function getLastNameFormComponent(): TextInput { - return Forms\Components\TextInput::make('last_name') + return TextInput::make('last_name') ->label('Last name') ->required() ->maxLength(255); } - public static function getEmailFormComponent(): Forms\Components\TextInput + public static function getEmailFormComponent(): TextInput { - return Forms\Components\TextInput::make('email') + return TextInput::make('email') ->email() ->required() ->maxLength(255) @@ -345,22 +361,4 @@ public static function getEloquentQuery(): Builder SoftDeletingScope::class, ]); } - - public static function getPermissionPrefixes(): array - { - return [ - 'view_any', - 'view', - 'create', - 'update', - 'delete', - 'delete_any', - 'restore', - 'restore_any', - 'force_delete', - 'force_delete_any', - 'impersonate', - 'send_email', - ]; - } } diff --git a/src/Filament/Resources/UserResource/Pages/EditUser.php b/src/Filament/Resources/UserResource/Pages/EditUser.php index 25b356d..ad86047 100644 --- a/src/Filament/Resources/UserResource/Pages/EditUser.php +++ b/src/Filament/Resources/UserResource/Pages/EditUser.php @@ -3,7 +3,8 @@ namespace Eclipse\Core\Filament\Resources\UserResource\Pages; use Eclipse\Core\Filament\Resources\UserResource; -use Filament\Actions; +use Filament\Actions\DeleteAction; +use Filament\Actions\ViewAction; use Filament\Resources\Pages\EditRecord; use Nben\FilamentRecordNav\Actions\NextRecordAction; use Nben\FilamentRecordNav\Actions\PreviousRecordAction; @@ -30,8 +31,8 @@ protected function getHeaderActions(): array return [ PreviousRecordAction::make(), NextRecordAction::make(), - Actions\ViewAction::make(), - Actions\DeleteAction::make(), + ViewAction::make(), + DeleteAction::make(), ]; } } diff --git a/src/Filament/Resources/UserResource/Pages/ListUsers.php b/src/Filament/Resources/UserResource/Pages/ListUsers.php index 02facc5..225fd21 100644 --- a/src/Filament/Resources/UserResource/Pages/ListUsers.php +++ b/src/Filament/Resources/UserResource/Pages/ListUsers.php @@ -3,20 +3,20 @@ namespace Eclipse\Core\Filament\Resources\UserResource\Pages; use Eclipse\Core\Filament\Resources\UserResource; -use Filament\Actions; +use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use Filament\Support\Enums\MaxWidth; +use Filament\Support\Enums\Width; class ListUsers extends ListRecords { protected static string $resource = UserResource::class; - protected ?string $maxContentWidth = MaxWidth::Full->value; + protected Width|string|null $maxContentWidth = Width::Full->value; protected function getHeaderActions(): array { return [ - Actions\CreateAction::make(), + CreateAction::make(), ]; } } diff --git a/src/Filament/Resources/UserResource/Pages/ViewUser.php b/src/Filament/Resources/UserResource/Pages/ViewUser.php index 2b25d9c..068c650 100644 --- a/src/Filament/Resources/UserResource/Pages/ViewUser.php +++ b/src/Filament/Resources/UserResource/Pages/ViewUser.php @@ -4,12 +4,12 @@ use Eclipse\Core\Filament\Actions\SendEmailAction; use Eclipse\Core\Filament\Resources\UserResource; -use Filament\Actions; +use Filament\Actions\EditAction; use Filament\Resources\Pages\ViewRecord; use Nben\FilamentRecordNav\Actions\NextRecordAction; use Nben\FilamentRecordNav\Actions\PreviousRecordAction; use Nben\FilamentRecordNav\Concerns\WithRecordNavigation; -use STS\FilamentImpersonate\Pages\Actions\Impersonate; +use STS\FilamentImpersonate\Actions\Impersonate; class ViewUser extends ViewRecord { @@ -32,7 +32,7 @@ protected function getHeaderActions(): array return [ PreviousRecordAction::make(), NextRecordAction::make(), - Actions\EditAction::make(), + EditAction::make(), SendEmailAction::make(), Impersonate::make() ->record($this->getRecord()) diff --git a/src/Filament/Resources/UserResource/RelationManagers/AddressesRelationManager.php b/src/Filament/Resources/UserResource/RelationManagers/AddressesRelationManager.php index b21748d..0e62242 100644 --- a/src/Filament/Resources/UserResource/RelationManagers/AddressesRelationManager.php +++ b/src/Filament/Resources/UserResource/RelationManagers/AddressesRelationManager.php @@ -6,13 +6,27 @@ use Eclipse\Core\Enums\AddressType; use Eclipse\Core\Settings\EclipseSettings; -use Filament\Forms; -use Filament\Forms\Get; -use Filament\Infolists; +use Filament\Actions\BulkActionGroup; +use Filament\Actions\CreateAction; +use Filament\Actions\DeleteAction; +use Filament\Actions\DeleteBulkAction; +use Filament\Actions\EditAction; +use Filament\Actions\ForceDeleteBulkAction; +use Filament\Actions\RestoreBulkAction; +use Filament\Actions\ViewAction; +use Filament\Forms\Components\CheckboxList; +use Filament\Forms\Components\Repeater; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\TextInput; +use Filament\Infolists\Components\TextEntry; use Filament\Resources\RelationManagers\RelationManager; +use Filament\Schemas\Components\Flex; +use Filament\Schemas\Components\Grid; +use Filament\Schemas\Components\Utilities\Get; use Filament\Support\Enums\FontWeight; -use Filament\Tables; -use Filament\Tables\Actions\BulkActionGroup; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Filters\SelectFilter; +use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; @@ -32,42 +46,42 @@ public static function canViewForRecord(Model $ownerRecord, string $pageClass): public static function getAddressForm(): array { return [ - Forms\Components\CheckboxList::make('type') + CheckboxList::make('type') ->live() ->default([AddressType::DEFAULT_ADDRESS->value]) ->options(AddressType::class) ->columns(2), - Forms\Components\TextInput::make('recipient') + TextInput::make('recipient') ->maxLength(100) ->label('Full name') ->required(), - Forms\Components\TextInput::make('company_name') + TextInput::make('company_name') ->visible(fn (Get $get): bool => in_array(AddressType::COMPANY_ADDRESS->value, $get('type') ?? [])) ->required() ->maxLength(100), - Forms\Components\TextInput::make('company_vat_id') + TextInput::make('company_vat_id') ->visible(fn (Get $get): bool => in_array(AddressType::COMPANY_ADDRESS->value, $get('type') ?? [])) ->label('Company VAT ID') ->maxLength(50), - Forms\Components\Repeater::make('street_address') + Repeater::make('street_address') ->minItems(1) ->maxItems(3) ->required() ->simple( - Forms\Components\TextInput::make('street_address') + TextInput::make('street_address') ->maxLength(255) ->required() ) ->addActionLabel(__('Add address line')), - Forms\Components\Split::make([ - Forms\Components\TextInput::make('postal_code') + Flex::make([ + TextInput::make('postal_code') ->required() ->maxLength(50), - Forms\Components\TextInput::make('city') + TextInput::make('city') ->required() ->maxLength(100), ]), - Forms\Components\Select::make('country_id') + Select::make('country_id') ->required() ->relationship('country', 'name'), ]; @@ -76,26 +90,26 @@ public static function getAddressForm(): array public static function getAddressInfolist(): array { return [ - Infolists\Components\Grid::make()->schema([ - Infolists\Components\TextEntry::make('type') + Grid::make()->schema([ + TextEntry::make('type') ->badge() ->formatStateUsing(fn ($state) => self::formatAddressTypeLabels($state)), - Infolists\Components\TextEntry::make('recipient'), - Infolists\Components\TextEntry::make('company_name') + TextEntry::make('recipient'), + TextEntry::make('company_name') ->visible(fn ($record): bool => self::hasCompanyAddress($record->type)), - Infolists\Components\TextEntry::make('company_vat_id') + TextEntry::make('company_vat_id') ->visible(fn ($record): bool => self::hasCompanyAddress($record->type)) ->default('-') ->label('Company VAT ID'), - Infolists\Components\TextEntry::make('street_address') + TextEntry::make('street_address') ->listWithLineBreaks(), - Infolists\Components\TextEntry::make('country') + TextEntry::make('country') ->formatStateUsing(fn ($state) => "{$state->name} {$state->flag}"), - Infolists\Components\Split::make([ - Infolists\Components\TextEntry::make('postal_code') + Flex::make([ + TextEntry::make('postal_code') ->badge() ->color('warning'), - Infolists\Components\TextEntry::make('city'), + TextEntry::make('city'), ])->columnSpanFull(), ]), ]; @@ -137,7 +151,7 @@ public function table(Table $table): Table return $table ->modifyQueryUsing(fn (Builder $query) => $query->with(['country'])) ->columns([ - Tables\Columns\TextColumn::make('recipient') + TextColumn::make('recipient') ->weight(FontWeight::Bold) ->description(function ($record) { $recipient = []; @@ -153,16 +167,16 @@ public function table(Table $table): Table return new HtmlString(implode('
', $recipient)); }) ->searchable(['recipient', 'company_name', 'street_address', 'country_id']), - Tables\Columns\TextColumn::make('company_vat_id') + TextColumn::make('company_vat_id') ->placeholder('-') ->label('Company VAT ID'), - Tables\Columns\TextColumn::make('type') + TextColumn::make('type') ->badge() ->placeholder('-') ->formatStateUsing(fn ($state) => self::formatAddressTypeLabels($state)), ]) ->filters([ - Tables\Filters\SelectFilter::make('type') + SelectFilter::make('type') ->options(AddressType::class) ->query(function (Builder $query, array $data): Builder { if (filled($data['value'])) { @@ -171,27 +185,27 @@ public function table(Table $table): Table return $query; }), - Tables\Filters\TrashedFilter::make(), + TrashedFilter::make(), ]) - ->actions([ - Tables\Actions\ViewAction::make() - ->infolist(self::getAddressInfolist()), - Tables\Actions\EditAction::make() - ->form(self::getAddressForm()), - Tables\Actions\DeleteAction::make(), + ->recordActions([ + ViewAction::make() + ->schema(self::getAddressInfolist()), + EditAction::make() + ->schema(self::getAddressForm()), + DeleteAction::make(), ]) - ->bulkActions([ + ->toolbarActions([ BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), - Tables\Actions\ForceDeleteBulkAction::make(), - Tables\Actions\RestoreBulkAction::make(), + DeleteBulkAction::make(), + ForceDeleteBulkAction::make(), + RestoreBulkAction::make(), ]), ]) ->headerActions([ - Tables\Actions\CreateAction::make() + CreateAction::make() ->label('Add new address') ->icon('heroicon-o-plus-circle') - ->form(self::getAddressForm()), + ->schema(self::getAddressForm()), ]); } } diff --git a/src/Http/Middleware/SetupSite.php b/src/Http/Middleware/SetupSite.php index 972845c..c817d3d 100644 --- a/src/Http/Middleware/SetupSite.php +++ b/src/Http/Middleware/SetupSite.php @@ -14,7 +14,7 @@ class SetupSite /** * Handle an incoming request. * - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + * @param Closure(Request):Response $next */ public function handle(Request $request, Closure $next): Response { diff --git a/src/Listeners/LogEmailToDatabase.php b/src/Listeners/LogEmailToDatabase.php index 49f9e05..2c5b612 100644 --- a/src/Listeners/LogEmailToDatabase.php +++ b/src/Listeners/LogEmailToDatabase.php @@ -3,8 +3,11 @@ namespace Eclipse\Core\Listeners; use Eclipse\Core\Models\MailLog; +use Exception; use Illuminate\Mail\Events\MessageSent; use Illuminate\Support\Facades\Log; +use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\Header\Headers; class LogEmailToDatabase { @@ -37,7 +40,7 @@ public function handle(MessageSent $event): void 'status' => 'sent', 'sent_at' => now(), ]); - } catch (\Exception $e) { + } catch (Exception $e) { Log::error('Email logging failed: '.$e->getMessage(), [ 'exception' => $e, 'trace' => $e->getTraceAsString(), @@ -48,7 +51,7 @@ public function handle(MessageSent $event): void /** * Extract a header value from the headers. */ - private function extractHeaderValue(\Symfony\Component\Mime\Header\Headers $headers, string $headerName): ?string + private function extractHeaderValue(Headers $headers, string $headerName): ?string { if (! $headers->has($headerName)) { return null; @@ -56,7 +59,7 @@ private function extractHeaderValue(\Symfony\Component\Mime\Header\Headers $head try { return $headers->get($headerName)->getBodyAsString(); - } catch (\Exception $e) { + } catch (Exception $e) { Log::warning("Failed to extract header {$headerName}: ".$e->getMessage()); return null; @@ -66,7 +69,7 @@ private function extractHeaderValue(\Symfony\Component\Mime\Header\Headers $head /** * Extract the email body from the message. */ - private function extractEmailBody(\Symfony\Component\Mime\Email $message): string + private function extractEmailBody(Email $message): string { if (method_exists($message, 'getHtmlBody')) { return $message->getHtmlBody() ?? ''; @@ -82,7 +85,7 @@ private function extractEmailBody(\Symfony\Component\Mime\Email $message): strin /** * Collect all headers from the message. */ - private function collectAllHeaders(\Symfony\Component\Mime\Header\Headers $headers): array + private function collectAllHeaders(Headers $headers): array { $allHeaders = []; diff --git a/src/Mail/SendEmailToUser.php b/src/Mail/SendEmailToUser.php index 8949384..17ca6e4 100644 --- a/src/Mail/SendEmailToUser.php +++ b/src/Mail/SendEmailToUser.php @@ -14,6 +14,7 @@ use Illuminate\Mail\Mailables\Envelope; use Illuminate\Mail\Mailables\Headers; use Illuminate\Queue\SerializesModels; +use Throwable; class SendEmailToUser extends Mailable implements ShouldQueue { @@ -125,7 +126,7 @@ public function attachments(): array /** * Handle the failed event. */ - public function failed(\Throwable $exception): void + public function failed(Throwable $exception): void { if (! $this->sender) { return; diff --git a/src/Models/User.php b/src/Models/User.php index 087c712..7d28a8d 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -14,6 +14,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; @@ -91,7 +92,7 @@ protected function casts(): array /** * Get the sites. * - * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + * @return BelongsToMany */ public function sites() { @@ -118,12 +119,12 @@ public function canAccessTenant(Model $tenant): bool return $this->sites()->whereKey($tenant)->exists(); } - public function getTenants(Panel $panel): Collection + public function getTenants(?Panel $panel = null): Collection { return $this->sites()->where('is_active', true)->get(); } - public function canAccessPanel(Panel $panel): bool + public function canAccessPanel(?Panel $panel = null): bool { return true; } diff --git a/src/Policies/LocalePolicy.php b/src/Policies/LocalePolicy.php index 05ce87f..bb918ac 100644 --- a/src/Policies/LocalePolicy.php +++ b/src/Policies/LocalePolicy.php @@ -1,10 +1,12 @@ can('view_any_locale'); + return $authUser->can('view_any_locale'); } /** * Determine whether the user can create models. */ - public function create(User $user): bool + public function create(AuthUser $authUser): bool { - return $user->can('create_locale'); + return $authUser->can('create_locale'); } /** * Determine whether the user can update the model. */ - public function update(User $user, Locale $locale): bool + public function update(AuthUser $authUser, Locale $locale): bool { - return $user->can('update_locale'); + return $authUser->can('update_locale'); } /** * Determine whether the user can delete the model. */ - public function delete(User $user, Locale $locale): bool + public function delete(AuthUser $authUser, Locale $locale): bool { - return $user->can('delete_locale'); + return $authUser->can('delete_locale'); } /** * Determine whether the user can bulk delete. */ - public function deleteAny(User $user): bool + public function deleteAny(AuthUser $authUser): bool { - return $user->can('delete_any_locale'); + return $authUser->can('delete_any_locale'); } } diff --git a/src/Policies/MailLogPolicy.php b/src/Policies/MailLogPolicy.php index 09f123e..250d2b2 100644 --- a/src/Policies/MailLogPolicy.php +++ b/src/Policies/MailLogPolicy.php @@ -1,10 +1,12 @@ can('view_any_mail::log'); + return $authUser->can('view_any_mail_log'); } /** * Determine whether the user can view the model. */ - public function view(User $user, MailLog $mailLog): bool + public function view(AuthUser $authUser, MailLog $mailLog): bool { - return $user->can('view_mail::log'); + return $authUser->can('view_mail_log'); } } diff --git a/src/Policies/SitePolicy.php b/src/Policies/SitePolicy.php index ebc21d3..fd7671e 100644 --- a/src/Policies/SitePolicy.php +++ b/src/Policies/SitePolicy.php @@ -1,10 +1,12 @@ can('view_any_site'); + return $authUser->can('view_any_site'); } /** * Determine whether the user can create models. */ - public function create(User $user): bool + public function create(AuthUser $authUser): bool { - return $user->can('create_site'); + return $authUser->can('create_site'); } /** * Determine whether the user can update the model. */ - public function update(User $user, Site $site): bool + public function update(AuthUser $authUser, Site $site): bool { - return $user->can('update_site'); + return $authUser->can('update_site'); } /** * Determine whether the user can delete the model. */ - public function delete(User $user, Site $site): bool + public function delete(AuthUser $authUser, Site $site): bool { - return $user->can('delete_site'); + return $authUser->can('delete_site'); } /** * Determine whether the user can bulk delete. */ - public function deleteAny(User $user): bool + public function deleteAny(AuthUser $authUser): bool { - return $user->can('delete_any_site'); + return $authUser->can('delete_any_site'); } } diff --git a/src/Policies/User/RolePolicy.php b/src/Policies/User/RolePolicy.php index 3dae457..7178a17 100644 --- a/src/Policies/User/RolePolicy.php +++ b/src/Policies/User/RolePolicy.php @@ -1,10 +1,12 @@ can('view_any_role'); + return $authUser->can('view_any_role'); } /** * Determine whether the user can view the model. */ - public function view(User $user, Role $role): bool + public function view(AuthUser $authUser, Role $role): bool { - return $user->can('view_role'); + return $authUser->can('view_role'); } /** * Determine whether the user can create models. */ - public function create(User $user): bool + public function create(AuthUser $authUser): bool { - return $user->can('create_role'); + return $authUser->can('create_role'); } /** * Determine whether the user can update the model. */ - public function update(User $user, Role $role): bool + public function update(AuthUser $authUser, Role $role): bool { - return $user->can('update_role'); + return $authUser->can('update_role'); } /** * Determine whether the user can delete the model. */ - public function delete(User $user, Role $role): bool + public function delete(AuthUser $authUser, Role $role): bool { - return $user->can('delete_role'); + return $authUser->can('delete_role'); } /** * Determine whether the user can bulk delete. */ - public function deleteAny(User $user): bool + public function deleteAny(AuthUser $authUser): bool { - return $user->can('delete_any_role'); + return $authUser->can('delete_any_role'); } } diff --git a/src/Policies/UserPolicy.php b/src/Policies/UserPolicy.php index 125befe..be51838 100644 --- a/src/Policies/UserPolicy.php +++ b/src/Policies/UserPolicy.php @@ -2,8 +2,8 @@ namespace Eclipse\Core\Policies; -use Eclipse\Core\Models\User; use Illuminate\Auth\Access\HandlesAuthorization; +use Illuminate\Foundation\Auth\User as AuthUser; class UserPolicy { @@ -12,100 +12,100 @@ class UserPolicy /** * Determine whether the user can view any models. */ - public function viewAny(User $user): bool + public function viewAny(AuthUser $authUser): bool { - return $user->can('view_any_user'); + return $authUser->can('view_any_user'); } /** * Determine whether the user can view the model. */ - public function view(User $user): bool + public function view(AuthUser $authUser): bool { - return $user->can('view_user'); + return $authUser->can('view_user'); } /** * Determine whether the user can create models. */ - public function create(User $user): bool + public function create(AuthUser $authUser): bool { - return $user->can('create_user'); + return $authUser->can('create_user'); } /** * Determine whether the user can update the model. */ - public function update(User $user): bool + public function update(AuthUser $authUser): bool { - return $user->can('update_user'); + return $authUser->can('update_user'); } /** * Determine whether the user can delete the model. */ - public function delete(User $authenticatedUser, User $user): bool + public function delete(AuthUser $authUser, AuthUser $user): bool { - if ($authenticatedUser->id === $user->id) { + if ($authUser->id === $user->id) { return false; } - return $authenticatedUser->can('delete_user'); + return $authUser->can('delete_user'); } /** * Determine whether the user can bulk delete. */ - public function deleteAny(User $user): bool + public function deleteAny(AuthUser $authUser): bool { - return $user->can('delete_any_user'); + return $authUser->can('delete_any_user'); } /** * Determine whether the user can restore the model. */ - public function restore(User $user): bool + public function restore(AuthUser $authUser): bool { - return $user->can('restore_user'); + return $authUser->can('restore_user'); } /** * Determine whether the user can bulk restore. */ - public function restoreAny(User $user): bool + public function restoreAny(AuthUser $authUser): bool { - return $user->can('restore_any_user'); + return $authUser->can('restore_any_user'); } /** * Determine whether the user can permanently delete the model. */ - public function forceDelete(User $user): bool + public function forceDelete(AuthUser $authUser): bool { - return $user->can('force_delete_user'); + return $authUser->can('force_delete_user'); } /** * Determine whether the user can permanently bulk delete. */ - public function forceDeleteAny(User $user): bool + public function forceDeleteAny(AuthUser $authUser): bool { - return $user->can('force_delete_any_user'); + return $authUser->can('force_delete_any_user'); } /** * Determine whether the user can impersonate other users. */ - public function impersonate(User $user): bool + public function impersonate(AuthUser $authUser): bool { - return $user->can('impersonate_user'); + return $authUser->can('impersonate_user'); } /** * Determine whether the user can send emails to other users. */ - public function sendEmail(User $user): bool + public function sendEmail(AuthUser $authUser): bool { - return $user->can('send_email_user'); + return $authUser->can('send_email_user'); } } diff --git a/src/Providers/AdminPanelProvider.php b/src/Providers/AdminPanelProvider.php index 3d67986..5d6e90f 100644 --- a/src/Providers/AdminPanelProvider.php +++ b/src/Providers/AdminPanelProvider.php @@ -24,16 +24,14 @@ use Filament\Notifications\Livewire\Notifications; use Filament\Panel; use Filament\PanelProvider; -use Filament\SpatieLaravelTranslatablePlugin; use Filament\Support\Colors\Color; use Filament\Support\Enums\Alignment; use Filament\Support\Enums\Platform; use Filament\Support\Enums\VerticalAlignment; use Filament\Support\Facades\FilamentView; use Filament\View\PanelsRenderHook; -use Filament\Widgets; -use Hasnayeen\Themes\Http\Middleware\SetTheme; -use Hasnayeen\Themes\ThemesPlugin; +use Filament\Widgets\AccountWidget; +use Filament\Widgets\FilamentInfoWidget; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; @@ -44,8 +42,10 @@ use Illuminate\Support\Facades\Schema; use Illuminate\View\Middleware\ShareErrorsFromSession; use Illuminate\View\View; +use LaraZeus\SpatieTranslatable\SpatieTranslatablePlugin; use pxlrbt\FilamentEnvironmentIndicator\EnvironmentIndicatorPlugin; use pxlrbt\FilamentSpotlight\SpotlightPlugin; +use pxlrbt\FilamentSpotlightPro\SpotlightProviders\RegisterResources; use ShuvroRoy\FilamentSpatieLaravelHealth\FilamentSpatieLaravelHealthPlugin; class AdminPanelProvider extends PanelProvider @@ -100,13 +100,12 @@ public function panel(Panel $panel): Panel ->tenantDomain('{tenant:domain}') ->tenantMiddleware([ SyncShieldTenant::class, - SetTheme::class, ], isPersistent: true) // ->tenantMenu(config('eclipse.multi_site', false)) ->tenantMenu(false) ->widgets([ - Widgets\AccountWidget::class, - Widgets\FilamentInfoWidget::class, + AccountWidget::class, + FilamentInfoWidget::class, ]) ->middleware([ EncryptCookies::class, @@ -131,9 +130,8 @@ public function panel(Panel $panel): Panel ->modelClass(User::class) ->users(config('eclipse.developer_logins') ?: []), EclipseWorld::make(), - SpatieLaravelTranslatablePlugin::make() + SpatieTranslatablePlugin::make() ->defaultLocales($localeIds), - ThemesPlugin::make(), FilamentSpatieLaravelHealthPlugin::make() ->usingPage(HealthCheckResults::class) ->authorize(fn (): bool => auth()->user()->hasRole('super_admin')), @@ -156,7 +154,7 @@ public function panel(Panel $panel): Panel ->group('Tools') ->sort(2000) // Always visible for local env, otherwise the viewHorizon permission is required - ->visible(fn (User $user): bool => app()->isLocal() || $user->can('viewHorizon')), + ->visible(fn (): bool => app()->isLocal() || (auth()->user()?->can('viewHorizon') ?? false)), NavigationItem::make('Log viewer') ->url('/'.config('log-viewer.route_path', 'log-viewer'), shouldOpenInNewTab: true) ->icon('heroicon-s-arrow-top-right-on-square') @@ -184,7 +182,7 @@ public function panel(Panel $panel): Panel $panel->plugin( \pxlrbt\FilamentSpotlightPro\SpotlightPlugin::make() ->registerItems([ - \pxlrbt\FilamentSpotlightPro\SpotlightProviders\RegisterResources::make(), + RegisterResources::make(), ]) ->hotkeys(['ΒΈ']) ); diff --git a/tests/Feature/Filament/Resources/LocaleResourceTest.php b/tests/Feature/Filament/Resources/LocaleResourceTest.php index 09fd8b7..476c27b 100644 --- a/tests/Feature/Filament/Resources/LocaleResourceTest.php +++ b/tests/Feature/Filament/Resources/LocaleResourceTest.php @@ -3,11 +3,13 @@ use Eclipse\Core\Filament\Resources\LocaleResource; use Eclipse\Core\Filament\Resources\LocaleResource\Pages\ListLocales; use Eclipse\Core\Models\Locale; +use Illuminate\Support\Arr; use function Pest\Livewire\livewire; beforeEach(function () { $this->set_up_super_admin_and_tenant(); + LocaleResource::canViewAny(); }); test('unauthorized access can be prevented', function () { @@ -47,8 +49,14 @@ test('form validation works', function () { $component = livewire(ListLocales::class); + // Check if create action is visible + $component->assertActionVisible('create'); + + // Mount the create action + $component->mountAction('create'); + // Test required fields - $component->callAction('create') + $component->callMountedAction() ->assertHasActionErrors([ 'id' => 'required', 'name' => 'required', @@ -60,7 +68,11 @@ ]); // Test with valid data - $component->callAction('create', Locale::factory()->definition()) + $validData = Locale::factory()->definition(); + $validData['system_locale'] = 'en_US.UTF-8'; + $component->mountAction('create') + ->setActionData($validData) + ->callMountedAction() ->assertHasNoActionErrors(); }); @@ -70,9 +82,12 @@ // Remove is_active and is_available_in_panel attributes, since they're not used when creating a locale unset($data['is_active']); unset($data['is_available_in_panel']); + $data['system_locale'] = 'en_US.UTF-8'; livewire(ListLocales::class) - ->callAction('create', $data) + ->mountAction('create') + ->setActionData($data) + ->callMountedAction() ->assertHasNoActionErrors(); $locale = Locale::where('id', $data['id'])->first(); @@ -86,7 +101,8 @@ test('existing locale can be updated', function () { $locale = Locale::factory()->create(); - $new_data = \Illuminate\Support\Arr::except(Locale::factory()->definition(), ['id', 'is_active', 'is_available_in_panel']); + $new_data = Arr::except(Locale::factory()->definition(), ['id', 'is_active', 'is_available_in_panel']); + $new_data['system_locale'] = 'en_US.UTF-8'; livewire(ListLocales::class) ->callTableAction('edit', $locale, $new_data) diff --git a/tests/Feature/Filament/Resources/MailLogResourceTest.php b/tests/Feature/Filament/Resources/MailLogResourceTest.php index 31357d2..8d671af 100644 --- a/tests/Feature/Filament/Resources/MailLogResourceTest.php +++ b/tests/Feature/Filament/Resources/MailLogResourceTest.php @@ -3,12 +3,13 @@ use Eclipse\Core\Filament\Resources\MailLogResource; use Eclipse\Core\Filament\Resources\MailLogResource\Pages\ListMailLogs; use Eclipse\Core\Models\MailLog; -use Filament\Tables\Actions\ViewAction; +use Filament\Actions\ViewAction; use function Pest\Livewire\livewire; beforeEach(function () { $this->set_up_super_admin_and_tenant(); + MailLogResource::canViewAny(); }); test('authorized access can be allowed', function () { diff --git a/tests/Feature/Filament/Resources/UserResourceTest.php b/tests/Feature/Filament/Resources/UserResourceTest.php index 5dd19c9..ec82038 100644 --- a/tests/Feature/Filament/Resources/UserResourceTest.php +++ b/tests/Feature/Filament/Resources/UserResourceTest.php @@ -2,16 +2,18 @@ use Eclipse\Core\Filament\Resources\UserResource; use Eclipse\Core\Filament\Resources\UserResource\Pages\CreateUser; +use Eclipse\Core\Filament\Resources\UserResource\Pages\EditUser; use Eclipse\Core\Filament\Resources\UserResource\Pages\ListUsers; use Eclipse\Core\Models\User; -use Filament\Tables\Actions\DeleteAction; -use Filament\Tables\Actions\DeleteBulkAction; +use Filament\Actions\DeleteAction; +use Filament\Actions\DeleteBulkAction; use Illuminate\Support\Facades\Hash; use function Pest\Livewire\livewire; beforeEach(function () { $this->set_up_super_admin_and_tenant(); + UserResource::canViewAny(); }); test('authorized access can be allowed', function () { @@ -90,7 +92,7 @@ // Without password, since it's not required ]; - livewire(\Eclipse\Core\Filament\Resources\UserResource\Pages\EditUser::class, ['record' => $user->id]) + livewire(EditUser::class, ['record' => $user->id]) ->fillForm($data) ->call('save') ->assertHasNoFormErrors(); diff --git a/tests/Feature/UserEmailTest.php b/tests/Feature/UserEmailTest.php index ccd7fcb..e07f43d 100644 --- a/tests/Feature/UserEmailTest.php +++ b/tests/Feature/UserEmailTest.php @@ -1,6 +1,8 @@ regularRole->givePermissionTo(['view_any_user']); // Create users with site association - $site = \Eclipse\Core\Models\Site::first(); + $site = Site::first(); $this->authorizedUser = User::factory()->create(); $this->authorizedUser->assignRole($this->emailRole); @@ -58,7 +60,7 @@ expect($this->unauthorizedUser->can('sendEmail', User::class))->toBeFalse(); // Test that action is properly configured - $action = \Eclipse\Core\Filament\Actions\SendEmailTableAction::makeAction(); + $action = SendEmailTableAction::makeAction(); expect($action->getName())->toBe('sendEmail'); expect($action->getIcon())->toBe('heroicon-o-envelope'); }); @@ -67,7 +69,7 @@ $this->actingAs($this->authorizedUser); // Test that action has the proper visibility configuration - $action = \Eclipse\Core\Filament\Actions\SendEmailTableAction::makeAction(); + $action = SendEmailTableAction::makeAction(); // Action should be properly configured expect($action)->not->toBeNull(); diff --git a/tests/Feature/UserImpersonationTest.php b/tests/Feature/UserImpersonationTest.php index 6f1d6e7..d002b7f 100644 --- a/tests/Feature/UserImpersonationTest.php +++ b/tests/Feature/UserImpersonationTest.php @@ -4,8 +4,7 @@ use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; -use STS\FilamentImpersonate\Pages\Actions\Impersonate as ImpersonatePageAction; -use STS\FilamentImpersonate\Tables\Actions\Impersonate as ImpersonateTableAction; +use STS\FilamentImpersonate\Actions\Impersonate as ImpersonateAction; beforeEach(function () { $this->set_up_super_admin_and_tenant(); @@ -55,7 +54,7 @@ $this->assertFalse($this->unauthorizedUser->hasPermissionTo('impersonate_user')); // Create an instance of the Impersonate action - $action = ImpersonateTableAction::make('impersonate') + $action = ImpersonateAction::make('impersonate') ->record($this->targetUser); // Assert the action is not authorized @@ -63,7 +62,7 @@ $this->assertFalse($action->isEnabled()); // Create an instance of the Impersonate page action - $action = ImpersonatePageAction::make('impersonate') + $action = ImpersonateAction::make('impersonate') ->record($this->targetUser); // Assert the action is not authorized @@ -92,7 +91,7 @@ $this->assertTrue($this->authorizedUser->hasPermissionTo('impersonate_user')); // Create an instance of the Impersonate action - $action = ImpersonateTableAction::make('impersonate') + $action = ImpersonateAction::make('impersonate') ->record($this->targetUser); // Assert the action is authorized @@ -100,7 +99,7 @@ $this->assertTrue($action->isEnabled()); // Create an instance of the Impersonate page action - $action = ImpersonatePageAction::make('impersonate') + $action = ImpersonateAction::make('impersonate') ->record($this->targetUser); // Assert the action is authorized diff --git a/tests/Pest.php b/tests/Pest.php index a5fd441..3c15268 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,6 +1,7 @@ use(Illuminate\Foundation\Testing\RefreshDatabase::class) + ->use(RefreshDatabase::class) ->beforeEach(function () { $this->seed(CoreSeeder::class); }) diff --git a/tests/TestCase.php b/tests/TestCase.php index 795aae7..bcce7a8 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -65,7 +65,7 @@ protected function set_up_super_admin_and_tenant(): self $site = Site::first(); $this->superAdmin = User::factory()->make(); - $this->superAdmin->assignRole('super_admin')->save(); + $this->superAdmin->assignRole('super_admin', $site->id)->save(); $this->superAdmin->sites()->attach($site); $this->actingAs($this->superAdmin); diff --git a/workbench/app/Providers/WorkbenchServiceProvider.php b/workbench/app/Providers/WorkbenchServiceProvider.php index 5e009da..d683933 100644 --- a/workbench/app/Providers/WorkbenchServiceProvider.php +++ b/workbench/app/Providers/WorkbenchServiceProvider.php @@ -2,6 +2,7 @@ namespace Workbench\App\Providers; +use Eclipse\Core\Providers\AdminPanelProvider; use Illuminate\Support\ServiceProvider; class WorkbenchServiceProvider extends ServiceProvider @@ -11,7 +12,7 @@ class WorkbenchServiceProvider extends ServiceProvider */ public function register(): void { - $this->app->register(\Eclipse\Core\Providers\AdminPanelProvider::class); + $this->app->register(AdminPanelProvider::class); } /** diff --git a/workbench/database/factories/UserFactory.php b/workbench/database/factories/UserFactory.php index dfcab01..8a58e9e 100644 --- a/workbench/database/factories/UserFactory.php +++ b/workbench/database/factories/UserFactory.php @@ -10,7 +10,7 @@ /** * @template TModel of \Workbench\App\Models\User * - * @extends \Illuminate\Database\Eloquent\Factories\Factory + * @extends Factory */ class UserFactory extends Factory {