feat(setup preprod)
All checks were successful
Deploy Roxane to Preprod / deploy (push) Successful in 26h10m32s
All checks were successful
Deploy Roxane to Preprod / deploy (push) Successful in 26h10m32s
This commit is contained in:
48
app/Filament/Widgets/DashboardStatsWidget.php
Normal file
48
app/Filament/Widgets/DashboardStatsWidget.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Widgets;
|
||||||
|
|
||||||
|
use App\Models\Member;
|
||||||
|
use App\Models\Membership;
|
||||||
|
use Filament\Support\Enums\IconPosition;
|
||||||
|
use Filament\Widgets\StatsOverviewWidget;
|
||||||
|
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||||
|
|
||||||
|
class DashboardStatsWidget extends StatsOverviewWidget
|
||||||
|
{
|
||||||
|
protected static ?int $sort = 1;
|
||||||
|
|
||||||
|
protected function getStats(): array
|
||||||
|
{
|
||||||
|
$activeMembers = Member::where('status', 'valid')->count();
|
||||||
|
$activeMemberships = Membership::where('status', 'active')->count();
|
||||||
|
$newThisMonth = Membership::whereMonth('created_at', now()->month)
|
||||||
|
->whereYear('created_at', now()->year)
|
||||||
|
->count();
|
||||||
|
$unpaidMemberships = Membership::where('payment_status', 'unpaid')
|
||||||
|
->where('status', 'active')
|
||||||
|
->count();
|
||||||
|
|
||||||
|
return [
|
||||||
|
Stat::make('Membres actifs', $activeMembers)
|
||||||
|
->description('Statut "Valide"')
|
||||||
|
->descriptionIcon('heroicon-o-user-group', IconPosition::Before)
|
||||||
|
->color('success'),
|
||||||
|
|
||||||
|
Stat::make('Adhésions actives', $activeMemberships)
|
||||||
|
->description('En cours')
|
||||||
|
->descriptionIcon('heroicon-o-identification', IconPosition::Before)
|
||||||
|
->color('primary'),
|
||||||
|
|
||||||
|
Stat::make('Nouvelles adhésions', $newThisMonth)
|
||||||
|
->description('Ce mois-ci')
|
||||||
|
->descriptionIcon('heroicon-o-calendar', IconPosition::Before)
|
||||||
|
->color('info'),
|
||||||
|
|
||||||
|
Stat::make('Paiements en attente', $unpaidMemberships)
|
||||||
|
->description('Adhésions actives non réglées')
|
||||||
|
->descriptionIcon('heroicon-o-banknotes', IconPosition::Before)
|
||||||
|
->color($unpaidMemberships > 0 ? 'warning' : 'success'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
77
app/Filament/Widgets/LatestMembershipsWidget.php
Normal file
77
app/Filament/Widgets/LatestMembershipsWidget.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Widgets;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Memberships\MembershipResource;
|
||||||
|
use App\Models\Membership;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Filament\Widgets\TableWidget;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
class LatestMembershipsWidget extends TableWidget
|
||||||
|
{
|
||||||
|
protected static ?int $sort = 3;
|
||||||
|
|
||||||
|
protected static ?string $heading = 'Dernières adhésions';
|
||||||
|
|
||||||
|
protected int|string|array $columnSpan = 'full';
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->query(
|
||||||
|
fn (): Builder => Membership::query()
|
||||||
|
->with(['member', 'package'])
|
||||||
|
->latest()
|
||||||
|
->limit(8)
|
||||||
|
)
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('member.full_name')
|
||||||
|
->label('Adhérent')
|
||||||
|
->searchable(['members.firstname', 'members.lastname']),
|
||||||
|
|
||||||
|
TextColumn::make('package.name')
|
||||||
|
->label('Package'),
|
||||||
|
|
||||||
|
TextColumn::make('status')
|
||||||
|
->label('Statut adhésion')
|
||||||
|
->formatStateUsing(fn (string $state) => Membership::getAttributeLabel($state))
|
||||||
|
->badge()
|
||||||
|
->color(fn (string $state): string => match ($state) {
|
||||||
|
'active' => 'success',
|
||||||
|
'expired' => 'danger',
|
||||||
|
'pending' => 'warning',
|
||||||
|
default => 'gray',
|
||||||
|
}),
|
||||||
|
|
||||||
|
TextColumn::make('payment_status')
|
||||||
|
->label('Paiement')
|
||||||
|
->formatStateUsing(fn (string $state) => Membership::getAttributeLabel($state))
|
||||||
|
->badge()
|
||||||
|
->color(fn (string $state): string => match ($state) {
|
||||||
|
'paid' => 'success',
|
||||||
|
'unpaid' => 'danger',
|
||||||
|
'partial' => 'warning',
|
||||||
|
default => 'gray',
|
||||||
|
}),
|
||||||
|
|
||||||
|
TextColumn::make('amount')
|
||||||
|
->label('Montant')
|
||||||
|
->money('EUR'),
|
||||||
|
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->label('Créée le')
|
||||||
|
->dateTime('d/m/Y')
|
||||||
|
->sortable(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
Action::make('edit')
|
||||||
|
->label('Voir')
|
||||||
|
->icon('heroicon-o-pencil-square')
|
||||||
|
->url(fn (Membership $record): string => MembershipResource::getUrl('edit', ['record' => $record])),
|
||||||
|
])
|
||||||
|
->paginated(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
50
app/Filament/Widgets/MembershipsPerMonthChart.php
Normal file
50
app/Filament/Widgets/MembershipsPerMonthChart.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Widgets;
|
||||||
|
|
||||||
|
use App\Models\Membership;
|
||||||
|
use Filament\Widgets\ChartWidget;
|
||||||
|
|
||||||
|
class MembershipsPerMonthChart extends ChartWidget
|
||||||
|
{
|
||||||
|
protected static ?int $sort = 2;
|
||||||
|
|
||||||
|
protected ?string $heading = 'Adhésions par mois';
|
||||||
|
|
||||||
|
protected ?string $description = 'Nouvelles adhésions sur les 12 derniers mois';
|
||||||
|
|
||||||
|
protected ?string $maxHeight = '280px';
|
||||||
|
|
||||||
|
protected function getData(): array
|
||||||
|
{
|
||||||
|
$data = collect(range(11, 0))->map(function (int $monthsAgo) {
|
||||||
|
$date = now()->subMonths($monthsAgo);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'month' => $date->translatedFormat('M Y'),
|
||||||
|
'count' => Membership::whereYear('created_at', $date->year)
|
||||||
|
->whereMonth('created_at', $date->month)
|
||||||
|
->count(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
'datasets' => [
|
||||||
|
[
|
||||||
|
'label' => 'Adhésions',
|
||||||
|
'data' => $data->pluck('count')->toArray(),
|
||||||
|
'borderColor' => 'rgb(244, 63, 94)',
|
||||||
|
'backgroundColor' => 'rgba(244, 63, 94, 0.1)',
|
||||||
|
'fill' => true,
|
||||||
|
'tension' => 0.4,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'labels' => $data->pluck('month')->toArray(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getType(): string
|
||||||
|
{
|
||||||
|
return 'line';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,9 @@
|
|||||||
namespace App\Providers\Filament;
|
namespace App\Providers\Filament;
|
||||||
|
|
||||||
use Andreia\FilamentNordTheme\FilamentNordThemePlugin;
|
use Andreia\FilamentNordTheme\FilamentNordThemePlugin;
|
||||||
use App\Filament\Resources\Members\Widgets\MemberCount;
|
use App\Filament\Widgets\DashboardStatsWidget;
|
||||||
use App\Filament\Resources\Memberships\Widgets\MembershipsChart;
|
use App\Filament\Widgets\LatestMembershipsWidget;
|
||||||
|
use App\Filament\Widgets\MembershipsPerMonthChart;
|
||||||
use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
|
use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
|
||||||
use Filament\Http\Middleware\Authenticate;
|
use Filament\Http\Middleware\Authenticate;
|
||||||
use Filament\Http\Middleware\AuthenticateSession;
|
use Filament\Http\Middleware\AuthenticateSession;
|
||||||
@@ -14,8 +15,7 @@ use Filament\Pages\Dashboard;
|
|||||||
use Filament\Panel;
|
use Filament\Panel;
|
||||||
use Filament\PanelProvider;
|
use Filament\PanelProvider;
|
||||||
use Filament\Support\Colors\Color;
|
use Filament\Support\Colors\Color;
|
||||||
use Filament\Widgets\AccountWidget;
|
use Filament\View\PanelsRenderHook;
|
||||||
use Filament\Widgets\FilamentInfoWidget;
|
|
||||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
||||||
@@ -46,10 +46,9 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
])
|
])
|
||||||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
|
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
|
||||||
->widgets([
|
->widgets([
|
||||||
AccountWidget::class,
|
DashboardStatsWidget::class,
|
||||||
FilamentInfoWidget::class,
|
MembershipsPerMonthChart::class,
|
||||||
MemberCount::class,
|
LatestMembershipsWidget::class,
|
||||||
// MembershipsChart::class,
|
|
||||||
])
|
])
|
||||||
->middleware([
|
->middleware([
|
||||||
EncryptCookies::class,
|
EncryptCookies::class,
|
||||||
@@ -68,6 +67,10 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
])
|
])
|
||||||
->authMiddleware([
|
->authMiddleware([
|
||||||
Authenticate::class,
|
Authenticate::class,
|
||||||
]);
|
])
|
||||||
|
->renderHook(
|
||||||
|
PanelsRenderHook::GLOBAL_SEARCH_BEFORE,
|
||||||
|
fn () => view('filament.components.visit-site-button'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<a
|
||||||
|
href="{{ url('/') }}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white"
|
||||||
|
>
|
||||||
|
<x-heroicon-o-globe-alt class="h-4 w-4" />
|
||||||
|
Voir le site
|
||||||
|
</a>
|
||||||
Reference in New Issue
Block a user