github MDDev31/filament-dynamic-dashboard v1.2

42 minutes ago

Secondary filters, a single-column layout for small screens, Laravel 11 support, a round of access-control and stability fixes, and a test suite. No database change.

Upgrade notes

  • Run php artisan filament:assets and php artisan view:clear after updating.
  • Laravel 11.28 is now the minimum version.
  • In a panel without SPA mode, the page now does a regular page load after a widget is added, edited or deleted or the dashboard is switched, and the canvas assets are only loaded on dashboard pages. See Changed.
  • If you published the package's views, update your copies of livewire/dashboard-grid.blade.php and schemas/widget-wrapper.blade.php from the package. Without the new @assets block of the first one, the canvas scripts are no longer loaded outside SPA mode.
  • currentDashboardId can no longer be set from the browser. Custom JavaScript that wrote it through $wire has to trigger the dashboard selector instead.
  • The available() / availableFor() model scopes are now the public methods scopeAvailable() / scopeAvailableFor(). Calls are unchanged, but a subclass that overrode the former protected methods must rename its override.

Added

  • Secondary filters. Override the new DynamicDashboard::getSecondaryDashboardFilters() to keep the main filters on the bar and put the others behind a More filters toggle, as a flat list of filter names or grouped under headings. The toggle shows how many secondary filters have a value, the area opens instantly (no request), keeps its state across re-renders, and stays open while one of its filters has a value. Secondary filters remain ordinary filters for default values, per-dashboard visibility, the session, reset and pageFilters. A page that does not override the method renders exactly as before. New translation keys more_filters and more_filters_count in every locale.
  • Small-screen layout. Below 768px, whatever the template, sections stack and every widget takes one full-width row, in reading order. Each widget is at least as tall as it was designed and grows with its content. Widgets cannot be dragged or resized in that layout and nothing is saved, so the grid layout is never altered from a phone; widening the window brings the grid back without a reload.
  • Locale aliases pt_BR and zh_CN, which reuse the pt and zh translations. Applications set to those locales previously fell back to English.
  • Dashboard::visibleToCurrentUser() query scope (global dashboards plus the current user's personal ones).
  • DashboardWidget::nextFreeRow() — first free row of a dashboard section.
  • DynamicDashboard::redirectToDashboard() — the reload performed after a change the canvas cannot apply in place; override it to choose between a Livewire navigation and a regular redirect.
  • CanvasJs / CanvasCss asset classes, which decide per panel whether the canvas assets are loaded on request.
  • GridStack's licence files next to the vendored bundle in resources/dist.
  • Test suite (Pest), PHPStan/Larastan (level 5), a Pint configuration, a GitHub Actions workflow and .gitattributes.

Changed

  • Minimum Laravel version is now 11.28 (the lowest version Filament 4 supports); illuminate/contracts constraint updated accordingly.
  • After a widget is added, edited or deleted, or the dashboard is switched, the page reloads the way the panel is configured, like Filament's own pages: a Livewire navigation in SPA mode, a regular page load otherwise (it was always a Livewire navigation).
  • In a panel without SPA mode, GridStack and the canvas CSS/JS are only added to dashboard pages instead of every panel page. Nothing changes in SPA mode.
  • The page reads the current dashboard through getCurrentDashboard() everywhere, instead of relying on it having been loaded earlier in the request.
  • The TemplateRegistry singleton is registered in packageRegistered().
  • spatie/laravel-permission is listed under suggest in composer.json.
  • On devices without hover (touch screens) the widget icons (drag, edit, delete) are always visible instead of appearing on hover.
  • The page subheading falls back to the page's own subheading when the dashboard has no description, instead of rendering an empty element.
  • README: upgrade notes for 1.2, the ApexCharts sizing advice (chart.height: 'auto'), and corrections where it no longer matched the code (number of shipped presets, default widget size, what a section's name is used for, what stays visible on a locked dashboard).

Deprecated

  • DashboardWidget::getWidget() — no longer used by the package; it will be removed in the next major version.

Removed

  • The unused templateColumns option of the dashboardGrid Alpine component.

Fixed

  • Laravel 11 compatibility: the available() / availableFor() scopes used the #[Scope] attribute, which only exists from Laravel 12. They are now classic scopeAvailable() / scopeAvailableFor() methods — call sites are unchanged.
  • After a dashboard was unlocked live from the manager, drags were not saved until the page was reloaded. On a dashboard loaded unlocked, the dragged widget never finished its move: the canvas kept its GridStack instances in Alpine's reactive data, which hands them back wrapped in proxies; they are now always read from the elements GridStack attaches them to. On a dashboard loaded locked, the handlers that save a move were never attached; they are now attached whatever the state at load.
  • A stored widget type naming a class that is not a dynamic widget any more no longer breaks the page; the widget is skipped, like one whose class is gone.
  • Widgets are keyed by their id (key('widget-'.$id)); the key was passed in a position the @livewire directive ignores. Livewire derived a key from the widget's position instead, so after a widget was dragged to another section the next page re-render mounted it a second time on the server.
  • The canvas component was initialised twice (x-init="init()" on top of Alpine's own init() call), registering every listener twice.
  • The dynamic-dashboard:editable-changed listener, the per-widget Livewire commit hook and the GridStack instances are now released when the canvas leaves the page. They used to pile up on the window with every wire:navigate visit.
  • New widgets, and widgets moved by a template change, are placed on the first row below the lowest edge (y + h) of the widgets already in the section. They were placed at max(y) + 1, which overlapped any widget taller than one row.
  • A dashboard's template attribute was cached on the model instance, so a second template_key change on the same instance still resolved the previous template and skipped the move of orphan widgets.
  • persistLayout() skips items with non-numeric coordinates, a negative position or an empty size, and stores a section slug unknown to the current template as the template's first section (the fallback already applied at render time) instead of saving the stale slug.
  • AsWidgetSettings no longer passes null to json_decode() when a widget's settings column is NULL (PHP deprecation), always returns an array, no longer instantiates the widget class to read its casts, and no longer fails when the model was loaded without its type column.
  • Soft-deleting a user no longer hard-deletes their personal dashboards; they are removed when the user is force-deleted. The listener is registered on the event dispatcher, so the user model is no longer booted on every request.
  • upgrade_dynamic_dashboard_tables_to_v2 migration: foreign keys and indexes are now checked with Schema::getForeignKeys() / Schema::hasIndex() instead of try/catch blocks that either never caught anything (inside Blueprint closures) or left a failed statement behind (which aborts the migration transaction on PostgreSQL). Only affects installs that have not published the migration yet.
  • The dashboard manager counted the available dashboards up to four times per table row; the count is now computed once per request.
  • Translations: the 21 locales other than en / fr were missing template, section, drag, personal and personal_help, and still carried the removed v1 grid/block keys. All 23 locales now share the same keys.

Security

  • Widget edit and delete actions now resolve the widget inside the current dashboard only. A forged widget ID could previously reach any dashboard, including another user's personal one.
  • Adding, editing and deleting a widget are refused on the server while the dashboard is locked, not only hidden in the page. Saving the layout (persistLayout()) now follows the same rule as those actions and is also refused once the dashboard is no longer available to the user (deactivated, or turned into someone else's personal dashboard).
  • Without an authenticated user, personal dashboards are never visible. A personal dashboard whose creator no longer exists (created_by null) used to match "no user".
  • The dashboard manager table no longer lists other users' personal dashboards, so they can't be edited, toggled, reordered or deleted from it (this is the behaviour the README already described).
  • currentDashboardId on the page, and pageClass / currentDashboardId on the manager, are now #[Locked]. A client-side update could previously switch to a dashboard without going through canDisplay().
  • The dashboard description is sanitized before being rendered as the page subheading.

Don't miss a new filament-dynamic-dashboard release

NewReleases is sending notifications on new releases.