diff --git a/docs/content/2.essentials/1.configuration.md b/docs/content/2.essentials/1.configuration.md index 1bec9ff9..c235c00b 100644 --- a/docs/content/2.essentials/1.configuration.md +++ b/docs/content/2.essentials/1.configuration.md @@ -135,6 +135,46 @@ Configure the custom fields management page: ], ``` +#### Replacing the Management Page + +The config above covers the common cases. Filament reads some things off the page +class itself rather than from config — sub-navigation, breadcrumbs, header actions — +so when you need one of those, register your own page instead: + +```php +use App\Filament\Pages\Settings\CustomFields; + +CustomFieldsPlugin::make() + ->managementPage(CustomFields::class) +``` + +```php +namespace App\Filament\Pages\Settings; + +use Filament\Panel; +use Filament\Pages\Enums\SubNavigationPosition; +use Relaticle\CustomFields\Filament\Management\Pages\CustomFieldsManagementPage; + +class CustomFields extends CustomFieldsManagementPage +{ + protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Start; + + public static function getSlug(?Panel $panel = null): string + { + return 'settings/custom-fields'; + } + + public function getSubNavigation(): array + { + // Render this page inside your own settings navigation. + } +} +``` + +The page must extend `CustomFieldsManagementPage`, and it replaces the packaged page +rather than sitting alongside it — only one management page is registered on the panel, +so there is no second route to the same screen. + ### Database Configuration Customize table names and paths: diff --git a/src/CustomFieldsPlugin.php b/src/CustomFieldsPlugin.php index 1102b0e2..13700e16 100644 --- a/src/CustomFieldsPlugin.php +++ b/src/CustomFieldsPlugin.php @@ -10,6 +10,7 @@ use Filament\Panel; use Filament\Support\Concerns\EvaluatesClosures; use Filament\Support\Enums\Width; +use InvalidArgumentException; use Relaticle\CustomFields\Enums\CustomFieldsFeature; use Relaticle\CustomFields\Facades\CustomFieldsType; use Relaticle\CustomFields\FeatureSystem\FeatureManager; @@ -29,6 +30,9 @@ class CustomFieldsPlugin implements Plugin protected Width|Closure|null $sectionModalWidth = null; + /** @var class-string|null */ + protected ?string $managementPage = null; + public function getId(): string { return 'custom-fields'; @@ -38,7 +42,7 @@ public function register(Panel $panel): void { $panel ->pages([ - CustomFieldsManagementPage::class, + $this->getManagementPage(), ]) ->tenantMiddleware([SetTenantContextMiddleware::class], true); } @@ -119,6 +123,39 @@ public function sectionModalWidth(Width|Closure|null $width): static return $this; } + /** + * Register your own management page in place of the packaged one, so the host + * application controls the things Filament reads off the page class itself — + * slug, sub-navigation, cluster, heading. The subclass keeps all packaged + * behaviour; only the page registered on the panel changes. + * + * @param class-string $page + */ + public function managementPage(string $page): static + { + // is_a() rather than is_subclass_of(), so passing the packaged page + // itself is an explicit no-op rather than an error. + if (! is_a($page, CustomFieldsManagementPage::class, allow_string: true)) { + throw new InvalidArgumentException(sprintf( + '[%s] must extend [%s] to be used as the management page.', + $page, + CustomFieldsManagementPage::class, + )); + } + + $this->managementPage = $page; + + return $this; + } + + /** + * @return class-string + */ + public function getManagementPage(): string + { + return $this->managementPage ?? CustomFieldsManagementPage::class; + } + public function getSectionModalWidth(): Width { $width = $this->evaluate($this->sectionModalWidth); diff --git a/tests/Feature/ManagementPageOverrideTest.php b/tests/Feature/ManagementPageOverrideTest.php new file mode 100644 index 00000000..bf999a88 --- /dev/null +++ b/tests/Feature/ManagementPageOverrideTest.php @@ -0,0 +1,45 @@ +getManagementPage()) + ->toBe(CustomFieldsManagementPage::class); +}); + +it('registers an overridden management page', function (): void { + expect(CustomFieldsPlugin::make()->managementPage(CustomManagementPage::class)->getManagementPage()) + ->toBe(CustomManagementPage::class); +}); + +it('accepts the packaged page itself as an explicit no-op', function (): void { + expect(CustomFieldsPlugin::make()->managementPage(CustomFieldsManagementPage::class)->getManagementPage()) + ->toBe(CustomFieldsManagementPage::class); +}); + +it('rejects a management page that does not extend the packaged one', function (): void { + CustomFieldsPlugin::make()->managementPage(Panel::class); +})->throws(InvalidArgumentException::class, 'must extend'); + +it('puts only the overridden page on the panel', function (): void { + $panel = Panel::make(); + + CustomFieldsPlugin::make()->managementPage(CustomManagementPage::class)->register($panel); + + expect($panel->getPages()) + ->toContain(CustomManagementPage::class) + ->not->toContain(CustomFieldsManagementPage::class); +}); + +it('puts the packaged page on the panel when not overridden', function (): void { + $panel = Panel::make(); + + CustomFieldsPlugin::make()->register($panel); + + expect($panel->getPages())->toContain(CustomFieldsManagementPage::class); +}); diff --git a/tests/Fixtures/Pages/CustomManagementPage.php b/tests/Fixtures/Pages/CustomManagementPage.php new file mode 100644 index 00000000..211d095c --- /dev/null +++ b/tests/Fixtures/Pages/CustomManagementPage.php @@ -0,0 +1,16 @@ +