Summary Consolidates the “Tenant Operate Hub” work (Spec 085) and the follow-up adjustments from the 086 session merge into a single branch ready to merge into dev. Primary focus: stabilize Ops/Operate Hub UX flows, tighten/align authorization semantics, and make the full Sail test suite green. Key Changes Ops UX / Verification Readonly members can view verification operation runs (reports) while starting verification remains restricted. Normalized failure reason-code handling and aligned UX expectations with the provider reason-code taxonomy. Onboarding wizard UX “Start verification” CTA is hidden while a verification run is active; “Refresh” is shown during in-progress runs. Treats provider_permission_denied as a blocking reason (while keeping legacy compatibility). Test + fixture hardening Standardized use of default provider connection fixtures in tests where sync/restore flows require it. Fixed multiple Filament URL/tenant-context test cases to avoid 404s and reduce tenancy routing brittleness. Policy sync / restore safety Enrollment configuration type collision classification tests now exercise the real sync path (with required provider connection present). Restore edge-case safety tests updated to reflect current provider-connection requirements. Testing vendor/bin/sail artisan test --compact (green) vendor/bin/sail bin pint --dirty (green) Notes Includes merged 086 session work already (no separate PR needed). Co-authored-by: Ahmed Darrazi <ahmeddarrazi@ebc83aaa-d947-4a08-b88e-bd72ac9645f7.fritz.box> Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box> Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.fritz.box> Reviewed-on: #103
150 lines
4.8 KiB
PHP
150 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Pages\Monitoring;
|
|
|
|
use App\Filament\Resources\OperationRunResource;
|
|
use App\Filament\Widgets\Operations\OperationsKpiHeader;
|
|
use App\Models\OperationRun;
|
|
use App\Models\Tenant;
|
|
use App\Support\OperateHub\OperateHubShell;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use BackedEnum;
|
|
use Filament\Actions\Action;
|
|
use Filament\Facades\Filament;
|
|
use Filament\Forms\Concerns\InteractsWithForms;
|
|
use Filament\Forms\Contracts\HasForms;
|
|
use Filament\Pages\Page;
|
|
use Filament\Tables\Concerns\InteractsWithTable;
|
|
use Filament\Tables\Contracts\HasTable;
|
|
use Filament\Tables\Table;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use UnitEnum;
|
|
|
|
class Operations extends Page implements HasForms, HasTable
|
|
{
|
|
use InteractsWithForms;
|
|
use InteractsWithTable;
|
|
|
|
public string $activeTab = 'all';
|
|
|
|
protected static bool $isDiscovered = false;
|
|
|
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-queue-list';
|
|
|
|
protected static string|UnitEnum|null $navigationGroup = 'Monitoring';
|
|
|
|
protected static ?string $title = 'Operations';
|
|
|
|
// Must be non-static
|
|
protected string $view = 'filament.pages.monitoring.operations';
|
|
|
|
public function mount(): void
|
|
{
|
|
$this->mountInteractsWithTable();
|
|
}
|
|
|
|
protected function getHeaderWidgets(): array
|
|
{
|
|
return [
|
|
OperationsKpiHeader::class,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array<Action>
|
|
*/
|
|
protected function getHeaderActions(): array
|
|
{
|
|
$operateHubShell = app(OperateHubShell::class);
|
|
|
|
$actions = [
|
|
Action::make('operate_hub_scope_operations')
|
|
->label($operateHubShell->scopeLabel(request()))
|
|
->color('gray')
|
|
->disabled(),
|
|
];
|
|
|
|
$activeTenant = $operateHubShell->activeEntitledTenant(request());
|
|
|
|
if ($activeTenant instanceof Tenant) {
|
|
$actions[] = Action::make('operate_hub_back_to_tenant_operations')
|
|
->label('Back to '.$activeTenant->name)
|
|
->icon('heroicon-o-arrow-left')
|
|
->color('gray')
|
|
->url(\App\Filament\Pages\TenantDashboard::getUrl(panel: 'tenant', tenant: $activeTenant));
|
|
|
|
$actions[] = Action::make('operate_hub_show_all_tenants')
|
|
->label('Show all tenants')
|
|
->color('gray')
|
|
->action(function (): void {
|
|
Filament::setTenant(null, true);
|
|
|
|
app(WorkspaceContext::class)->clearLastTenantId(request());
|
|
|
|
$this->removeTableFilter('tenant_id');
|
|
|
|
$this->redirect('/admin/operations');
|
|
});
|
|
}
|
|
|
|
return $actions;
|
|
}
|
|
|
|
public function updatedActiveTab(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function table(Table $table): Table
|
|
{
|
|
return OperationRunResource::table($table)
|
|
->query(function (): Builder {
|
|
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
|
|
|
|
$activeTenant = app(OperateHubShell::class)->activeEntitledTenant(request());
|
|
|
|
$query = OperationRun::query()
|
|
->with('user')
|
|
->latest('id')
|
|
->when(
|
|
$workspaceId,
|
|
fn (Builder $query): Builder => $query->where('workspace_id', (int) $workspaceId),
|
|
)
|
|
->when(
|
|
! $workspaceId,
|
|
fn (Builder $query): Builder => $query->whereRaw('1 = 0'),
|
|
)
|
|
->when(
|
|
$activeTenant instanceof Tenant,
|
|
fn (Builder $query): Builder => $query->where('tenant_id', (int) $activeTenant->getKey()),
|
|
);
|
|
|
|
return $this->applyActiveTab($query);
|
|
});
|
|
}
|
|
|
|
private function applyActiveTab(Builder $query): Builder
|
|
{
|
|
return match ($this->activeTab) {
|
|
'active' => $query->whereIn('status', [
|
|
OperationRunStatus::Queued->value,
|
|
OperationRunStatus::Running->value,
|
|
]),
|
|
'succeeded' => $query
|
|
->where('status', OperationRunStatus::Completed->value)
|
|
->where('outcome', OperationRunOutcome::Succeeded->value),
|
|
'partial' => $query
|
|
->where('status', OperationRunStatus::Completed->value)
|
|
->where('outcome', OperationRunOutcome::PartiallySucceeded->value),
|
|
'failed' => $query
|
|
->where('status', OperationRunStatus::Completed->value)
|
|
->where('outcome', OperationRunOutcome::Failed->value),
|
|
default => $query,
|
|
};
|
|
}
|
|
}
|