From 5c21264e9c84711569956df27d1e231365920d16 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Sat, 20 Sep 2025 10:52:21 +0200 Subject: [PATCH 01/20] feat: migrate to f4 --- composer.json | 40 +++--- config/filament-shield.php | 106 +++++++------- config/themes.php | 36 ----- database/factories/AddressFactory.php | 3 +- database/factories/UserFactory.php | 3 +- ...139_add_themes_settings_to_users_table.php | 23 --- ..._16_150346_create_user_addresses_table.php | 3 - ..._phone_country_columns_to_users_tables.php | 5 - database/seeders/CoreSeeder.php | 1 - src/Console/Commands/PostComposerUpdate.php | 1 - src/EclipseServiceProvider.php | 3 +- src/Filament/Actions/SendEmailAction.php | 13 +- src/Filament/Actions/SendEmailTableAction.php | 2 +- src/Filament/Pages/EditProfile.php | 11 +- src/Filament/Pages/ManageEclipse.php | 16 +-- src/Filament/Pages/ManageUserSettings.php | 20 +-- src/Filament/Resources/LocaleResource.php | 58 ++++---- .../LocaleResource/Pages/EditLocale.php | 4 +- .../LocaleResource/Pages/ListLocales.php | 8 +- src/Filament/Resources/MailLogResource.php | 102 ++++++++------ .../MailLogResource/Pages/ListMailLogs.php | 4 +- src/Filament/Resources/SiteResource.php | 52 ++++--- .../Resources/SiteResource/Pages/EditSite.php | 4 +- .../SiteResource/Pages/ListSites.php | 8 +- src/Filament/Resources/UserResource.php | 133 ++++++++++-------- .../Resources/UserResource/Pages/EditUser.php | 7 +- .../UserResource/Pages/ListUsers.php | 8 +- .../Resources/UserResource/Pages/ViewUser.php | 4 +- .../AddressesRelationManager.php | 98 +++++++------ src/Http/Middleware/SetupSite.php | 2 +- src/Listeners/LogEmailToDatabase.php | 13 +- src/Mail/SendEmailToUser.php | 3 +- src/Models/User.php | 10 +- src/Models/User/Address.php | 6 - src/Providers/AdminPanelProvider.php | 20 ++- .../Providers/WorkbenchServiceProvider.php | 3 +- workbench/database/factories/UserFactory.php | 2 +- 37 files changed, 411 insertions(+), 424 deletions(-) delete mode 100644 config/themes.php delete mode 100644 database/migrations/2025_05_27_071139_add_themes_settings_to_users_table.php diff --git a/composer.json b/composer.json index 95cc046..adce4fa 100644 --- a/composer.json +++ b/composer.json @@ -42,37 +42,41 @@ }, "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", - "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", + "dutchcodingcompany/filament-developer-logins": "^2.0", + "eclipsephp/common": "dev-f4@dev", + "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" }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/KilianTrunk/eclipsephp-common.git" + } + ], "require-dev": { + "filament/upgrade": "^4.0", "laravel/pint": "^1.21", "orchestra/testbench": "^10.0", "pestphp/pest": "^3.7", diff --git a/config/filament-shield.php b/config/filament-shield.php index 65c1230..47c48f1 100644 --- a/config/filament-shield.php +++ b/config/filament-shield.php @@ -1,29 +1,33 @@ [ - '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' => false, + ], ], '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,61 +35,65 @@ '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' => 'pascal', + 'generate' => true, ], - 'entities' => [ - 'pages' => true, - 'widgets' => true, - 'resources' => true, - 'custom_permissions' => false, + 'policies' => [ + 'path' => app_path('Policies'), + 'merge' => true, + '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, - - 'pages' => [ - 'Dashboard', + 'resources' => [ + 'subject' => 'model', + 'manage' => [ + RoleResource::class => [ + 'viewAny', 'view', 'create', 'update', 'delete', + ], + ], + 'exclude' => [ ], + ], - 'widgets' => [ - 'AccountWidget', 'FilamentInfoWidget', + 'pages' => [ + 'subject' => 'class', + 'prefix' => 'view', + 'exclude' => [ + Dashboard::class, ], + ], - 'resources' => [], + 'widgets' => [ + 'subject' => 'class', + 'prefix' => 'view', + 'exclude' => [ + AccountWidget::class, + FilamentInfoWidget::class, + ], ], + 'custom_permissions' => [], + 'discovery' => [ 'discover_all_resources' => false, 'discover_all_widgets' => false, '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/factories/AddressFactory.php b/database/factories/AddressFactory.php index b28d220..632c738 100644 --- a/database/factories/AddressFactory.php +++ b/database/factories/AddressFactory.php @@ -5,7 +5,6 @@ use Eclipse\Core\Enums\AddressType; use Eclipse\Core\Models\User; use Eclipse\Core\Models\User\Address; -use Eclipse\World\Models\Country; use Illuminate\Database\Eloquent\Factories\Factory; class AddressFactory extends Factory @@ -43,7 +42,7 @@ public function definition(): array 'postal_code' => fake()->postcode(), 'city' => fake()->city(), 'type' => $type, - 'country_id' => Country::inRandomOrder()->first()?->id ?? Country::factory()->create()->id, + 'country_id' => 1, 'user_id' => User::inRandomOrder()->first()?->id ?? User::factory()->create()->id, ]; } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 1091ec2..2e06cc9 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -3,7 +3,6 @@ namespace Eclipse\Core\Database\Factories; use Eclipse\Core\Models\User; -use Eclipse\World\Models\Country; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; @@ -39,7 +38,7 @@ public function definition(): array 'phone_number' => fake()->phoneNumber(), 'email_verified_at' => now(), 'password' => static::$password ??= Hash::make('password'), - 'country_id' => Country::inRandomOrder()->first()?->id ?? Country::factory()->create()->id, + 'country_id' => 1, 'date_of_birth' => now()->subYears(rand(20, 40)), 'remember_token' => Str::random(10), 'login_count' => 0, 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/migrations/2025_06_16_150346_create_user_addresses_table.php b/database/migrations/2025_06_16_150346_create_user_addresses_table.php index 95cb9bc..c6e730c 100644 --- a/database/migrations/2025_06_16_150346_create_user_addresses_table.php +++ b/database/migrations/2025_06_16_150346_create_user_addresses_table.php @@ -28,9 +28,6 @@ public function up(): void ->cascadeOnDelete(); $table->softDeletes(); $table->timestamps(); - $table->foreign('country_id') - ->references('id') - ->on('world_countries'); }); } diff --git a/database/migrations/2025_06_24_000000_add_dob_phone_country_columns_to_users_tables.php b/database/migrations/2025_06_24_000000_add_dob_phone_country_columns_to_users_tables.php index 93b6929..62d4fea 100644 --- a/database/migrations/2025_06_24_000000_add_dob_phone_country_columns_to_users_tables.php +++ b/database/migrations/2025_06_24_000000_add_dob_phone_country_columns_to_users_tables.php @@ -22,11 +22,6 @@ public function up() $table->index('phone_number'); $table->index('country_id'); $table->index('date_of_birth'); - - $table->foreign('country_id') - ->references('id') - ->on('world_countries') - ->onDelete('set null'); }); } diff --git a/database/seeders/CoreSeeder.php b/database/seeders/CoreSeeder.php index ec37038..a86bb05 100644 --- a/database/seeders/CoreSeeder.php +++ b/database/seeders/CoreSeeder.php @@ -16,7 +16,6 @@ public function run(): void '--all' => null, '--panel' => 'admin', '--option' => 'permissions', - '--minimal' => null, ]); // Seed additional roles 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..7ac2cfe 100644 --- a/src/EclipseServiceProvider.php +++ b/src/EclipseServiceProvider.php @@ -2,7 +2,7 @@ namespace Eclipse\Core; -use BezhanSalleh\FilamentLanguageSwitch\LanguageSwitch; +use BezhanSalleh\LanguageSwitch\LanguageSwitch; use Eclipse\Common\Foundation\Providers\PackageServiceProvider; use Eclipse\Common\Package; use Eclipse\Core\Console\Commands\ClearCommand; @@ -69,7 +69,6 @@ public function configurePackage(SpatiePackage|Package $package): void 'permission', 'settings', 'telescope', - 'themes', 'health', ]) ->hasViews() 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..39a82c3 100644 --- a/src/Filament/Pages/ManageUserSettings.php +++ b/src/Filament/Pages/ManageUserSettings.php @@ -3,28 +3,30 @@ 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'), ]), ]); diff --git a/src/Filament/Resources/LocaleResource.php b/src/Filament/Resources/LocaleResource.php index ef1140c..d5e5bf7 100644 --- a/src/Filament/Resources/LocaleResource.php +++ b/src/Filament/Resources/LocaleResource.php @@ -4,16 +4,22 @@ 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; @@ -22,9 +28,9 @@ class LocaleResource extends Resource implements HasShieldPermissions { 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 +69,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 +97,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 +135,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 +160,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'), ]; } 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..09d83d9 100644 --- a/src/Filament/Resources/MailLogResource.php +++ b/src/Filament/Resources/MailLogResource.php @@ -3,12 +3,22 @@ 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; @@ -17,9 +27,9 @@ class MailLogResource extends Resource implements HasShieldPermissions { 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 +48,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 +102,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 +155,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 +216,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 +277,7 @@ protected static function buildEmailPreview(MailLog $record): HtmlString public static function getPages(): array { return [ - 'index' => MailLogResource\Pages\ListMailLogs::route('/'), + 'index' => ListMailLogs::route('/'), ]; } 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..4ef5a2a 100644 --- a/src/Filament/Resources/SiteResource.php +++ b/src/Filament/Resources/SiteResource.php @@ -3,25 +3,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 { 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 +36,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 +48,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 +91,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'), ]; } 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..e673880 100644 --- a/src/Filament/Resources/UserResource.php +++ b/src/Filament/Resources/UserResource.php @@ -5,24 +5,41 @@ 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; @@ -35,16 +52,16 @@ class UserResource extends Resource implements HasShieldPermissions { 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 +69,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 +91,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 +120,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 +146,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 +154,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 +172,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 +203,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 +222,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 +266,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 +314,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) 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..815a8a4 100644 --- a/src/Filament/Resources/UserResource/Pages/ViewUser.php +++ b/src/Filament/Resources/UserResource/Pages/ViewUser.php @@ -4,7 +4,7 @@ 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; @@ -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..e0cb477 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -5,7 +5,6 @@ use Eclipse\Core\Database\Factories\UserFactory; use Eclipse\Core\Models\User\Address; use Eclipse\Core\Settings\UserSettings; -use Eclipse\World\Models\Country; use Exception; use Filament\Models\Contracts\FilamentUser; use Filament\Models\Contracts\HasAvatar; @@ -13,7 +12,7 @@ use Filament\Panel; 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 +90,7 @@ protected function casts(): array /** * Get the sites. * - * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + * @return BelongsToMany */ public function sites() { @@ -103,11 +102,6 @@ public function addresses(): HasMany return $this->hasMany(Address::class); } - public function country(): BelongsTo - { - return $this->belongsTo(Country::class); - } - public function getFilamentAvatarUrl(): ?string { return $this->getMedia('avatars')->first()?->getUrl(); diff --git a/src/Models/User/Address.php b/src/Models/User/Address.php index 1e2edb8..4d414e3 100644 --- a/src/Models/User/Address.php +++ b/src/Models/User/Address.php @@ -5,7 +5,6 @@ use Eclipse\Core\Database\Factories\AddressFactory; use Eclipse\Core\Enums\AddressType; use Eclipse\Core\Models\User; -use Eclipse\World\Models\Country; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -51,11 +50,6 @@ public function user(): BelongsTo return $this->belongsTo(User::class); } - public function country(): BelongsTo - { - return $this->belongsTo(Country::class); - } - protected static function newFactory(): AddressFactory { return AddressFactory::new(); diff --git a/src/Providers/AdminPanelProvider.php b/src/Providers/AdminPanelProvider.php index 3d67986..d4e3df0 100644 --- a/src/Providers/AdminPanelProvider.php +++ b/src/Providers/AdminPanelProvider.php @@ -16,7 +16,6 @@ use Eclipse\Core\Models\Site; use Eclipse\Core\Models\User; use Eclipse\Core\Services\Registry; -use Eclipse\World\EclipseWorld; use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\DisableBladeIconComponents; use Filament\Http\Middleware\DispatchServingFilamentEvent; @@ -24,16 +23,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 +41,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 +99,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, @@ -130,10 +128,8 @@ public function panel(Panel $panel): Panel ->enabled(app()->isLocal()) ->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')), @@ -184,7 +180,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/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 { From 429b8843d0ab83a374315344e613937f272f1ac4 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Sat, 20 Sep 2025 11:14:00 +0200 Subject: [PATCH 02/20] chore: fixes --- config/filament-shield.php | 4 ++-- src/Models/User.php | 5 +++++ src/Models/User/Address.php | 5 +++++ src/Providers/AdminPanelProvider.php | 2 +- tests/Feature/Filament/Resources/LocaleResourceTest.php | 3 ++- tests/Feature/Filament/Resources/MailLogResourceTest.php | 2 +- tests/Feature/Filament/Resources/UserResourceTest.php | 7 ++++--- tests/Feature/UserEmailTest.php | 8 +++++--- tests/Pest.php | 3 ++- 9 files changed, 27 insertions(+), 12 deletions(-) diff --git a/config/filament-shield.php b/config/filament-shield.php index 47c48f1..0d0acdb 100644 --- a/config/filament-shield.php +++ b/config/filament-shield.php @@ -36,8 +36,8 @@ ], 'permissions' => [ - 'separator' => ':', - 'case' => 'pascal', + 'separator' => '_', + 'case' => 'lower_snake', 'generate' => true, ], diff --git a/src/Models/User.php b/src/Models/User.php index e0cb477..0993e69 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -102,6 +102,11 @@ public function addresses(): HasMany return $this->hasMany(Address::class); } + public function country(): BelongsTo + { + return $this->belongsTo(static::class, 'country_id')->whereRaw('1 = 0'); + } + public function getFilamentAvatarUrl(): ?string { return $this->getMedia('avatars')->first()?->getUrl(); diff --git a/src/Models/User/Address.php b/src/Models/User/Address.php index 4d414e3..b2b2c1f 100644 --- a/src/Models/User/Address.php +++ b/src/Models/User/Address.php @@ -50,6 +50,11 @@ public function user(): BelongsTo return $this->belongsTo(User::class); } + public function country(): BelongsTo + { + return $this->belongsTo(static::class, 'country_id')->whereRaw('1 = 0'); + } + protected static function newFactory(): AddressFactory { return AddressFactory::new(); diff --git a/src/Providers/AdminPanelProvider.php b/src/Providers/AdminPanelProvider.php index d4e3df0..53db17a 100644 --- a/src/Providers/AdminPanelProvider.php +++ b/src/Providers/AdminPanelProvider.php @@ -152,7 +152,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') diff --git a/tests/Feature/Filament/Resources/LocaleResourceTest.php b/tests/Feature/Filament/Resources/LocaleResourceTest.php index 09fd8b7..5e2877b 100644 --- a/tests/Feature/Filament/Resources/LocaleResourceTest.php +++ b/tests/Feature/Filament/Resources/LocaleResourceTest.php @@ -3,6 +3,7 @@ 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; @@ -86,7 +87,7 @@ 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']); 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..597f280 100644 --- a/tests/Feature/Filament/Resources/MailLogResourceTest.php +++ b/tests/Feature/Filament/Resources/MailLogResourceTest.php @@ -3,7 +3,7 @@ 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; diff --git a/tests/Feature/Filament/Resources/UserResourceTest.php b/tests/Feature/Filament/Resources/UserResourceTest.php index 5dd19c9..fe0990e 100644 --- a/tests/Feature/Filament/Resources/UserResourceTest.php +++ b/tests/Feature/Filament/Resources/UserResourceTest.php @@ -2,10 +2,11 @@ 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; @@ -90,7 +91,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/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); }) From 34f6496f25e4d235a8dc15fa62fa53bb969735de Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Mon, 22 Sep 2025 14:17:44 +0200 Subject: [PATCH 03/20] chore: more fixes --- composer.json | 7 ++++++- database/factories/AddressFactory.php | 3 ++- database/factories/UserFactory.php | 3 ++- .../2025_06_16_150346_create_user_addresses_table.php | 3 +++ ...00000_add_dob_phone_country_columns_to_users_tables.php | 5 +++++ src/Models/User.php | 4 +++- src/Models/User/Address.php | 3 ++- src/Providers/AdminPanelProvider.php | 2 ++ 8 files changed, 25 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index adce4fa..0aa9cdb 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,7 @@ "datalinx/php-utils": "^2.5", "dutchcodingcompany/filament-developer-logins": "^2.0", "eclipsephp/common": "dev-f4@dev", + "eclipsephp/world-plugin": "dev-f4@dev", "filament/filament": "^4.0", "filament/spatie-laravel-media-library-plugin": "^4.0", "lara-zeus/spatie-translatable": "^1.0", @@ -73,7 +74,11 @@ { "type": "vcs", "url": "https://github.com/KilianTrunk/eclipsephp-common.git" - } + }, + { + "type": "vcs", + "url": "https://github.com/KilianTrunk/eclipsephp-world-plugin.git" + } ], "require-dev": { "filament/upgrade": "^4.0", diff --git a/database/factories/AddressFactory.php b/database/factories/AddressFactory.php index 632c738..b28d220 100644 --- a/database/factories/AddressFactory.php +++ b/database/factories/AddressFactory.php @@ -5,6 +5,7 @@ use Eclipse\Core\Enums\AddressType; use Eclipse\Core\Models\User; use Eclipse\Core\Models\User\Address; +use Eclipse\World\Models\Country; use Illuminate\Database\Eloquent\Factories\Factory; class AddressFactory extends Factory @@ -42,7 +43,7 @@ public function definition(): array 'postal_code' => fake()->postcode(), 'city' => fake()->city(), 'type' => $type, - 'country_id' => 1, + 'country_id' => Country::inRandomOrder()->first()?->id ?? Country::factory()->create()->id, 'user_id' => User::inRandomOrder()->first()?->id ?? User::factory()->create()->id, ]; } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 2e06cc9..1091ec2 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -3,6 +3,7 @@ namespace Eclipse\Core\Database\Factories; use Eclipse\Core\Models\User; +use Eclipse\World\Models\Country; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; @@ -38,7 +39,7 @@ public function definition(): array 'phone_number' => fake()->phoneNumber(), 'email_verified_at' => now(), 'password' => static::$password ??= Hash::make('password'), - 'country_id' => 1, + 'country_id' => Country::inRandomOrder()->first()?->id ?? Country::factory()->create()->id, 'date_of_birth' => now()->subYears(rand(20, 40)), 'remember_token' => Str::random(10), 'login_count' => 0, diff --git a/database/migrations/2025_06_16_150346_create_user_addresses_table.php b/database/migrations/2025_06_16_150346_create_user_addresses_table.php index c6e730c..95cb9bc 100644 --- a/database/migrations/2025_06_16_150346_create_user_addresses_table.php +++ b/database/migrations/2025_06_16_150346_create_user_addresses_table.php @@ -28,6 +28,9 @@ public function up(): void ->cascadeOnDelete(); $table->softDeletes(); $table->timestamps(); + $table->foreign('country_id') + ->references('id') + ->on('world_countries'); }); } diff --git a/database/migrations/2025_06_24_000000_add_dob_phone_country_columns_to_users_tables.php b/database/migrations/2025_06_24_000000_add_dob_phone_country_columns_to_users_tables.php index 62d4fea..93b6929 100644 --- a/database/migrations/2025_06_24_000000_add_dob_phone_country_columns_to_users_tables.php +++ b/database/migrations/2025_06_24_000000_add_dob_phone_country_columns_to_users_tables.php @@ -22,6 +22,11 @@ public function up() $table->index('phone_number'); $table->index('country_id'); $table->index('date_of_birth'); + + $table->foreign('country_id') + ->references('id') + ->on('world_countries') + ->onDelete('set null'); }); } diff --git a/src/Models/User.php b/src/Models/User.php index 0993e69..e188ca6 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -5,6 +5,7 @@ use Eclipse\Core\Database\Factories\UserFactory; use Eclipse\Core\Models\User\Address; use Eclipse\Core\Settings\UserSettings; +use Eclipse\World\Models\Country; use Exception; use Filament\Models\Contracts\FilamentUser; use Filament\Models\Contracts\HasAvatar; @@ -12,6 +13,7 @@ use Filament\Panel; 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; @@ -104,7 +106,7 @@ public function addresses(): HasMany public function country(): BelongsTo { - return $this->belongsTo(static::class, 'country_id')->whereRaw('1 = 0'); + return $this->belongsTo(Country::class); } public function getFilamentAvatarUrl(): ?string diff --git a/src/Models/User/Address.php b/src/Models/User/Address.php index b2b2c1f..1e2edb8 100644 --- a/src/Models/User/Address.php +++ b/src/Models/User/Address.php @@ -5,6 +5,7 @@ use Eclipse\Core\Database\Factories\AddressFactory; use Eclipse\Core\Enums\AddressType; use Eclipse\Core\Models\User; +use Eclipse\World\Models\Country; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -52,7 +53,7 @@ public function user(): BelongsTo public function country(): BelongsTo { - return $this->belongsTo(static::class, 'country_id')->whereRaw('1 = 0'); + return $this->belongsTo(Country::class); } protected static function newFactory(): AddressFactory diff --git a/src/Providers/AdminPanelProvider.php b/src/Providers/AdminPanelProvider.php index 53db17a..5d6e90f 100644 --- a/src/Providers/AdminPanelProvider.php +++ b/src/Providers/AdminPanelProvider.php @@ -16,6 +16,7 @@ use Eclipse\Core\Models\Site; use Eclipse\Core\Models\User; use Eclipse\Core\Services\Registry; +use Eclipse\World\EclipseWorld; use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\DisableBladeIconComponents; use Filament\Http\Middleware\DispatchServingFilamentEvent; @@ -128,6 +129,7 @@ public function panel(Panel $panel): Panel ->enabled(app()->isLocal()) ->modelClass(User::class) ->users(config('eclipse.developer_logins') ?: []), + EclipseWorld::make(), SpatieTranslatablePlugin::make() ->defaultLocales($localeIds), FilamentSpatieLaravelHealthPlugin::make() From 291fea92d6e61206620534e6e1f6c70eac470109 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Mon, 22 Sep 2025 15:52:46 +0200 Subject: [PATCH 04/20] chore: remove filament upgrade --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 0aa9cdb..b06235e 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,6 @@ } ], "require-dev": { - "filament/upgrade": "^4.0", "laravel/pint": "^1.21", "orchestra/testbench": "^10.0", "pestphp/pest": "^3.7", From da94123a541f358b62a365da8eeabdddad27f4aa Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Tue, 23 Sep 2025 14:49:38 +0200 Subject: [PATCH 05/20] chore: format --- config/filament-shield.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config/filament-shield.php b/config/filament-shield.php index 0d0acdb..8f5784e 100644 --- a/config/filament-shield.php +++ b/config/filament-shield.php @@ -6,7 +6,6 @@ use Filament\Widgets\FilamentInfoWidget; return [ - 'shield_resource' => [ 'slug' => 'shield/roles', 'show_model_path' => true, @@ -66,8 +65,7 @@ 'viewAny', 'view', 'create', 'update', 'delete', ], ], - 'exclude' => [ - ], + 'exclude' => [], ], 'pages' => [ From 265fa4386c385cda27397383f5549fe5d2a3934a Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Tue, 23 Sep 2025 15:22:55 +0200 Subject: [PATCH 06/20] chore: update seeder --- database/seeders/CoreSeeder.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/database/seeders/CoreSeeder.php b/database/seeders/CoreSeeder.php index a86bb05..8a4c54a 100644 --- a/database/seeders/CoreSeeder.php +++ b/database/seeders/CoreSeeder.php @@ -2,6 +2,7 @@ namespace Eclipse\Core\Database\Seeders; +use Eclipse\Core\Models\Site; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\Artisan; @@ -9,8 +10,15 @@ class CoreSeeder extends Seeder { public function run(): void { + // Seed locales $this->call(LocaleSeeder::class); + // Seed sites + $this->call(SiteSeeder::class); + + // Set permissions team ID + setPermissionsTeamId(Site::first()->id); + // Seed roles and permissions with Filament Shield plugin Artisan::call('shield:generate', [ '--all' => null, @@ -21,10 +29,7 @@ public function run(): void // Seed additional roles $this->call(RoleSeeder::class); - // Sites - $this->call(SiteSeeder::class); - - // Users + // Seed users $this->call(UserSeeder::class); } } From 6dd963322d5a0ccb8d06b98f9a72ed4f3d3352d6 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Wed, 24 Sep 2025 12:49:19 +0200 Subject: [PATCH 07/20] chore: fix attempt --- src/Http/Middleware/SetupSite.php | 3 +++ src/Models/User.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/Http/Middleware/SetupSite.php b/src/Http/Middleware/SetupSite.php index c817d3d..29024b5 100644 --- a/src/Http/Middleware/SetupSite.php +++ b/src/Http/Middleware/SetupSite.php @@ -7,6 +7,7 @@ use Eclipse\Core\Services\Registry; use Illuminate\Http\Request; use Opcodes\LogViewer\Facades\LogViewer; +use Spatie\Permission\PermissionRegistrar; use Symfony\Component\HttpFoundation\Response; class SetupSite @@ -26,6 +27,8 @@ public function handle(Request $request, Closure $next): Response Registry::setSite($site); + app(PermissionRegistrar::class)->setPermissionsTeamId($site->id); + // Set log viewer restriction... must be done after the site is initialized LogViewer::auth(function ($request) { return $request->user() && $request->user()->hasRole('super_admin'); diff --git a/src/Models/User.php b/src/Models/User.php index e188ca6..667610a 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -47,6 +47,8 @@ class User extends Authenticatable implements FilamentUser, HasAvatar, HasMedia, protected $dates = ['deleted_at']; + protected $guard_name = 'web'; + /** * The attributes that are mass assignable. * From ff779b01ba1f5827a19491a2f3d10ad81720e656 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Wed, 24 Sep 2025 12:55:46 +0200 Subject: [PATCH 08/20] Revert "chore: fix attempt" This reverts commit 6dd963322d5a0ccb8d06b98f9a72ed4f3d3352d6. --- src/Http/Middleware/SetupSite.php | 3 --- src/Models/User.php | 2 -- 2 files changed, 5 deletions(-) diff --git a/src/Http/Middleware/SetupSite.php b/src/Http/Middleware/SetupSite.php index 29024b5..c817d3d 100644 --- a/src/Http/Middleware/SetupSite.php +++ b/src/Http/Middleware/SetupSite.php @@ -7,7 +7,6 @@ use Eclipse\Core\Services\Registry; use Illuminate\Http\Request; use Opcodes\LogViewer\Facades\LogViewer; -use Spatie\Permission\PermissionRegistrar; use Symfony\Component\HttpFoundation\Response; class SetupSite @@ -27,8 +26,6 @@ public function handle(Request $request, Closure $next): Response Registry::setSite($site); - app(PermissionRegistrar::class)->setPermissionsTeamId($site->id); - // Set log viewer restriction... must be done after the site is initialized LogViewer::auth(function ($request) { return $request->user() && $request->user()->hasRole('super_admin'); diff --git a/src/Models/User.php b/src/Models/User.php index 667610a..e188ca6 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -47,8 +47,6 @@ class User extends Authenticatable implements FilamentUser, HasAvatar, HasMedia, protected $dates = ['deleted_at']; - protected $guard_name = 'web'; - /** * The attributes that are mass assignable. * From 57d196a363c1ecab7c0b16cc3f12be89dff4fc38 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Wed, 24 Sep 2025 14:54:06 +0200 Subject: [PATCH 09/20] chore: fix seeder --- database/seeders/CoreSeeder.php | 40 ++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/database/seeders/CoreSeeder.php b/database/seeders/CoreSeeder.php index 8a4c54a..ce2a938 100644 --- a/database/seeders/CoreSeeder.php +++ b/database/seeders/CoreSeeder.php @@ -10,15 +10,8 @@ class CoreSeeder extends Seeder { public function run(): void { - // Seed locales $this->call(LocaleSeeder::class); - // Seed sites - $this->call(SiteSeeder::class); - - // Set permissions team ID - setPermissionsTeamId(Site::first()->id); - // Seed roles and permissions with Filament Shield plugin Artisan::call('shield:generate', [ '--all' => null, @@ -29,7 +22,38 @@ public function run(): void // Seed additional roles $this->call(RoleSeeder::class); - // Seed users + // Sites + $this->call(SiteSeeder::class); + + // Assign permissions to roles + $this->assignPermissionsToRoles(); + + // Users $this->call(UserSeeder::class); } + + private function assignPermissionsToRoles(): void + { + $allPermissions = \Eclipse\Core\Models\User\Permission::all(); + $primarySite = Site::first(); + + $superAdminRoles = \Eclipse\Core\Models\User\Role::where('name', 'super_admin')->get(); + $adminRoles = \Eclipse\Core\Models\User\Role::where('name', 'admin')->get(); + + foreach ($superAdminRoles as $role) { + if (! $role->site_id) { + $role->site_id = $primarySite->id; + $role->save(); + } + $role->syncPermissions($allPermissions); + } + + foreach ($adminRoles as $role) { + if (! $role->site_id) { + $role->site_id = $primarySite->id; + $role->save(); + } + $role->syncPermissions($allPermissions); + } + } } From 15fc93ce508c4d2621cbe772abeb29ff9e4a26f9 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Thu, 25 Sep 2025 10:46:03 +0200 Subject: [PATCH 10/20] chore: further fixes --- composer.json | 14 ++------------ database/seeders/CoreSeeder.php | 28 +++++++++++----------------- database/seeders/RoleSeeder.php | 6 +++++- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/composer.json b/composer.json index b06235e..752bbbe 100644 --- a/composer.json +++ b/composer.json @@ -47,8 +47,8 @@ "bezhansalleh/filament-shield": "^4.0", "datalinx/php-utils": "^2.5", "dutchcodingcompany/filament-developer-logins": "^2.0", - "eclipsephp/common": "dev-f4@dev", - "eclipsephp/world-plugin": "dev-f4@dev", + "eclipsephp/common": "dev-main", + "eclipsephp/world-plugin": "dev-main", "filament/filament": "^4.0", "filament/spatie-laravel-media-library-plugin": "^4.0", "lara-zeus/spatie-translatable": "^1.0", @@ -70,16 +70,6 @@ "symfony/mailgun-mailer": "^7.3", "typesense/typesense-php": "^5.0" }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/KilianTrunk/eclipsephp-common.git" - }, - { - "type": "vcs", - "url": "https://github.com/KilianTrunk/eclipsephp-world-plugin.git" - } - ], "require-dev": { "laravel/pint": "^1.21", "orchestra/testbench": "^10.0", diff --git a/database/seeders/CoreSeeder.php b/database/seeders/CoreSeeder.php index ce2a938..14e354c 100644 --- a/database/seeders/CoreSeeder.php +++ b/database/seeders/CoreSeeder.php @@ -3,6 +3,8 @@ 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; @@ -34,26 +36,18 @@ public function run(): void private function assignPermissionsToRoles(): void { - $allPermissions = \Eclipse\Core\Models\User\Permission::all(); - $primarySite = Site::first(); + $allPermissions = Permission::all(); - $superAdminRoles = \Eclipse\Core\Models\User\Role::where('name', 'super_admin')->get(); - $adminRoles = \Eclipse\Core\Models\User\Role::where('name', 'admin')->get(); + foreach (Site::all() as $site) { + foreach (['super_admin', 'admin'] as $roleName) { + $role = Role::firstOrCreate([ + 'name' => $roleName, + 'guard_name' => 'web', + 'site_id' => $site->id, + ]); - foreach ($superAdminRoles as $role) { - if (! $role->site_id) { - $role->site_id = $primarySite->id; - $role->save(); + $role->syncPermissions($allPermissions); } - $role->syncPermissions($allPermissions); - } - - foreach ($adminRoles as $role) { - if (! $role->site_id) { - $role->site_id = $primarySite->id; - $role->save(); - } - $role->syncPermissions($allPermissions); } } } 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); } } From 989681181e055087d7f93f5f6a60f02fff5b2330 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Sun, 5 Oct 2025 13:49:59 +0200 Subject: [PATCH 11/20] chore: permissions f4 syntax & minor other fixes --- config/filament-shield.php | 42 ++++++++++++++- .../filament/components/my-settings.blade.php | 5 +- src/Filament/Pages/ManageUserSettings.php | 3 +- src/Filament/Resources/LocaleResource.php | 14 +---- src/Filament/Resources/MailLogResource.php | 11 +--- src/Filament/Resources/SiteResource.php | 14 +---- src/Filament/Resources/UserResource.php | 23 +------- .../Resources/UserResource/Pages/ViewUser.php | 2 +- src/Policies/LocalePolicy.php | 24 +++++---- src/Policies/MailLogPolicy.php | 12 +++-- src/Policies/SitePolicy.php | 24 +++++---- src/Policies/User/RolePolicy.php | 28 +++++----- src/Policies/UserPolicy.php | 52 +++++++++---------- 13 files changed, 126 insertions(+), 128 deletions(-) diff --git a/config/filament-shield.php b/config/filament-shield.php index 8f5784e..228fbba 100644 --- a/config/filament-shield.php +++ b/config/filament-shield.php @@ -1,6 +1,10 @@ 'model', 'manage' => [ RoleResource::class => [ - 'viewAny', 'view', 'create', 'update', 'delete', + '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', ], ], 'exclude' => [], 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/Filament/Pages/ManageUserSettings.php b/src/Filament/Pages/ManageUserSettings.php index 39a82c3..53641bc 100644 --- a/src/Filament/Pages/ManageUserSettings.php +++ b/src/Filament/Pages/ManageUserSettings.php @@ -28,7 +28,8 @@ public function form(Schema $schema): Schema ->label('Outgoing email address'), 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 d5e5bf7..ad30a5a 100644 --- a/src/Filament/Resources/LocaleResource.php +++ b/src/Filament/Resources/LocaleResource.php @@ -2,7 +2,6 @@ namespace Eclipse\Core\Filament\Resources; -use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions; use Eclipse\Common\Foundation\Models\Scopes\ActiveScope; use Eclipse\Core\Filament\Resources\LocaleResource\Pages\CreateLocale; use Eclipse\Core\Filament\Resources\LocaleResource\Pages\EditLocale; @@ -24,7 +23,7 @@ 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; @@ -188,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/MailLogResource.php b/src/Filament/Resources/MailLogResource.php index 09d83d9..6a279ba 100644 --- a/src/Filament/Resources/MailLogResource.php +++ b/src/Filament/Resources/MailLogResource.php @@ -2,7 +2,6 @@ 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; @@ -23,7 +22,7 @@ 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; @@ -291,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/SiteResource.php b/src/Filament/Resources/SiteResource.php index 4ef5a2a..cde7022 100644 --- a/src/Filament/Resources/SiteResource.php +++ b/src/Filament/Resources/SiteResource.php @@ -2,7 +2,6 @@ namespace Eclipse\Core\Filament\Resources; -use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions; use Eclipse\Core\Filament\Resources\SiteResource\Pages\CreateSite; use Eclipse\Core\Filament\Resources\SiteResource\Pages\EditSite; use Eclipse\Core\Filament\Resources\SiteResource\Pages\ListSites; @@ -18,7 +17,7 @@ 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; @@ -102,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/UserResource.php b/src/Filament/Resources/UserResource.php index e673880..8aa9206 100644 --- a/src/Filament/Resources/UserResource.php +++ b/src/Filament/Resources/UserResource.php @@ -2,7 +2,6 @@ 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\UserResource\Pages\CreateUser; @@ -46,9 +45,9 @@ 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; @@ -362,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/ViewUser.php b/src/Filament/Resources/UserResource/Pages/ViewUser.php index 815a8a4..068c650 100644 --- a/src/Filament/Resources/UserResource/Pages/ViewUser.php +++ b/src/Filament/Resources/UserResource/Pages/ViewUser.php @@ -9,7 +9,7 @@ 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 { 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'); } } From c5820dca72623a399ff4740def89c5b0574f517d Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Sun, 5 Oct 2025 14:19:46 +0200 Subject: [PATCH 12/20] chore: make panel nullable prop for tests --- src/Models/User.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Models/User.php b/src/Models/User.php index e188ca6..c5dc71f 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -119,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; } From 5c69bf628f15836180d7f30c0757bb4e5c0e4320 Mon Sep 17 00:00:00 2001 From: KilianTrunk <75316208+KilianTrunk@users.noreply.github.com> Date: Sun, 5 Oct 2025 12:20:03 +0000 Subject: [PATCH 13/20] style: fix code style --- src/Models/User.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Models/User.php b/src/Models/User.php index c5dc71f..7d28a8d 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -119,12 +119,12 @@ public function canAccessTenant(Model $tenant): bool return $this->sites()->whereKey($tenant)->exists(); } - public function getTenants(Panel $panel = null): Collection + public function getTenants(?Panel $panel = null): Collection { return $this->sites()->where('is_active', true)->get(); } - public function canAccessPanel(Panel $panel = null): bool + public function canAccessPanel(?Panel $panel = null): bool { return true; } From 9536c7509a17b737dc9572f636c42dcbabcf44d6 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Sun, 5 Oct 2025 14:48:05 +0200 Subject: [PATCH 14/20] chore: add custom permissions handling --- config/filament-shield.php | 7 +++++-- database/seeders/CoreSeeder.php | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/config/filament-shield.php b/config/filament-shield.php index 228fbba..415dd5f 100644 --- a/config/filament-shield.php +++ b/config/filament-shield.php @@ -18,7 +18,7 @@ 'pages' => true, 'widgets' => true, 'resources' => true, - 'custom_permissions' => false, + 'custom_permissions' => true, ], ], @@ -125,7 +125,10 @@ ], ], - 'custom_permissions' => [], + 'custom_permissions' => [ + 'impersonate_user', + 'send_email_user', + ], 'discovery' => [ 'discover_all_resources' => false, diff --git a/database/seeders/CoreSeeder.php b/database/seeders/CoreSeeder.php index 14e354c..6644ddb 100644 --- a/database/seeders/CoreSeeder.php +++ b/database/seeders/CoreSeeder.php @@ -49,5 +49,15 @@ private function assignPermissionsToRoles(): void $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); } } From 8008e92c4f806135ce5b7e29e1788c3a4a788581 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Sun, 5 Oct 2025 14:50:01 +0200 Subject: [PATCH 15/20] chore: update test runner --- .github/workflows/test-runner.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-runner.yml b/.github/workflows/test-runner.yml index b78f75f..8376556 100644 --- a/.github/workflows/test-runner.yml +++ b/.github/workflows/test-runner.yml @@ -3,13 +3,11 @@ name: Tests on: # Run testing on all push and pull requests for the main branch 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: From 9c15559fc74855987eac02ac3c8114a2d06a3877 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Sun, 5 Oct 2025 14:52:46 +0200 Subject: [PATCH 16/20] chore: update comment --- .github/workflows/test-runner.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-runner.yml b/.github/workflows/test-runner.yml index 8376556..fd9ce47 100644 --- a/.github/workflows/test-runner.yml +++ b/.github/workflows/test-runner.yml @@ -1,7 +1,7 @@ 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: paths: - '**/*.php' From 81e5ad3780af7aa8264ec87be74761ae7d8a0c8e Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Wed, 8 Oct 2025 13:04:43 +0200 Subject: [PATCH 17/20] chore: test & shield fixes --- config/filament-shield.php | 49 +--------------- src/EclipseServiceProvider.php | 58 +++++++++++++++++++ .../Filament/Resources/LocaleResourceTest.php | 23 +++++++- .../Resources/MailLogResourceTest.php | 1 + .../Filament/Resources/UserResourceTest.php | 1 + tests/Feature/UserImpersonationTest.php | 11 ++-- tests/TestCase.php | 2 +- 7 files changed, 88 insertions(+), 57 deletions(-) diff --git a/config/filament-shield.php b/config/filament-shield.php index 415dd5f..2d8bbb8 100644 --- a/config/filament-shield.php +++ b/config/filament-shield.php @@ -1,10 +1,5 @@ [ 'path' => app_path('Policies'), - 'merge' => true, + 'merge' => false, 'generate' => true, 'methods' => [ 'viewAny', 'view', 'create', 'update', 'restore', 'restoreAny', @@ -64,47 +59,7 @@ 'resources' => [ 'subject' => 'model', - 'manage' => [ - 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', - ], - ], + 'manage' => [], 'exclude' => [], ], diff --git a/src/EclipseServiceProvider.php b/src/EclipseServiceProvider.php index 7ac2cfe..13a2f18 100644 --- a/src/EclipseServiceProvider.php +++ b/src/EclipseServiceProvider.php @@ -2,6 +2,7 @@ namespace Eclipse\Core; +use BezhanSalleh\FilamentShield\Resources\Roles\RoleResource; use BezhanSalleh\LanguageSwitch\LanguageSwitch; use Eclipse\Common\Foundation\Providers\PackageServiceProvider; use Eclipse\Common\Package; @@ -9,6 +10,10 @@ 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; @@ -118,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/tests/Feature/Filament/Resources/LocaleResourceTest.php b/tests/Feature/Filament/Resources/LocaleResourceTest.php index 5e2877b..7ac6411 100644 --- a/tests/Feature/Filament/Resources/LocaleResourceTest.php +++ b/tests/Feature/Filament/Resources/LocaleResourceTest.php @@ -3,12 +3,15 @@ use Eclipse\Core\Filament\Resources\LocaleResource; use Eclipse\Core\Filament\Resources\LocaleResource\Pages\ListLocales; use Eclipse\Core\Models\Locale; +use Eclipse\Core\Models\Site; +use Filament\Facades\Filament; 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 () { @@ -48,8 +51,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', @@ -61,7 +70,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(); }); @@ -71,9 +84,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(); @@ -88,6 +104,7 @@ $locale = Locale::factory()->create(); $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 597f280..8d671af 100644 --- a/tests/Feature/Filament/Resources/MailLogResourceTest.php +++ b/tests/Feature/Filament/Resources/MailLogResourceTest.php @@ -9,6 +9,7 @@ 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 fe0990e..ec82038 100644 --- a/tests/Feature/Filament/Resources/UserResourceTest.php +++ b/tests/Feature/Filament/Resources/UserResourceTest.php @@ -13,6 +13,7 @@ beforeEach(function () { $this->set_up_super_admin_and_tenant(); + UserResource::canViewAny(); }); test('authorized access can be allowed', function () { 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/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); From d05c8dac974f49039c364792e09ee7429178c83e Mon Sep 17 00:00:00 2001 From: KilianTrunk <75316208+KilianTrunk@users.noreply.github.com> Date: Wed, 8 Oct 2025 11:05:07 +0000 Subject: [PATCH 18/20] style: fix code style --- tests/Feature/Filament/Resources/LocaleResourceTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Feature/Filament/Resources/LocaleResourceTest.php b/tests/Feature/Filament/Resources/LocaleResourceTest.php index 7ac6411..476c27b 100644 --- a/tests/Feature/Filament/Resources/LocaleResourceTest.php +++ b/tests/Feature/Filament/Resources/LocaleResourceTest.php @@ -3,8 +3,6 @@ use Eclipse\Core\Filament\Resources\LocaleResource; use Eclipse\Core\Filament\Resources\LocaleResource\Pages\ListLocales; use Eclipse\Core\Models\Locale; -use Eclipse\Core\Models\Site; -use Filament\Facades\Filament; use Illuminate\Support\Arr; use function Pest\Livewire\livewire; From df6df1c5b5bb308ad9b8cf208b8ef49030f1f497 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Wed, 8 Oct 2025 14:32:57 +0200 Subject: [PATCH 19/20] chore: trigger-rerun of tests --- tests/Feature/Filament/Resources/LocaleResourceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Feature/Filament/Resources/LocaleResourceTest.php b/tests/Feature/Filament/Resources/LocaleResourceTest.php index 476c27b..a3654a6 100644 --- a/tests/Feature/Filament/Resources/LocaleResourceTest.php +++ b/tests/Feature/Filament/Resources/LocaleResourceTest.php @@ -13,7 +13,7 @@ }); test('unauthorized access can be prevented', function () { - // Create regular user with no permissions + // Create regular user with no permission $this->set_up_common_user_and_tenant(); // Create test locale From d3095fa90541db67499b3e73b3bb564072ccb8f4 Mon Sep 17 00:00:00 2001 From: Kilian Trunk Date: Wed, 8 Oct 2025 14:33:05 +0200 Subject: [PATCH 20/20] Revert "chore: trigger-rerun of tests" This reverts commit df6df1c5b5bb308ad9b8cf208b8ef49030f1f497. --- tests/Feature/Filament/Resources/LocaleResourceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Feature/Filament/Resources/LocaleResourceTest.php b/tests/Feature/Filament/Resources/LocaleResourceTest.php index a3654a6..476c27b 100644 --- a/tests/Feature/Filament/Resources/LocaleResourceTest.php +++ b/tests/Feature/Filament/Resources/LocaleResourceTest.php @@ -13,7 +13,7 @@ }); test('unauthorized access can be prevented', function () { - // Create regular user with no permission + // Create regular user with no permissions $this->set_up_common_user_and_tenant(); // Create test locale