330 lines
21 KiB
PHP
330 lines
21 KiB
PHP
<x-filament-panels::page>
|
|
@php
|
|
$scope = $this->appliedScope();
|
|
$sections = $this->sections();
|
|
$emptyState = $this->calmEmptyState();
|
|
$workbench = $this->decisionWorkbench();
|
|
$selectedItem = $workbench['selected_item'] ?? null;
|
|
$diagnostics = $workbench['diagnostics'] ?? [];
|
|
@endphp
|
|
|
|
<div class="space-y-6">
|
|
<div class="space-y-3" data-testid="governance-inbox-secondary-filters">
|
|
<div class="flex flex-wrap gap-2 text-sm text-gray-600 dark:text-gray-300">
|
|
@if (filled($scope['workspace_label'] ?? null))
|
|
<span class="inline-flex items-center rounded-md bg-gray-100 px-2.5 py-1 text-xs font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-200">
|
|
Workspace: {{ $scope['workspace_label'] }}
|
|
</span>
|
|
@endif
|
|
|
|
<span class="inline-flex items-center rounded-md bg-gray-100 px-2.5 py-1 text-xs font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-200">
|
|
Scope: {{ $scope['family_label'] ?? 'All attention' }}
|
|
</span>
|
|
|
|
<span class="inline-flex items-center rounded-md bg-gray-100 px-2.5 py-1 text-xs font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-200">
|
|
Visible items: {{ $scope['total_count'] ?? 0 }}
|
|
</span>
|
|
|
|
@if (filled($scope['tenant_label'] ?? null))
|
|
<span class="inline-flex items-center rounded-md bg-warning-50 px-2.5 py-1 text-xs font-medium text-warning-700 dark:bg-warning-500/10 dark:text-warning-300">
|
|
Environment: {{ $scope['tenant_label'] }}
|
|
</span>
|
|
@endif
|
|
</div>
|
|
|
|
@if ($this->hasTenantPrefilter())
|
|
@include('filament.partials.workspace-hub-environment-filter-chip', [
|
|
'label' => $scope['tenant_label'] ?? null,
|
|
'clearUrl' => $this->pageUrl(['environment_id' => null, 'family' => null]),
|
|
])
|
|
@endif
|
|
|
|
<div class="flex flex-wrap gap-2" data-testid="governance-inbox-family-filters">
|
|
<a
|
|
href="{{ $this->pageUrl(['family' => null]) }}"
|
|
class="inline-flex items-center gap-2 rounded-md border px-2.5 py-1 text-xs font-medium transition {{ $this->family === null ? 'border-primary-300 bg-primary-50 text-primary-700 dark:border-primary-700/60 dark:bg-primary-950/40 dark:text-primary-300' : 'border-gray-200 text-gray-700 hover:border-gray-300 dark:border-gray-700 dark:text-gray-200 dark:hover:border-gray-600' }}"
|
|
>
|
|
All attention
|
|
<span class="rounded-md bg-black/5 px-2 py-0.5 text-xs dark:bg-white/10">{{ $scope['total_count'] ?? 0 }}</span>
|
|
</a>
|
|
|
|
@foreach ($this->availableFamilies() as $family)
|
|
<a
|
|
href="{{ $this->pageUrl(['family' => $family['key']]) }}"
|
|
class="inline-flex items-center gap-2 rounded-md border px-2.5 py-1 text-xs font-medium transition {{ $this->isActiveFamily($family['key']) ? 'border-primary-300 bg-primary-50 text-primary-700 dark:border-primary-700/60 dark:bg-primary-950/40 dark:text-primary-300' : 'border-gray-200 text-gray-700 hover:border-gray-300 dark:border-gray-700 dark:text-gray-200 dark:hover:border-gray-600' }}"
|
|
>
|
|
{{ $family['label'] }}
|
|
<span class="rounded-md bg-black/5 px-2 py-0.5 text-xs dark:bg-white/10">{{ $family['count'] }}</span>
|
|
</a>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_22rem]" data-testid="governance-inbox-decision-workbench">
|
|
<section class="rounded-lg border border-gray-200 bg-white p-5 shadow-sm dark:border-gray-800 dark:bg-gray-900" data-testid="governance-inbox-priority-card">
|
|
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
|
<div class="space-y-2">
|
|
<p class="text-xs font-medium uppercase text-gray-500 dark:text-gray-400">
|
|
Decision workbench
|
|
</p>
|
|
<h2 class="text-xl font-semibold text-gray-950 dark:text-white">
|
|
{{ $workbench['question'] }}
|
|
</h2>
|
|
</div>
|
|
|
|
@if ($selectedItem !== null)
|
|
<span class="inline-flex w-fit items-center rounded-lg bg-warning-50 px-2.5 py-1 text-xs font-medium text-warning-700 dark:bg-warning-500/10 dark:text-warning-300">
|
|
{{ $selectedItem['status_label'] }}
|
|
</span>
|
|
@endif
|
|
</div>
|
|
|
|
@if ($selectedItem === null)
|
|
<div class="mt-5 rounded-lg border border-dashed border-gray-300 bg-gray-50 p-5 dark:border-gray-700 dark:bg-gray-950/40" data-testid="governance-inbox-empty-decision-state">
|
|
<h3 class="text-base font-semibold text-gray-950 dark:text-white">
|
|
{{ $emptyState['title'] }}
|
|
</h3>
|
|
<p class="mt-1 text-sm leading-6 text-gray-600 dark:text-gray-300">
|
|
{{ $emptyState['body'] }}
|
|
</p>
|
|
|
|
@if (filled($emptyState['action_label'] ?? null) && filled($emptyState['action_url'] ?? null))
|
|
<div class="mt-4">
|
|
<x-filament::button tag="a" color="gray" href="{{ $emptyState['action_url'] }}">
|
|
{{ $emptyState['action_label'] }}
|
|
</x-filament::button>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@else
|
|
<div class="mt-5 space-y-5">
|
|
<div class="space-y-2">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<span class="inline-flex items-center rounded-lg bg-gray-100 px-2.5 py-1 text-xs font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-200">
|
|
{{ $selectedItem['section_label'] }}
|
|
</span>
|
|
<span class="inline-flex items-center rounded-lg bg-gray-100 px-2.5 py-1 text-xs font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-200">
|
|
Environment: {{ $selectedItem['environment_label'] }}
|
|
</span>
|
|
</div>
|
|
|
|
<h3 class="text-lg font-semibold text-gray-950 dark:text-white">
|
|
{{ $selectedItem['title'] }}
|
|
</h3>
|
|
|
|
<p class="text-sm leading-6 text-gray-600 dark:text-gray-300">
|
|
{{ $selectedItem['decision_label'] }}
|
|
</p>
|
|
</div>
|
|
|
|
<dl class="grid grid-cols-1 gap-3 md:grid-cols-2">
|
|
<div class="rounded-lg border border-gray-200 p-3 dark:border-gray-800">
|
|
<dt class="text-xs font-medium text-gray-500 dark:text-gray-400">Reason</dt>
|
|
<dd class="mt-1 text-sm leading-6 text-gray-800 dark:text-gray-100">{{ $selectedItem['reason_label'] }}</dd>
|
|
</div>
|
|
<div class="rounded-lg border border-gray-200 p-3 dark:border-gray-800">
|
|
<dt class="text-xs font-medium text-gray-500 dark:text-gray-400">Impact</dt>
|
|
<dd class="mt-1 text-sm leading-6 text-gray-800 dark:text-gray-100">{{ $selectedItem['impact_label'] }}</dd>
|
|
</div>
|
|
<div class="rounded-lg border border-gray-200 p-3 dark:border-gray-800">
|
|
<dt class="text-xs font-medium text-gray-500 dark:text-gray-400">Owner</dt>
|
|
<dd class="mt-1 text-sm leading-6 text-gray-800 dark:text-gray-100">{{ $selectedItem['owner_label'] }}</dd>
|
|
</div>
|
|
<div class="rounded-lg border border-gray-200 p-3 dark:border-gray-800">
|
|
<dt class="text-xs font-medium text-gray-500 dark:text-gray-400">Due</dt>
|
|
<dd class="mt-1 text-sm leading-6 text-gray-800 dark:text-gray-100">{{ $selectedItem['due_label'] }}</dd>
|
|
</div>
|
|
</dl>
|
|
|
|
<div class="grid grid-cols-1 gap-3 md:grid-cols-2">
|
|
<div class="rounded-lg border border-gray-200 p-3 dark:border-gray-800">
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Evidence</p>
|
|
<p class="mt-1 text-sm leading-6 text-gray-800 dark:text-gray-100">{{ $selectedItem['evidence_label'] }}</p>
|
|
</div>
|
|
<div class="rounded-lg border border-gray-200 p-3 dark:border-gray-800">
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Accepted risk</p>
|
|
<p class="mt-1 text-sm leading-6 text-gray-800 dark:text-gray-100">{{ $selectedItem['exception_label'] }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
@if (filled($selectedItem['primary_action_url'] ?? null))
|
|
<div class="flex flex-col gap-2 rounded-lg border border-gray-200 p-3 dark:border-gray-800 sm:flex-row sm:items-center sm:justify-between">
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Primary next action</p>
|
|
<p class="mt-1 text-sm leading-6 text-gray-800 dark:text-gray-100">{{ $selectedItem['primary_action_label'] }}</p>
|
|
</div>
|
|
|
|
<x-filament::button
|
|
tag="a"
|
|
href="{{ $selectedItem['primary_action_url'] }}"
|
|
icon="heroicon-o-arrow-top-right-on-square"
|
|
>
|
|
{{ $selectedItem['primary_action_label'] }}
|
|
</x-filament::button>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
</section>
|
|
|
|
<aside class="rounded-lg border border-gray-200 bg-white p-5 shadow-sm dark:border-gray-800 dark:bg-gray-900" data-testid="governance-inbox-decision-detail">
|
|
<div class="space-y-4">
|
|
<div class="space-y-1">
|
|
<p class="text-xs font-medium uppercase text-gray-500 dark:text-gray-400">Decision summary</p>
|
|
<h2 class="text-base font-semibold text-gray-950 dark:text-white">
|
|
{{ $selectedItem['title'] ?? 'No selected decision' }}
|
|
</h2>
|
|
</div>
|
|
|
|
@if ($selectedItem !== null)
|
|
<div class="space-y-3 text-sm">
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Impact</p>
|
|
<p class="mt-1 text-gray-800 dark:text-gray-100">{{ $selectedItem['impact_label'] }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Evidence path</p>
|
|
@if (filled($selectedItem['evidence_path_url'] ?? null))
|
|
<a href="{{ $selectedItem['evidence_path_url'] }}" class="mt-1 inline-flex items-center gap-1 text-sm font-medium text-primary-600 hover:underline dark:text-primary-300">
|
|
{{ $selectedItem['evidence_path_label'] }}
|
|
<x-filament::icon icon="heroicon-m-arrow-top-right-on-square" class="h-4 w-4" />
|
|
</a>
|
|
@else
|
|
<p class="mt-1 text-gray-800 dark:text-gray-100">{{ $selectedItem['evidence_path_label'] }}</p>
|
|
@endif
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Accepted risk</p>
|
|
<p class="mt-1 text-gray-800 dark:text-gray-100">{{ $selectedItem['exception_label'] }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Owner / due</p>
|
|
<p class="mt-1 text-gray-800 dark:text-gray-100">{{ $selectedItem['owner_label'] }} · {{ $selectedItem['due_label'] }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
@if (filled($selectedItem['primary_action_url'] ?? null))
|
|
<div class="space-y-2">
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Primary next action</p>
|
|
<x-filament::button
|
|
tag="a"
|
|
color="gray"
|
|
href="{{ $selectedItem['primary_action_url'] }}"
|
|
icon="heroicon-o-arrow-top-right-on-square"
|
|
class="w-full"
|
|
>
|
|
{{ $selectedItem['primary_action_label'] }}
|
|
</x-filament::button>
|
|
</div>
|
|
@endif
|
|
@else
|
|
<div class="space-y-3 text-sm">
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Impact</p>
|
|
<p class="mt-1 text-gray-800 dark:text-gray-100">Unavailable</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Owner / due</p>
|
|
<p class="mt-1 text-gray-800 dark:text-gray-100">Unavailable</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Evidence path</p>
|
|
<p class="mt-1 text-gray-800 dark:text-gray-100">Unavailable</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Accepted risk</p>
|
|
<p class="mt-1 text-gray-800 dark:text-gray-100">Unavailable</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Primary next action</p>
|
|
<p class="mt-1 text-gray-800 dark:text-gray-100">No action available</p>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<details class="rounded-lg border border-gray-200 p-3 dark:border-gray-800" data-testid="governance-inbox-diagnostics">
|
|
<summary class="cursor-pointer text-sm font-medium text-gray-700 dark:text-gray-200">
|
|
{{ $diagnostics['label'] ?? 'Diagnostics' }} · {{ $diagnostics['state'] ?? 'Collapsed' }}
|
|
</summary>
|
|
<p class="mt-3 text-sm leading-6 text-gray-600 dark:text-gray-300">
|
|
{{ $diagnostics['body'] ?? 'Diagnostics are not default-visible.' }}
|
|
</p>
|
|
</details>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
|
|
@if ($sections !== [])
|
|
<div class="space-y-4" data-testid="governance-inbox-queue-context">
|
|
<div class="flex flex-col gap-1">
|
|
<h2 class="text-lg font-semibold text-gray-950 dark:text-white">Queue context</h2>
|
|
<p class="text-sm leading-6 text-gray-600 dark:text-gray-300">
|
|
Secondary source-family context remains available after the priority decision.
|
|
</p>
|
|
</div>
|
|
|
|
@foreach ($sections as $section)
|
|
<div class="rounded-lg border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-gray-900">
|
|
<div class="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
|
<div class="space-y-2">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<h3 class="text-base font-semibold text-gray-950 dark:text-white">{{ $section['label'] }}</h3>
|
|
<span class="inline-flex items-center rounded-lg bg-gray-100 px-2.5 py-1 text-xs font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-200">
|
|
{{ $section['count'] }}
|
|
</span>
|
|
</div>
|
|
|
|
<p class="max-w-3xl text-sm leading-6 text-gray-600 dark:text-gray-300">{{ $section['summary'] }}</p>
|
|
</div>
|
|
|
|
<x-filament::button tag="a" color="gray" href="{{ $section['dominant_action_url'] }}">
|
|
{{ $section['dominant_action_label'] }}
|
|
</x-filament::button>
|
|
</div>
|
|
|
|
@if ($section['count'] === 0)
|
|
<div class="mt-4 rounded-lg border border-dashed border-gray-300 bg-gray-50 p-4 text-sm leading-6 text-gray-600 dark:border-gray-700 dark:bg-gray-950/50 dark:text-gray-300">
|
|
{{ $section['empty_state'] }}
|
|
</div>
|
|
@else
|
|
<ul class="mt-4 grid gap-3">
|
|
@foreach ($section['entries'] as $entry)
|
|
<li class="rounded-lg border border-gray-200 p-4 dark:border-gray-800">
|
|
<div class="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
|
<div class="space-y-1.5">
|
|
@if (filled($entry['tenant_label'] ?? null))
|
|
<div class="text-xs font-medium uppercase text-gray-500 dark:text-gray-400">
|
|
{{ $entry['tenant_label'] }}
|
|
</div>
|
|
@endif
|
|
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<a href="{{ $entry['destination_url'] }}" class="text-sm font-semibold text-gray-950 hover:text-primary-600 dark:text-white dark:hover:text-primary-300">
|
|
{{ $entry['headline'] }}
|
|
</a>
|
|
|
|
<span class="inline-flex items-center rounded-lg bg-gray-100 px-2.5 py-1 text-xs font-medium text-gray-700 dark:bg-gray-800 dark:text-gray-200">
|
|
{{ $entry['status_label'] }}
|
|
</span>
|
|
</div>
|
|
|
|
@if (filled($entry['subline'] ?? null))
|
|
<p class="text-sm leading-6 text-gray-600 dark:text-gray-300">{{ $entry['subline'] }}</p>
|
|
@endif
|
|
</div>
|
|
|
|
<x-filament::button tag="a" color="gray" size="sm" href="{{ $entry['destination_url'] }}">
|
|
Open source
|
|
</x-filament::button>
|
|
</div>
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</x-filament-panels::page>
|