154 lines
5.4 KiB
PHP
154 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Resources\FindingResource\Pages;
|
|
|
|
use App\Filament\Resources\FindingResource;
|
|
use App\Models\Finding;
|
|
use App\Support\Auth\Capabilities;
|
|
use App\Support\Rbac\UiEnforcement;
|
|
use App\Support\Rbac\UiTooltips;
|
|
use Filament\Actions;
|
|
use Filament\Forms\Components\TextInput;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Resources\Pages\ListRecords;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Arr;
|
|
|
|
class ListFindings extends ListRecords
|
|
{
|
|
protected static string $resource = FindingResource::class;
|
|
|
|
protected function getHeaderActions(): array
|
|
{
|
|
return [
|
|
UiEnforcement::forAction(
|
|
Actions\Action::make('acknowledge_all_matching')
|
|
->label('Acknowledge all matching')
|
|
->icon('heroicon-o-check')
|
|
->color('gray')
|
|
->requiresConfirmation()
|
|
->visible(fn (): bool => $this->getStatusFilterValue() === Finding::STATUS_NEW)
|
|
->modalDescription(function (): string {
|
|
$count = $this->getAllMatchingCount();
|
|
|
|
return "You are about to acknowledge {$count} finding".($count === 1 ? '' : 's').' matching the current filters.';
|
|
})
|
|
->form(function (): array {
|
|
$count = $this->getAllMatchingCount();
|
|
|
|
if ($count <= 100) {
|
|
return [];
|
|
}
|
|
|
|
return [
|
|
TextInput::make('confirmation')
|
|
->label('Type ACKNOWLEDGE to confirm')
|
|
->required()
|
|
->in(['ACKNOWLEDGE'])
|
|
->validationMessages([
|
|
'in' => 'Please type ACKNOWLEDGE to confirm.',
|
|
]),
|
|
];
|
|
})
|
|
->action(function (array $data): void {
|
|
$query = $this->buildAllMatchingQuery();
|
|
$count = (clone $query)->count();
|
|
|
|
if ($count === 0) {
|
|
Notification::make()
|
|
->title('No matching findings')
|
|
->body('There are no new findings matching the current filters.')
|
|
->warning()
|
|
->send();
|
|
|
|
return;
|
|
}
|
|
|
|
$updated = $query->update([
|
|
'status' => Finding::STATUS_ACKNOWLEDGED,
|
|
'acknowledged_at' => now(),
|
|
'acknowledged_by_user_id' => auth()->id(),
|
|
]);
|
|
|
|
$this->deselectAllTableRecords();
|
|
$this->resetPage();
|
|
|
|
Notification::make()
|
|
->title('Bulk acknowledge completed')
|
|
->body("Acknowledged {$updated} finding".($updated === 1 ? '' : 's').'.')
|
|
->success()
|
|
->send();
|
|
})
|
|
)
|
|
->preserveVisibility()
|
|
->requireCapability(Capabilities::TENANT_FINDINGS_ACKNOWLEDGE)
|
|
->tooltip(UiTooltips::INSUFFICIENT_PERMISSION)
|
|
->apply(),
|
|
];
|
|
}
|
|
|
|
protected function buildAllMatchingQuery(): Builder
|
|
{
|
|
$query = Finding::query();
|
|
|
|
$tenantId = \Filament\Facades\Filament::getTenant()?->getKey();
|
|
|
|
if (! is_numeric($tenantId)) {
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
$query->where('tenant_id', (int) $tenantId);
|
|
|
|
$query->where('status', Finding::STATUS_NEW);
|
|
|
|
$findingType = $this->getFindingTypeFilterValue();
|
|
if (is_string($findingType) && $findingType !== '') {
|
|
$query->where('finding_type', $findingType);
|
|
}
|
|
|
|
$scopeKeyState = $this->getTableFilterState('scope_key') ?? [];
|
|
$scopeKey = Arr::get($scopeKeyState, 'scope_key');
|
|
if (is_string($scopeKey) && $scopeKey !== '') {
|
|
$query->where('scope_key', $scopeKey);
|
|
}
|
|
|
|
$runIdsState = $this->getTableFilterState('run_ids') ?? [];
|
|
$baselineRunId = Arr::get($runIdsState, 'baseline_run_id');
|
|
if (is_numeric($baselineRunId)) {
|
|
$query->where('baseline_run_id', (int) $baselineRunId);
|
|
}
|
|
|
|
$currentRunId = Arr::get($runIdsState, 'current_run_id');
|
|
if (is_numeric($currentRunId)) {
|
|
$query->where('current_run_id', (int) $currentRunId);
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
protected function getAllMatchingCount(): int
|
|
{
|
|
return (int) $this->buildAllMatchingQuery()->count();
|
|
}
|
|
|
|
protected function getStatusFilterValue(): string
|
|
{
|
|
$state = $this->getTableFilterState('status') ?? [];
|
|
$value = Arr::get($state, 'value');
|
|
|
|
return is_string($value) && $value !== ''
|
|
? $value
|
|
: Finding::STATUS_NEW;
|
|
}
|
|
|
|
protected function getFindingTypeFilterValue(): string
|
|
{
|
|
$state = $this->getTableFilterState('finding_type') ?? [];
|
|
$value = Arr::get($state, 'value');
|
|
|
|
return is_string($value) && $value !== ''
|
|
? $value
|
|
: Finding::FINDING_TYPE_DRIFT;
|
|
}
|
|
}
|