TenantAtlas/apps/platform/resources/views/filament/pages/workspace-overview.blade.php
Ahmed Darrazi 4b615e4988
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 5m13s
feat: add governance inbox resolution intake
2026-06-20 09:45:08 +02:00

385 lines
24 KiB
PHP

<x-filament-panels::page>
@php
$workspace = $overview['workspace'] ?? ['name' => 'Workspace'];
$quickActions = $overview['quick_actions'] ?? [];
$myFindingsSignal = $overview['my_findings_signal'] ?? null;
$findingsHygieneSignal = $overview['findings_hygiene_signal'] ?? null;
$zeroTenantState = $overview['zero_tenant_state'] ?? null;
$attentionItems = $overview['attention_items'] ?? [];
$summaryMetrics = $overview['summary_metrics'] ?? [];
$hasVisibleEnvironments = (int) ($overview['accessible_tenant_count'] ?? 0) > 0;
$priorityAttention = null;
$priorityAttentionIndex = null;
foreach ($attentionItems as $index => $candidateAttention) {
if (! is_array($candidateAttention)) {
continue;
}
$candidateDestination = $candidateAttention['destination'] ?? null;
$candidateActionUrl = is_array($candidateDestination) && ($candidateDestination['disabled'] ?? false) === false
? ($candidateDestination['url'] ?? null)
: null;
if (is_string($candidateActionUrl) && $candidateActionUrl !== '') {
$priorityAttention = $candidateAttention;
$priorityAttentionIndex = $index;
break;
}
}
$primaryQuickActions = array_values(array_filter(
$quickActions,
static fn (array $action): bool => ($action['key'] ?? null) === 'choose_environment',
));
$operationalQuickActions = array_values(array_filter(
$quickActions,
static fn (array $action): bool => in_array($action['key'] ?? null, ['operations', 'alerts'], true),
));
$adminQuickActions = array_values(array_filter(
$quickActions,
static fn (array $action): bool => in_array($action['key'] ?? null, ['switch_workspace', 'manage_workspaces'], true),
));
$listedAttentionItems = $attentionItems;
if ($priorityAttentionIndex !== null) {
unset($listedAttentionItems[$priorityAttentionIndex]);
$listedAttentionItems = array_values($listedAttentionItems);
}
@endphp
<div class="space-y-6">
<x-filament::section>
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
<div class="min-w-0 space-y-2">
<div class="flex flex-wrap items-center gap-2">
<span class="inline-flex items-center gap-1.5 rounded-full border border-primary-200 bg-primary-50 px-3 py-1 text-xs font-medium text-primary-700 dark:border-primary-700/60 dark:bg-primary-950/40 dark:text-primary-300">
<x-filament::icon icon="heroicon-o-home" class="h-3.5 w-3.5" />
Workspace overview
</span>
@if (filled($workspace['slug'] ?? null))
<span class="inline-flex items-center gap-1 rounded-full border border-gray-200 bg-gray-50 px-3 py-1 text-xs font-medium text-gray-600 dark:border-white/10 dark:bg-white/5 dark:text-gray-300">
{{ $workspace['slug'] }}
</span>
@endif
</div>
<h2 class="text-2xl font-semibold tracking-tight text-gray-950 dark:text-white">
{{ $workspace['name'] ?? 'Workspace' }}
</h2>
<p class="max-w-2xl text-sm leading-6 text-gray-600 dark:text-gray-300">
Workspace-scoped command center for visible environments. Attention signals are ranked before diagnostic activity.
</p>
</div>
<div class="grid grid-cols-2 gap-3 sm:flex sm:items-center">
<div class="rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 dark:border-white/10 dark:bg-white/5">
<div class="text-xs font-medium text-gray-500 dark:text-gray-400">
Visible environments
</div>
<div class="mt-1 text-lg font-semibold text-gray-950 dark:text-white">
{{ $overview['accessible_tenant_count'] ?? 0 }}
</div>
</div>
<div class="rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 dark:border-white/10 dark:bg-white/5">
<div class="text-xs font-medium text-gray-500 dark:text-gray-400">
Scope
</div>
<div class="mt-1 text-sm font-semibold text-gray-950 dark:text-white">
Workspace
</div>
</div>
</div>
</div>
</x-filament::section>
@if (is_array($priorityAttention))
@php
$priorityDestination = $priorityAttention['destination'] ?? null;
$priorityActionUrl = is_array($priorityDestination) && ($priorityDestination['disabled'] ?? false) === false
? ($priorityDestination['url'] ?? null)
: null;
$priorityIsCritical = ($priorityAttention['badge_color'] ?? null) === 'danger' || ($priorityAttention['urgency'] ?? null) === 'critical';
@endphp
<section class="rounded-2xl border p-5 shadow-sm {{ $priorityIsCritical ? 'border-danger-200 bg-danger-50/70 dark:border-danger-700/50 dark:bg-danger-950/20' : 'border-warning-200 bg-warning-50/70 dark:border-warning-700/50 dark:bg-warning-950/20' }}">
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div class="min-w-0 space-y-3">
<div class="flex flex-wrap items-center gap-2">
<span class="inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium {{ $priorityIsCritical ? 'border-danger-200 bg-white text-danger-700 dark:border-danger-700/50 dark:bg-danger-500/10 dark:text-danger-200' : 'border-warning-200 bg-white text-warning-800 dark:border-warning-700/50 dark:bg-warning-500/10 dark:text-warning-100' }}">
<x-filament::icon icon="heroicon-o-exclamation-triangle" class="h-3.5 w-3.5" />
Priority attention
</span>
<span class="inline-flex items-center rounded-full border border-gray-200 bg-white px-2.5 py-0.5 text-xs font-medium text-gray-700 dark:border-white/10 dark:bg-white/10 dark:text-gray-200">
{{ $priorityAttention['tenant_label'] ?? 'Environment' }}
</span>
<x-filament::badge :color="$priorityAttention['badge_color'] ?? 'warning'" size="sm">
{{ $priorityAttention['badge'] ?? 'Needs attention' }}
</x-filament::badge>
</div>
<div class="space-y-1">
<h2 class="text-base font-semibold text-gray-950 dark:text-white">
{{ $priorityAttention['title'] ?? 'Workspace attention needed' }}
</h2>
<p class="max-w-3xl text-sm leading-6 text-gray-700 dark:text-gray-200">
{{ $priorityAttention['body'] ?? 'Review the highest-priority environment before treating recent operations as health.' }}
</p>
@if (filled($priorityAttention['supporting_message'] ?? null))
<p class="text-xs leading-5 text-gray-600 dark:text-gray-300">
{{ $priorityAttention['supporting_message'] }}
</p>
@endif
</div>
</div>
@if (is_string($priorityActionUrl) && $priorityActionUrl !== '')
<x-filament::button
tag="a"
:color="$priorityIsCritical ? 'danger' : 'warning'"
:href="$priorityActionUrl"
icon="heroicon-o-arrow-right"
class="w-full justify-center sm:w-auto"
>
{{ $priorityDestination['label'] ?? 'Review priority environment' }}
</x-filament::button>
@endif
</div>
</section>
@endif
@livewire(\App\Filament\Widgets\Workspace\WorkspaceSummaryStats::class, [
'metrics' => $summaryMetrics,
], key('workspace-overview-summary-' . ($workspace['id'] ?? 'none')))
@if ($quickActions !== [])
<section class="rounded-2xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-white/5">
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div class="space-y-1">
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
Workspace shortcuts
</h2>
<p class="hidden text-sm leading-6 text-gray-600 dark:text-gray-300 sm:block">
Operational paths stay available without competing with the priority queue.
</p>
</div>
@if ($primaryQuickActions !== [])
<x-filament::button
tag="a"
color="primary"
:href="$primaryQuickActions[0]['url']"
:icon="$primaryQuickActions[0]['icon']"
class="w-full justify-center sm:w-auto"
>
{{ $primaryQuickActions[0]['label'] }}
</x-filament::button>
@endif
</div>
<div class="mt-4 grid grid-cols-1 gap-3 lg:grid-cols-[minmax(0,1fr)_minmax(14rem,18rem)]">
@if ($operationalQuickActions !== [])
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
@foreach ($operationalQuickActions as $action)
<a
href="{{ $action['url'] }}"
class="rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 text-left transition hover:border-primary-300 hover:bg-white hover:shadow-sm dark:border-white/10 dark:bg-white/5 dark:hover:border-primary-500/40 dark:hover:bg-white/10"
>
<div class="flex items-start gap-3">
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-white text-gray-700 ring-1 ring-gray-200 dark:bg-white/10 dark:text-gray-200 dark:ring-white/10">
<x-filament::icon :icon="$action['icon']" class="h-5 w-5" />
</div>
<div class="min-w-0">
<div class="text-sm font-semibold text-gray-950 dark:text-white">
{{ $action['label'] }}
</div>
<div class="mt-1 hidden text-xs leading-5 text-gray-600 dark:text-gray-300 sm:block">
{{ $action['description'] }}
</div>
</div>
</div>
</a>
@endforeach
</div>
@endif
@if ($adminQuickActions !== [])
<div class="rounded-xl border border-gray-200 bg-gray-50 p-3 dark:border-white/10 dark:bg-white/5">
<div class="text-xs font-medium uppercase text-gray-500 dark:text-gray-400">
Workspace admin
</div>
<div class="mt-2 space-y-2">
@foreach ($adminQuickActions as $action)
<a
href="{{ $action['url'] }}"
class="flex items-center gap-2 rounded-lg px-2 py-1.5 text-sm font-medium text-gray-700 transition hover:bg-white hover:text-primary-700 dark:text-gray-200 dark:hover:bg-white/10 dark:hover:text-primary-300"
>
<x-filament::icon :icon="$action['icon']" class="h-4 w-4 text-gray-500 dark:text-gray-400" />
<span>{{ $action['label'] }}</span>
</a>
@endforeach
</div>
</div>
@endif
</div>
</section>
@endif
@if ($hasVisibleEnvironments && is_array($myFindingsSignal))
<section class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm dark:border-white/10 dark:bg-white/5">
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div class="space-y-3">
<div class="inline-flex w-fit items-center gap-2 rounded-full border border-primary-200 bg-primary-50 px-3 py-1 text-xs font-medium text-primary-700 dark:border-primary-700/60 dark:bg-primary-950/40 dark:text-primary-300">
<x-filament::icon icon="heroicon-o-clipboard-document-check" class="h-3.5 w-3.5" />
Assigned to me
</div>
<div class="space-y-1">
<h2 class="text-base font-semibold text-gray-950 dark:text-white">
{{ $myFindingsSignal['headline'] }}
</h2>
<p class="max-w-2xl text-sm leading-6 text-gray-600 dark:text-gray-300">
{{ $myFindingsSignal['description'] }}
</p>
</div>
<div class="flex flex-wrap gap-2 text-xs">
<span class="inline-flex items-center rounded-full border border-gray-200 bg-gray-50 px-3 py-1 font-medium text-gray-700 dark:border-white/10 dark:bg-white/5 dark:text-gray-300">
Open assigned: {{ $myFindingsSignal['open_assigned_count'] }}
</span>
<span class="inline-flex items-center rounded-full border px-3 py-1 font-medium {{ ($myFindingsSignal['overdue_assigned_count'] ?? 0) > 0 ? 'border-danger-200 bg-danger-50 text-danger-700 dark:border-danger-700/50 dark:bg-danger-950/30 dark:text-danger-200' : 'border-success-200 bg-success-50 text-success-700 dark:border-success-700/50 dark:bg-success-950/30 dark:text-success-200' }}">
Overdue: {{ $myFindingsSignal['overdue_assigned_count'] }}
</span>
<span class="inline-flex items-center rounded-full border px-3 py-1 font-medium {{ ($myFindingsSignal['is_calm'] ?? false) ? 'border-success-200 bg-success-50 text-success-700 dark:border-success-700/50 dark:bg-success-950/30 dark:text-success-200' : 'border-warning-200 bg-warning-50 text-warning-700 dark:border-warning-700/50 dark:bg-warning-950/30 dark:text-warning-200' }}">
{{ ($myFindingsSignal['is_calm'] ?? false) ? 'Calm' : 'Needs follow-up' }}
</span>
</div>
</div>
<x-filament::button
tag="a"
color="primary"
:href="$myFindingsSignal['cta_url']"
icon="heroicon-o-arrow-right"
>
{{ $myFindingsSignal['cta_label'] }}
</x-filament::button>
</div>
</section>
@endif
@if ($hasVisibleEnvironments && is_array($findingsHygieneSignal))
@php
$hygieneIsCalm = (bool) ($findingsHygieneSignal['is_calm'] ?? false);
$brokenAssignmentCount = (int) ($findingsHygieneSignal['broken_assignment_count'] ?? 0);
$staleInProgressCount = (int) ($findingsHygieneSignal['stale_in_progress_count'] ?? 0);
@endphp
<section class="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm dark:border-white/10 dark:bg-white/5">
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div class="space-y-3">
<div class="inline-flex w-fit items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium {{ $hygieneIsCalm ? 'border-gray-200 bg-gray-50 text-gray-700 dark:border-white/10 dark:bg-white/5 dark:text-gray-300' : 'border-danger-200 bg-danger-50 text-danger-700 dark:border-danger-700/60 dark:bg-danger-950/40 dark:text-danger-200' }}">
<x-filament::icon icon="heroicon-o-wrench-screwdriver" class="h-3.5 w-3.5" />
Findings hygiene
</div>
<div class="space-y-1">
<h2 class="text-base font-semibold text-gray-950 dark:text-white">
{{ $findingsHygieneSignal['headline'] }}
</h2>
<p class="max-w-2xl text-sm leading-6 text-gray-600 dark:text-gray-300">
{{ $findingsHygieneSignal['description'] }}
</p>
</div>
<div class="flex flex-wrap gap-2 text-xs">
<span class="inline-flex items-center rounded-full border border-gray-200 bg-gray-50 px-3 py-1 font-medium text-gray-700 dark:border-white/10 dark:bg-white/5 dark:text-gray-300">
Unique issues: {{ $findingsHygieneSignal['unique_issue_count'] }}
</span>
<span class="inline-flex items-center rounded-full border px-3 py-1 font-medium {{ $brokenAssignmentCount > 0 ? 'border-danger-200 bg-danger-50 text-danger-700 dark:border-danger-700/50 dark:bg-danger-950/30 dark:text-danger-200' : 'border-gray-200 bg-gray-50 text-gray-700 dark:border-white/10 dark:bg-white/5 dark:text-gray-300' }}">
Broken assignments: {{ $findingsHygieneSignal['broken_assignment_count'] }}
</span>
<span class="inline-flex items-center rounded-full border px-3 py-1 font-medium {{ $staleInProgressCount > 0 ? 'border-warning-200 bg-warning-50 text-warning-700 dark:border-warning-700/50 dark:bg-warning-950/30 dark:text-warning-200' : 'border-gray-200 bg-gray-50 text-gray-700 dark:border-white/10 dark:bg-white/5 dark:text-gray-300' }}">
Stale in progress: {{ $findingsHygieneSignal['stale_in_progress_count'] }}
</span>
<span class="inline-flex items-center rounded-full border px-3 py-1 font-medium {{ ($findingsHygieneSignal['is_calm'] ?? false) ? 'border-success-200 bg-success-50 text-success-700 dark:border-success-700/50 dark:bg-success-950/30 dark:text-success-200' : 'border-warning-200 bg-warning-50 text-warning-700 dark:border-warning-700/50 dark:bg-warning-950/30 dark:text-warning-200' }}">
{{ ($findingsHygieneSignal['is_calm'] ?? false) ? 'Calm' : 'Needs repair' }}
</span>
</div>
</div>
<x-filament::button
tag="a"
:color="$hygieneIsCalm ? 'gray' : 'danger'"
:href="$findingsHygieneSignal['cta_url']"
icon="heroicon-o-arrow-right"
>
{{ $findingsHygieneSignal['cta_label'] }}
</x-filament::button>
</div>
</section>
@endif
@if (is_array($zeroTenantState))
<section class="rounded-2xl border border-warning-200 bg-warning-50 p-5 shadow-sm dark:border-warning-700/50 dark:bg-warning-950/30">
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div class="space-y-1">
<h2 class="text-sm font-semibold text-warning-900 dark:text-warning-100">
{{ $zeroTenantState['title'] }}
</h2>
<p class="text-sm text-warning-800 dark:text-warning-200">
{{ $zeroTenantState['body'] }}
</p>
</div>
<x-filament::button
tag="a"
color="warning"
:href="$zeroTenantState['action_url']"
icon="heroicon-o-arrow-right"
>
{{ $zeroTenantState['action_label'] }}
</x-filament::button>
</div>
</section>
@endif
<div class="space-y-6">
<div class="flex flex-wrap gap-2 text-xs text-gray-600 dark:text-gray-300">
<span class="inline-flex items-center rounded-full border border-danger-200 bg-danger-50 px-3 py-1 font-medium text-danger-700 dark:border-danger-700/50 dark:bg-danger-950/30 dark:text-danger-200">
Governance risk counts affected environments
</span>
<span class="inline-flex items-center rounded-full border border-warning-200 bg-warning-50 px-3 py-1 font-medium text-warning-700 dark:border-warning-700/50 dark:bg-warning-950/30 dark:text-warning-200">
Backup health stays separate from recovery evidence
</span>
<span class="inline-flex items-center rounded-full border border-gray-200 bg-white px-3 py-1 font-medium text-gray-600 dark:border-white/10 dark:bg-white/5 dark:text-gray-300">
Calm wording stays bounded to visible environments and checked domains
</span>
<span class="inline-flex items-center rounded-full border border-gray-200 bg-white px-3 py-1 font-medium text-gray-600 dark:border-white/10 dark:bg-white/5 dark:text-gray-300">
Recent operations stay diagnostic
</span>
</div>
<div class="grid grid-cols-1 gap-6 xl:grid-cols-2">
@livewire(\App\Filament\Widgets\Workspace\WorkspaceNeedsAttention::class, [
'items' => $listedAttentionItems,
'emptyState' => $overview['attention_empty_state'] ?? [],
'triageReviewProgress' => $overview['triage_review_progress'] ?? [],
], key('workspace-overview-attention-' . ($workspace['id'] ?? 'none')))
@livewire(\App\Filament\Widgets\Workspace\WorkspaceRecentOperations::class, [
'operations' => $overview['recent_operations'] ?? [],
'emptyState' => $overview['recent_operations_empty_state'] ?? [],
], key('workspace-overview-operations-' . ($workspace['id'] ?? 'none')))
</div>
</div>
</div>
</x-filament-panels::page>