TenantAtlas/apps/platform/app/Filament/System/Widgets/CustomerHealthTopWorkspaces.php
ahmido 86505483bf
Some checks failed
Main Confidence / confidence (push) Failing after 52s
feat(customer-health): add decision card to tenant/workspace detail (spec 245) (#283)
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
2026-04-27 08:30:01 +00:00

137 lines
4.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\System\Widgets;
use App\Models\PlatformUser;
use App\Support\Auth\PlatformCapabilities;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\CustomerHealth\WorkspaceHealthSummaryQuery;
use App\Support\SystemConsole\SystemConsoleWindow;
use App\Support\System\SystemOperationRunLinks;
use Filament\Widgets\Widget;
class CustomerHealthTopWorkspaces extends Widget
{
protected static bool $isLazy = false;
protected int|string|array $columnSpan = 'full';
protected string $view = 'filament.system.widgets.customer-health-top-workspaces';
public ?string $window = null;
public static function canView(): bool
{
$user = auth('platform')->user();
if (! $user instanceof PlatformUser) {
return false;
}
if ($user->hasCapability(PlatformCapabilities::DIRECTORY_VIEW)) {
return true;
}
if (! static::canOpenRuns($user)) {
return false;
}
$window = SystemConsoleWindow::fromNullable((string) request()->query('window'));
return app(WorkspaceHealthSummaryQuery::class)
->attentionNeeded($window, 10)
->contains(fn (array $summary): bool => static::canAccessNextLink($summary['next_link'], $user));
}
/**
* @return array<string, mixed>
*/
protected function getViewData(): array
{
$window = SystemConsoleWindow::fromNullable($this->window ?? (string) request()->query('window'));
$windowLabel = SystemConsoleWindow::options()[$window->value] ?? 'Last 24 hours';
$user = auth('platform')->user();
return [
'windowLabel' => $windowLabel,
'rows' => app(WorkspaceHealthSummaryQuery::class)
->attentionNeeded($window, 10)
->filter(fn (array $summary): bool => $user instanceof PlatformUser && static::canAccessNextLink($summary['next_link'], $user))
->map(fn (array $summary): array => $this->presentSummary($summary)),
];
}
/**
* @param array{label: string, url: string} $nextLink
*/
private static function canAccessNextLink(array $nextLink, PlatformUser $user): bool
{
if ($user->hasCapability(PlatformCapabilities::DIRECTORY_VIEW)) {
return true;
}
return static::canOpenRuns($user)
&& $nextLink['url'] === SystemOperationRunLinks::index();
}
private static function canOpenRuns(PlatformUser $user): bool
{
return $user->hasCapability(PlatformCapabilities::OPERATIONS_VIEW)
|| ($user->hasCapability(PlatformCapabilities::OPS_VIEW) && $user->hasCapability(PlatformCapabilities::RUNBOOKS_VIEW));
}
/**
* @param array{
* workspace_id: int,
* workspace_name: string,
* overall_level: string,
* dimensions: array<string, array{label: string, level: string, windowed: bool}>,
* dominant_dimension_keys: list<string>,
* non_ok_dimension_count: int,
* next_link: array{label: string, url: string}
* } $summary
* @return array{
* workspace_id: int,
* workspace_label: string,
* overall: array{label: string, color: string, icon: ?string},
* dominant_copy: string,
* dominant_dimensions: list<array{label: string, color: string, icon: ?string}>,
* next_link: array{label: string, url: string}
* }
*/
private function presentSummary(array $summary): array
{
$dominantDimensions = collect($summary['dominant_dimension_keys'])
->take(2)
->map(function (string $dimensionKey) use ($summary): array {
$dimension = $summary['dimensions'][$dimensionKey];
$badge = BadgeRenderer::spec(BadgeDomain::SystemHealth, $dimension['level']);
return [
'label' => $dimension['label'],
'color' => $badge->color,
'icon' => $badge->icon,
];
})
->values()
->all();
$overallBadge = BadgeRenderer::spec(BadgeDomain::SystemHealth, $summary['overall_level']);
return [
'workspace_id' => $summary['workspace_id'],
'workspace_label' => $summary['workspace_name'],
'overall' => [
'label' => $overallBadge->label,
'color' => $overallBadge->color,
'icon' => $overallBadge->icon,
],
'dominant_copy' => implode(', ', array_column($dominantDimensions, 'label')),
'dominant_dimensions' => $dominantDimensions,
'next_link' => $summary['next_link'],
];
}
}