loadLogs(false); } protected function getAgent(): AgentClient { return $this->agent ??= new AgentClient; } public function loadLogs(bool $refreshTable = true): void { try { $result = $this->getAgent()->send('email.get_logs', [ 'limit' => 200, ]); $this->logs = $result['logs'] ?? []; $this->logsLoaded = true; } catch (\Exception $e) { $this->logs = []; $this->logsLoaded = true; Notification::make() ->title(__('Failed to load email logs')) ->body($e->getMessage()) ->danger() ->send(); } if ($refreshTable) { $this->resetTable(); } } public function loadQueue(bool $refreshTable = true): void { try { $result = $this->getAgent()->send('mail.queue_list'); $this->queueItems = $result['queue'] ?? []; $this->queueLoaded = true; } catch (\Exception $e) { $this->queueItems = []; $this->queueLoaded = true; Notification::make() ->title(__('Failed to load mail queue')) ->body($e->getMessage()) ->danger() ->send(); } if ($refreshTable) { $this->resetTable(); } } public function setViewMode(string $mode): void { $mode = in_array($mode, ['logs', 'queue'], true) ? $mode : 'logs'; if ($this->viewMode === $mode) { return; } $this->viewMode = $mode; if ($mode === 'queue') { $this->loadQueue(false); } else { $this->loadLogs(false); } $this->resetTable(); } public function table(Table $table): Table { return $table ->paginated([25, 50, 100]) ->defaultPaginationPageOption(25) ->records(function () { if ($this->viewMode === 'queue') { if (! $this->queueLoaded) { $this->loadQueue(false); } $records = $this->queueItems; } else { if (! $this->logsLoaded) { $this->loadLogs(false); } $records = $this->logs; } return collect($records) ->mapWithKeys(function (array $record, int $index): array { $queueId = $record['queue_id'] ?? ''; $timestamp = (int) ($record['timestamp'] ?? 0); $keyParts = array_filter([ $queueId, $timestamp > 0 ? (string) $timestamp : '', ], fn (string $part): bool => $part !== ''); $key = implode('-', $keyParts); return [$key !== '' ? $key : (string) $index => $record]; }) ->all(); }) ->columns($this->viewMode === 'queue' ? $this->getQueueColumns() : $this->getLogColumns()) ->recordActions($this->viewMode === 'queue' ? $this->getQueueActions() : []) ->emptyStateHeading($this->viewMode === 'queue' ? __('Mail queue is empty') : __('No email logs found')) ->emptyStateDescription($this->viewMode === 'queue' ? __('No deferred messages found.') : __('Mail logs are empty or unavailable.')) ->headerActions([ Action::make('viewLogs') ->label(__('Logs')) ->color($this->viewMode === 'logs' ? 'primary' : 'gray') ->action(fn () => $this->setViewMode('logs')), Action::make('viewQueue') ->label(__('Queue')) ->color($this->viewMode === 'queue' ? 'primary' : 'gray') ->action(fn () => $this->setViewMode('queue')), Action::make('refresh') ->label(__('Refresh')) ->icon('heroicon-o-arrow-path') ->action(function (): void { if ($this->viewMode === 'queue') { $this->loadQueue(); } else { $this->loadLogs(); } }), ]); } protected function getLogColumns(): array { return [ TextColumn::make('timestamp') ->label(__('Time')) ->formatStateUsing(function (array $record): string { $timestamp = (int) ($record['timestamp'] ?? 0); return $timestamp > 0 ? date('Y-m-d H:i:s', $timestamp) : ''; }) ->sortable(), TextColumn::make('queue_id') ->label(__('Queue ID')) ->fontFamily('mono') ->copyable() ->toggleable(), TextColumn::make('component') ->label(__('Component')) ->toggleable(isToggledHiddenByDefault: true), TextColumn::make('from') ->label(__('From')) ->wrap() ->searchable(), TextColumn::make('to') ->label(__('To')) ->wrap() ->searchable(), TextColumn::make('status') ->label(__('Status')) ->badge() ->formatStateUsing(fn (array $record): string => (string) ($record['status'] ?? 'unknown')), TextColumn::make('relay') ->label(__('Relay')) ->toggleable(), TextColumn::make('message') ->label(__('Details')) ->wrap() ->limit(80) ->toggleable(isToggledHiddenByDefault: true), ]; } protected function getQueueColumns(): array { return [ TextColumn::make('id') ->label(__('Queue ID')) ->fontFamily('mono') ->copyable(), TextColumn::make('arrival') ->label(__('Arrival')), TextColumn::make('sender') ->label(__('Sender')) ->wrap() ->searchable(), TextColumn::make('recipients') ->label(__('Recipients')) ->formatStateUsing(function (array $record): string { $recipients = $record['recipients'] ?? []; if (empty($recipients)) { return __('Unknown'); } $first = $recipients[0] ?? ''; $count = count($recipients); return $count > 1 ? $first.' +'.($count - 1) : $first; }) ->wrap(), TextColumn::make('size') ->label(__('Size')) ->formatStateUsing(fn (array $record): string => $record['size'] ?? ''), TextColumn::make('status') ->label(__('Status')) ->wrap(), ]; } protected function getQueueActions(): array { return [ Action::make('retry') ->label(__('Retry')) ->icon('heroicon-o-arrow-path') ->color('info') ->action(function (array $record): void { try { $result = $this->getAgent()->send('mail.queue_retry', ['id' => $record['id'] ?? '']); if ($result['success'] ?? false) { Notification::make()->title(__('Message retried'))->success()->send(); $this->loadQueue(); } else { throw new \Exception($result['error'] ?? __('Failed to retry message')); } } catch (\Exception $e) { Notification::make()->title(__('Retry failed'))->body($e->getMessage())->danger()->send(); } }), Action::make('delete') ->label(__('Delete')) ->icon('heroicon-o-trash') ->color('danger') ->requiresConfirmation() ->action(function (array $record): void { try { $result = $this->getAgent()->send('mail.queue_delete', ['id' => $record['id'] ?? '']); if ($result['success'] ?? false) { Notification::make()->title(__('Message deleted'))->success()->send(); $this->loadQueue(); } else { throw new \Exception($result['error'] ?? __('Failed to delete message')); } } catch (\Exception $e) { Notification::make()->title(__('Delete failed'))->body($e->getMessage())->danger()->send(); } }), ]; } }