From e15960c743cd3479c6214c2ffaa8b73751be21c2 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Sun, 14 Jan 2024 10:44:47 +0000 Subject: [PATCH] cluster improvements & docs --- packages/panels/docs/02-getting-started.md | 2 +- .../docs/03-resources/01-getting-started.md | 4 + .../docs/03-resources/04-editing-records.md | 2 + .../docs/03-resources/05-viewing-records.md | 2 + .../docs/03-resources/07-relation-managers.md | 2 + packages/panels/docs/04-pages.md | 19 ++-- packages/panels/docs/05-dashboard.md | 2 +- packages/panels/docs/06-navigation.md | 32 +++++- packages/panels/docs/09-configuration.md | 2 +- packages/panels/docs/10-clusters.md | 88 +++++++++++++++ .../docs/{10-tenancy.md => 11-tenancy.md} | 6 +- .../docs/{11-themes.md => 12-themes.md} | 0 .../docs/{12-plugins.md => 13-plugins.md} | 2 +- .../docs/{13-testing.md => 14-testing.md} | 0 ...4-upgrade-guide.md => 15-upgrade-guide.md} | 0 .../views/components/layout/base.blade.php | 11 ++ .../Commands/Aliases/MakeClusterCommand.php | 12 +++ .../src/Commands/MakeClusterCommand.php | 102 ++++++++++++++++++ .../panels/src/Commands/MakePageCommand.php | 16 +++ .../src/Commands/MakeResourceCommand.php | 15 +++ .../panels/src/FilamentServiceProvider.php | 1 + packages/panels/stubs/Cluster.stub | 10 ++ packages/panels/stubs/Page.stub | 4 +- packages/panels/stubs/Resource.stub | 4 +- .../Aliases/MakeSettingsPageCommand.php | 2 +- .../src/Commands/MakeSettingsPageCommand.php | 70 ++++++++++-- .../stubs/SettingsPage.stub | 4 +- 27 files changed, 376 insertions(+), 38 deletions(-) create mode 100644 packages/panels/docs/10-clusters.md rename packages/panels/docs/{10-tenancy.md => 11-tenancy.md} (98%) rename packages/panels/docs/{11-themes.md => 12-themes.md} (100%) rename packages/panels/docs/{12-plugins.md => 13-plugins.md} (98%) rename packages/panels/docs/{13-testing.md => 14-testing.md} (100%) rename packages/panels/docs/{14-upgrade-guide.md => 15-upgrade-guide.md} (100%) create mode 100644 packages/panels/src/Commands/Aliases/MakeClusterCommand.php create mode 100644 packages/panels/src/Commands/MakeClusterCommand.php create mode 100644 packages/panels/stubs/Cluster.stub diff --git a/packages/panels/docs/02-getting-started.md b/packages/panels/docs/02-getting-started.md index a15599073a..43c54cb87f 100644 --- a/packages/panels/docs/02-getting-started.md +++ b/packages/panels/docs/02-getting-started.md @@ -146,7 +146,7 @@ This will create several files in the `app/Filament/Resources` directory: | | +-- ListPatients.php ``` -Visit `/admin/patients` in your browser and observe a new link called "Patients" in the sidebar. Clicking the link will display an empty table. Let's add a form to create new patients. +Visit `/admin/patients` in your browser and observe a new link called "Patients" in the navigation. Clicking the link will display an empty table. Let's add a form to create new patients. ### Setting up the resource form diff --git a/packages/panels/docs/03-resources/01-getting-started.md b/packages/panels/docs/03-resources/01-getting-started.md index b5f954b061..9e90d29f4b 100644 --- a/packages/panels/docs/03-resources/01-getting-started.md +++ b/packages/panels/docs/03-resources/01-getting-started.md @@ -364,6 +364,8 @@ public static function getNavigationParentItem(): ?string } ``` +> If you're reaching for a third level of navigation like this, you should consider using [clusters](clusters) instead, which are a logical grouping of resources and [custom pages](../pages), which can share their own separate navigation. + ## Generating URLs to resource pages Filament provides `getUrl()` static method on resource classes to generate URLs to resources and specific pages within them. Traditionally, you would need to construct the URL by hand or by using Laravel's `route()` helper, but these methods depend on knowledge of the resource's slug or route naming conventions. @@ -506,6 +508,8 @@ public static function getRecordSubNavigation(Page $page): array Each item in the sub-navigation can be customized using the [same navigation methods as normal pages](../navigation). +> If you're looking to add sub-navigation to switch *between* entire resources and [custom pages](../pages), you might be looking for [clusters](../clusters), which are used to group these together. The `getRecordSubNavigation()` method is intended to construct a navigation between pages that relate to a particular record *inside* a resource. + ### Sub-navigation position The sub-navigation is rendered at the start of the page by default. You may change the position by setting the `$subNavigationPosition` property on the resource. The value may be `SubNavigationPosition::Start`, `SubNavigationPosition::End`, or `SubNavigationPosition::Top` to render the sub-navigation as tabs: diff --git a/packages/panels/docs/03-resources/04-editing-records.md b/packages/panels/docs/03-resources/04-editing-records.md index e1193e9385..c0b8668ff2 100644 --- a/packages/panels/docs/03-resources/04-editing-records.md +++ b/packages/panels/docs/03-resources/04-editing-records.md @@ -311,6 +311,8 @@ public function form(Form $form): Form } ``` +## Adding edit pages to resource sub-navigation + If you're using [resource sub-navigation](getting-started#resource-sub-navigation), you can register this page as normal in `getRecordSubNavigation()` of the resource: ```php diff --git a/packages/panels/docs/03-resources/05-viewing-records.md b/packages/panels/docs/03-resources/05-viewing-records.md index a02948ea0c..a98f93f57b 100644 --- a/packages/panels/docs/03-resources/05-viewing-records.md +++ b/packages/panels/docs/03-resources/05-viewing-records.md @@ -147,6 +147,8 @@ public function infolist(Infolist $infolist): Infolist } ``` +## Adding view pages to resource sub-navigation + If you're using [resource sub-navigation](getting-started#resource-sub-navigation), you can register this page as normal in `getRecordSubNavigation()` of the resource: ```php diff --git a/packages/panels/docs/03-resources/07-relation-managers.md b/packages/panels/docs/03-resources/07-relation-managers.md index ca162d1b37..aa58d6f4ed 100644 --- a/packages/panels/docs/03-resources/07-relation-managers.md +++ b/packages/panels/docs/03-resources/07-relation-managers.md @@ -859,6 +859,8 @@ public static function getPages(): array Now, you can customize the page in exactly the same way as a relation manager, with the same `table()` and `form()`. +### Adding relation pages to resource sub-navigation + If you're using [resource sub-navigation](getting-started#resource-sub-navigation), you can register this page as normal in `getRecordSubNavigation()` of the resource: ```php diff --git a/packages/panels/docs/04-pages.md b/packages/panels/docs/04-pages.md index 9c52f32666..90d22f1d4c 100644 --- a/packages/panels/docs/04-pages.md +++ b/packages/panels/docs/04-pages.md @@ -18,26 +18,17 @@ This command will create two files - a page class in the `/Pages` directory of t Page classes are all full-page [Livewire](https://livewire.laravel.com) components with a few extra utilities you can use with the panel. -## Conditionally hiding pages in navigation +## Authorization -You can prevent pages from appearing in the menu by overriding the `shouldRegisterNavigation()` method in your Page class. This is useful if you want to control which users can see the page in the sidebar. +You can prevent pages from appearing in the menu by overriding the `canAccess()` method in your Page class. This is useful if you want to control which users can see the page in the navigation, and also which users can visit the page directly: ```php -public static function shouldRegisterNavigation(): bool +public static function canAccess(): bool { return auth()->user()->canManageSettings(); } ``` -Please be aware that all users will still be able to visit this page through its direct URL, so to fully limit access, you must also check in the `mount()` method of the page: - -```php -public function mount(): void -{ - abort_unless(auth()->user()->canManageSettings(), 403); -} -``` - ## Adding actions to pages Actions are buttons that can perform tasks on the page, or visit a URL. You can read more about their capabilities [here](../actions). @@ -336,3 +327,7 @@ use App\Filament\Pages\Settings; Settings::getUrl(panel: 'marketing'); ``` + +## Adding sub-navigation between pages + +You may want to add a common sub-navigation to multiple pages, to allow users to quickly navigate between them. You can do this by defining a [cluster](clusters). Clusters can also contain [resources](resources), and you can switch between multiple pages or resources within a cluster. diff --git a/packages/panels/docs/05-dashboard.md b/packages/panels/docs/05-dashboard.md index ef2607c419..e3942cf9b2 100644 --- a/packages/panels/docs/05-dashboard.md +++ b/packages/panels/docs/05-dashboard.md @@ -274,7 +274,7 @@ You may also customize the title of the dashboard by overriding the `$title` pro protected static ?string $title = 'Finance dashboard'; ``` -The primary dashboard shown to a user is the first one they have access to (controlled by `canAccess()` method), according to the defined navigation sort order. +The primary dashboard shown to a user is the first one they have access to (controlled by [`canAccess()` method](pages#authorization)), according to the defined navigation sort order. The default sort order for dashboards is `-2`. You can control the sort order of custom dashboards with `$navigationSort`: diff --git a/packages/panels/docs/06-navigation.md b/packages/panels/docs/06-navigation.md index 1ca4267400..01ee526a4f 100644 --- a/packages/panels/docs/06-navigation.md +++ b/packages/panels/docs/06-navigation.md @@ -4,7 +4,9 @@ title: Navigation ## Overview -By default, Filament will register navigation items for each of your [resources](resources/getting-started) and [custom pages](pages). These classes contain static properties and methods that you can override, to configure that navigation item. +By default, Filament will register navigation items for each of your [resources](resources/getting-started), [custom pages](pages), and [clusters](clusters). These classes contain static properties and methods that you can override, to configure that navigation item. + +If you're looking to add a second layer of navigation to your app, you can use [clusters](clusters). These are useful for grouping resources and pages together. ## Customizing a navigation item's label @@ -79,9 +81,9 @@ You may group navigation items by specifying a `$navigationGroup` property on a protected static ?string $navigationGroup = 'Settings'; ``` -All items in the same navigation group will be displayed together under the same group label, "Settings" in this case. Ungrouped items will remain at the top of the sidebar. +All items in the same navigation group will be displayed together under the same group label, "Settings" in this case. Ungrouped items will remain at the start of the navigation. -#### Grouping navigation items under other items +### Grouping navigation items under other items You may group navigation items as children of other items, by passing the label of the parent item as the `$navigationParentItem`: @@ -91,8 +93,19 @@ protected static ?string $navigationParentItem = 'Notifications'; protected static ?string $navigationGroup = 'Settings'; ``` +You may also use the `getNavigationParentItem()` method to set a dynamic parent item label: + +```php +public static function getNavigationParentItem(): ?string +{ + return __('filament/navigation.groups.settings.items.notifications'); +} +``` + As seen above, if the parent item has a navigation group, that navigation group must also be defined, so the correct parent item can be identified. +> If you're reaching for a third level of navigation like this, you should consider using [clusters](clusters) instead, which are a logical grouping of resources and custom pages, which can share their own separate navigation. + ### Customizing navigation groups You may customize navigation groups by calling `navigationGroups()` in the [configuration](configuration), and passing `NavigationGroup` objects in order: @@ -124,7 +137,7 @@ In this example, we pass in a custom `icon()` for the groups, and make one `coll #### Ordering navigation groups -By using `navigationGroups()`, you are defining a new order for the navigation groups in the sidebar. If you just want to reorder the groups and not define an entire `NavigationGroup` object, you may just pass the labels of the groups in the new order: +By using `navigationGroups()`, you are defining a new order for the navigation groups. If you just want to reorder the groups and not define an entire `NavigationGroup` object, you may just pass the labels of the groups in the new order: ```php $panel @@ -238,6 +251,17 @@ To prevent resources or pages from showing up in navigation, you may use: protected static bool $shouldRegisterNavigation = false; ``` +Or, you may override the `shouldRegisterNavigation()` method: + +```php +public static function shouldRegisterNavigation(): bool +{ + return false; +} +``` + +Please note that these methods do not control direct access to the resource or page. They only control whether the resource or page will show up in the navigation. If you want to also control access, then you should use [resource authorization](resources/getting-started#authorization) or [page authorization](pages#authorization). + ## Using top navigation By default, Filament will use a sidebar navigation. You may use a top navigation instead by using the [configuration](configuration): diff --git a/packages/panels/docs/09-configuration.md b/packages/panels/docs/09-configuration.md index 89d09783c8..639376247f 100644 --- a/packages/panels/docs/09-configuration.md +++ b/packages/panels/docs/09-configuration.md @@ -8,7 +8,7 @@ By default, the configuration file is located at `app/Providers/Filament/AdminPa ## Introducing panels -By default, when you install the package, there is one panel that has been set up for you - and it lives on `/admin`. All the [resources](resources/getting-started), [pages](pages), and [dashboard widgets](dashboard) you create get registered to this panel. +By default, when you install the package, there is one panel that has been set up for you - and it lives on `/admin`. All the [resources](resources/getting-started), [custom pages](pages), and [dashboard widgets](dashboard) you create get registered to this panel. However, you can create as many panels as you want, and each can have its own set of resources, pages and widgets. diff --git a/packages/panels/docs/10-clusters.md b/packages/panels/docs/10-clusters.md new file mode 100644 index 0000000000..dece5242e9 --- /dev/null +++ b/packages/panels/docs/10-clusters.md @@ -0,0 +1,88 @@ +--- +title: Clusters +--- + +## Overview + +Clusters are a hierarchical structure in panels that allow you to group [resources](resources) and [custom pages](pages) together. They are useful for organizing your panel into logical sections, and can help reduce the size of your panel's sidebar. + +When using a cluster, a few things happen: + +- A new navigation item is added to the navigation, which is a link to the first resource or page in the cluster. +- The individual navigation items for the resources or pages are no longer visible in the main navigation. +- A new sub-navigation UI is added to each resource or page in the cluster, which contains the navigation items for the resources or pages in the cluster. +- Resources and pages in the cluster get a new URL, prefixed with the name of the cluster. If you are generating URLs to [resources](resources/getting-started#generating-urls-to-resource-pages) and [pages](pages#generating-urls-to-pages) correctly, then this change should be handled for you automatically. +- The cluster's name is in the breadcrumbs of all resources and pages in the cluster. When clicking it, you are taken to the first resource or page in the cluster. + +## Creating a cluster + +Before creating your first cluster, you must tell the panel where cluster classes should be located. Alongside methods like `discoverResources()` and `discoverPages()` in the [configuration](configuration), you can use `discoverClusters()`: + +```php +public function panel(Panel $panel): Panel +{ + return $panel + // ... + ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources') + ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages') + ->discoverClusters(in: app_path('Filament/Clusters'), for: 'App\\Filament\\Clusters'); +} +``` + +Now, you can create a cluster with the `php artisan make:filament:cluster` command: + +```bash +php artisan make:filament:cluster Settings +``` + +This will create a new cluster class in the `app/Filament/Clusters` directory: + +```php + + document.addEventListener('DOMContentLoaded', () => { + setTimeout(() => { + const activeSidebarItem = document.querySelector('.fi-sidebar-item-active') + const sidebarWrapper = document.querySelector('.fi-sidebar-nav') + + sidebarWrapper.scrollTo(0, activeSidebarItem.offsetTop - (window.innerHeight / 2)) + }, 0) + }) + + @if (! filament()->hasDarkMode())