mddev31/filament-dynamic-dashboard
Installation:
composer require mddev31/filament-dynamic-dashboard
php artisan vendor:publish --tag=filament-dynamic-dashboard-migrations
php artisan migrate
php artisan filament:assets
Create a Dashboard Page:
Extend MDDev\DynamicDashboard\Pages\DynamicDashboard in your Filament panel:
namespace App\Filament\Pages;
use MDDev\DynamicDashboard\Pages\DynamicDashboard;
class MyDashboard extends DynamicDashboard
{
// Override methods like getDashboardFilters() if needed
}
Create a Dynamic Widget:
Implement DynamicWidget and use helper traits:
use MDDev\DynamicDashboard\Concerns\HasSizeDefaults;
use MDDev\DynamicDashboard\Contracts\DynamicWidget;
class MyWidget extends Filament\Widgets\StatsOverviewWidget implements DynamicWidget
{
use HasSizeDefaults;
public static function getWidgetLabel(): string
{
return 'My Widget';
}
}
Register the Widget:
Add it to your Filament panel's widget list (via widgets() method in your panel provider).
Access the Dashboard: Visit the dashboard page in your Filament admin panel. Drag widgets onto the canvas to start customizing.
Create a Personalized Analytics Dashboard:
SalesChartWidget and StatsOverviewWidget to your dashboard.SalesChartWidget with settings (e.g., resultType, groupBy).split-6-6).Widget Development:
HasEmptySettings trait for widgets without configurable options.getSettingsFormSchema() and getSettingsCasts() for typed settings (e.g., BackedEnums, arrays).getDynamicDashboardMin/MaxWidth/Height() to lock or limit resizing.
Example:
public static function getDynamicDashboardMinWidth(): int { return 4; }
public static function getDynamicDashboardMaxHeight(): int { return 1; } // Lock height
Dashboard Management:
standard-12, split-6-6) via the dashboard manager.getDashboardFilters() to add global filters (e.g., date range, user role) that apply to all widgets.is_personal in the dashboard manager to control visibility.Integration with Filament:
InteractsWithPageFilters to pass filter values to widgets:
protected function getStats(): array
{
return [
Stat::make('Users', $this->pageFilters['country'] ?? 'All'),
];
}
spatie/laravel-permission to restrict dashboard access by role:
public static function canDisplay(): bool
{
return auth()->user()->hasRole('analyst');
}
Layout Customization:
config/filament-dynamic-dashboard.php file and extending the templates array.
Example:
'templates' => [
'custom-layout' => [
'sections' => [
'main' => ['columns' => 12],
'sidebar' => ['columns' => 4, 'width' => 300],
],
],
],
section_slug in widget placement logic to group related widgets.Livewire Widgets:
showWidgetLoader() to show loading states:
public static function showWidgetLoader(): bool { return true; }
wire:ignore on the widget’s root element to prevent full-page reloads during resizing.Shared State:
resolveFilterDefaults():
public static function resolveFilterDefaults(): array
{
return [
'date_range' => now()->subDays(7)->format('Y-m-d'),
];
}
Widget Settings Persistence:
public ResultTypeEnum $resultType; // Automatically cast from JSON
GridStack.js Customization:
gridstack key:
'gridstack' => [
'float' => true, // Allow widgets to float outside sections
'acceptWidgets' => true, // Enable widget dropping
],
Upgrade Migration:
php artisan migrate --pretend # Dry run to verify
php artisan view:clear
php artisan cache:clear
Widget Size Conflicts:
getMaxHeight() (Filament’s method) conflicts with getDynamicDashboardMaxHeight() (static), rename the latter to avoid PHP collisions.getDynamicDashboard... prefix explicitly:
public static function getDynamicDashboardMaxHeight(): int { return 2; }
Template Key Mismatch:
section_slug where the widgets are stored.dashboard_widgets table for section_slug values matching your template’s sections.Permission Denied:
canEdit() or canDisplay() return false, verify:
is_locked flag in the manager).created_by field is null for global dashboards.Settings Not Saving:
getSettingsCasts() includes all typed properties (e.g., BackedEnums).DynamicWidget and uses HasSizeDefaults or HasEmptySettings.Layout Rendering Issues:
section_slug).config/filament-dynamic-dashboard.php.Widget Not Appearing:
widgets() method.dashboard_widgets table for the widget’s type (should match the class name).Filter Not Applying:
resolveFilterDefaults() to transform stored defaults into filter-compatible values:
public static function resolveFilterDefaults(): array
{
return [
'date_from' => Carbon::parse($defaults['date_range'] ?? now()->subDays(30)),
];
}
Default Layouts:
standard-12, split-6-6) for quick setup. Customize via the dashboard manager.Widget Organization:
main, sidebar) for better UX:
// In your template JSON:
'sections' => [
'main' => ['columns' => 8],
'sidebar' => ['columns' => 4, 'width' => 300],
],
Performance:
wire:ignore and wire:load:
<div wire:ignore>
{{ $widget->render() }}
</div>
Extending GridStack:
gridstack options:
'gridstack' => [
'cellHeight' => 50, // Adjust row height
'minRow' => 1, // Minimum rows per widget
],
Personal Dashboards:
is_personal = true) to hide them from others. Useful for user-specific views:
public static function canDisplay(): bool
{
return auth()->user()->id === $dashboard->created_by;
}
Backup Layouts:
How can I help you explore Laravel packages today?