user()?->can('manage_ai_agent') ?? false; } public function table(Table $table): Table { return $table ->records(function (array $filters, int $page, int $recordsPerPage): LengthAwarePaginator { $wasUsed = $filters['was_used']['value'] ?? null; $result = app(BnfexpressAdminClient::class)->listSuggestionMisses( wasUsed: $wasUsed === null || $wasUsed === '' ? null : (bool) $wasUsed, limit: $recordsPerPage, offset: ($page - 1) * $recordsPerPage, ); return $this->paginateBareList($result, 'misses', 'id', $page, $recordsPerPage); }) ->columns([ TextColumn::make('id'), TextColumn::make('text_norm') ->limit(80) ->wrap(), TextColumn::make('lang') ->placeholder('—'), TextColumn::make('syllables') ->placeholder('—'), IconColumn::make('was_used') ->boolean(), TextColumn::make('created_at') ->dateTime() ->sortable(), ]) ->defaultSort('created_at', 'desc') // SelectFilter (not TernaryFilter) so the value lands in // $filters['was_used']['value'] predictably — TernaryFilter's // internal field key isn't documented for the custom-data path. ->filters([ SelectFilter::make('was_used') ->label('Used?') ->options(['1' => 'Used', '0' => 'Not used']), ]) ->recordActions([ $this->dismissAction(), ]) ->toolbarActions([ $this->promoteBulkAction(), ]); } protected function dismissAction(): Action { return Action::make('dismiss') ->color('danger') ->icon(Heroicon::OutlinedTrash) ->requiresConfirmation() ->action(function (array $record): void { $this->callBnfexpress( fn () => app(BnfexpressAdminClient::class)->dismissSuggestionMiss($record['id']), successTitle: 'Miss dismissed', failureTitle: 'Failed to dismiss miss', ); $this->resetTable(); }); } protected function promoteBulkAction(): BulkAction { return BulkAction::make('promote') ->label('Promote Selected') ->icon(Heroicon::OutlinedArrowUp) ->fetchSelectedRecords(false) ->schema([ TextInput::make('lang') ->label('Language override') ->helperText("Applied to every selected miss; leave blank to keep each one's own language.") ->maxLength(10), TextInput::make('intent'), ]) ->deselectRecordsAfterCompletion() ->action(function (array $data, Collection $records): void { // Same caveat as ManageSuggestions' deleteSelected — the collection // holds full row arrays, not just keys, for a custom-data table. $this->callBnfexpressForResult( fn () => app(BnfexpressAdminClient::class)->promoteSuggestionMisses( $records->keys()->all(), $data['lang'] ?: null, $data['intent'] ?: null, ), function (array $result): void { Notification::make() ->title("{$result['created']} promoted, {$result['skipped']} skipped") ->success() ->send(); $this->resetTable(); }, failureTitle: 'Failed to promote suggestion misses', ); }); } }