F
Filament3mo ago
nowak

How to avoid showing all widgets on all dashboard pages?

Hi, I want to create multiple dashboards, each with their own specific widgets. I have this kitchen dashboard for example:
?php

namespace App\Filament\Pages;

use App\Models\MealType;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Form;
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;

class KitchenDashboard extends BaseDashboard
{

use BaseDashboard\Concerns\HasFiltersForm;

protected static ?string $title = 'Kitchen Dashboard';
protected static ?string $navigationIcon = 'icon-chef-hat';
protected static string $routePath = 'kitchen';

public function filtersForm(Form $form): Form
{
return $form
->schema([
Section::make()
->schema([
DatePicker::make('deliveryDate')
->formatStateUsing(fn ($state) => $state ?? now()->format('Y-m-d')),
Select::make('mealType')
->options(fn() => MealType::all()
->sortBy('id')
->where('is_visible','true')
->pluck('name', 'id')
->map(function ($name) {
return ucfirst($name);
})
)
->default(2)
->selectablePlaceholder(false)
->native(false)
])
->columns(2),
]);
}
}
?php

namespace App\Filament\Pages;

use App\Models\MealType;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Form;
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;

class KitchenDashboard extends BaseDashboard
{

use BaseDashboard\Concerns\HasFiltersForm;

protected static ?string $title = 'Kitchen Dashboard';
protected static ?string $navigationIcon = 'icon-chef-hat';
protected static string $routePath = 'kitchen';

public function filtersForm(Form $form): Form
{
return $form
->schema([
Section::make()
->schema([
DatePicker::make('deliveryDate')
->formatStateUsing(fn ($state) => $state ?? now()->format('Y-m-d')),
Select::make('mealType')
->options(fn() => MealType::all()
->sortBy('id')
->where('is_visible','true')
->pluck('name', 'id')
->map(function ($name) {
return ucfirst($name);
})
)
->default(2)
->selectablePlaceholder(false)
->native(false)
])
->columns(2),
]);
}
}
This dashboard shows all my widgets in app/Filament/Widgets/, as if BaseDashboard automatically fetches and returns all existing widgets. How can I avoid this, and select which widgets should be shown on which dashboards?
24 Replies
awcodes
awcodes3mo ago
Move the widgets to a different location so they aren’t auto registered and register them manually in each panel service provider with ->widget()
nowak
nowakOP3mo ago
Thank you! But I only have one panel, and therefore only one app/Providers/Filament/AdminPanelProvider.php. I have multiple dashboard pages within the same panel. Should I register all of my widgets in this service provider with ->widgets([])?
awcodes
awcodes3mo ago
Hmm, multiple dashboards doesn’t make sense to me. In that case it sounds like you need to run checks in each widget that determines if the widget can be shown based on the panel’s id which you can get from the FilamentManger facade.
nowak
nowakOP3mo ago
Hmm.. Would you create multiple panels if you have a filament app for a food producing and delivery business, if the kitchen, the office, the couriers etc. need their own dasboard page, or would you just create multiple dashboard pages? I like the thought of separate panels, but I don't want to add complexity for no good reason. The documentation mentions multiple dashboard pages here: https://filamentphp.com/docs/3.x/panels/dashboard#creating-multiple-dashboards But the details of how to choose which widgets to show on which dashboard is, AFAIK, not documented. But I did just try to overwrite the getWidgets method from the Filament\Pages\Dashboard class:
/**
* @return array<class-string<Widget> | WidgetConfiguration>
*/
public function getWidgets(): array
{
return Filament::getWidgets();
}
/**
* @return array<class-string<Widget> | WidgetConfiguration>
*/
public function getWidgets(): array
{
return Filament::getWidgets();
}
like this in my KitchenDashboard page:
public function getWidgets(): array
{
return [
Widgets\KitchenStatsOverviewWidget::class,
Widgets\GroupOrdersTable::class
];
}
public function getWidgets(): array
{
return [
Widgets\KitchenStatsOverviewWidget::class,
Widgets\GroupOrdersTable::class
];
}
Which seems to give control over which widgets to show on each dashboard page, but I am not sure if this is the recommended way.
awcodes
awcodes3mo ago
There’s no real right or wrong way. Personally, if the 3 groups have different levels of access I would make separate panels. But the getWidgets method will work as long as they aren’t in the auto discover path in the panel service provider. So, you could use the auto discovery path for widgets that are shared across each one, and just make context aware widgets in a different directory and include them with an array_merge in each dashboard with the getWidgets method.
nowak
nowakOP3mo ago
Maybe I should look more into multiple panels, as I like the idea of keeping different access levels separated to their own space, instead of defining access on each page. But how does filament know what panel to show to which user? And can users jump between panels? Where can I read in-depth documentation about multiple filament panels? I feel like I need to expand my knowledge on multiple panels before I start playing with it. Re. dashboard widgets, I do not think the widgets need to be moved out of the auto discovery path when overwriting the getWidgets() method, it just seems to work.
awcodes
awcodes3mo ago
If you are overriding the methods the auto discover won’t matter unless you have widgets that you want to show regardless of the panel in which case you’ll want to consider a merge. For panel access that could either be handled with a middleware based on user role / permission or via the canAccessPanel method on the user model.
nowak
nowakOP3mo ago
If a user can access multiple panels, will there be a panels switcher at the top of the panel pages? Or is it simply a question of navigating to the specific panel url?
awcodes
awcodes3mo ago
Filament doesn’t know what panel to show other than what panels you tell it too. Not natively, but there is a panel switch plugin.
nowak
nowakOP3mo ago
So it is basically completely separate "admin" panels, like starting from scratch basically?
awcodes
awcodes3mo ago
In a sense, yes.
nowak
nowakOP3mo ago
But the resources are kept the same, in the same folder structure etc.? Or wait, no they can't be the same.
awcodes
awcodes3mo ago
But everything is reusable across panels. It’s just how OOP works. Each panel can register resources in the same way you can register widgets to each dashboard. It can be. That’s the beauty of it.
nowak
nowakOP3mo ago
But now as soon as I add a resource, it appears in my standard admin panel. If I then create a kitchen panel, I would expect that the same resources would appear in the same manner, basically resulting in no difference between the panels. But logically, the panels should show different resources, right? Like some resources should be shown to admins, and others for the kitchen staff. I know I can add resource and page access logic, but then the users would be able to access different resources based on their roles, regardless of whether the same or multiple panels are being used.
awcodes
awcodes3mo ago
Right but this is where the auto discovery comes into play. There flags you can pass the make commands to put them under specific panels for auto discovery, but it sounds to me that you need to abandon the auto discovery and load the manually where they are needed. It’s not wrong, just a use case for your app. If a resource isn’t registered with a panel then it won’t even exist under that panel.
nowak
nowakOP3mo ago
Where do I see what resources are registered with which panel?
awcodes
awcodes3mo ago
They are either auto discovered based on the path or you define them in the panel service provider. For instance if you remove ->discoverResources() from a panel service provider then it won’t have any resources at all. And you can register them with ->resources() instead manually.
nowak
nowakOP3mo ago
Ahhhh, it clicked now.
awcodes
awcodes3mo ago
Access to those resources should be defined by the model policies at that point.
nowak
nowakOP3mo ago
Yes, I use filament shield for roles and permissions, and model policies as well. Thanks a ton, this was very helpful!
awcodes
awcodes3mo ago
Hopefully it all makes a little more sense now. But there’s no right or wrong way, just what’s optimal for your app. And understanding there’s more than one way to accomplish the goal. Even with filament it’s all still just laravel. 🙂
nanopanda
nanopanda3mo ago
@nowak FWIW, my app has a main dashboard and 4 additional dashboards grouped under an "Analytics" navigationGroup. The analytics dashboards have their own collections of widgets that have specific filter form criteria and are tailored to specifc types of data / Models. All you really need to do is override the getWidgets() method in your Dashboard classes to include only the widgets that you want. If you want Shield permssions for individual widgets ( not just the Dashboard pages ) then you'll still need to register all of them in your panel provider. I also have my Models ( and their Policies ) in sub-folders by functional area, with Resources and Widgets following the same structural pattern.
nowak
nowakOP3mo ago
Thank you for sharing! And thanks for the detail about still needing to register widgets in the panel provider for shield permissions to work with individual widgets, very useful!
Want results from more Discord servers?
Add your server