Promote suggestion misses through the bulk-add modal, then delete them

Suggestion Misses' "Promote Selected" now opens the same bulk-add modal
shape as Suggestions' "Create Many" (editable phrases textarea +
lang/intent), prefilled with the selected misses' own text/language, and
submits via batchCreateSuggestions() so edited/added lines are honored.

Once that succeeds, the selected misses are deleted via the new
batchDeleteSuggestionMisses() (DELETE /admin/suggestion-misses/batch) so
they don't linger in the list now that they're suggestions. This is
best-effort: a failed cleanup doesn't undo the promotion, just surfaces a
separate warning notification.
This commit is contained in:
Nyan Lin Paing
2026-09-03 20:42:56 +07:00
parent 74578d10e3
commit 051b091ef8
4 changed files with 134 additions and 17 deletions
@@ -5,9 +5,11 @@ namespace Modules\AiAgent\Filament\Pages;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Actions\BulkAction;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Schemas\Components\Text;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
@@ -20,6 +22,7 @@ use Illuminate\Support\Collection;
use Modules\AiAgent\Filament\Concerns\HandlesBnfexpressErrors;
use Modules\AiAgent\Filament\Concerns\PaginatesBnfexpressLists;
use Modules\Shared\Bnfexpress\BnfexpressAdminClient;
use Modules\Shared\Bnfexpress\Exceptions\BnfexpressApiException;
use UnitEnum;
/**
@@ -112,35 +115,74 @@ class ManageSuggestionMisses extends Page implements HasTable
});
}
/**
* Opens the same "bulk add" modal shape as ManageSuggestions'
* createManyAction() (info text + editable items textarea + lang/intent),
* prefilled with the selected misses' own text one per line so an
* admin can review/tweak the batch before it's created. Submits via
* batchCreateSuggestions() (same endpoint createManyAction uses, and per
* its own comment, bnfexpress's thin wrapper around the misses-promotion
* promote()), not promoteSuggestionMisses() editing the textarea means
* the created rows no longer map 1:1 to the original miss ids. Once that
* succeeds, the selected misses are deleted via batchDeleteSuggestionMisses()
* so they don't linger in this list now that they're suggestions best
* effort, since editing the textarea already broke the id mapping and the
* promotion itself is done by that point either way.
*/
protected function promoteBulkAction(): BulkAction
{
return BulkAction::make('promote')
->label('Promote Selected')
->icon(Heroicon::OutlinedArrowUp)
->fetchSelectedRecords(false)
->fillForm(fn (Collection $records): array => [
'items_raw' => $records->pluck('text_norm')->implode("\n"),
'lang' => $records->pluck('lang')->filter()->unique()->count() === 1
? $records->pluck('lang')->filter()->first()
: null,
])
->schema([
Text::make('Created rows are tagged source: mined by bnfexpress.')
->color('gray'),
Textarea::make('items_raw')
->label('Phrases (one per line)')
->required()
->rows(8),
TextInput::make('lang')
->label('Language override')
->helperText("Applied to every selected miss; leave blank to keep each one's own language.")
->required()
->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.
$items = collect(preg_split('/\r\n|\r|\n/', (string) $data['items_raw']))
->map(fn (string $line): string => trim($line))
->filter()
->map(fn (string $text): array => array_filter([
'text' => $text,
'lang' => $data['lang'],
'intent' => $data['intent'] ?: null,
], fn (mixed $value): bool => $value !== null))
->values()
->all();
$this->callBnfexpressForResult(
fn () => app(BnfexpressAdminClient::class)->promoteSuggestionMisses(
$records->keys()->all(),
$data['lang'] ?: null,
$data['intent'] ?: null,
),
function (array $result): void {
fn () => app(BnfexpressAdminClient::class)->batchCreateSuggestions($items),
function (array $result) use ($records): void {
Notification::make()
->title("{$result['created']} promoted, {$result['skipped']} skipped")
->success()
->send();
try {
app(BnfexpressAdminClient::class)->batchDeleteSuggestionMisses($records->keys()->all());
} catch (BnfexpressApiException $exception) {
Notification::make()
->title('Promoted, but failed to remove the original misses')
->body($exception->getMessage())
->warning()
->send();
}
$this->resetTable();
},
failureTitle: 'Failed to promote suggestion misses',