Some checks failed
Main Confidence / confidence (push) Failing after 52s
Add Customer Health decision card to tenant & workspace detail pages (spec 245). What I changed: - Render a decision-first Customer Health card on tenant and workspace detail pages. - Reuse `WorkspaceHealthSummaryQuery` and preserve `window` query param. - Update attention widget link text to "Review health details" and include `?window=`. - Add/adjust tests to cover new behavior and explainability. - Run Pint formatting. Compare URL: https://git.cloudarix.de/ahmido/TenantAtlas/compare/dev...245-customer-health-score Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #283
177 lines
6.0 KiB
PHP
177 lines
6.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\System\Widgets\CustomerHealthTopWorkspaces;
|
|
use App\Models\Finding;
|
|
use App\Models\OperationRun;
|
|
use App\Models\PlatformUser;
|
|
use App\Models\ProductUsageEvent;
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\ReviewPack;
|
|
use App\Models\Tenant;
|
|
use App\Models\Workspace;
|
|
use App\Support\Auth\PlatformCapabilities;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\ProductTelemetry\ProductUsageEventCatalog;
|
|
use App\Support\Providers\ProviderConsentStatus;
|
|
use App\Support\Providers\ProviderVerificationStatus;
|
|
use App\Support\System\SystemDirectoryLinks;
|
|
use App\Support\System\SystemOperationRunLinks;
|
|
use App\Support\SystemConsole\SystemConsoleWindow;
|
|
use Carbon\CarbonImmutable;
|
|
use Filament\Facades\Filament;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function (): void {
|
|
Filament::setCurrentPanel('system');
|
|
Filament::bootCurrentPanel();
|
|
|
|
CarbonImmutable::setTestNow(CarbonImmutable::parse('2026-04-27 12:00:00'));
|
|
});
|
|
|
|
afterEach(function (): void {
|
|
CarbonImmutable::setTestNow();
|
|
});
|
|
|
|
it('lists the worst workspaces first with dominant reasons and one platform-safe next link per row', function (): void {
|
|
actingAsExplainabilitySystemUser();
|
|
|
|
$opsWorkspace = seedExplainabilityWorkspace('Alpha Operations');
|
|
OperationRun::factory()
|
|
->forTenant($opsWorkspace['tenant'])
|
|
->create([
|
|
'workspace_id' => (int) $opsWorkspace['workspace']->getKey(),
|
|
'status' => OperationRunStatus::Queued->value,
|
|
'outcome' => OperationRunOutcome::Pending->value,
|
|
'created_at' => now()->subHours(2),
|
|
'started_at' => null,
|
|
]);
|
|
|
|
$providerWorkspace = seedExplainabilityWorkspace('Beta Provider');
|
|
ProviderConnection::query()
|
|
->where('tenant_id', (int) $providerWorkspace['tenant']->getKey())
|
|
->update([
|
|
'is_enabled' => true,
|
|
'consent_status' => ProviderConsentStatus::Granted->value,
|
|
'verification_status' => ProviderVerificationStatus::Blocked->value,
|
|
]);
|
|
|
|
$viewData = customerHealthTopWorkspaceViewData(SystemConsoleWindow::LastWeek);
|
|
$rows = collect($viewData['rows'])->take(2)->values();
|
|
|
|
expect($rows[0]['workspace_label'])->toBe('Alpha Operations')
|
|
->and($rows[0]['overall']['label'])->toBe('Critical')
|
|
->and(array_column($rows[0]['dominant_dimensions'], 'label'))->toBe(['Operational stability'])
|
|
->and($rows[0]['next_link'])->toBe([
|
|
'label' => 'Open runs',
|
|
'url' => SystemOperationRunLinks::index(),
|
|
])
|
|
->and($rows[1]['workspace_label'])->toBe('Beta Provider')
|
|
->and($rows[1]['overall']['label'])->toBe('Critical')
|
|
->and(array_column($rows[1]['dominant_dimensions'], 'label'))->toBe(['Provider connection health'])
|
|
->and($rows[1]['next_link'])->toBe([
|
|
'label' => 'Review health details',
|
|
'url' => SystemDirectoryLinks::tenantDetail($providerWorkspace['tenant']).'?window='.SystemConsoleWindow::LastWeek,
|
|
])
|
|
->and($rows->every(fn (array $row): bool => count($row['dominant_dimensions']) <= 2))->toBeTrue()
|
|
->and($rows->every(fn (array $row): bool => ! str_contains($row['next_link']['url'], '/admin/')))->toBeTrue()
|
|
->and(array_key_exists('failed_count', $rows[0]))->toBeFalse();
|
|
});
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
function customerHealthTopWorkspaceViewData(string $window = SystemConsoleWindow::LastDay): array
|
|
{
|
|
$component = Livewire::withQueryParams([
|
|
'window' => $window,
|
|
])->test(CustomerHealthTopWorkspaces::class);
|
|
|
|
$method = new ReflectionMethod(CustomerHealthTopWorkspaces::class, 'getViewData');
|
|
$method->setAccessible(true);
|
|
|
|
return $method->invoke($component->instance());
|
|
}
|
|
|
|
function actingAsExplainabilitySystemUser(): PlatformUser
|
|
{
|
|
$user = PlatformUser::factory()->create([
|
|
'capabilities' => [
|
|
PlatformCapabilities::ACCESS_SYSTEM_PANEL,
|
|
PlatformCapabilities::CONSOLE_VIEW,
|
|
PlatformCapabilities::DIRECTORY_VIEW,
|
|
PlatformCapabilities::OPERATIONS_VIEW,
|
|
],
|
|
'is_active' => true,
|
|
]);
|
|
|
|
test()->actingAs($user, 'platform');
|
|
|
|
return $user;
|
|
}
|
|
|
|
/**
|
|
* @return array{workspace: Workspace, tenant: Tenant}
|
|
*/
|
|
function seedExplainabilityWorkspace(string $workspaceName): array
|
|
{
|
|
$workspace = Workspace::factory()->create(['name' => $workspaceName]);
|
|
$tenant = Tenant::factory()->for($workspace)->create([
|
|
'name' => $workspaceName.' Tenant',
|
|
'status' => Tenant::STATUS_ACTIVE,
|
|
]);
|
|
|
|
ProviderConnection::factory()
|
|
->for($tenant)
|
|
->verifiedHealthy()
|
|
->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'is_default' => true,
|
|
]);
|
|
|
|
ReviewPack::factory()
|
|
->for($tenant)
|
|
->ready()
|
|
->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'created_at' => now()->subHour(),
|
|
'generated_at' => now()->subHour(),
|
|
'expires_at' => now()->addDays(30),
|
|
]);
|
|
|
|
ProductUsageEvent::factory()
|
|
->for($tenant)
|
|
->forEvent(ProductUsageEventCatalog::ONBOARDING_CHECKPOINT_COMPLETED)
|
|
->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'occurred_at' => now()->subMinutes(20),
|
|
]);
|
|
|
|
OperationRun::factory()
|
|
->forTenant($tenant)
|
|
->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'status' => OperationRunStatus::Completed->value,
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'created_at' => now()->subMinutes(10),
|
|
'started_at' => now()->subMinutes(15),
|
|
'completed_at' => now()->subMinutes(5),
|
|
]);
|
|
|
|
Finding::factory()
|
|
->for($tenant)
|
|
->closed()
|
|
->create([
|
|
'severity' => Finding::SEVERITY_LOW,
|
|
]);
|
|
|
|
return [
|
|
'workspace' => $workspace,
|
|
'tenant' => $tenant,
|
|
];
|
|
} |