Zum Hauptinhalt springen

Dashboard & Reports

Dashboard

Das Dashboard (/admin) zeigt eine Übersicht der wichtigsten Kennzahlen mit konfigurierbarem Datumsbereich.

Konfiguration

File: Dashboard.php

class Dashboard extends BaseDashboard
{
protected static ?string $navigationIcon = 'heroicon-o-home';
protected static ?int $navigationSort = -2;

public function getHeaderWidgets(): array
{
return [
DateRangeFilter::class,
];
}

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

Widget-Layout

WidgetBeschreibungSpan
DateRangeFilterGlobaler Datumsbereich-SelektorHeader
OrdersOverviewWidgetStat-Cards mit Aufträgen/UmsatzFull
NewRegistrationRequestsWidgetTabelle RegistrierungsanfragenFull
RecentReviewsWidgetAktuelle BewertungenFull

DateRangeFilter

File: DateRangeFilter.php

Der DateRangeFilter ist ein shared Widget, das Session-basiert den Datumsbereich für alle Dashboard-Widgets steuert.

Vordefinierte Bereiche

LabelBereich
HeuteAktueller Tag
GesternVorheriger Tag
Letzte 7 TageLetzte Woche
Letzte 30 TageLetzter Monat
Dieser MonatAktueller Monat
Letzter MonatVorheriger Monat
Dieses JahrAktuelles Jahr
Letztes JahrVorheriges Jahr

Event-Kommunikation

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

// 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);
}

In anderen Widgets lauschen

use Livewire\Attributes\On;

class OrdersOverviewWidget 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 {
// Default: Letzte 30 Tage
$this->startDate = now()->subDays(30)->toDateString();
$this->endDate = now()->toDateString();
}
}
}

OrdersOverviewWidget

File: OrdersOverviewWidget.php

Zeigt 3 Stat-Cards mit Trends und Mini-Charts.

Stat-Cards

CardBeschreibungChart
Aufträge im ZeitraumGesamtzahl mit Trend vs. VorperiodeLinien-Chart
Aktive AufträgeOpen + Assigned + Query + NotMatchedMini-Chart
Umsatz im ZeitraumSumme abgeschlossener AufträgeRevenue-Chart

Polling

protected static ?string $pollingInterval = '60s';

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'),
];
}

NewRegistrationRequestsWidget

File: NewRegistrationRequestsWidget.php

Tabelle mit neuen Registrierungsanfragen (State = Requested).

Query

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

Spalten

SpalteBeschreibung
created_atRegistrierungsdatum
nameName mit E-Mail
rolesRollen-Badge
phoneTelefonnummer
email_verified_atVerifiziert-Icon

Actions

ActionBeschreibung
approveBenutzer aktivieren (State → Active)
viewZur Benutzer-Detailseite

Reports-Seite

File: Reports.php

Die Reports-Seite (/admin/reports) bietet erweiterte Auswertungen und Datenexport.

Konfiguration

class Reports extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-chart-bar';
protected static ?string $navigationLabel = 'Berichte';
protected static ?string $title = 'Auswertungen & Kennzahlen';
protected static ?int $navigationSort = 2;
}

Widget-Layout

public function getWidgets(): array
{
return [
KeyMetricsWidget::class, // Fullscreen
CategoryDistributionWidget::class, // Fullscreen
InterpreterLanguagesWidget::class, // Half
LanguageStatsWidget::class, // Half
ClientCountriesWidget::class, // Full
LocationsMapWidget::class, // Fullscreen
];
}

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

Excel-Export

protected function getHeaderActions(): array
{
return [
Action::make('export_excel')
->label('Als Excel exportieren')
->icon('heroicon-o-arrow-down-tray')
->action(function () {
$dateRange = session('dashboard_date_range', [
'start' => now()->subDays(30)->toDateString(),
'end' => now()->toDateString(),
]);

return Excel::download(
new ReportsExport($dateRange['start'], $dateRange['end']),
'auswertungen-' . now()->format('Y-m-d') . '.xlsx'
);
}),
];
}

Report Widgets

KeyMetricsWidget

Übersicht der wichtigsten Kennzahlen als Stat-Cards.

CategoryDistributionWidget

Gestapeltes Balkendiagramm der Auftragsverteilung nach Kategorien.

InterpreterLanguagesWidget

Verteilung der Sprachkenntnisse unter Sprachmittlern.

LanguageStatsWidget

Sprachstatistiken der Aufträge (meistgefragte Sprachen).

ClientCountriesWidget

Herkunftsländer der Klienten als Balkendiagramm.

LocationsMapWidget

File: LocationsMapWidget.php

Google Maps mit Clustering der Auftragsstandorte.

use Cheesegrits\FilamentGoogleMaps\Widgets\MapWidget;

class LocationsMapWidget extends MapWidget
{
protected static ?string $heading = 'Auftragsstandorte';
protected static ?int $sort = 6;
protected int | string | array $columnSpan = 'full';

protected function getMarkers(): array
{
return Order::query()
->whereNotNull('location_latitude')
->whereNotNull('location_longitude')
->get()
->map(fn ($order) => [
'lat' => $order->location_latitude,
'lng' => $order->location_longitude,
'title' => $order->order_number,
])
->toArray();
}
}

Custom Widget erstellen

Stat Widget

namespace App\Filament\Widgets;

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

class MyStatsWidget extends BaseWidget
{
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'),
];
}
}

Chart Widget

namespace App\Filament\Widgets;

use Filament\Widgets\ChartWidget;

class MyChartWidget extends ChartWidget
{
protected static ?string $heading = 'Chart';
protected static ?int $sort = 2;

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

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