Merge pull request #8 from truncgil/dashboard

Add dashboard and widgets: Implemented a new Dashboard page with vari…
This commit is contained in:
Truncgil Technology
2025-10-24 22:16:54 +03:00
committed by GitHub
9 changed files with 604 additions and 1 deletions
+63
View File
@@ -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;
+107
View File
@@ -0,0 +1,107 @@
<?php
return [
'title' => 'Dashboard',
'navigation_label' => 'Dashboard',
// Widgets
'widgets' => [
'system_stats' => [
'title' => 'System Statistics',
'description' => 'Platform overview',
],
'recent_activities' => [
'title' => 'Recent Activities',
'description' => 'Latest created content',
],
'translation_progress' => [
'title' => 'Translation Status',
'description' => 'Translation progress by language',
],
'user_activity' => [
'title' => 'User Activity',
'description' => 'Active users and roles',
],
'content_overview' => [
'title' => 'Content Overview',
'description' => 'Pages and translation status',
],
],
// Stats labels
'stats' => [
'total_users' => 'Total Users',
'total_pages' => 'Total Pages',
'total_translations' => 'Total Translations',
'active_languages' => 'Active Languages',
'published_pages' => 'Published Pages',
'draft_translations' => 'Draft Translations',
'pending_translations' => 'Pending Translations',
'published_translations' => 'Published Translations',
],
// Activity labels
'activities' => [
'recent_pages' => 'Recent Pages',
'recent_translations' => 'Recent Translations',
'recent_users' => 'Recent Users',
'no_activity' => 'No activity yet',
'view_all' => 'View All',
'creator' => 'Creator',
],
// Translation progress
'translation_progress' => [
'language' => 'Language',
'native_name' => 'Native Name',
'total_fields' => 'Total Fields',
'translated_fields' => 'Translated Fields',
'progress_percentage' => 'Progress',
'status' => 'Status',
'last_updated' => 'Last Updated',
'active' => 'Active',
'default' => 'Default',
],
// User activity
'user_activity' => [
'active_users' => 'Active Users',
'user_roles' => 'User Roles',
'last_login' => 'Last Login',
'created_at' => 'Created At',
'role' => 'Role',
'permissions' => 'Permissions',
'username' => 'Username',
'email' => 'Email',
'email_verified' => 'Email Verified',
'unverified' => 'Unverified',
],
// Content overview
'content_overview' => [
'pages_by_status' => 'Pages by Status',
'translations_by_status' => 'Translations by Status',
'published' => 'Published',
'draft' => 'Draft',
'review' => 'Review',
'approved' => 'Approved',
],
// Messages
'messages' => [
'welcome' => 'Welcome to Citrus Platform!',
'system_healthy' => 'System is running healthy',
'no_data' => 'No data available yet',
'loading' => 'Loading...',
],
// Actions
'actions' => [
'create_page' => 'New Page',
'create_translation' => 'New Translation',
'manage_users' => 'User Management',
'manage_languages' => 'Language Management',
'view_reports' => 'View Reports',
],
];
+107
View File
@@ -0,0 +1,107 @@
<?php
return [
'title' => 'Dashboard',
'navigation_label' => 'Dashboard',
// Widgets
'widgets' => [
'system_stats' => [
'title' => 'Sistem İstatistikleri',
'description' => 'Platform genel durumu',
],
'recent_activities' => [
'title' => 'Son Aktiviteler',
'description' => 'Son oluşturulan içerikler',
],
'translation_progress' => [
'title' => 'Çeviri Durumu',
'description' => 'Dil bazında çeviri ilerlemesi',
],
'user_activity' => [
'title' => 'Kullanıcı Aktivitesi',
'description' => 'Aktif kullanıcılar ve roller',
],
'content_overview' => [
'title' => 'İçerik Özeti',
'description' => 'Sayfa ve çeviri durumu',
],
],
// Stats labels
'stats' => [
'total_users' => 'Toplam Kullanıcı',
'total_pages' => 'Toplam Sayfa',
'total_translations' => 'Toplam Çeviri',
'active_languages' => 'Aktif Dil',
'published_pages' => 'Yayınlanan Sayfa',
'draft_translations' => 'Taslak Çeviri',
'pending_translations' => 'Bekleyen Çeviri',
'published_translations' => 'Yayınlanan Çeviri',
],
// Activity labels
'activities' => [
'recent_pages' => 'Son Sayfalar',
'recent_translations' => 'Son Çeviriler',
'recent_users' => 'Son Kullanıcılar',
'no_activity' => 'Henüz aktivite yok',
'view_all' => 'Tümünü Görüntüle',
'creator' => 'Oluşturan',
],
// Translation progress
'translation_progress' => [
'language' => 'Dil',
'native_name' => 'Yerel Ad',
'total_fields' => 'Toplam Alan',
'translated_fields' => 'Çevrilen Alan',
'progress_percentage' => 'İlerleme',
'status' => 'Durum',
'last_updated' => 'Son Güncelleme',
'active' => 'Aktif',
'default' => 'Varsayılan',
],
// User activity
'user_activity' => [
'active_users' => 'Aktif Kullanıcılar',
'user_roles' => 'Kullanıcı Rolleri',
'last_login' => 'Son Giriş',
'created_at' => 'Oluşturulma',
'role' => 'Rol',
'permissions' => 'Yetkiler',
'username' => 'Kullanıcı Adı',
'email' => 'E-posta',
'email_verified' => 'E-posta Doğrulandı',
'unverified' => 'Doğrulanmamış',
],
// Content overview
'content_overview' => [
'pages_by_status' => 'Sayfa Durumları',
'translations_by_status' => 'Çeviri Durumları',
'published' => 'Yayınlanan',
'draft' => 'Taslak',
'review' => 'İnceleme',
'approved' => 'Onaylanan',
],
// Messages
'messages' => [
'welcome' => 'Citrus Platform\'a hoş geldiniz!',
'system_healthy' => 'Sistem sağlıklı çalışıyor',
'no_data' => 'Henüz veri bulunmuyor',
'loading' => 'Yükleniyor...',
],
// Actions
'actions' => [
'create_page' => 'Yeni Sayfa',
'create_translation' => 'Yeni Çeviri',
'manage_users' => 'Kullanıcı Yönetimi',
'manage_languages' => 'Dil Yönetimi',
'view_reports' => 'Raporları Görüntüle',
],
];