diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a91953b..9f85c1e 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -10,5 +10,3 @@ parameters: tmpDir: build/phpstan checkOctaneCompatibility: true checkModelProperties: true - checkMissingIterableValueType: false - diff --git a/src/Resources/MailResource.php b/src/Resources/MailResource.php index a22b615..9e1a54f 100644 --- a/src/Resources/MailResource.php +++ b/src/Resources/MailResource.php @@ -2,6 +2,7 @@ namespace Vormkracht10\FilamentMails\Resources; +use Filament\Facades\Filament; use Filament\Forms\Components\TagsInput; use Filament\Infolists\Components\Grid; use Filament\Infolists\Components\RepeatableEntry; @@ -174,10 +175,19 @@ public static function infolist(Infolist $infolist): Infolist TextEntry::make('type') ->label(__('Type')) ->badge() - ->url(fn (MailEvent $record) => route('filament.' . filament()->getCurrentPanel()?->getId() . '.resources.mails.events.view', [ - 'record' => $record, - 'tenant' => filament()->getTenant()?->id, - ])) + ->url(function (Mail $record) { + $panel = Filament::getCurrentPanel(); + $tenant = Filament::getTenant(); + + if (! $panel || ! $tenant) { + return null; + } + + return route('filament.' . $panel->getId() . '.resources.mails.events.view', [ + 'record' => $record, + 'tenant' => $tenant->getKey(), + ]); + }) ->color(fn (EventType $state): string => match ($state) { EventType::DELIVERED => 'success', EventType::CLICKED => 'clicked', @@ -192,10 +202,19 @@ public static function infolist(Infolist $infolist): Infolist return ucfirst($state->value); }), TextEntry::make('occurred_at') - ->url(fn (MailEvent $record) => route('filament.' . filament()->getCurrentPanel()?->getId() . '.resources.mails.events.view', [ - 'record' => $record, - 'tenant' => filament()->getTenant()?->id, - ])) + ->url(function (MailEvent $record) { + $panel = Filament::getCurrentPanel(); + $tenant = Filament::getTenant(); + + if (! $panel || ! $tenant) { + return null; + } + + return route('filament.' . $panel->getId() . '.resources.mails.events.view', [ + 'record' => $record, + 'tenant' => $tenant->getKey(), + ]); + }) ->since() ->dateTimeTooltip('d-m-Y H:i') ->label(__('Occurred At')), @@ -366,8 +385,8 @@ public static function table(Table $table): Table ->fillForm(function (Mail $record) { return [ 'to' => array_keys($record->to), - 'cc' => is_array($record->cc) ? array_keys($record->cc) : null, - 'bcc' => is_array($record->bcc) ? array_keys($record->bcc) : null, + 'cc' => array_keys($record->cc), + 'bcc' => array_keys($record->bcc), ]; }) ->action(function (Mail $record, array $data) { @@ -430,7 +449,7 @@ private static function formatMailState(array $emails, bool $mailOnly = false): { return collect($emails) ->mapWithKeys(fn ($value, $key) => [$key => $value ?? $key]) - ->map(fn ($value, $key) => $mailOnly ? $key : ($value === null ? $key : "$value <$key>")) + ->map(fn ($value, $key) => $mailOnly ? $key : ($value == null ? $key : "$value <$key>")) ->implode(', '); } diff --git a/src/Resources/MailResource/Pages/ListMails.php b/src/Resources/MailResource/Pages/ListMails.php index af3ff3e..27588db 100644 --- a/src/Resources/MailResource/Pages/ListMails.php +++ b/src/Resources/MailResource/Pages/ListMails.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Builder; use Vormkracht10\FilamentMails\Resources\MailResource; use Vormkracht10\FilamentMails\Resources\MailResource\Widgets\MailStatsWidget; +use Vormkracht10\Mails\Models\Mail; class ListMails extends ListRecords { @@ -24,8 +25,11 @@ protected function getActions(): array public function getTabs(): array { + /** @var Mail $class */ $class = config('mails.models.mail'); + $class = new $class; + return [ 'all' => Tab::make() ->label(__('All')) @@ -38,46 +42,57 @@ public function getTabs(): array ->badgeColor('info') ->icon('heroicon-o-paper-airplane') ->badge($class::sent()->count()) - ->modifyQueryUsing(fn (Builder $query) => $query->sent()), + ->modifyQueryUsing(function (Builder $query) use ($class): Builder { + return $class->sent(); + }), 'delivered' => Tab::make() ->label(__('Delivered')) ->badgeColor('success') ->icon('heroicon-o-check-circle') ->badge($class::delivered()->count()) - ->modifyQueryUsing(fn (Builder $query) => $query->delivered()), + ->modifyQueryUsing(function (Builder $query) use ($class): Builder { + return $class->delivered(); + }), 'opened' => Tab::make() ->label(__('Opened')) ->badgeColor('info') ->icon('heroicon-o-eye') ->badge($class::opened()->count()) - ->modifyQueryUsing(fn (Builder $query) => $query->opened()), + ->modifyQueryUsing(function (Builder $query) use ($class): Builder { + return $class->opened(); + }), 'clicked' => Tab::make() ->label(__('Clicked')) ->badgeColor('clicked') ->icon('heroicon-o-cursor-arrow-rays') ->badge($class::clicked()->count()) - ->modifyQueryUsing(fn (Builder $query) => $query->clicked()), + ->modifyQueryUsing(function (Builder $query) use ($class): Builder { + return $class->clicked(); + }), 'bounced' => Tab::make() ->label(__('Bounced')) ->badgeColor('danger') ->icon('heroicon-o-x-circle') ->badge(fn () => $class::softBounced()->count() + $class::hardBounced()->count()) - ->modifyQueryUsing(fn (Builder $query) => $query->where(function ($query) { - $query->softBounced()->orWhere(function ($query) { - $query->hardBounced(); + ->modifyQueryUsing(function (Builder $query) use ($class): Builder { + return $query->where(function (Builder $subQuery) use ($class) { + return $subQuery->whereIn('id', $class::softBounced()->select('id')) + ->orWhereIn('id', $class::hardBounced()->select('id')); }); - })), + }), 'unsent' => Tab::make() ->label(__('Unsent')) ->badgeColor('gray') ->icon('heroicon-o-x-circle') ->badge($class::unsent()->count()) - ->modifyQueryUsing(fn (Builder $query) => $query->unsent()), + ->modifyQueryUsing(function (Builder $query) use ($class): Builder { + return $class->unsent(); + }), ]; } diff --git a/src/Resources/MailResource/Widgets/MailStatsWidget.php b/src/Resources/MailResource/Widgets/MailStatsWidget.php index f3259e6..d5af043 100644 --- a/src/Resources/MailResource/Widgets/MailStatsWidget.php +++ b/src/Resources/MailResource/Widgets/MailStatsWidget.php @@ -2,6 +2,7 @@ namespace Vormkracht10\FilamentMails\Resources\MailResource\Widgets; +use Filament\Facades\Filament; use Filament\Widgets\StatsOverviewWidget as BaseWidget; use Filament\Widgets\StatsOverviewWidget\Stat; @@ -26,6 +27,20 @@ protected function getStats(): array return []; } + $generateUrl = function (string $activeTab): ?string { + $panel = Filament::getCurrentPanel(); + $tenant = Filament::getTenant(); + + if (! $panel || ! $tenant) { + return null; + } + + return route('filament.' . $panel->getId() . '.resources.mails.index', [ + 'activeTab' => $activeTab, + 'tenant' => $tenant->getKey(), + ]); + }; + $widgets[] = Stat::make(__('Delivered'), number_format(($deliveredMails / $mailCount) * 100, 1) . '%') ->label(__('Delivered')) ->description($deliveredMails . ' ' . __('of') . ' ' . $mailCount . ' ' . __('emails')) @@ -35,10 +50,7 @@ protected function getStats(): array ->label(__('Opened')) ->description($openedMails . ' ' . __('of') . ' ' . $mailCount . ' ' . __('emails')) ->color('info') - ->url(route('filament.' . filament()->getCurrentPanel()?->getId() . '.resources.mails.index', [ - 'activeTab' => 'opened', - 'tenant' => filament()->getTenant()?->id, - ])); + ->url($generateUrl('opened')); $widgets[] = Stat::make(__('Clicked'), number_format(($clickedMails / $mailCount) * 100, 1) . '%') ->label(__('Clicked')) @@ -49,10 +61,7 @@ protected function getStats(): array ->label(__('Bounced')) ->description($bouncedMails . ' ' . __('of') . ' ' . $mailCount . ' ' . __('emails')) ->color('danger') - ->url(route('filament.' . filament()->getCurrentPanel()?->getId() . '.resources.mails.index', [ - 'activeTab' => 'bounced', - 'tenant' => filament()->getTenant()?->id, - ])); + ->url($generateUrl('bounced')); return $widgets; }