Zum Hauptinhalt springen

Widgets

Übersicht aller Admin-Panel Widgets für Dashboard und Reports.

Dashboard Widgets

Definiert in Dashboard.php

DateRangeFilter

File: DateRangeFilter.php

Globaler Datumsbereich-Selektor für alle Dashboard-Widgets.

EigenschaftWert
TypCustom Widget
SpanFull Width
Sort-1 (erscheint zuerst)

Vordefinierte Bereiche:

  • Heute, Gestern
  • Letzte 7/30 Tage
  • Dieser/Letzter Monat
  • Dieses/Letztes Jahr

Event-Kommunikation:

// Dispatch bei Filteränderung
public function applyFilter(): void
{
// Datumsbereich parsen
$dates = explode(' - ', $this->dateRange);
$start = Carbon::createFromFormat('Y-m-d', $dates[0]);
$end = Carbon::createFromFormat('Y-m-d', $dates[1]);

// In Session speichern
session(['dashboard_date_range' => [
'start' => $start->toDateString(),
'end' => $end->toDateString(),
]]);

// Event für andere Widgets
$this->dispatch('dateRangeUpdated', start: $start, end: $end);
}

OrdersOverviewWidget

File: OrdersOverviewWidget.php

Drei Stat-Cards mit Auftrags- und Umsatzstatistiken.

EigenschaftWert
TypStatsOverviewWidget
Polling60 Sekunden
Sort1

Stat-Cards:

CardBeschreibungChart
Aufträge im ZeitraumGesamtzahl mit TrendLinien-Chart (täglich/wöchentlich)
Aktive AufträgeOpen + Assigned + Query + NotMatchedMini-Chart
Umsatz im ZeitraumSumme abgeschlossener AufträgeRevenue-Chart

Trend-Berechnung:

protected function calculateTrend(int $current, int $previous): array
{
if ($previous === 0) {
return ['value' => 0, 'direction' => 'flat'];
}

$change = (($current - $previous) / $previous) * 100;

return [
'value' => abs(round($change, 1)),
'direction' => $change > 0 ? 'up' : ($change < 0 ? 'down' : 'flat'),
];
}

Event-Listener:

use Livewire\Attributes\On;

#[On('dateRangeUpdated')]
public function updateDateRange($start, $end): void
{
$this->startDate = $start;
$this->endDate = $end;
}

NewRegistrationRequestsWidget

File: NewRegistrationRequestsWidget.php

Tabelle mit neuen Registrierungsanfragen.

EigenschaftWert
TypTableWidget
HeadingNeue Registrierungsanfragen
SpanFull Width
Sort2

Query:

protected function getTableQuery(): Builder
{
return User::query()
->where('state', Requested::class)
->orderBy('created_at', 'desc')
->limit(10);
}

Spalten:

  • created_at: Registrierungsdatum
  • name: Name mit E-Mail als Description
  • roles: Rollen-Badge
  • phone: Telefonnummer
  • email_verified_at: Verifiziert-Icon

Actions:

ActionIconBeschreibung
approvecheckBenutzer aktivieren (State → Active)
vieweyeZur Benutzer-Detailseite

RecentReviewsWidget

Zeigt die neuesten Bewertungen.

EigenschaftWert
TypTableWidget
Sort3

Query:

return OrderRating::query()
->whereNotNull('submitted_at')
->orderBy('submitted_at', 'desc')
->limit(5);

Report Widgets

Definiert in Reports.php

KeyMetricsWidget

File: KeyMetricsWidget.php

Übersicht der wichtigsten Kennzahlen.

EigenschaftWert
SpanFull Width
Sort1

Metriken:

  • Gesamtaufträge
  • Abgeschlossene Aufträge
  • Durchschnittliche Auftragsdauer
  • Gesamtumsatz
  • Aktive Sprachmittler
  • Aktive Auftraggeber

CategoryDistributionWidget

File: CategoryDistributionWidget.php

Gestapeltes Balkendiagramm der Auftragsverteilung nach Kategorien.

EigenschaftWert
TypChartWidget
Chart-Typbar (stacked)
SpanFull Width
Sort2
protected function getData(): array
{
$categories = Category::withCount(['orders' => function ($q) {
$q->whereBetween('scheduled_at', [$this->startDate, $this->endDate]);
}])->get();

return [
'datasets' => [
[
'label' => 'Aufträge',
'data' => $categories->pluck('orders_count'),
'backgroundColor' => $this->generateColors($categories->count()),
],
],
'labels' => $categories->pluck('name'),
];
}

LanguageStatsWidget

File: LanguageStatsWidget.php

Sprachstatistiken der Aufträge.

EigenschaftWert
TypChartWidget
Chart-Typdoughnut
SpanHalf Width
Sort3

InterpreterLanguagesWidget

File: InterpreterLanguagesWidget.php

Verteilung der Sprachkenntnisse unter Sprachmittlern.

EigenschaftWert
TypChartWidget
Chart-Typbar (horizontal)
SpanHalf Width
Sort4

ClientCountriesWidget

File: ClientCountriesWidget.php

Herkunftsländer der Klienten.

EigenschaftWert
TypChartWidget
Chart-Typbar
SpanFull Width
Sort5

LocationsMapWidget

File: LocationsMapWidget.php

Google Maps mit Clustering der Auftragsstandorte.

EigenschaftWert
TypMapWidget (filament-google-maps)
SpanFull Width
Sort6
use Cheesegrits\FilamentGoogleMaps\Widgets\MapWidget;

class LocationsMapWidget extends MapWidget
{
protected static ?string $heading = 'Auftragsstandorte';

protected function getMarkers(): array
{
return Order::query()
->whereNotNull('location_latitude')
->whereNotNull('location_longitude')
->whereBetween('scheduled_at', [$this->startDate, $this->endDate])
->get()
->map(fn ($order) => [
'lat' => (float) $order->location_latitude,
'lng' => (float) $order->location_longitude,
'title' => $order->order_number,
'icon' => $this->getMarkerIcon($order->state),
])
->toArray();
}

protected function getMapConfig(): array
{
return [
'center' => ['lat' => 51.1657, 'lng' => 10.4515], // Deutschland
'zoom' => 6,
'mapTypeId' => 'roadmap',
'clustering' => true,
];
}
}

Custom Widget erstellen

Stat Widget

namespace App\Filament\Widgets;

use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;

class MyStatsWidget extends BaseWidget
{
// Polling-Intervall
protected static ?string $pollingInterval = '60s';

// Sortierung
protected static ?int $sort = 10;

protected function getStats(): array
{
return [
Stat::make('Unique Label', 'Value')
->description('Description text')
->descriptionIcon('heroicon-m-arrow-trending-up')
->chart([7, 2, 10, 3, 15, 4, 17])
->color('success'),

Stat::make('Another Stat', '100')
->description('10% increase')
->descriptionIcon('heroicon-m-arrow-trending-up')
->color('warning'),
];
}
}

Chart Widget

namespace App\Filament\Widgets;

use Filament\Widgets\ChartWidget;

class MyChartWidget extends ChartWidget
{
protected static ?string $heading = 'My Chart';
protected static ?int $sort = 2;
protected int | string | array $columnSpan = 'full';

protected function getData(): array
{
return [
'datasets' => [
[
'label' => 'Data Series',
'data' => [0, 10, 5, 2, 21, 32, 45, 74, 65, 45, 77, 89],
'backgroundColor' => '#36A2EB',
'borderColor' => '#36A2EB',
],
],
'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
];
}

protected function getType(): string
{
return 'line'; // 'bar', 'pie', 'doughnut', 'radar', 'polarArea'
}

protected function getOptions(): array
{
return [
'scales' => [
'y' => [
'beginAtZero' => true,
],
],
];
}
}

Table Widget

namespace App\Filament\Widgets;

use Filament\Tables;
use Filament\Widgets\TableWidget as BaseWidget;

class MyTableWidget extends BaseWidget
{
protected static ?string $heading = 'My Table';
protected int | string | array $columnSpan = 'full';

protected function getTableQuery(): Builder
{
return MyModel::query()->latest()->limit(10);
}

protected function getTableColumns(): array
{
return [
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('created_at')
->dateTime(),
];
}

protected function getTableActions(): array
{
return [
Tables\Actions\ViewAction::make(),
];
}
}

Event-Listener Pattern

use Livewire\Attributes\On;

class MyWidget extends Widget
{
public ?string $startDate = null;
public ?string $endDate = null;

public function mount(): void
{
$this->loadDateRange();
}

#[On('dateRangeUpdated')]
public function updateDateRange($start, $end): void
{
$this->startDate = $start;
$this->endDate = $end;
}

protected function loadDateRange(): void
{
$range = session('dashboard_date_range');
if ($range) {
$this->startDate = $range['start'];
$this->endDate = $range['end'];
} else {
$this->startDate = now()->subDays(30)->toDateString();
$this->endDate = now()->toDateString();
}
}
}

Widget zu Page hinzufügen

Header Widgets

// In Dashboard.php oder Reports.php
public function getHeaderWidgets(): array
{
return [
DateRangeFilter::class,
];
}

Body Widgets

public function getWidgets(): array
{
return [
OrdersOverviewWidget::class,
NewRegistrationRequestsWidget::class,
RecentReviewsWidget::class,
];
}

Widget-Columns

public function getWidgetsColumns(): int|string|array
{
return [
'default' => 1,
'md' => 2,
'xl' => 4,
];
}