Add dashboard and widgets: Implemented a new Dashboard page with various widgets including Content Overview, Recent Activities, System Stats, Translation Progress, and User Activity. Added localization support for dashboard elements in English and Turkish.
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Pages;
|
||||
|
||||
use App\Filament\Admin\Widgets\ContentOverviewWidget;
|
||||
use App\Filament\Admin\Widgets\RecentActivitiesWidget;
|
||||
use App\Filament\Admin\Widgets\SystemStatsWidget;
|
||||
use App\Filament\Admin\Widgets\TranslationProgressWidget;
|
||||
use App\Filament\Admin\Widgets\UserActivityWidget;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Pages\Dashboard as BaseDashboard;
|
||||
|
||||
class Dashboard extends BaseDashboard
|
||||
{
|
||||
public function getTitle(): string
|
||||
{
|
||||
return __('dashboard.title');
|
||||
}
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('dashboard.navigation_label');
|
||||
}
|
||||
|
||||
public function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('create_page')
|
||||
->label(__('dashboard.actions.create_page'))
|
||||
->icon('heroicon-m-plus')
|
||||
->url(route('filament.admin.resources.pages.create'))
|
||||
->color('primary'),
|
||||
|
||||
Action::make('manage_users')
|
||||
->label(__('dashboard.actions.manage_users'))
|
||||
->icon('heroicon-m-users')
|
||||
->url(route('filament.admin.resources.users.index'))
|
||||
->color('gray'),
|
||||
];
|
||||
}
|
||||
|
||||
public function getHeaderWidgets(): array
|
||||
{
|
||||
return [
|
||||
SystemStatsWidget::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [
|
||||
RecentActivitiesWidget::class,
|
||||
TranslationProgressWidget::class,
|
||||
UserActivityWidget::class,
|
||||
ContentOverviewWidget::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function getColumns(): array | int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use App\Models\Page;
|
||||
use App\Models\Translation;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
|
||||
class ContentOverviewWidget extends ChartWidget
|
||||
{
|
||||
protected static ?int $sort = 5;
|
||||
protected int | string | array $columnSpan = 'full';
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$pagesByStatus = Page::selectRaw('status, count(*) as count')
|
||||
->groupBy('status')
|
||||
->pluck('count', 'status')
|
||||
->toArray();
|
||||
|
||||
$translationsByStatus = Translation::selectRaw('status, count(*) as count')
|
||||
->groupBy('status')
|
||||
->pluck('count', 'status')
|
||||
->toArray();
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => __('dashboard.content_overview.pages_by_status'),
|
||||
'data' => [
|
||||
$pagesByStatus['published'] ?? 0,
|
||||
$pagesByStatus['draft'] ?? 0,
|
||||
$pagesByStatus['review'] ?? 0,
|
||||
],
|
||||
'backgroundColor' => [
|
||||
'rgb(34, 197, 94)', // success - published
|
||||
'rgb(156, 163, 175)', // gray - draft
|
||||
'rgb(245, 158, 11)', // warning - review
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => __('dashboard.content_overview.translations_by_status'),
|
||||
'data' => [
|
||||
$translationsByStatus['published'] ?? 0,
|
||||
$translationsByStatus['draft'] ?? 0,
|
||||
$translationsByStatus['review'] ?? 0,
|
||||
$translationsByStatus['approved'] ?? 0,
|
||||
],
|
||||
'backgroundColor' => [
|
||||
'rgb(34, 197, 94)', // success - published
|
||||
'rgb(156, 163, 175)', // gray - draft
|
||||
'rgb(245, 158, 11)', // warning - review
|
||||
'rgb(59, 130, 246)', // info - approved
|
||||
],
|
||||
],
|
||||
],
|
||||
'labels' => [
|
||||
__('dashboard.content_overview.published'),
|
||||
__('dashboard.content_overview.draft'),
|
||||
__('dashboard.content_overview.review'),
|
||||
__('dashboard.content_overview.approved'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'bar';
|
||||
}
|
||||
|
||||
protected function getOptions(): array
|
||||
{
|
||||
return [
|
||||
'responsive' => true,
|
||||
'maintainAspectRatio' => false,
|
||||
'plugins' => [
|
||||
'legend' => [
|
||||
'position' => 'top',
|
||||
],
|
||||
],
|
||||
'scales' => [
|
||||
'y' => [
|
||||
'beginAtZero' => true,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use App\Models\Page;
|
||||
use App\Models\Translation;
|
||||
use App\Models\User;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as BaseWidget;
|
||||
|
||||
class RecentActivitiesWidget extends BaseWidget
|
||||
{
|
||||
protected static ?int $sort = 2;
|
||||
protected int | string | array $columnSpan = 'full';
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(
|
||||
Page::query()
|
||||
->latest()
|
||||
->limit(5)
|
||||
)
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('title')
|
||||
->label(__('dashboard.activities.recent_pages'))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('status')
|
||||
->badge()
|
||||
->color(fn (string $state): string => match ($state) {
|
||||
'published' => 'success',
|
||||
'draft' => 'gray',
|
||||
'review' => 'warning',
|
||||
default => 'gray',
|
||||
}),
|
||||
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label(__('dashboard.user_activity.created_at'))
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('author.name')
|
||||
->label(__('dashboard.activities.creator'))
|
||||
->sortable(),
|
||||
])
|
||||
->heading(__('dashboard.activities.recent_pages'))
|
||||
->paginated(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use App\Models\Language;
|
||||
use App\Models\Page;
|
||||
use App\Models\Translation;
|
||||
use App\Models\User;
|
||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
class SystemStatsWidget extends BaseWidget
|
||||
{
|
||||
protected static ?int $sort = 1;
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
return [
|
||||
Stat::make(__('dashboard.stats.total_users'), User::count())
|
||||
->description(__('dashboard.stats.total_users'))
|
||||
->descriptionIcon('heroicon-m-users')
|
||||
->color('primary'),
|
||||
|
||||
Stat::make(__('dashboard.stats.total_pages'), Page::count())
|
||||
->description(__('dashboard.stats.published_pages') . ': ' . Page::where('status', 'published')->count())
|
||||
->descriptionIcon('heroicon-m-document-text')
|
||||
->color('success'),
|
||||
|
||||
Stat::make(__('dashboard.stats.total_translations'), Translation::count())
|
||||
->description(__('dashboard.stats.published_translations') . ': ' . Translation::where('status', 'published')->count())
|
||||
->descriptionIcon('heroicon-m-language')
|
||||
->color('info'),
|
||||
|
||||
Stat::make(__('dashboard.stats.active_languages'), Language::where('is_active', true)->count())
|
||||
->description(__('dashboard.stats.active_languages'))
|
||||
->descriptionIcon('heroicon-m-globe-alt')
|
||||
->color('warning'),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getColumns(): int
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use App\Models\Language;
|
||||
use App\Models\Translation;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as BaseWidget;
|
||||
|
||||
class TranslationProgressWidget extends BaseWidget
|
||||
{
|
||||
protected static ?int $sort = 3;
|
||||
protected int | string | array $columnSpan = 'full';
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(
|
||||
Language::query()
|
||||
->where('is_active', true)
|
||||
->withCount([
|
||||
'translations as total_translations',
|
||||
'translations as published_translations' => function ($query) {
|
||||
$query->where('status', 'published');
|
||||
},
|
||||
'translations as draft_translations' => function ($query) {
|
||||
$query->where('status', 'draft');
|
||||
},
|
||||
])
|
||||
)
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->label(__('dashboard.translation_progress.language'))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('native_name')
|
||||
->label(__('dashboard.translation_progress.native_name'))
|
||||
->searchable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('total_translations')
|
||||
->label(__('dashboard.translation_progress.total_fields'))
|
||||
->numeric()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('published_translations')
|
||||
->label(__('dashboard.translation_progress.translated_fields'))
|
||||
->numeric()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('progress_percentage')
|
||||
->label(__('dashboard.translation_progress.progress_percentage'))
|
||||
->getStateUsing(function ($record) {
|
||||
if ($record->total_translations == 0) {
|
||||
return '0%';
|
||||
}
|
||||
$percentage = round(($record->published_translations / $record->total_translations) * 100);
|
||||
return $percentage . '%';
|
||||
})
|
||||
->badge()
|
||||
->color(function (string $state): string {
|
||||
$percentage = (int) str_replace('%', '', $state);
|
||||
if ($percentage >= 80) {
|
||||
return 'success';
|
||||
} elseif ($percentage >= 50) {
|
||||
return 'warning';
|
||||
} else {
|
||||
return 'danger';
|
||||
}
|
||||
}),
|
||||
|
||||
Tables\Columns\IconColumn::make('is_active')
|
||||
->label(__('dashboard.translation_progress.active'))
|
||||
->boolean(),
|
||||
|
||||
Tables\Columns\IconColumn::make('is_default')
|
||||
->label(__('dashboard.translation_progress.default'))
|
||||
->boolean(),
|
||||
])
|
||||
->heading(__('dashboard.widgets.translation_progress.title'))
|
||||
->paginated(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use App\Models\User;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as BaseWidget;
|
||||
|
||||
class UserActivityWidget extends BaseWidget
|
||||
{
|
||||
protected static ?int $sort = 4;
|
||||
protected int | string | array $columnSpan = 'full';
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(
|
||||
User::query()
|
||||
->with('roles')
|
||||
->latest()
|
||||
->limit(10)
|
||||
)
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->label(__('dashboard.user_activity.username'))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('email')
|
||||
->label(__('dashboard.user_activity.email'))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('roles.name')
|
||||
->label(__('dashboard.user_activity.role'))
|
||||
->badge()
|
||||
->color('primary'),
|
||||
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label(__('dashboard.user_activity.created_at'))
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('email_verified_at')
|
||||
->label(__('dashboard.user_activity.email_verified'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->placeholder(__('dashboard.user_activity.unverified')),
|
||||
])
|
||||
->heading(__('dashboard.widgets.user_activity.title'))
|
||||
->paginated(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use Filament\Http\Middleware\Authenticate;
|
||||
use Filament\Http\Middleware\AuthenticateSession;
|
||||
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
||||
use Filament\Pages\Dashboard;
|
||||
use App\Filament\Admin\Pages\Dashboard;
|
||||
use Filament\Panel;
|
||||
use Filament\PanelProvider;
|
||||
use Filament\Support\Colors\Color;
|
||||
|
||||
Reference in New Issue
Block a user