diff --git a/.github/agents/copilot-instructions.md b/.github/agents/copilot-instructions.md index 62ca367..6685ca5 100644 --- a/.github/agents/copilot-instructions.md +++ b/.github/agents/copilot-instructions.md @@ -31,6 +31,8 @@ ## Active Technologies - PHP 8.4.x + Laravel 12, Filament v5, Livewire v4, Microsoft Graph integration via `GraphClientInterface` (095-graph-contracts-registry-completeness) - PHP 8.4.15 (Laravel 12) + Filament v5, Livewire v4, Laravel Queue, Laravel Notifications (100-alert-target-test-actions) - PostgreSQL (Sail locally); SQLite is used in some tests (101-golden-master-baseline-governance-v1) +- PHP 8.4 (Laravel 12) + Filament v5, Livewire v4, `OperateHubShell` support class (103-ia-scope-filter-semantics) +- PostgreSQL — no schema changes (103-ia-scope-filter-semantics) - PHP 8.4.15 (feat/005-bulk-operations) @@ -50,6 +52,7 @@ ## Code Style PHP 8.4.15: Follow standard conventions ## Recent Changes +- 103-ia-scope-filter-semantics: Added PHP 8.4 (Laravel 12) + Filament v5, Livewire v4, `OperateHubShell` support class - 101-golden-master-baseline-governance-v1: Added PHP 8.4.x - 100-alert-target-test-actions: Added PHP 8.4.15 (Laravel 12) + Filament v5, Livewire v4, Laravel Queue, Laravel Notifications diff --git a/app/Filament/Resources/AlertDestinationResource/Pages/ListAlertDestinations.php b/app/Filament/Resources/AlertDestinationResource/Pages/ListAlertDestinations.php index 6116bb1..c4f43c7 100644 --- a/app/Filament/Resources/AlertDestinationResource/Pages/ListAlertDestinations.php +++ b/app/Filament/Resources/AlertDestinationResource/Pages/ListAlertDestinations.php @@ -5,7 +5,6 @@ namespace App\Filament\Resources\AlertDestinationResource\Pages; use App\Filament\Resources\AlertDestinationResource; -use App\Support\OperateHub\OperateHubShell; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; @@ -16,10 +15,6 @@ class ListAlertDestinations extends ListRecords protected function getHeaderActions(): array { return [ - ...app(OperateHubShell::class)->headerActions( - scopeActionName: 'operate_hub_scope_alerts', - returnActionName: 'operate_hub_return_alerts', - ), CreateAction::make() ->label('Create target') ->disabled(fn (): bool => ! AlertDestinationResource::canCreate()), diff --git a/app/Filament/Resources/AlertRuleResource.php b/app/Filament/Resources/AlertRuleResource.php index fd27be7..c3a02c8 100644 --- a/app/Filament/Resources/AlertRuleResource.php +++ b/app/Filament/Resources/AlertRuleResource.php @@ -28,6 +28,7 @@ use Filament\Forms\Components\Toggle; use Filament\Notifications\Notification; use Filament\Resources\Resource; +use Filament\Schemas\Components\Section; use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Schema; use Filament\Tables\Columns\TextColumn; @@ -144,64 +145,76 @@ public static function form(Schema $schema): Schema { return $schema ->schema([ - TextInput::make('name') - ->required() - ->maxLength(255), - Toggle::make('is_enabled') - ->label('Enabled') - ->default(true), - Select::make('event_type') - ->required() - ->options(self::eventTypeOptions()) - ->native(false), - Select::make('minimum_severity') - ->required() - ->options(self::severityOptions()) - ->native(false), - Select::make('tenant_scope_mode') - ->required() - ->options([ - AlertRule::TENANT_SCOPE_ALL => 'All tenants', - AlertRule::TENANT_SCOPE_ALLOWLIST => 'Allowlist', - ]) - ->default(AlertRule::TENANT_SCOPE_ALL) - ->native(false) - ->live(), - Select::make('tenant_allowlist') - ->label('Tenant allowlist') - ->multiple() - ->options(self::tenantOptions()) - ->visible(fn (Get $get): bool => $get('tenant_scope_mode') === AlertRule::TENANT_SCOPE_ALLOWLIST) - ->native(false), - TextInput::make('cooldown_seconds') - ->label('Cooldown (seconds)') - ->numeric() - ->minValue(0) - ->nullable(), - Toggle::make('quiet_hours_enabled') - ->label('Enable quiet hours') - ->default(false) - ->live(), - TextInput::make('quiet_hours_start') - ->label('Quiet hours start') - ->type('time') - ->visible(fn (Get $get): bool => (bool) $get('quiet_hours_enabled')), - TextInput::make('quiet_hours_end') - ->label('Quiet hours end') - ->type('time') - ->visible(fn (Get $get): bool => (bool) $get('quiet_hours_enabled')), - Select::make('quiet_hours_timezone') - ->label('Quiet hours timezone') - ->options(self::timezoneOptions()) - ->searchable() - ->native(false) - ->visible(fn (Get $get): bool => (bool) $get('quiet_hours_enabled')), - Select::make('destination_ids') - ->label('Destinations') - ->multiple() - ->required() - ->options(self::destinationOptions()) - ->native(false), + Section::make('Rule') + ->schema([ + TextInput::make('name') + ->required() + ->maxLength(255), + Toggle::make('is_enabled') + ->label('Enabled') + ->default(true), + Select::make('event_type') + ->required() + ->options(self::eventTypeOptions()) + ->native(false), + Select::make('minimum_severity') + ->required() + ->options(self::severityOptions()) + ->native(false), + ]), + Section::make('Applies to') + ->schema([ + Select::make('tenant_scope_mode') + ->label('Applies to tenants') + ->required() + ->options([ + AlertRule::TENANT_SCOPE_ALL => 'All tenants', + AlertRule::TENANT_SCOPE_ALLOWLIST => 'Selected tenants', + ]) + ->default(AlertRule::TENANT_SCOPE_ALL) + ->native(false) + ->live() + ->helperText('This rule is workspace-wide. Use this to limit where it applies.'), + Select::make('tenant_allowlist') + ->label('Selected tenants') + ->multiple() + ->options(self::tenantOptions()) + ->visible(fn (Get $get): bool => $get('tenant_scope_mode') === AlertRule::TENANT_SCOPE_ALLOWLIST) + ->native(false) + ->helperText('Only these tenants will trigger this rule.'), + ]), + Section::make('Delivery') + ->schema([ + TextInput::make('cooldown_seconds') + ->label('Cooldown (seconds)') + ->numeric() + ->minValue(0) + ->nullable(), + Toggle::make('quiet_hours_enabled') + ->label('Enable quiet hours') + ->default(false) + ->live(), + TextInput::make('quiet_hours_start') + ->label('Quiet hours start') + ->type('time') + ->visible(fn (Get $get): bool => (bool) $get('quiet_hours_enabled')), + TextInput::make('quiet_hours_end') + ->label('Quiet hours end') + ->type('time') + ->visible(fn (Get $get): bool => (bool) $get('quiet_hours_enabled')), + Select::make('quiet_hours_timezone') + ->label('Quiet hours timezone') + ->options(self::timezoneOptions()) + ->searchable() + ->native(false) + ->visible(fn (Get $get): bool => (bool) $get('quiet_hours_enabled')), + Select::make('destination_ids') + ->label('Destinations') + ->multiple() + ->required() + ->options(self::destinationOptions()) + ->native(false), + ]), ]); } diff --git a/app/Filament/Resources/AlertRuleResource/Pages/ListAlertRules.php b/app/Filament/Resources/AlertRuleResource/Pages/ListAlertRules.php index f0b0012..4295d6f 100644 --- a/app/Filament/Resources/AlertRuleResource/Pages/ListAlertRules.php +++ b/app/Filament/Resources/AlertRuleResource/Pages/ListAlertRules.php @@ -5,7 +5,6 @@ namespace App\Filament\Resources\AlertRuleResource\Pages; use App\Filament\Resources\AlertRuleResource; -use App\Support\OperateHub\OperateHubShell; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; @@ -16,10 +15,6 @@ class ListAlertRules extends ListRecords protected function getHeaderActions(): array { return [ - ...app(OperateHubShell::class)->headerActions( - scopeActionName: 'operate_hub_scope_alerts', - returnActionName: 'operate_hub_return_alerts', - ), CreateAction::make() ->label('Create rule') ->disabled(fn (): bool => ! AlertRuleResource::canCreate()), diff --git a/app/Filament/Resources/BaselineProfileResource.php b/app/Filament/Resources/BaselineProfileResource.php index b5b94cf..37065c0 100644 --- a/app/Filament/Resources/BaselineProfileResource.php +++ b/app/Filament/Resources/BaselineProfileResource.php @@ -140,35 +140,44 @@ public static function form(Schema $schema): Schema { return $schema ->schema([ - TextInput::make('name') - ->required() - ->maxLength(255) - ->helperText('A descriptive name for this baseline profile.'), - Textarea::make('description') - ->rows(3) - ->maxLength(1000) - ->helperText('Explain the purpose and scope of this baseline.'), - TextInput::make('version_label') - ->label('Version label') - ->maxLength(50) - ->placeholder('e.g. v2.1 — February rollout') - ->helperText('Optional label to identify this version.'), - Select::make('status') - ->required() - ->options([ - BaselineProfile::STATUS_DRAFT => 'Draft', - BaselineProfile::STATUS_ACTIVE => 'Active', - BaselineProfile::STATUS_ARCHIVED => 'Archived', + Section::make('Profile') + ->schema([ + TextInput::make('name') + ->required() + ->maxLength(255) + ->helperText('A descriptive name for this baseline profile.'), + Textarea::make('description') + ->rows(3) + ->maxLength(1000) + ->helperText('Explain the purpose and scope of this baseline.'), + TextInput::make('version_label') + ->label('Version label') + ->maxLength(50) + ->placeholder('e.g. v2.1 — February rollout') + ->helperText('Optional label to identify this version.'), + Select::make('status') + ->required() + ->options([ + BaselineProfile::STATUS_DRAFT => 'Draft', + BaselineProfile::STATUS_ACTIVE => 'Active', + BaselineProfile::STATUS_ARCHIVED => 'Archived', + ]) + ->default(BaselineProfile::STATUS_DRAFT) + ->native(false) + ->helperText('Only active baselines are enforced during compliance checks.'), ]) - ->default(BaselineProfile::STATUS_DRAFT) - ->native(false) - ->helperText('Only active baselines are enforced during compliance checks.'), - Select::make('scope_jsonb.policy_types') - ->label('Policy type scope') - ->multiple() - ->options(self::policyTypeOptions()) - ->helperText('Leave empty to include all policy types.') - ->native(false), + ->columns(2) + ->columnSpanFull(), + Section::make('Scope') + ->schema([ + Select::make('scope_jsonb.policy_types') + ->label('Policy type scope') + ->multiple() + ->options(self::policyTypeOptions()) + ->helperText('Leave empty to include all policy types.') + ->native(false), + ]) + ->columnSpanFull(), ]); } diff --git a/app/Filament/Resources/ProviderConnectionResource.php b/app/Filament/Resources/ProviderConnectionResource.php index ac3e057..cb1fa56 100644 --- a/app/Filament/Resources/ProviderConnectionResource.php +++ b/app/Filament/Resources/ProviderConnectionResource.php @@ -32,6 +32,7 @@ use Filament\Forms\Components\Toggle; use Filament\Notifications\Notification; use Filament\Resources\Resource; +use Filament\Schemas\Components\Section; use Filament\Schemas\Schema; use Filament\Tables; use Filament\Tables\Filters\Filter; @@ -401,29 +402,39 @@ public static function form(Schema $schema): Schema { return $schema ->schema([ - TextInput::make('display_name') - ->label('Display name') - ->required() - ->disabled(fn (): bool => ! static::hasTenantCapability(Capabilities::PROVIDER_MANAGE)) - ->maxLength(255), - TextInput::make('entra_tenant_id') - ->label('Entra tenant ID') - ->required() - ->maxLength(255) - ->disabled(fn (): bool => ! static::hasTenantCapability(Capabilities::PROVIDER_MANAGE)) - ->rules(['uuid']), - Toggle::make('is_default') - ->label('Default connection') - ->disabled(fn (): bool => ! static::hasTenantCapability(Capabilities::PROVIDER_MANAGE)) - ->helperText('Exactly one default connection is required per tenant/provider.'), - TextInput::make('status') - ->label('Status') - ->disabled() - ->dehydrated(false), - TextInput::make('health_status') - ->label('Health') - ->disabled() - ->dehydrated(false), + Section::make('Connection') + ->schema([ + TextInput::make('display_name') + ->label('Display name') + ->required() + ->disabled(fn (): bool => ! static::hasTenantCapability(Capabilities::PROVIDER_MANAGE)) + ->maxLength(255), + TextInput::make('entra_tenant_id') + ->label('Entra tenant ID') + ->required() + ->maxLength(255) + ->disabled(fn (): bool => ! static::hasTenantCapability(Capabilities::PROVIDER_MANAGE)) + ->rules(['uuid']), + Toggle::make('is_default') + ->label('Default connection') + ->disabled(fn (): bool => ! static::hasTenantCapability(Capabilities::PROVIDER_MANAGE)) + ->helperText('Exactly one default connection is required per tenant/provider.'), + ]) + ->columns(2) + ->columnSpanFull(), + Section::make('Status') + ->schema([ + TextInput::make('status') + ->label('Status') + ->disabled() + ->dehydrated(false), + TextInput::make('health_status') + ->label('Health') + ->disabled() + ->dehydrated(false), + ]) + ->columns(2) + ->columnSpanFull(), ]); } diff --git a/app/Filament/Resources/TenantResource.php b/app/Filament/Resources/TenantResource.php index f36e73c..3103cd7 100644 --- a/app/Filament/Resources/TenantResource.php +++ b/app/Filament/Resources/TenantResource.php @@ -46,6 +46,7 @@ use Filament\Infolists; use Filament\Notifications\Notification; use Filament\Resources\Resource; +use Filament\Schemas\Components\Section; use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Components\Utilities\Set; use Filament\Schemas\Schema; @@ -832,69 +833,129 @@ public static function infolist(Schema $schema): Schema // ... [Infolist Omitted - No Change] ... return $schema ->schema([ - Infolists\Components\TextEntry::make('name'), - Infolists\Components\TextEntry::make('tenant_id')->label('Tenant ID')->copyable(), - Infolists\Components\TextEntry::make('domain')->copyable(), - Infolists\Components\TextEntry::make('status') - ->badge() - ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantStatus)) - ->color(BadgeRenderer::color(BadgeDomain::TenantStatus)) - ->icon(BadgeRenderer::icon(BadgeDomain::TenantStatus)) - ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantStatus)), - Infolists\Components\TextEntry::make('app_status') - ->badge() - ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantAppStatus)) - ->color(BadgeRenderer::color(BadgeDomain::TenantAppStatus)) - ->icon(BadgeRenderer::icon(BadgeDomain::TenantAppStatus)) - ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantAppStatus)), - Infolists\Components\ViewEntry::make('provider_connection_state') - ->label('Provider connection') - ->state(fn (Tenant $record): array => static::providerConnectionState($record)) - ->view('filament.infolists.entries.provider-connection-state') - ->columnSpanFull(), - Infolists\Components\TextEntry::make('created_at')->dateTime(), - Infolists\Components\TextEntry::make('updated_at')->dateTime(), - Infolists\Components\TextEntry::make('rbac_status') - ->label('RBAC status') - ->badge() - ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantRbacStatus)) - ->color(BadgeRenderer::color(BadgeDomain::TenantRbacStatus)) - ->icon(BadgeRenderer::icon(BadgeDomain::TenantRbacStatus)) - ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantRbacStatus)), - Infolists\Components\TextEntry::make('rbac_status_reason')->label('RBAC reason'), - Infolists\Components\TextEntry::make('rbac_last_checked_at')->label('RBAC last checked')->since(), - Infolists\Components\TextEntry::make('rbac_role_display_name')->label('RBAC role'), - Infolists\Components\TextEntry::make('rbac_role_definition_id')->label('Role definition ID')->copyable(), - Infolists\Components\TextEntry::make('rbac_scope_mode')->label('RBAC scope'), - Infolists\Components\TextEntry::make('rbac_scope_id')->label('Scope ID'), - Infolists\Components\TextEntry::make('rbac_group_id')->label('RBAC group ID')->copyable(), - Infolists\Components\TextEntry::make('rbac_role_assignment_id')->label('Role assignment ID')->copyable(), - Infolists\Components\ViewEntry::make('rbac_summary') - ->label('Last RBAC Setup') - ->view('filament.infolists.entries.rbac-summary') - ->visible(fn (Tenant $record) => filled($record->rbac_last_setup_at)), - Infolists\Components\TextEntry::make('admin_consent_url') - ->label('Admin consent URL') - ->state(fn (Tenant $record) => static::adminConsentUrl($record)) - ->visible(fn (?string $state) => filled($state)) - ->copyable(), - Infolists\Components\RepeatableEntry::make('permissions') - ->label('Required permissions') - ->state(fn (Tenant $record) => static::storedPermissionSnapshot($record)) + Section::make('Identity') ->schema([ - Infolists\Components\TextEntry::make('key')->label('Permission')->badge(), - Infolists\Components\TextEntry::make('type')->badge(), - Infolists\Components\TextEntry::make('features') - ->label('Features') - ->formatStateUsing(fn ($state) => is_array($state) ? implode(', ', $state) : (string) $state), + Infolists\Components\TextEntry::make('name'), + Infolists\Components\TextEntry::make('tenant_id')->label('Tenant ID')->copyable(), + Infolists\Components\TextEntry::make('domain')->copyable(), Infolists\Components\TextEntry::make('status') ->badge() - ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantPermissionStatus)) - ->color(BadgeRenderer::color(BadgeDomain::TenantPermissionStatus)) - ->icon(BadgeRenderer::icon(BadgeDomain::TenantPermissionStatus)) - ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantPermissionStatus)), + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantStatus)) + ->color(BadgeRenderer::color(BadgeDomain::TenantStatus)) + ->icon(BadgeRenderer::icon(BadgeDomain::TenantStatus)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantStatus)), + Infolists\Components\TextEntry::make('app_status') + ->badge() + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantAppStatus)) + ->color(BadgeRenderer::color(BadgeDomain::TenantAppStatus)) + ->icon(BadgeRenderer::icon(BadgeDomain::TenantAppStatus)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantAppStatus)), + ]) + ->columns(2) + ->columnSpanFull(), + Section::make('Provider') + ->schema([ + Infolists\Components\ViewEntry::make('provider_connection_state') + ->label('Provider connection') + ->state(fn (Tenant $record): array => static::providerConnectionState($record)) + ->view('filament.infolists.entries.provider-connection-state') + ->columnSpanFull(), ]) ->columnSpanFull(), + Section::make('RBAC') + ->schema([ + Infolists\Components\TextEntry::make('rbac_not_configured_hint') + ->label('Status') + ->state('Not configured') + ->icon('heroicon-o-shield-exclamation') + ->color('warning') + ->columnSpanFull() + ->visible(fn (Tenant $record): bool => blank($record->rbac_status)), + Infolists\Components\TextEntry::make('rbac_status') + ->label('RBAC status') + ->badge() + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantRbacStatus)) + ->color(BadgeRenderer::color(BadgeDomain::TenantRbacStatus)) + ->icon(BadgeRenderer::icon(BadgeDomain::TenantRbacStatus)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantRbacStatus)) + ->visible(fn (Tenant $record): bool => filled($record->rbac_status)), + Infolists\Components\TextEntry::make('rbac_status_reason') + ->label('RBAC reason') + ->visible(fn (Tenant $record): bool => filled($record->rbac_status)), + Infolists\Components\TextEntry::make('rbac_last_checked_at') + ->label('RBAC last checked') + ->since() + ->visible(fn (Tenant $record): bool => filled($record->rbac_status)), + Infolists\Components\TextEntry::make('rbac_role_display_name') + ->label('RBAC role') + ->visible(fn (Tenant $record): bool => filled($record->rbac_status)), + Infolists\Components\TextEntry::make('rbac_role_definition_id') + ->label('Role definition ID') + ->copyable() + ->visible(fn (Tenant $record): bool => filled($record->rbac_status)), + Infolists\Components\TextEntry::make('rbac_scope_mode') + ->label('RBAC scope') + ->visible(fn (Tenant $record): bool => filled($record->rbac_status)), + Infolists\Components\TextEntry::make('rbac_scope_id') + ->label('Scope ID') + ->visible(fn (Tenant $record): bool => filled($record->rbac_status)), + Infolists\Components\TextEntry::make('rbac_group_id') + ->label('RBAC group ID') + ->copyable() + ->visible(fn (Tenant $record): bool => filled($record->rbac_status)), + Infolists\Components\TextEntry::make('rbac_role_assignment_id') + ->label('Role assignment ID') + ->copyable() + ->visible(fn (Tenant $record): bool => filled($record->rbac_status)), + Infolists\Components\ViewEntry::make('rbac_summary') + ->label('Last RBAC Setup') + ->view('filament.infolists.entries.rbac-summary') + ->visible(fn (Tenant $record) => filled($record->rbac_last_setup_at)), + ]) + ->columns(2) + ->columnSpanFull() + ->collapsible(), + Section::make('Integration') + ->schema([ + Infolists\Components\TextEntry::make('admin_consent_url') + ->label('Admin consent URL') + ->state(fn (Tenant $record) => static::adminConsentUrl($record)) + ->visible(fn (?string $state) => filled($state)) + ->copyable() + ->columnSpanFull(), + ]) + ->columnSpanFull() + ->collapsible(), + Section::make('Metadata') + ->schema([ + Infolists\Components\TextEntry::make('created_at')->dateTime(), + Infolists\Components\TextEntry::make('updated_at')->dateTime(), + ]) + ->columns(2) + ->columnSpanFull() + ->collapsed(), + Section::make('Required permissions') + ->schema([ + Infolists\Components\RepeatableEntry::make('permissions') + ->label('') + ->state(fn (Tenant $record) => static::storedPermissionSnapshot($record)) + ->schema([ + Infolists\Components\TextEntry::make('key')->label('Permission')->badge(), + Infolists\Components\TextEntry::make('type')->badge(), + Infolists\Components\TextEntry::make('features') + ->label('Features') + ->formatStateUsing(fn ($state) => is_array($state) ? implode(', ', $state) : (string) $state), + Infolists\Components\TextEntry::make('status') + ->badge() + ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantPermissionStatus)) + ->color(BadgeRenderer::color(BadgeDomain::TenantPermissionStatus)) + ->icon(BadgeRenderer::icon(BadgeDomain::TenantPermissionStatus)) + ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantPermissionStatus)), + ]) + ->columnSpanFull(), + ]) + ->columnSpanFull() + ->collapsible(), ]); } diff --git a/app/Filament/Resources/TenantResource/Pages/ViewTenant.php b/app/Filament/Resources/TenantResource/Pages/ViewTenant.php index 9ca54b8..c95c637 100644 --- a/app/Filament/Resources/TenantResource/Pages/ViewTenant.php +++ b/app/Filament/Resources/TenantResource/Pages/ViewTenant.php @@ -22,6 +22,11 @@ class ViewTenant extends ViewRecord { protected static string $resource = TenantResource::class; + public function getHeaderWidgetsColumns(): int|array + { + return 1; + } + protected function getHeaderWidgets(): array { return [ diff --git a/app/Filament/Widgets/Alerts/AlertsKpiHeader.php b/app/Filament/Widgets/Alerts/AlertsKpiHeader.php index 035dac2..2e6e3b8 100644 --- a/app/Filament/Widgets/Alerts/AlertsKpiHeader.php +++ b/app/Filament/Widgets/Alerts/AlertsKpiHeader.php @@ -12,8 +12,8 @@ use App\Models\AlertRule; use App\Models\Tenant; use App\Models\User; +use App\Support\OperateHub\OperateHubShell; use App\Support\Workspaces\WorkspaceContext; -use Filament\Facades\Filament; use Filament\Widgets\StatsOverviewWidget; use Filament\Widgets\StatsOverviewWidget\Stat; use Illuminate\Database\Eloquent\Builder; @@ -96,7 +96,7 @@ private function deliveriesQueryForViewer(User $user, int $workspaceId): Builder ->where('workspace_id', $workspaceId) ->whereIn('tenant_id', $user->tenantMemberships()->select('tenant_id')); - $activeTenant = Filament::getTenant(); + $activeTenant = app(OperateHubShell::class)->activeEntitledTenant(request()); if ($activeTenant instanceof Tenant) { $query->where('tenant_id', (int) $activeTenant->getKey()); diff --git a/app/Support/OperateHub/OperateHubShell.php b/app/Support/OperateHub/OperateHubShell.php index 281efc6..d361e60 100644 --- a/app/Support/OperateHub/OperateHubShell.php +++ b/app/Support/OperateHub/OperateHubShell.php @@ -25,10 +25,10 @@ public function scopeLabel(?Request $request = null): string $activeTenant = $this->activeEntitledTenant($request); if ($activeTenant instanceof Tenant) { - return 'Scope: Tenant — '.$activeTenant->name; + return 'Filtered by tenant: '.$activeTenant->name; } - return 'Scope: Workspace — all tenants'; + return 'All tenants'; } /** diff --git a/resources/views/filament/widgets/tenant/recent-operations-summary.blade.php b/resources/views/filament/widgets/tenant/recent-operations-summary.blade.php index 6c6ac9e..816c3c5 100644 --- a/resources/views/filament/widgets/tenant/recent-operations-summary.blade.php +++ b/resources/views/filament/widgets/tenant/recent-operations-summary.blade.php @@ -4,24 +4,22 @@ /** @var string $operationsIndexUrl */ @endphp -