diff --git a/app/Filament/Resources/ArticleResource/Widgets/ArticleStatsOverview.php b/app/Filament/Resources/ArticleResource/Widgets/ArticleStatsOverview.php new file mode 100644 index 00000000..eac5b82d --- /dev/null +++ b/app/Filament/Resources/ArticleResource/Widgets/ArticleStatsOverview.php @@ -0,0 +1,66 @@ +startOfWeek(); + $currentWeekEnd = now()->endOfWeek(); + + return [ + Stat::make('Total Article', Article::query()->published()->count()) + ->icon('heroicon-o-newspaper') + ->chart( + Trend::query(Article::query()->published()) + ->between( + start: $currentWeekStart, + end: $currentWeekEnd, + )->perDay() + ->count() + ->map(fn (TrendValue $value) => $value->aggregate)->toArray() + )->description(__('Total des articles postés')), + + Stat::make( + 'Article récent publié', + Article::query() + ->recent() + ->whereBetween('created_at', [ + $currentWeekStart, + $currentWeekEnd, + ])->count() + ) + ->chart( + Trend::query(Article::query()) + ->between( + start: $currentWeekStart, + end: $currentWeekEnd, + ) + ->perDay() + ->count() + ->map(fn (TrendValue $value) => $value->aggregate) + ->toArray() + )->icon('heroicon-o-newspaper') + ->color('primary') + ->description('Total des articles Postés de la semaine'), + ]; + } +} diff --git a/app/Filament/Resources/ArticleResource/Widgets/MostLikedPostsChart.php b/app/Filament/Resources/ArticleResource/Widgets/MostLikedPostsChart.php new file mode 100644 index 00000000..d9c34d3a --- /dev/null +++ b/app/Filament/Resources/ArticleResource/Widgets/MostLikedPostsChart.php @@ -0,0 +1,55 @@ +popular() + ->get(); + + return [ + 'datasets' => [ + [ + 'label' => 'Total aimé', + 'data' => $articles->pluck('reactions_count')->toArray(), + ], + ], + 'labels' => $articles->pluck('title') + ->map(fn ($title) => Str::limit($title, $this->titleLength, '...')) + ->toArray(), + ]; + } + + protected function getType(): string + { + return 'bar'; + } + + protected function getOptions(): array + { + return [ + 'scales' => [ + 'y' => [ + 'beginAtZero' => true, + ], + ], + ]; + } +} diff --git a/app/Filament/Resources/ArticleResource/Widgets/MostViewedPostsChart.php b/app/Filament/Resources/ArticleResource/Widgets/MostViewedPostsChart.php new file mode 100644 index 00000000..cc72ae86 --- /dev/null +++ b/app/Filament/Resources/ArticleResource/Widgets/MostViewedPostsChart.php @@ -0,0 +1,47 @@ +startOfWeek(), now()->endOfWeek())) // @phpstan-ignore-line + ->published() + ->orderByDesc('views_count') + ->orderByDesc('published_at') + ->get(); + + return [ + 'datasets' => [ + [ + 'label' => 'Article le plus vu', + 'data' => $articles->pluck('views_count')->toArray(), + ], + ], + 'labels' => $articles->pluck('title') + ->map(fn ($title) => Str::limit($title, $this->titleLength, '...')) + ->toArray(), + ]; + } + + protected function getType(): string + { + return 'bar'; + } +} diff --git a/app/Filament/Resources/UserResource/Widgets/UserActivityWidget.php b/app/Filament/Resources/UserResource/Widgets/UserActivityWidget.php new file mode 100644 index 00000000..c332d0a1 --- /dev/null +++ b/app/Filament/Resources/UserResource/Widgets/UserActivityWidget.php @@ -0,0 +1,58 @@ +withCount('activities') + ->verifiedUsers() + ->whereHas('activities', fn (Builder $query) => $query->whereBetween('created_at', [ + now()->startOfWeek(), + now()->endOfWeek(), + ])) + ->orderByDesc('activities_count') + ->limit(10) + ->get(); + + return [ + 'datasets' => [ + [ + 'label' => 'Total des activités', + 'data' => $users->pluck('activities_count')->toArray(), + ], + ], + 'labels' => $users->pluck('name')->toArray(), + ]; + } + + protected function getType(): string + { + return 'bar'; + } + + protected function getOptions(): array + { + return [ + 'scales' => [ + 'y' => [ + 'beginAtZero' => true, + ], + ], + ]; + } +} diff --git a/app/Filament/Resources/UserResource/Widgets/UserChartWidget.php b/app/Filament/Resources/UserResource/Widgets/UserChartWidget.php new file mode 100644 index 00000000..cd6a706b --- /dev/null +++ b/app/Filament/Resources/UserResource/Widgets/UserChartWidget.php @@ -0,0 +1,79 @@ + 'Last Week', + 'month' => 'Last Month', + '3months' => 'Last 3 Months', + ]; + } + + protected function getData(): array + { + match ($this->filter) { // @phpstan-ignore-line + 'week' => $data = Trend::model(User::class) + ->between( + start: now()->subWeeks(), + end: now() + ) + ->perDay() + ->count(), + + 'month' => $data = Trend::model(User::class) + ->between( + start: now()->subMonth(), + end: now() + ) + ->perDay() + ->count(), + + '3months' => $data = Trend::model(User::class) + ->between( + start: now()->subMonths(3), + end: now() + ) + ->perMonth() + ->count(), + }; + + return [ + 'datasets' => [ + [ + 'label' => 'Compte créé', + 'data' => $data->map(fn (TrendValue $value) => $value->aggregate), // @phpstan-ignore-line + ], + ], + 'labels' => $data->map(fn (TrendValue $value) => $value->date), // @phpstan-ignore-line + ]; + } + + protected function getType(): string + { + return 'line'; + } +} diff --git a/app/Filament/Resources/UserResource/Widgets/UserStatsOverview.php b/app/Filament/Resources/UserResource/Widgets/UserStatsOverview.php new file mode 100644 index 00000000..2fbdbcf4 --- /dev/null +++ b/app/Filament/Resources/UserResource/Widgets/UserStatsOverview.php @@ -0,0 +1,92 @@ +count()) + ->chart( + Trend::query(User::query()) + ->between( + start: now()->startOfWeek(), + end: now()->endOfWeek(), + ) + ->perDay() + ->count() + ->map(fn (TrendValue $value) => $value->aggregate) + ->toArray() + ) + ->description('Nombre total de comptes') + ->icon('heroicon-o-user') + ->color('primary'), + + Stat::make('Compte créé cette semaine', User::query() + ->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()]) + ->count()) + ->chart( + Trend::query(User::query()) + ->between( + start: now()->startOfWeek(), + end: now()->endOfWeek(), + ) + ->perDay() + ->count() + ->map(fn (TrendValue $value) => $value->aggregate) + ->toArray() + ) + ->description('Nombre de comptes créés cette semaine') + ->icon('heroicon-o-user') + ->color('primary'), + + Stat::make('Utilisateurs vérifiés', User::VerifiedUsers()->count()) + ->chart( + Trend::query(User::VerifiedUsers()) + ->between( + start: now()->subMonths(3), + end: now(), + ) + ->perDay() + ->count() + ->map(fn (TrendValue $value) => $value->aggregate) + ->toArray() + ) + ->description('Nombre total de comptes vérifiés') + ->icon('heroicon-o-check-badge') + ->color('primary'), + + Stat::make('Utilisateurs non vérifiés', User::UnVerifiedUsers()->count()) + ->chart( + Trend::query(User::UnVerifiedUsers()) + ->between( + start: now()->subMonths(3), + end: now(), + ) + ->perDay() + ->count() + ->map(fn (TrendValue $value) => $value->aggregate) + ->toArray() + ) + ->description('Nombre de comptes non vérifiés') + ->icon('heroicon-o-x-mark') + ->color('warning'), + ]; + } +} diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 587c599f..39ea131a 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -4,6 +4,12 @@ namespace App\Providers\Filament; +use App\Filament\Resources\ArticleResource\Widgets\ArticleStatsOverview; +use App\Filament\Resources\ArticleResource\Widgets\MostLikedPostsChart; +use App\Filament\Resources\ArticleResource\Widgets\MostViewedPostsChart; +use App\Filament\Resources\UserResource\Widgets\UserActivityWidget; +use App\Filament\Resources\UserResource\Widgets\UserChartWidget; +use App\Filament\Resources\UserResource\Widgets\UserStatsOverview; use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\DisableBladeIconComponents; use Filament\Http\Middleware\DispatchServingFilamentEvent; @@ -12,7 +18,6 @@ use Filament\PanelProvider; use Filament\SpatieLaravelTranslatablePlugin; use Filament\Support\Colors\Color; -use Filament\Widgets; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; @@ -46,8 +51,12 @@ public function panel(Panel $panel): Panel Pages\Dashboard::class, ]) ->widgets([ - Widgets\AccountWidget::class, - Widgets\FilamentInfoWidget::class, + UserStatsOverview::class, + UserChartWidget::class, + UserActivityWidget::class, + ArticleStatsOverview::class, + MostLikedPostsChart::class, + MostViewedPostsChart::class, ]) ->plugins([ SpatieLaravelTranslatablePlugin::make() diff --git a/app/Widgets/MostActiveUsersPerWeek.php b/app/Widgets/MostActiveUsersPerWeek.php deleted file mode 100644 index 467b5ea7..00000000 --- a/app/Widgets/MostActiveUsersPerWeek.php +++ /dev/null @@ -1,49 +0,0 @@ - - */ - protected array $config = []; - - /** - * The number of seconds before each reload. - */ - public int|float $reloadTimeout = 60 * 60 * 24 * 2; // 2 days - - /** - * The number of minutes before cache expires. - * False means no caching at all. - */ - public int|float|bool $cacheTime = 0; - - public function run(): View - { - $users = User::with('activities') - ->withCount('activities') - ->verifiedUsers() - ->whereHas('activities', fn (Builder $query) => $query->whereBetween('created_at', [ - now()->startOfWeek(), - now()->endOfWeek(), - ])) - ->orderByDesc('activities_count') - ->limit(5) - ->get(); - - return view('widgets.most_active_users_per_week', [ - 'config' => $this->config, - 'users' => $users, - ]); - } -} diff --git a/app/Widgets/MostLikedPostsPerWeek.php b/app/Widgets/MostLikedPostsPerWeek.php deleted file mode 100644 index 7bb4796c..00000000 --- a/app/Widgets/MostLikedPostsPerWeek.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ - protected array $config = []; - - /** - * The number of seconds before each reload. - */ - public int|float $reloadTimeout = 172800; // 2 days - - /** - * The number of minutes before cache expires. - * False means no caching at all. - */ - public int|float|bool $cacheTime = 0; - - /** - * Treat this method as a controller action. - * Return view() or other content to display. - */ - public function run(): View - { - $articles = Article::published() - ->popular() - ->limit(5) - ->get(); - - return view('widgets.most_liked_posts_per_week', [ - 'config' => $this->config, - 'articles' => $articles, - ]); - } -} diff --git a/app/Widgets/MostViewedPostsPerWeek.php b/app/Widgets/MostViewedPostsPerWeek.php deleted file mode 100644 index 98b4e064..00000000 --- a/app/Widgets/MostViewedPostsPerWeek.php +++ /dev/null @@ -1,45 +0,0 @@ - - */ - protected array $config = []; - - /** - * The number of seconds before each reload. - */ - public int|float $reloadTimeout = 172800; // 2 days - - /** - * The number of minutes before cache expires. - * False means no caching at all. - */ - public int|float|bool $cacheTime = 90; - - public function run(): View - { - $articles = Article::withViewsCount(Period::create(now()->startOfWeek(), now()->endOfWeek())) // @phpstan-ignore-line - ->published() - ->orderByDesc('views_count') - ->orderByDesc('published_at') - ->limit(5) - ->get(); - - return view('widgets.most_viewed_posts_per_week', [ - 'config' => $this->config, - 'articles' => $articles, - ]); - } -} diff --git a/app/Widgets/RecentNumbers.php b/app/Widgets/RecentNumbers.php deleted file mode 100644 index 38d59b97..00000000 --- a/app/Widgets/RecentNumbers.php +++ /dev/null @@ -1,76 +0,0 @@ -subMonth(); - $countUsers = User::count(); - $lastMonthRegistered = User::query()->whereBetween('created_at', [ - $lastMonth->startOfMonth()->format('Y-m-d'), - $lastMonth->endOfMonth()->format('Y-m-d'), - ])->count(); - $currentMonthRegistered = User::query()->where('created_at', '>=', now()->startOfMonth())->count(); - $difference = $currentMonthRegistered - $lastMonthRegistered; - - $countArticles = Article::count(); - $lastMonthArticles = Article::query()->whereBetween('created_at', [ - $lastMonth->startOfMonth()->format('Y-m-d'), - $lastMonth->endOfMonth()->format('Y-m-d'), - ])->count(); - $currentMonthArticles = Article::query()->where('created_at', '>=', now()->startOfMonth())->count(); - $differenceArticle = $currentMonthArticles - $lastMonthArticles; - - $totalViews = views(Article::class)->count(); - $lastMonthViews = views(Article::class) - ->period(Period::create(now()->subMonth()->startOfMonth(), now()->subMonth()->endOfMonth())) - ->remember(now()->addMonth()) - ->count(); - $currentViews = views(Article::class)->period(Period::upto(now()->startOfMonth()))->count(); - $differenceViews = $currentViews - $lastMonthViews; - - return view('widgets.recent_numbers', [ - 'config' => $this->config, - 'users' => [ - 'count' => $countUsers, - 'increase' => $difference > 0, - 'decreased' => $difference < 0, - 'current' => $currentMonthRegistered, - ], - 'articles' => [ - 'count' => $countArticles, - 'increase' => $differenceArticle > 0, - 'decreased' => $differenceArticle < 0, - 'current' => $currentMonthArticles, - ], - 'views' => [ - 'count' => $totalViews, - 'increase' => $differenceViews > 0, - 'decreased' => $differenceViews < 0, - 'current' => $currentViews, - ], - ]); - } -} diff --git a/app/Widgets/RecentPostsPerWeek.php b/app/Widgets/RecentPostsPerWeek.php deleted file mode 100644 index 4e4df0a3..00000000 --- a/app/Widgets/RecentPostsPerWeek.php +++ /dev/null @@ -1,29 +0,0 @@ - - */ - protected array $config = []; - - public function run(): View - { - $articles = Article::recent() - ->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()]) - ->limit(5) - ->get(); - - return view('widgets.recent_posts_per_week', [ - 'config' => $this->config, - 'articles' => $articles, - ]); - } -} diff --git a/composer.json b/composer.json index a90ded22..a555c15a 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "filament/filament": "^3.2", "filament/spatie-laravel-media-library-plugin": "^3.2", "filament/spatie-laravel-translatable-plugin": "^3.2", + "flowframe/laravel-trend": "^0.3.0", "gehrisandro/tailwind-merge-laravel": "^1.2", "graham-campbell/markdown": "^15.2", "guzzlehttp/guzzle": "^7.7.0", diff --git a/composer.lock b/composer.lock index 193fcfcd..3bbc1a27 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6b11f598bdc899a012175d5a089c602c", + "content-hash": "c58723e922fe0d0007e6772eca9a7520", "packages": [ { "name": "abraham/twitteroauth", @@ -3236,6 +3236,80 @@ }, "time": "2024-11-24T11:22:49+00:00" }, + { + "name": "flowframe/laravel-trend", + "version": "v0.3.0", + "source": { + "type": "git", + "url": "https://github.com/Flowframe/laravel-trend.git", + "reference": "391849c27a1d4791efae930746a51d982820bd3a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Flowframe/laravel-trend/zipball/391849c27a1d4791efae930746a51d982820bd3a", + "reference": "391849c27a1d4791efae930746a51d982820bd3a", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.37|^9|^10.0|^11.0", + "php": "^8.2", + "spatie/laravel-package-tools": "^1.4.3" + }, + "require-dev": { + "nunomaduro/collision": "^5.3|^6.1|^8.0", + "orchestra/testbench": "^6.15|^7.0|^8.0|^9.0", + "pestphp/pest": "^1.18|^2.34", + "pestphp/pest-plugin-laravel": "^1.1|^2.3", + "spatie/laravel-ray": "^1.23", + "vimeo/psalm": "^4.8|^5.6" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Trend": "Flowframe\\Trend\\TrendFacade" + }, + "providers": [ + "Flowframe\\Trend\\TrendServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Flowframe\\Trend\\": "src", + "Flowframe\\Trend\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Klopstra", + "email": "lars@flowframe.nl", + "role": "Developer" + } + ], + "description": "Easily generate model trends", + "homepage": "https://github.com/flowframe/laravel-trend", + "keywords": [ + "Flowframe", + "laravel", + "laravel-trend" + ], + "support": { + "issues": "https://github.com/Flowframe/laravel-trend/issues", + "source": "https://github.com/Flowframe/laravel-trend/tree/v0.3.0" + }, + "funding": [ + { + "url": "https://github.com/larsklopstra", + "type": "github" + } + ], + "time": "2024-09-27T09:20:30+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.3.0", diff --git a/resources/views/widgets/most_active_users_per_week.blade.php b/resources/views/widgets/most_active_users_per_week.blade.php deleted file mode 100644 index b8f0b7df..00000000 --- a/resources/views/widgets/most_active_users_per_week.blade.php +++ /dev/null @@ -1,37 +0,0 @@ -
-
-

Utilisateurs actifs

- - {{ $users->count() }} - -
-
- Utilisateur - Profil -
- -
diff --git a/resources/views/widgets/most_liked_posts_per_week.blade.php b/resources/views/widgets/most_liked_posts_per_week.blade.php deleted file mode 100644 index b2ee0819..00000000 --- a/resources/views/widgets/most_liked_posts_per_week.blade.php +++ /dev/null @@ -1,22 +0,0 @@ -
-
-

Articles les plus likés

-
-
- Article - Likes -
- -
diff --git a/resources/views/widgets/most_viewed_posts_per_week.blade.php b/resources/views/widgets/most_viewed_posts_per_week.blade.php deleted file mode 100644 index 1f328677..00000000 --- a/resources/views/widgets/most_viewed_posts_per_week.blade.php +++ /dev/null @@ -1,22 +0,0 @@ -
-
-

Articles les plus visités

-
-
- Article - Vues -
- -
diff --git a/resources/views/widgets/recent_numbers.blade.php b/resources/views/widgets/recent_numbers.blade.php deleted file mode 100644 index a256f4c0..00000000 --- a/resources/views/widgets/recent_numbers.blade.php +++ /dev/null @@ -1,57 +0,0 @@ -
-
-
-
-
- -

{{ __('Utilisateurs') }}

-
-
-
-

{{ $users['count'] }}

- @if ($users['increase']) - - @else - - @endif - vs mois dernier -
-
- -
-
-
- -

{{ __('Articles') }}

-
-
-
-

{{ $articles['count'] }}

- @if ($articles['increase']) - - @else - - @endif - vs mois dernier -
-
- -
-
-
- -

{{ __('Vues sur les articles') }}

-
-
-
-

{{ $views['count'] }}

- @if ($views['increase']) - - @else - - @endif - vs mois dernier -
-
-
-
diff --git a/resources/views/widgets/recent_posts_per_week.blade.php b/resources/views/widgets/recent_posts_per_week.blade.php deleted file mode 100644 index 580036c6..00000000 --- a/resources/views/widgets/recent_posts_per_week.blade.php +++ /dev/null @@ -1,37 +0,0 @@ -
-
-

Articles de la semaine

- - {{ $articles->count() }} - -
-
- Article - Auteur -
- -