Filament dashboard bouwen: KPI-widgets die wel performen
Terug naar blog

Filament dashboard bouwen: KPI-widgets die wel performen

9 min leestijd

Een Filament dashboard bestaat uit drie soorten widgets: stats overview, chart en table. Daarmee bouw je in een paar dagen een KPI-dashboard voor een webshop of B2B-bedrijf. Het lastige deel is niet de UI. Het is de keuze welke 6 tot 10 KPI's je toont, en hoe je ze berekent zonder dat je...

Een Filament dashboard bestaat uit drie soorten widgets: stats overview, chart en table. Daarmee bouw je in een paar dagen een KPI-dashboard voor een webshop of B2B-bedrijf. Het lastige deel is niet de UI. Het is de keuze welke 6 tot 10 KPI's je toont, en hoe je ze berekent zonder dat je database bij elke 5 seconden polling kreunt.

Wat is een Filament dashboard precies?

Een Filament dashboard is een gewone Filament-pagina die widgets in een grid toont. Elke widget is een Livewire-component, dus alles wat Livewire kan, kan een widget ook.

Filament levert drie widget-templates mee:

  • Stats overview: een rij KPI-tegels met waarde, toelichting, icoon en optioneel een mini-grafiek.
  • Chart widget: grafieken op basis van Chart.js. Line, bar, pie, doughnut, radar, scatter, bubble en polar area.
  • Table widget: een volledige Filament-tabel op je dashboard, met zoeken, sorteren en acties.

Past geen van drie? Dan maak je een custom widget met een eigen Blade-view. Je genereert ze allemaal met php artisan make:filament-widget, met flags als --stats-overview, --chart of --table.

Nieuw met Filament? Lees eerst wat het framework is op onze pagina over Filament. Dit artikel gaat ervan uit dat je een panel hebt draaien op v4 of v5. De widget-API is in beide versies gelijk.

Onze eigen backoffice draait op Filament. Leads, afspraken, blogplanning en een database met Magento-shops: precies het soort data waar je met deze widgets snel een dashboard op bouwt.

Welke KPI's horen op een webshop- of B2B-dashboard?

Toon alleen KPI's waar iemand op stuurt. Een dashboard met 25 getallen wordt na twee weken niet meer geopend.

Een goede vuistregel: per rol één dashboard, per dashboard maximaal één rij stats en twee of drie grafieken. De directeur heeft andere vragen dan de orderpicker.

RolKPI'sWidget-typeVerversing
DirectieOmzet periode, brutomarge, gemiddelde orderwaarde, omzet vs. vorig jaarStats + line chartElk uur of handmatig
Sales B2BOpenstaande offertes, conversie offerte naar order, top-10 klanten, klanten zonder order in 90 dagenStats + tableElk kwartier
OperationsOrders te verwerken, orders ouder dan 24 uur, backorders, retourpercentageStats + table30-60 seconden
FinanceOpenstaande facturen, gemiddelde betaaltermijn, omzet per betaalmethodeStats + bar chartDagelijks
E-commerceConversie, verlaten winkelwagens, omzet per kanaalStats + chartElk uur

Let op de laatste rij. Conversie en sessies staan niet in je eigen database. Die haal je uit GA4 of je analytics-tool via een API. Dat kan prima in een widget, maar dan cache je de response. Anders betaal je per pageview een externe API-call.

Bij B2B zien wij in de praktijk dat "klanten zonder order in 90 dagen" de meest waardevolle tabel is. Het is een directe bellijst voor accountmanagers. Geen grafiek, gewoon een lijst met namen en een telefoonnummer.

Hoe bouw je een stats overview widget?

Een stats overview widget retourneert een array met Stat-objecten uit getStats(). Elke stat heeft een label, een waarde en optioneel een beschrijving, icoon, kleur en mini-chart.

php
use Filament\Widgets\StatsOverviewWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;

class OrderKpis extends StatsOverviewWidget
{
    protected ?string $pollingInterval = '60s';

    protected function getStats(): array
    {
        return [
            Stat::make('Te verwerken', Order::where('status', 'processing')->count())
                ->description('Orders wachtend op picking')
                ->color('warning'),
            Stat::make('Ouder dan 24 uur', Order::where('status', 'processing')
                ->where('created_at', '<', now()->subDay())->count())
                ->color('danger'),
        ];
    }
}

Drie praktische punten:

  • Vergelijk altijd. Een kaal getal zegt niets. "€ 48.200" wordt bruikbaar met "+12% t.o.v. vorige maand" in de description().
  • Gebruik kleur functioneel. Rood alleen als iemand actie moet nemen. Niet omdat het mooi staat.
  • Mini-charts via chart(). Je geeft een array met datapunten mee, bijvoorbeeld de omzet van de laatste 7 dagen. Dat geeft trend zonder extra grafiek.

Hoe maak je grafieken met een chart widget?

Een chart widget implementeert twee methodes: getData() levert datasets en labels, getType() bepaalt het grafiektype. Filament rendert het met Chart.js.

Voor tijdreeksen raadt Filament in de eigen documentatie het package flowframe/laravel-trend aan. Dat vult ook lege periodes met nullen, zodat je grafiek geen gaten heeft.

php
use Filament\Widgets\ChartWidget;
use Flowframe\Trend\Trend;
use Flowframe\Trend\TrendValue;

class OmzetPerMaand extends ChartWidget
{
    protected ?string $heading = 'Omzet per maand';

    protected function getData(): array
    {
        $data = Trend::query(Order::query()->where('status', 'paid'))
            ->between(start: now()->subMonths(11)->startOfMonth(), end: now()->endOfMonth())
            ->perMonth()
            ->sum('grand_total');

        return [
            'datasets' => [[
                'label' => 'Omzet',
                'data' => $data->map(fn (TrendValue $value) => $value->aggregate),
            ]],
            'labels' => $data->map(fn (TrendValue $value) => $value->date),
        ];
    }

    protected function getType(): string
    {
        return 'line';
    }
}

Een chart widget kan ook een eigen filter krijgen. Je definieert opties in getFilters() en leest de keuze uit via $this->filter. De documentatie waarschuwt expliciet: die property is door de gebruiker te manipuleren. Gebruik hem nooit ongecontroleerd in een query. Een match met een vaste lijst periodes is genoeg.

Welk grafiektype wanneer?

  • Line: ontwikkeling over tijd. Omzet, orders, nieuwe klanten.
  • Bar: vergelijken van categorieën. Omzet per kanaal, per land, per verkoper.
  • Doughnut/pie: alleen bij 2 tot 5 segmenten. Daarboven leest niemand het meer.

Wanneer gebruik je een table widget?

Gebruik een table widget als de KPI een lijst is waarop iemand actie moet nemen. "12 orders ouder dan 24 uur" is een stat. Welke 12 dat zijn, is een tabel.

Een table widget extendt Filament\Widgets\TableWidget en definieert table(Table $table): Table. Je krijgt dezelfde tabel-API als in je resources: kolommen, filters, sortering en acties. Meer over die API lees je in resources, forms en tables uitgelegd.

Twee tips uit de praktijk:

  • Beperk de query. Een dashboard-tabel is geen overzichtspagina. Toon de top 10 of de items die actie vragen. Voor de volledige lijst link je naar de resource.
  • Voeg een actie toe. Een knop "markeer als opgepakt" of "bel klant" maakt van een rapport een werklijst. Dat is het verschil tussen kijken en sturen.

Hoe werken filters op een Filament dashboard?

Met dashboard-filters stuur je alle widgets tegelijk aan, bijvoorbeeld met een periode of een verkoopkanaal. Daarvoor vervang je de standaard dashboardpagina door een eigen klasse die Filament\Pages\Dashboard extendt.

Er zijn twee varianten:

HasFiltersFormHasFiltersAction
Waar staan de filtersAltijd zichtbaar boven de widgetsIn een modal achter een knop
Wanneer herladen widgetsDirect bij elke wijzigingPas na klikken op "Apply"
ValidatieNee, data komt live en ongevalideerd binnenJa, de modal valideert voor toepassen
Geschikt voorLichte widgets, weinig filtersZware queries, meerdere filters

In de widgets voeg je de trait InteractsWithPageFilters toe. Daarna lees je de waarden uit $this->pageFilters:

php
use Filament\Widgets\Concerns\InteractsWithPageFilters;

class OmzetKpis extends StatsOverviewWidget
{
    use InteractsWithPageFilters;

    protected function getStats(): array
    {
        $start = $this->pageFilters['startDate'] ?? now()->startOfMonth();
        $end = $this->pageFilters['endDate'] ?? now();

        // query met whereBetween op $start en $end
    }
}

Standaard onthoudt Filament de filters in de sessie van de gebruiker. Wie gisteren op "vorig kwartaal" filterde, ziet dat vandaag weer. Wil je dat niet, zet dan $persistsFiltersInSession op false in je dashboardklasse.

Onze voorkeur bij webshopdashboards: de action-modal. Omzetqueries over een jaar aan orders wil je niet bij elke klik op een datepicker opnieuw draaien.

Hoe voorkom je dat polling je database sloopt?

Stats- en chart-widgets verversen standaard elke 5 seconden. Zet dat voor bijna elke widget hoger of uit. Dat is de belangrijkste performance-instelling op je dashboard.

De rekenstap: stel, 15 medewerkers hebben het dashboard de hele dag open. Er staan 4 widgets op met elk 3 queries. Bij 5 seconden polling zijn dat 15 x 4 x 3 = 180 queries per 5 seconden. Dat is ruim 2 queries per seconde, de hele werkdag lang. Zijn dat aggregaties over 500.000 orders, dan merk je dat in je checkout.

Zo pak je het aan:

  • Polling per widget instellen. protected ?string $pollingInterval = '60s'; of null om het uit te zetten. Alleen operationele widgets hebben echt live data nodig.
  • Lazy loading aanhouden. Widgets laden standaard pas als ze in beeld komen. Zet $isLazy alleen op false voor de bovenste rij stats.
  • Cachen. Wikkel aggregaties in Cache::remember() met een TTL die past bij de KPI. Directiecijfers mogen 15 minuten oud zijn. Neem de filterwaarden op in de cache key.
  • Vooraf aggregeren. Bij grote volumes bereken je dagtotalen 's nachts of elk uur via de scheduler en sla je ze op in een eigen tabel. Het dashboard leest dan 365 rijen in plaats van 500.000 orders. Hoe je dat inricht, lees je in de Laravel scheduler voor e-commerce.
  • Indexen op filterkolommen. Een samengestelde index op status en created_at scheelt bij de meeste orderqueries het meeste.

Wat niemand je vertelt: de trage widget is zelden de grafiek. Het is meestal een count() met drie whereHas-relaties in een stats-tegel. Draai Laravel Debugbar of Telescope op je dashboard en kijk welke query de tijd opeet. Voor tabellen met veel data gaan we dieper in op Filament performance bij grote datasets.

Waar komt de data vandaan als je Magento of Shopify draait?

Draait je webshop op Magento of Shopify, dan staat de orderdata niet in je Laravel-database. Je hebt twee opties.

  1. Live queryen via de API. Werkt voor kleine volumes. Maar je loopt tegen rate limits aan en elke widget-refresh wordt een externe call.
  2. Synchroniseren naar een eigen database. Een queue-job haalt orders op via webhooks of een periodieke sync. Het dashboard leest lokaal. Dit is bijna altijd de betere keuze.

Optie 2 heeft een bonus. Je kunt data combineren die nergens anders samenkomt: webshoporders, ERP-facturen, voorraad uit het WMS en offertes uit het CRM. Dat is precies waar een eigen dashboard wint van de rapportages in Magento-admin of Shopify Analytics. Hoe we die koppelingen bouwen, staat op onze pagina over Laravel-integraties.

Komt je huidige rapportage nog uit Excel-exports? Dan beschrijven we de overstap in van Excel naar een Laravel-dashboard.

Wanneer is Filament niet de juiste keuze voor je dashboard?

Filament is sterk voor operationele dashboards binnen een backoffice. Het is geen BI-tool.

De eerlijke versie:

  • Ad-hoc analyses. Wil een analist zelf draaitabellen maken en dimensies slepen? Dan is Metabase, Power BI of Looker Studio beter. Filament-widgets zijn vooraf geprogrammeerd.
  • Externe stakeholders zonder login. Een dashboard voor een investeerder of leverancier zet je niet in je adminpanel.
  • Alleen GA4-data. Komt alles uit Google Analytics, dan heb je geen Laravel-app nodig.

Filament wint als het dashboard onderdeel is van het werk. Dus: KPI's naast de orders, klanten en acties waar ze over gaan, met dezelfde rechten en dezelfde login. Meer over maatwerk-dashboards lees je op onze pagina over Laravel dashboards.

Samengevat

  • Een Filament dashboard bestaat uit stats overview-, chart- en table-widgets. Custom widgets voor de rest.
  • Kies per rol 6 tot 10 KPI's waar iemand echt op stuurt. Vergelijk altijd met een vorige periode.
  • Gebruik tabellen voor KPI's die een werklijst zijn. Voeg acties toe.
  • Dashboard-filters via HasFiltersAction als je queries zwaar zijn. Valideer filterwaarden altijd.
  • Zet de standaard polling van 5 seconden hoger of uit. Cache aggregaties en aggregeer vooraf bij grote volumes.
  • Synchroniseer Magento- of Shopify-data naar je eigen database in plaats van live API-calls.

Veelgestelde vragen

Wat is het verschil tussen een stats overview en een chart widget in Filament?

Een stats overview toont losse KPI-waarden als tegels, met optioneel een beschrijving en mini-grafiek. Een chart widget toont een volledige Chart.js-grafiek, zoals een line- of bar chart. Gebruik stats voor "hoeveel nu" en charts voor "hoe ontwikkelt het zich".

Hoe vaak ververst een Filament widget zijn data?

Stats overview- en chart-widgets pollen standaard elke 5 seconden. Je past dat aan met de property $pollingInterval, bijvoorbeeld '60s'. Met null zet je polling volledig uit. Voor de meeste management-KPI's is uitzetten de beste keuze.

Kan ik meerdere dashboards maken in één Filament panel?

Ja. Een dashboard is een pagina die Filament\Pages\Dashboard extendt, en je kunt er meerdere aanmaken. Met canView() op widgets bepaal je per gebruiker welke widgets zichtbaar zijn. Zo krijgt sales andere KPI's dan operations.

Hoe filter ik alle widgets op een dashboard tegelijk?

Voeg HasFiltersForm of HasFiltersAction toe aan je dashboardpagina. In elke widget gebruik je de trait InteractsWithPageFilters en lees je $this->pageFilters uit. De filters blijven standaard bewaard in de sessie van de gebruiker.

Is Filament geschikt als vervanging voor Power BI of Metabase?

Niet voor ad-hoc analyse. Filament-widgets zijn vooraf geprogrammeerd door een developer. Het is wel geschikt voor operationele dashboards waar KPI's direct gekoppeld zijn aan orders, klanten en acties in dezelfde backoffice.

Wat kost een Filament KPI-dashboard?

Filament zelf is gratis en open source (MIT). De kosten zitten in development: de KPI-definities, de queries, de datakoppelingen en de performance-aanpak. Een dashboard op bestaande Laravel-data is relatief snel gebouwd. Een dashboard dat Magento, ERP en WMS combineert vraagt eerst een sync-laag.

Wil je weten welke KPI's in jouw situatie het verschil maken, en waar de data vandaan moet komen? Neem contact op, dan kijken we er samen naar.

Ruthger Idema

Geschreven door Ruthger Idema

15+ jaar ervaring in e-commerce development. Gespecialiseerd in Magento, Shopify en Laravel maatwerk.

Meer over ons team →
Deel dit artikel:

Wil je jouw e-commerce naar het volgende niveau?

Plan een vrijblijvend gesprek met onze experts over Magento, Shopify of Laravel maatwerk.

Plan een Tech Check