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.
| Eigenschaft | Wert |
|---|---|
| Typ | Custom Widget |
| Span | Full 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.
| Eigenschaft | Wert |
|---|---|
| Typ | StatsOverviewWidget |
| Polling | 60 Sekunden |
| Sort | 1 |
Stat-Cards:
| Card | Beschreibung | Chart |
|---|---|---|
| Aufträge im Zeitraum | Gesamtzahl mit Trend | Linien-Chart (täglich/wöchentlich) |
| Aktive Aufträge | Open + Assigned + Query + NotMatched | Mini-Chart |
| Umsatz im Zeitraum | Summe abgeschlossener Aufträge | Revenue-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.
| Eigenschaft | Wert |
|---|---|
| Typ | TableWidget |
| Heading | Neue Registrierungsanfragen |
| Span | Full Width |
| Sort | 2 |
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:
| Action | Icon | Beschreibung |
|---|---|---|
| approve | check | Benutzer aktivieren (State → Active) |
| view | eye | Zur Benutzer-Detailseite |
RecentReviewsWidget
Zeigt die neuesten Bewertungen.
| Eigenschaft | Wert |
|---|---|
| Typ | TableWidget |
| Sort | 3 |
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.
| Eigenschaft | Wert |
|---|---|
| Span | Full Width |
| Sort | 1 |
Metriken:
- Gesamtaufträge
- Abgeschlossene Aufträge
- Durchschnittliche Auftragsdauer
- Gesamtumsatz
- Aktive Sprachmittler
- Aktive Auftraggeber
CategoryDistributionWidget
File: CategoryDistributionWidget.php
Gestapeltes Balkendiagramm der Auftragsverteilung nach Kategorien.
| Eigenschaft | Wert |
|---|---|
| Typ | ChartWidget |
| Chart-Typ | bar (stacked) |
| Span | Full Width |
| Sort | 2 |
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.
| Eigenschaft | Wert |
|---|---|
| Typ | ChartWidget |
| Chart-Typ | doughnut |
| Span | Half Width |
| Sort | 3 |
InterpreterLanguagesWidget
File: InterpreterLanguagesWidget.php
Verteilung der Sprachkenntnisse unter Sprachmittlern.
| Eigenschaft | Wert |
|---|---|
| Typ | ChartWidget |
| Chart-Typ | bar (horizontal) |
| Span | Half Width |
| Sort | 4 |
ClientCountriesWidget
File: ClientCountriesWidget.php
Herkunftsländer der Klienten.
| Eigenschaft | Wert |
|---|---|
| Typ | ChartWidget |
| Chart-Typ | bar |
| Span | Full Width |
| Sort | 5 |
LocationsMapWidget
File: LocationsMapWidget.php
Google Maps mit Clustering der Auftragsstandorte.
| Eigenschaft | Wert |
|---|---|
| Typ | MapWidget (filament-google-maps) |
| Span | Full Width |
| Sort | 6 |
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,
];
}