polish: customer-review workspace density and hierarchy (spec 344) (#416)
Refactored the customer-review workspace to emphasize the Operator Summary, tightening the hierarchy. Readiness flow and acknowledgment details were adjusted, and supporting proof panels moved to secondary visual weight. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #416
This commit is contained in:
parent
0987527d0e
commit
90f35b971e
@ -51,171 +51,127 @@
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-3 xl:grid-cols-12">
|
||||
<main class="min-w-0 space-y-4 md:col-span-2 xl:col-span-8">
|
||||
<div
|
||||
class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"
|
||||
data-testid="customer-review-decision-card"
|
||||
>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<x-filament::badge :color="$readiness['color']">
|
||||
{{ $readiness['label'] }}
|
||||
</x-filament::badge>
|
||||
<x-filament::badge color="gray">
|
||||
{{ __('localization.review.customer_safe') }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<h2 class="text-xl font-semibold text-gray-950 dark:text-white">
|
||||
{{ $readiness['question'] }}
|
||||
</h2>
|
||||
<p class="max-w-3xl text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ $readiness['reason'] }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-[minmax(0,1fr)_16rem]">
|
||||
<div class="flex h-full flex-col gap-2 rounded-lg border border-gray-200 bg-gray-50 p-4 text-sm dark:border-white/10 dark:bg-white/5">
|
||||
<div class="text-[0.7rem] font-semibold uppercase leading-4 tracking-wide text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.impact') }}
|
||||
</div>
|
||||
<p class="text-sm leading-6 text-gray-700 dark:text-gray-200">
|
||||
{{ $readiness['impact'] }}
|
||||
</p>
|
||||
<section class="space-y-4" data-testid="customer-review-operator-summary">
|
||||
<div
|
||||
class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"
|
||||
data-testid="customer-review-decision-card"
|
||||
>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<x-filament::badge :color="$readiness['color']">
|
||||
{{ $readiness['label'] }}
|
||||
</x-filament::badge>
|
||||
<x-filament::badge color="gray">
|
||||
{{ __('localization.review.customer_safe') }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
|
||||
<div class="flex h-full flex-col rounded-lg border border-gray-200 p-4 text-sm dark:border-white/10">
|
||||
<div class="text-[0.7rem] font-semibold uppercase leading-4 tracking-wide text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.latest_released_review') }}
|
||||
</div>
|
||||
<div class="mt-2 font-medium leading-5 text-gray-950 dark:text-white">
|
||||
{{ $latest['environment_label'] }}
|
||||
</div>
|
||||
<div class="mt-1 text-xs leading-5 text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.published_date', ['date' => $latest['published_label']]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@if ($readiness['primary_action_url'])
|
||||
<x-filament::button
|
||||
tag="a"
|
||||
:href="$readiness['primary_action_url']"
|
||||
:icon="$readiness['primary_action_icon']"
|
||||
target="_blank"
|
||||
data-testid="customer-review-primary-action"
|
||||
>
|
||||
{{ $readiness['primary_action_label'] }}
|
||||
</x-filament::button>
|
||||
@endif
|
||||
|
||||
@if ($latest['secondary_action_url'])
|
||||
<x-filament::button
|
||||
tag="a"
|
||||
:href="$latest['secondary_action_url']"
|
||||
color="gray"
|
||||
:icon="$latest['secondary_action_icon']"
|
||||
data-testid="customer-review-secondary-action"
|
||||
>
|
||||
{{ $latest['secondary_action_label'] }}
|
||||
</x-filament::button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"
|
||||
data-testid="customer-review-readiness-flow"
|
||||
>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.review_consumption_flow') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ __('localization.review.review_consumption_flow_description') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-2 2xl:grid-cols-3">
|
||||
@foreach ($readinessFlow as $step)
|
||||
<div
|
||||
class="flex min-h-36 flex-col gap-2 rounded-lg border border-gray-200 p-3 text-sm dark:border-white/10"
|
||||
data-testid="customer-review-readiness-step"
|
||||
data-step-label="{{ $step['title'] }}"
|
||||
data-step-state="{{ $step['label'] }}"
|
||||
data-step-current="{{ $step['is_current'] ? 'true' : 'false' }}"
|
||||
>
|
||||
<div class="flex flex-wrap items-start gap-2">
|
||||
<div class="min-w-0 font-medium text-gray-950 dark:text-white">
|
||||
{{ $step['title'] }}
|
||||
</div>
|
||||
<x-filament::badge :color="$step['color']" size="sm" class="max-w-full whitespace-normal text-left">
|
||||
{{ $step['label'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
<p class="text-xs leading-5 text-gray-600 dark:text-gray-300">
|
||||
{{ $step['description'] }}
|
||||
</p>
|
||||
@if ($step['is_current'])
|
||||
<div class="mt-auto text-xs font-medium text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.current_attention_point') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"
|
||||
data-testid="customer-review-acknowledgement-card"
|
||||
>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.review_acknowledgement') }}
|
||||
<div class="space-y-2">
|
||||
<h2 class="text-xl font-semibold text-gray-950 dark:text-white">
|
||||
{{ $readiness['question'] }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ $acknowledgement['reason'] }}
|
||||
</p>
|
||||
</div>
|
||||
<x-filament::badge :color="$acknowledgement['status_color']">
|
||||
{{ $acknowledgement['status_label'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-[minmax(0,1fr)_16rem]">
|
||||
<div class="flex h-full flex-col gap-2 rounded-lg border border-gray-200 bg-gray-50 p-4 text-sm dark:border-white/10 dark:bg-white/5">
|
||||
<div class="text-[0.7rem] font-semibold uppercase leading-4 tracking-wide text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.impact') }}
|
||||
</div>
|
||||
<p class="text-sm leading-6 text-gray-700 dark:text-gray-200">
|
||||
{{ $acknowledgement['impact'] }}
|
||||
<p class="max-w-3xl text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ $readiness['reason'] }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex h-full flex-col rounded-lg border border-gray-200 p-4 text-sm dark:border-white/10">
|
||||
<div class="text-[0.7rem] font-semibold uppercase leading-4 tracking-wide text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.basis') }}
|
||||
<div class="grid gap-3 md:grid-cols-[minmax(0,1fr)_16rem]">
|
||||
<div class="flex h-full flex-col gap-2 rounded-lg border border-gray-200 bg-gray-50 p-4 text-sm dark:border-white/10 dark:bg-white/5">
|
||||
<div class="text-[0.7rem] font-semibold uppercase leading-4 tracking-wide text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.impact') }}
|
||||
</div>
|
||||
<p class="text-sm leading-6 text-gray-700 dark:text-gray-200">
|
||||
{{ $readiness['impact'] }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 space-y-1.5">
|
||||
@foreach ($acknowledgement['basis'] as $basis)
|
||||
<div class="flex items-center justify-between gap-3 text-xs">
|
||||
<span class="text-gray-500 dark:text-gray-400">{{ $basis['label'] }}</span>
|
||||
<x-filament::badge :color="$basis['color']" size="sm">
|
||||
{{ $basis['value'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
@endforeach
|
||||
<div class="flex h-full flex-col rounded-lg border border-gray-200 p-4 text-sm dark:border-white/10">
|
||||
<div class="text-[0.7rem] font-semibold uppercase leading-4 tracking-wide text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.latest_released_review') }}
|
||||
</div>
|
||||
<div class="mt-2 font-medium leading-5 text-gray-950 dark:text-white">
|
||||
{{ $latest['environment_label'] }}
|
||||
</div>
|
||||
<div class="mt-1 text-xs leading-5 text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.published_date', ['date' => $latest['published_label']]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@if ($readiness['primary_action_url'])
|
||||
<x-filament::button
|
||||
tag="a"
|
||||
:href="$readiness['primary_action_url']"
|
||||
:icon="$readiness['primary_action_icon']"
|
||||
target="_blank"
|
||||
data-testid="customer-review-primary-action"
|
||||
>
|
||||
{{ $readiness['primary_action_label'] }}
|
||||
</x-filament::button>
|
||||
@endif
|
||||
|
||||
@if ($latest['secondary_action_url'])
|
||||
<x-filament::button
|
||||
tag="a"
|
||||
:href="$latest['secondary_action_url']"
|
||||
color="gray"
|
||||
:icon="$latest['secondary_action_icon']"
|
||||
data-testid="customer-review-secondary-action"
|
||||
>
|
||||
{{ $latest['secondary_action_label'] }}
|
||||
</x-filament::button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"
|
||||
data-testid="customer-review-acknowledgement-card"
|
||||
>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.review_acknowledgement') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ $acknowledgement['reason'] }}
|
||||
</p>
|
||||
</div>
|
||||
<x-filament::badge :color="$acknowledgement['status_color']">
|
||||
{{ $acknowledgement['status_label'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-[minmax(0,1fr)_16rem]">
|
||||
<div class="flex h-full flex-col gap-2 rounded-lg border border-gray-200 bg-gray-50 p-4 text-sm dark:border-white/10 dark:bg-white/5">
|
||||
<div class="text-[0.7rem] font-semibold uppercase leading-4 tracking-wide text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.impact') }}
|
||||
</div>
|
||||
<p class="text-sm leading-6 text-gray-700 dark:text-gray-200">
|
||||
{{ $acknowledgement['impact'] }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex h-full flex-col rounded-lg border border-gray-200 p-4 text-sm dark:border-white/10">
|
||||
<div class="text-[0.7rem] font-semibold uppercase leading-4 tracking-wide text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.basis') }}
|
||||
</div>
|
||||
|
||||
<div class="mt-2 space-y-1.5">
|
||||
@foreach ($acknowledgement['basis'] as $basis)
|
||||
<div class="flex items-center justify-between gap-3 text-xs">
|
||||
<span class="text-gray-500 dark:text-gray-400">{{ $basis['label'] }}</span>
|
||||
<x-filament::badge :color="$basis['color']" size="sm">
|
||||
{{ $basis['value'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($acknowledgement['acknowledged_at_label'] || $acknowledgement['acknowledged_by_label'])
|
||||
<dl class="grid gap-2 rounded-lg border border-dashed border-gray-200 bg-gray-50 p-3 text-xs dark:border-white/10 dark:bg-white/5 sm:grid-cols-2">
|
||||
@ -268,86 +224,162 @@ class="mt-2"
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"
|
||||
data-testid="customer-review-findings-summary"
|
||||
>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.findings_needing_attention') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ $findingPanel['summary'] }}
|
||||
</p>
|
||||
</div>
|
||||
<x-filament::badge :color="$findingPanel['status_color']">
|
||||
{{ $findingPanel['status_label'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2 sm:grid-cols-2 2xl:grid-cols-4">
|
||||
@foreach ($findingPanel['items'] as $item)
|
||||
<div class="flex items-center justify-between gap-3 rounded-lg border border-gray-200 px-3 py-2 text-xs dark:border-white/10">
|
||||
<span class="text-gray-500 dark:text-gray-400">{{ $item['label'] }}</span>
|
||||
<x-filament::badge :color="$item['color']" size="sm">
|
||||
{{ $item['value'] }}
|
||||
</x-filament::badge>
|
||||
<div
|
||||
class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"
|
||||
data-testid="customer-review-findings-summary"
|
||||
>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.findings_needing_attention') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ $findingPanel['summary'] }}
|
||||
</p>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<x-filament::badge :color="$findingPanel['status_color']">
|
||||
{{ $findingPanel['status_label'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.customer_safe_follow_ups') }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-2">
|
||||
@forelse ($followUps['entries'] as $followUp)
|
||||
<div class="rounded-lg border border-gray-200 p-3 text-sm dark:border-white/10">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="font-medium text-gray-950 dark:text-white">{{ $followUp['title'] }}</span>
|
||||
<x-filament::badge color="gray" size="sm">
|
||||
{{ $followUp['priority'] }}
|
||||
<div class="grid gap-2 sm:grid-cols-2 2xl:grid-cols-4">
|
||||
@foreach ($findingPanel['items'] as $item)
|
||||
<div class="flex items-center justify-between gap-3 rounded-lg border border-gray-200 px-3 py-2 text-xs dark:border-white/10">
|
||||
<span class="text-gray-500 dark:text-gray-400">{{ $item['label'] }}</span>
|
||||
<x-filament::badge :color="$item['color']" size="sm">
|
||||
{{ $item['value'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
<div class="mt-1 text-gray-600 dark:text-gray-300">
|
||||
{{ $followUp['summary'] }}
|
||||
</div>
|
||||
<div class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $followUp['proof'] }} · {{ $followUp['next_action'] }}
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ $followUps['empty_state'] }}
|
||||
</p>
|
||||
@endforelse
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="space-y-3" data-testid="customer-review-package-index">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.review_package_index') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ __('localization.review.review_package_index_description') }}
|
||||
</p>
|
||||
<section class="space-y-4" data-testid="customer-review-supporting-details">
|
||||
@php
|
||||
$currentReadinessStep = collect($readinessFlow)->firstWhere('is_current', true);
|
||||
@endphp
|
||||
|
||||
<details
|
||||
class="group rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900"
|
||||
data-testid="customer-review-readiness-flow"
|
||||
>
|
||||
<summary class="-m-2 cursor-pointer list-none rounded-lg p-2 marker:text-gray-400 transition hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 focus:ring-offset-white dark:hover:bg-white/5 dark:focus:ring-offset-gray-900">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.review_consumption_flow') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ __('localization.review.review_consumption_flow_description') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
@if ($currentReadinessStep !== null)
|
||||
<div class="shrink-0">
|
||||
<div class="text-[0.7rem] font-semibold uppercase leading-4 tracking-wide text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.current_attention_point') }}
|
||||
</div>
|
||||
<x-filament::badge :color="$currentReadinessStep['color']" size="sm" class="mt-1 max-w-full whitespace-normal text-left">
|
||||
{{ $currentReadinessStep['label'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<x-filament::icon
|
||||
icon="heroicon-m-chevron-down"
|
||||
class="mt-0.5 h-5 w-5 text-gray-400 transition-transform duration-150 group-open:rotate-180 dark:text-gray-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</summary>
|
||||
|
||||
<ul class="mt-4 divide-y divide-gray-100 text-sm dark:divide-white/10">
|
||||
@foreach ($readinessFlow as $step)
|
||||
<li
|
||||
class="py-3 first:pt-0 last:pb-0"
|
||||
data-testid="customer-review-readiness-step"
|
||||
data-step-label="{{ $step['title'] }}"
|
||||
data-step-state="{{ $step['label'] }}"
|
||||
data-step-current="{{ $step['is_current'] ? 'true' : 'false' }}"
|
||||
>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<div class="font-medium text-gray-950 dark:text-white">
|
||||
{{ $step['title'] }}
|
||||
</div>
|
||||
<p class="mt-1 text-xs leading-5 text-gray-600 dark:text-gray-300">
|
||||
{{ $step['description'] }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="shrink-0 text-right">
|
||||
<x-filament::badge :color="$step['color']" size="sm" class="max-w-full whitespace-normal text-left">
|
||||
{{ $step['label'] }}
|
||||
</x-filament::badge>
|
||||
@if ($step['is_current'])
|
||||
<div class="mt-1 text-xs font-medium text-gray-500 dark:text-gray-400">
|
||||
{{ __('localization.review.current_attention_point') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<div class="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-gray-900">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.customer_safe_follow_ups') }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-2">
|
||||
@forelse ($followUps['entries'] as $followUp)
|
||||
<div class="rounded-lg border border-gray-200 p-3 text-sm dark:border-white/10">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="font-medium text-gray-950 dark:text-white">{{ $followUp['title'] }}</span>
|
||||
<x-filament::badge color="gray" size="sm">
|
||||
{{ $followUp['priority'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
<div class="mt-1 text-gray-600 dark:text-gray-300">
|
||||
{{ $followUp['summary'] }}
|
||||
</div>
|
||||
<div class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $followUp['proof'] }} · {{ $followUp['next_action'] }}
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ $followUps['empty_state'] }}
|
||||
</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ $this->table }}
|
||||
</div>
|
||||
<div class="space-y-3" data-testid="customer-review-package-index">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-gray-950 dark:text-white">
|
||||
{{ __('localization.review.review_package_index') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ __('localization.review.review_package_index_description') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{ $this->table }}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<aside class="space-y-3 md:col-span-1 md:sticky md:top-4 md:self-start xl:col-span-4" data-testid="customer-review-evidence-aside">
|
||||
@ -497,32 +529,6 @@ class="mt-3 divide-y divide-gray-100 border-t border-gray-200 pt-1 dark:divide-w
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<details class="group mt-3 border-t border-gray-200 pt-3 dark:border-white/10">
|
||||
<summary class="cursor-pointer text-xs font-semibold uppercase text-gray-500 marker:text-gray-400 dark:text-gray-400">
|
||||
{{ __('localization.review.accepted_risk_records') }}
|
||||
</summary>
|
||||
|
||||
<div class="mt-2 space-y-2">
|
||||
@forelse ($acceptedRisks['entries'] as $risk)
|
||||
<div class="text-xs">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="font-medium text-gray-900 dark:text-gray-100">{{ $risk['title'] }}</span>
|
||||
<x-filament::badge color="gray" size="sm">
|
||||
{{ $risk['state_label'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
<div class="mt-1 leading-5 text-gray-500 dark:text-gray-400">
|
||||
{{ $risk['summary'] }}
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-xs leading-5 text-gray-500 dark:text-gray-400">
|
||||
{{ $acceptedRisks['empty_state'] }}
|
||||
</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-gray-200 bg-white p-3 shadow-sm dark:border-white/10 dark:bg-gray-900">
|
||||
|
||||
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\User;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use App\Support\Workspaces\WorkspaceContext;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
pest()->browser()->timeout(60_000);
|
||||
|
||||
beforeEach(function (): void {
|
||||
Storage::fake('exports');
|
||||
});
|
||||
|
||||
it('Spec344 smokes the customer review workspace hierarchy and density changes', function (): void {
|
||||
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
|
||||
$environment->forceFill(['name' => 'Spec344 Browser Workspace'])->save();
|
||||
|
||||
$snapshot = seedEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0);
|
||||
spec344BrowserCreatePublishedReview($environment, $user, $snapshot);
|
||||
|
||||
spec344AuthenticateBrowser($this, $user, $environment);
|
||||
|
||||
$page = visit(CustomerReviewWorkspace::environmentFilterUrl($environment))
|
||||
->resize(1236, 900)
|
||||
->waitForText(__('localization.review.review_acknowledgement'))
|
||||
->assertSee(__('localization.review.review_consumption_flow'))
|
||||
->assertNoJavaScriptErrors()
|
||||
->assertNoConsoleLogs();
|
||||
|
||||
$page->assertScript('document.querySelector("[data-testid=\"customer-review-readiness-flow\"]")?.open === false', true);
|
||||
$page->assertScript(
|
||||
'(() => { const ack = document.querySelector("[data-testid=\"customer-review-acknowledgement-card\"]"); const flow = document.querySelector("[data-testid=\"customer-review-readiness-flow\"]"); if (!ack || !flow) return false; return ack.getBoundingClientRect().top < flow.getBoundingClientRect().top; })()',
|
||||
true,
|
||||
);
|
||||
|
||||
$page->script('window.scrollTo(0, 0);');
|
||||
$page->screenshot(true, spec344BrowserScreenshotName('01-operator-summary'));
|
||||
spec344CopyBrowserScreenshot('01-operator-summary');
|
||||
|
||||
$page->script('document.querySelector("[data-testid=\"customer-review-acknowledgement-card\"]")?.scrollIntoView({ block: "start" });');
|
||||
$page->screenshot(true, spec344BrowserScreenshotName('02-acknowledgement-prominent'));
|
||||
spec344CopyBrowserScreenshot('02-acknowledgement-prominent');
|
||||
|
||||
$page->script('document.querySelector("[data-testid=\"customer-review-readiness-flow\"]")?.scrollIntoView({ block: "start" });');
|
||||
$page->assertScript('document.querySelector("[data-testid=\"customer-review-readiness-flow\"]")?.open === false', true);
|
||||
$page->screenshot(true, spec344BrowserScreenshotName('03-supporting-details-demoted'));
|
||||
spec344CopyBrowserScreenshot('03-supporting-details-demoted');
|
||||
|
||||
$page->script('document.querySelector("[data-testid=\"customer-review-diagnostics\"]")?.scrollIntoView({ block: "start" });');
|
||||
$page->assertScript('document.querySelector("[data-testid=\"customer-review-diagnostics\"]")?.open === false', true);
|
||||
$page->screenshot(true, spec344BrowserScreenshotName('04-diagnostics-collapsed'));
|
||||
spec344CopyBrowserScreenshot('04-diagnostics-collapsed');
|
||||
|
||||
$page->script("document.documentElement.classList.add('dark');");
|
||||
$page->script('window.scrollTo(0, 0);');
|
||||
$page->assertScript('document.documentElement.classList.contains("dark")', true);
|
||||
$page->screenshot(true, spec344BrowserScreenshotName('05-dark-mode'));
|
||||
spec344CopyBrowserScreenshot('05-dark-mode');
|
||||
});
|
||||
|
||||
function spec344BrowserScreenshotName(string $name): string
|
||||
{
|
||||
return 'spec344-customer-review-workspace-'.$name;
|
||||
}
|
||||
|
||||
function spec344CopyBrowserScreenshot(string $name): void
|
||||
{
|
||||
$filename = spec344BrowserScreenshotName($name).'.png';
|
||||
$source = base_path('tests/Browser/Screenshots/'.$filename);
|
||||
$targetDirectory = repo_path('specs/344-customer-review-workspace-density-audience-polish/artifacts/screenshots');
|
||||
|
||||
if (! is_dir($targetDirectory)) {
|
||||
@mkdir($targetDirectory, 0755, true);
|
||||
}
|
||||
|
||||
if (! is_file($source)) {
|
||||
$source = \Pest\Browser\Support\Screenshot::path($filename);
|
||||
}
|
||||
|
||||
for ($attempt = 0; $attempt < 10 && ! is_file($source); $attempt++) {
|
||||
usleep(100_000);
|
||||
clearstatcache(true, $source);
|
||||
}
|
||||
|
||||
if (is_file($source) && is_dir($targetDirectory) && is_writable($targetDirectory)) {
|
||||
@copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$name.'.png');
|
||||
}
|
||||
}
|
||||
|
||||
function spec344AuthenticateBrowser(mixed $test, User $user, ManagedEnvironment $environment): void
|
||||
{
|
||||
$workspaceId = (int) $environment->workspace_id;
|
||||
|
||||
$test->actingAs($user)->withSession([
|
||||
WorkspaceContext::SESSION_KEY => $workspaceId,
|
||||
WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
|
||||
(string) $workspaceId => (int) $environment->getKey(),
|
||||
],
|
||||
]);
|
||||
|
||||
session()->put(WorkspaceContext::SESSION_KEY, $workspaceId);
|
||||
session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
|
||||
(string) $workspaceId => (int) $environment->getKey(),
|
||||
]);
|
||||
|
||||
setAdminPanelContext($environment);
|
||||
}
|
||||
|
||||
function spec344BrowserCreatePublishedReview(ManagedEnvironment $environment, User $user, EvidenceSnapshot $snapshot): EnvironmentReview
|
||||
{
|
||||
$review = composeEnvironmentReviewForTest($environment, $user, $snapshot);
|
||||
|
||||
$review->forceFill([
|
||||
'status' => EnvironmentReviewStatus::Published->value,
|
||||
'generated_at' => now(),
|
||||
'published_at' => now(),
|
||||
'published_by_user_id' => (int) $user->getKey(),
|
||||
])->save();
|
||||
|
||||
return $review->refresh();
|
||||
}
|
||||
|
||||
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\User;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use App\Support\Workspaces\WorkspaceContext;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('prioritizes acknowledgement inside the operator summary before supporting details', function (): void {
|
||||
$environment = ManagedEnvironment::factory()->create(['name' => 'Spec344 Operator Summary']);
|
||||
[$user, $environment] = createUserWithTenant(tenant: $environment, role: 'owner', workspaceRole: 'manager');
|
||||
|
||||
$snapshot = seedEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0);
|
||||
spec344CreatePublishedReview($environment, $user, $snapshot);
|
||||
|
||||
$this->actingAs($user);
|
||||
setAdminPanelContext();
|
||||
session()->put(WorkspaceContext::SESSION_KEY, (int) $environment->workspace_id);
|
||||
|
||||
$component = Livewire::actingAs($user)->test(CustomerReviewWorkspace::class)
|
||||
->assertSee(__('localization.review.review_acknowledgement'));
|
||||
|
||||
$html = $component->html();
|
||||
|
||||
expect($html)->toContain('data-testid="customer-review-operator-summary"')
|
||||
->and($html)->toContain('data-testid="customer-review-supporting-details"');
|
||||
|
||||
$operatorSummaryStart = strpos($html, 'data-testid="customer-review-operator-summary"');
|
||||
$supportingDetailsStart = strpos($html, 'data-testid="customer-review-supporting-details"');
|
||||
|
||||
expect($operatorSummaryStart)->not->toBeFalse()
|
||||
->and($supportingDetailsStart)->not->toBeFalse()
|
||||
->and($operatorSummaryStart)->toBeLessThan($supportingDetailsStart);
|
||||
|
||||
$operatorSummaryHtml = substr($html, $operatorSummaryStart, $supportingDetailsStart - $operatorSummaryStart);
|
||||
|
||||
expect($operatorSummaryHtml)->toContain('data-testid="customer-review-decision-card"')
|
||||
->and($operatorSummaryHtml)->toContain('data-testid="customer-review-acknowledgement-card"')
|
||||
->and($operatorSummaryHtml)->toContain('data-testid="customer-review-findings-summary"');
|
||||
|
||||
$acknowledgementCardPosition = strpos($html, 'data-testid="customer-review-acknowledgement-card"');
|
||||
$readinessFlowPosition = strpos($html, 'data-testid="customer-review-readiness-flow"');
|
||||
|
||||
expect($acknowledgementCardPosition)->not->toBeFalse()
|
||||
->and($readinessFlowPosition)->not->toBeFalse()
|
||||
->and($acknowledgementCardPosition)->toBeLessThan($readinessFlowPosition);
|
||||
|
||||
expect($html)->toContain('data-testid="customer-review-diagnostics"')
|
||||
->and($html)->not->toContain('data-testid="customer-review-diagnostics" open');
|
||||
});
|
||||
|
||||
it('shows the environment filter chip when environment_id is present', function (): void {
|
||||
$environment = ManagedEnvironment::factory()->create(['name' => 'Spec344 Environment Filter']);
|
||||
[$user, $environment] = createUserWithTenant(tenant: $environment, role: 'owner', workspaceRole: 'manager');
|
||||
|
||||
$snapshot = seedEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0);
|
||||
spec344CreatePublishedReview($environment, $user, $snapshot);
|
||||
|
||||
$this->actingAs($user)
|
||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id])
|
||||
->get(CustomerReviewWorkspace::environmentFilterUrl($environment))
|
||||
->assertOk()
|
||||
->assertSee('Environment filter:')
|
||||
->assertSee('Spec344 Environment Filter')
|
||||
->assertDontSee('/admin/t', false)
|
||||
->assertDontSee('tenant_id=', false);
|
||||
});
|
||||
|
||||
it('disables the acknowledgement action when the actor lacks acknowledge permissions', function (): void {
|
||||
$environment = ManagedEnvironment::factory()->create(['name' => 'Spec344 Ack Disabled']);
|
||||
[$publisher, $environment] = createUserWithTenant(tenant: $environment, role: 'owner', workspaceRole: 'manager');
|
||||
[$actor] = createUserWithTenant(tenant: $environment, role: 'readonly');
|
||||
|
||||
$snapshot = seedEnvironmentReviewEvidence($environment, findingCount: 0, driftCount: 0);
|
||||
spec344CreatePublishedReview($environment, $publisher, $snapshot);
|
||||
|
||||
$this->actingAs($actor);
|
||||
setAdminPanelContext();
|
||||
session()->put(WorkspaceContext::SESSION_KEY, (int) $environment->workspace_id);
|
||||
|
||||
$component = Livewire::actingAs($actor)->test(CustomerReviewWorkspace::class);
|
||||
$payload = $component->instance()->latestReviewConsumptionPayload();
|
||||
|
||||
expect(data_get($payload, 'acknowledgement.action_name'))->toBe('acknowledgeReview')
|
||||
->and(data_get($payload, 'acknowledgement.action_disabled'))->toBeTrue();
|
||||
});
|
||||
|
||||
function spec344CreatePublishedReview(ManagedEnvironment $environment, User $user, EvidenceSnapshot $snapshot): EnvironmentReview
|
||||
{
|
||||
$review = composeEnvironmentReviewForTest($environment, $user, $snapshot);
|
||||
|
||||
$review->forceFill([
|
||||
'status' => EnvironmentReviewStatus::Published->value,
|
||||
'generated_at' => now(),
|
||||
'published_at' => now(),
|
||||
'published_by_user_id' => (int) $user->getKey(),
|
||||
])->save();
|
||||
|
||||
return $review->refresh();
|
||||
}
|
||||
@ -15,9 +15,11 @@ ## First Five Seconds
|
||||
|
||||
This is the most important customer-safe productization candidate. The page should answer what the customer can trust, what changed, what risks are accepted, which evidence supports the state, and what should happen next.
|
||||
|
||||
Spec 344 tightens the hierarchy so the Operator Summary (decision + acknowledgement + findings signal) comes first, while the review consumption flow and proof panels remain available as supporting details.
|
||||
|
||||
## Productization Review
|
||||
|
||||
- Decision-first: strong candidate, needs final target hierarchy.
|
||||
- Decision-first: improved by explicit Operator Summary-first hierarchy.
|
||||
- Evidence-first: must anchor all claims to review/evidence artifacts.
|
||||
- Context: workspace-level customer view.
|
||||
- Customer/auditor safety: primary concern.
|
||||
@ -35,14 +37,14 @@ ## Scores
|
||||
|
||||
| IA | Density | User Clarity | Sellability | Disclosure | Hierarchy | DS Fit | A11y | Responsive | Components | UX Writing | Perf |
|
||||
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| 3 | 3 | 3 | 5 | 3 | 3 | 4 | 3 | 3 | 4 | 3 | 4 |
|
||||
| 3 | 4 | 4 | 5 | 3 | 4 | 4 | 3 | 3 | 4 | 3 | 4 |
|
||||
|
||||
## Top Issues
|
||||
|
||||
1. Acknowledgement copy must remain customer-safe and explicitly non-legal (no compliance certification semantics).
|
||||
2. Evidence and accepted-risk meaning should be visible without raw diagnostics.
|
||||
3. Requires individual target mockup, not only cleanup.
|
||||
3. Sidebar proof panels can still compete visually with the main decision flow; keep them secondary and avoid duplicating “ready/available” signals at equal weight.
|
||||
|
||||
## Target Direction
|
||||
|
||||
P0 individual target mockup and follow-up implementation wave. This page is a core sellability surface.
|
||||
Spec 344 implements the first density/hierarchy polish wave. If the surface still feels too dense after real operator use, follow up with a targeted mockup and a second, narrower polish pass rather than adding new workflow surfaces.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 282 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 273 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 269 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 269 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 284 KiB |
@ -0,0 +1,48 @@
|
||||
# Specification Quality Checklist: Spec 344 - Customer Review Workspace Density & Audience Mode Polish
|
||||
|
||||
**Purpose**: Validate Spec 344 preparation completeness before implementation.
|
||||
**Created**: 2026-06-01
|
||||
**Feature**: `specs/344-customer-review-workspace-density-audience-polish/spec.md`
|
||||
|
||||
## Candidate Selection Gate
|
||||
|
||||
- [x] CHK001 The selected candidate is directly provided by the user as Spec 344 (follow-up after Specs 342–343).
|
||||
- [x] CHK002 The candidate aligns with current roadmap direction: customer-safe review consumption and operator workflow maturity on UI-038 (no portal/framework rewrite).
|
||||
- [x] CHK003 No existing `specs/344-*` package or `344-*` branch was found before this prep.
|
||||
- [x] CHK004 Related specs were checked for completed-spec signals and are treated as context only (337, 342, 343).
|
||||
- [x] CHK005 Close alternatives are deferred rather than hidden scope (governance inbox follow-through, localization adoption, provider readiness, PSA handoff).
|
||||
- [x] CHK006 Scope is narrowed to one strategic surface (`/admin/reviews/workspace`) and UI-only hierarchy changes.
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] CHK007 `spec.md` defines problem, user value, functional requirements, non-goals, acceptance criteria, assumptions, risks, and open questions.
|
||||
- [x] CHK008 `plan.md` lists likely affected repo surfaces and preserves repo-truth-first execution.
|
||||
- [x] CHK009 `tasks.md` is ordered into small phases with explicit test/browser/screenshot/validation tasks.
|
||||
- [x] CHK010 No unresolved template placeholders remain in `spec.md`, `plan.md`, or `tasks.md`.
|
||||
|
||||
## Constitution And Scope
|
||||
|
||||
- [x] CHK011 Proportionality review is present and explicitly rejects new persistence, new status families, and new frameworks.
|
||||
- [x] CHK012 Workspace/environment isolation boundaries and deny-as-not-found posture are preserved (no authorization weakening).
|
||||
- [x] CHK013 UI Surface Impact and UI/Productization Coverage are completed for UI-038 (strategic surface).
|
||||
- [x] CHK014 Filament v5 / Livewire v4 posture, panel provider location, destructive-action confirmation rules, asset strategy, and testing plan are explicit.
|
||||
|
||||
## Plan Quality
|
||||
|
||||
- [x] CHK015 Plan sequencing is baseline inventory → hierarchy refactor → keep Spec 343 semantics → tests/browser → UI audit artifact decision.
|
||||
- [x] CHK016 Deployment/ops impact is explicit (no env/migrations/queues/scheduler/assets expected).
|
||||
- [x] CHK017 No Graph/provider calls during UI render are enforced by plan constraints.
|
||||
|
||||
## Task Quality
|
||||
|
||||
- [x] CHK018 Tasks include concrete repo surfaces and avoid inventing new runtime paths beyond UI-038 touch points.
|
||||
- [x] CHK019 Tasks include Feature/Livewire tests and one bounded Browser smoke (strategic surface).
|
||||
- [x] CHK020 Tasks include screenshot artifacts and “unreachable state” handling without faking backend truth.
|
||||
- [x] CHK021 Tasks explicitly forbid new domain model/persistence and forbid rewriting completed specs.
|
||||
|
||||
## Spec Readiness Gate
|
||||
|
||||
- [x] CHK022 `spec.md`, `plan.md`, and `tasks.md` exist.
|
||||
- [x] CHK023 Open questions do not block safe implementation.
|
||||
- [x] CHK024 Scope is bounded enough for a later implementation loop.
|
||||
- [x] CHK025 Result: ready for implementation loop.
|
||||
@ -0,0 +1,96 @@
|
||||
# Implementation Plan: Spec 344 - Customer Review Workspace Density & Audience Mode Polish
|
||||
|
||||
**Branch**: `344-customer-review-workspace-density-audience-polish` | **Date**: 2026-06-01 | **Spec**: `specs/344-customer-review-workspace-density-audience-polish/spec.md`
|
||||
**Input**: User-provided Spec 344 draft (Codex attachment `pasted-text.txt`) + repo truth from Specs 337, 342, 343.
|
||||
|
||||
## Summary
|
||||
|
||||
Improve the Customer Review Workspace (UI-038) as a daily MSP/operator surface by reducing density and restoring decision hierarchy:
|
||||
|
||||
1) **Operator Summary first**: shareability + acknowledgement + primary next action + risk/findings signal.
|
||||
2) **Supporting details second**: evidence/proof path + review pack state + accepted risks + diagnostics + package index.
|
||||
|
||||
This slice is UI/productization polish only:
|
||||
|
||||
- preserve all semantics from Specs 342–343
|
||||
- no new persistence or status families
|
||||
- no new routes or navigation changes
|
||||
- no Graph/provider calls during render
|
||||
|
||||
## Technical Context
|
||||
|
||||
- **Language/Version**: PHP 8.4.15, Laravel 12.x.
|
||||
- **UI**: Filament v5 + Livewire v4 (no Livewire v3 APIs).
|
||||
- **Panel providers**: remain registered in `apps/platform/bootstrap/providers.php` (no changes expected).
|
||||
- **Storage**: PostgreSQL (no migrations expected).
|
||||
- **Testing**: Pest Feature/Livewire tests + one bounded Pest Browser smoke test for this strategic surface.
|
||||
- **Assets**: no new global assets expected; prefer Filament primitives and Tailwind utilities already in the stack.
|
||||
|
||||
## UI / Surface Guardrail Plan
|
||||
|
||||
- **Guardrail scope**: material change to an existing strategic customer-safe review surface (UI-038).
|
||||
- **Primary surface**:
|
||||
- `apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php`
|
||||
- `apps/platform/resources/views/filament/pages/reviews/customer-review-workspace.blade.php`
|
||||
- **New/changed surface elements**:
|
||||
- explicit “Operator Summary” zone (layout + grouping)
|
||||
- reordering so acknowledgement sits inside the primary decision area when required
|
||||
- de-duplication and compression of “ready/available/no action needed” signals
|
||||
- **Disclosure defaults**: diagnostics remain collapsed/secondary and capability-gated (`support_diagnostics.view`).
|
||||
- **One primary next action**: preserve exactly one dominant next action in the Operator Summary; demote secondary navigation/inspect links.
|
||||
- **UI audit artifacts**: decide post-diff whether `docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md` needs updating; route inventory entry (UI-038) should remain stable.
|
||||
|
||||
## Repo-Truth-First Execution Rule
|
||||
|
||||
Before any refactor, confirm the current runtime surface inventory:
|
||||
|
||||
- which cards/sections exist today and what they mean (no invented semantics)
|
||||
- where acknowledgement, accepted-risk, evidence path, review pack status, disclosure rules, and diagnostics currently render
|
||||
- which sections are redundant “green noise” vs required evidence/proof state
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1 — Baseline Inventory + Before/After Proof
|
||||
|
||||
- Capture baseline screenshots for the current hierarchy (pre-change).
|
||||
- Record a short inventory note in the PR description (later), not as a new framework or test snapshot.
|
||||
|
||||
### Phase 2 — Layout/Hiearchy Refactor (UI-only)
|
||||
|
||||
- Implement the two-layer structure: Operator Summary vs Supporting Details.
|
||||
- Move acknowledgement into the Operator Summary hierarchy when required.
|
||||
- Group or demote readiness-only cards so they do not compete with the pending action.
|
||||
- Preserve environment filter semantics and visibility (local page filter, not global context).
|
||||
- Preserve customer-safe wording and avoid “legal/compliance certification” phrasing.
|
||||
|
||||
### Phase 3 — Preserve Spec 343 Semantics
|
||||
|
||||
- Acknowledgement remains:
|
||||
- capability-gated
|
||||
- confirmation-gated
|
||||
- auditable
|
||||
- behavior-identical (only placement changes)
|
||||
|
||||
### Phase 4 — Tests (Pest)
|
||||
|
||||
- Keep Spec 343 tests passing; update only assertions that intentionally depend on hierarchy markers.
|
||||
- Add Spec 344 Feature/Livewire tests focusing on:
|
||||
- Operator Summary markers exist
|
||||
- acknowledgement action visibility in the primary zone when required
|
||||
- diagnostics remain capability-gated and collapsed/secondary
|
||||
- environment filter remains visible and page-local
|
||||
- Add one Spec 344 Browser smoke to prove scan-first hierarchy + screenshots.
|
||||
|
||||
### Phase 5 — UI Coverage Artifacts (post-diff decision)
|
||||
|
||||
- If the layout materially changes what the UI-006 page report describes, update:
|
||||
- `docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md`
|
||||
- Otherwise record a concise “no update needed” note in the active feature PR close-out entry.
|
||||
|
||||
## Deployment / Ops Impact
|
||||
|
||||
- **Env vars**: none.
|
||||
- **Migrations**: none expected.
|
||||
- **Queues/scheduler**: none.
|
||||
- **Storage/volumes**: none.
|
||||
- **Filament assets**: not expected; if any registered assets are introduced, include `cd apps/platform && php artisan filament:assets` in deployment/release steps.
|
||||
@ -0,0 +1,195 @@
|
||||
# Feature Specification: Spec 344 - Customer Review Workspace Density & Audience Mode Polish
|
||||
|
||||
**Feature Branch**: `344-customer-review-workspace-density-audience-polish`
|
||||
**Created**: 2026-06-01
|
||||
**Status**: Draft
|
||||
**Type**: Productization / UX hierarchy / MSP operator workflow polish
|
||||
**Runtime posture**: UI/productization polish only. Preserve Spec 343 acknowledgement behavior and Customer Review Workspace semantics. Do not introduce new domain models, persisted truth, or status families unless repo truth proves a minimal derived view state is unavoidable (derived-only, not persisted).
|
||||
**Input**: User-provided Spec 344 draft (Codex attachment `pasted-text.txt`) + repo truth from Specs 337, 342, 343 + current `CustomerReviewWorkspace` UI.
|
||||
|
||||
## Spec Candidate Check *(mandatory — SPEC-GATE-001)*
|
||||
|
||||
- **Problem**: The Customer Review Workspace (UI-038) is now functionally strong (Specs 342–343), but visually dense. Operator decision state, customer-safe sharing state, acknowledgement state, evidence path, review pack state, accepted-risk state, disclosure rules, diagnostics, and package index compete at equal weight.
|
||||
- **Today's failure**: An operator must scan too many regions before answering the first questions: “Is this review shareable?”, “What action is required now?”, “Is anything risky or blocking?”, “Where is the evidence/proof path?”, “What details can I inspect if needed?”
|
||||
- **User-visible improvement**: The first screen becomes scan-first and decision-first: one Operator Summary zone with shareability + acknowledgement + primary next action + risk/findings signal, and a clear Supporting Details layer for proof/diagnostics/index.
|
||||
- **Smallest enterprise-capable version**: Refactor only the existing `/admin/reviews/workspace` layout and hierarchy. Preserve all existing truth sources and actions; change ordering, grouping, and repetition only.
|
||||
- **Explicit non-goals**: No new customer portal, no new persisted view state, no new domain model, no new review pack format, no new evidence generation, no new accepted-risk lifecycle semantics, no new OperationRun types, no new navigation/shell redesign, no localization project, no “legal sign-off” semantics.
|
||||
- **Permanent complexity imported**: Targeted UI refactor on one page + small presenter extraction only if needed to prevent duplicated logic, plus focused Feature/Livewire tests and one bounded Browser smoke + screenshots. No migrations, no packages, no new global UI framework.
|
||||
- **Why now**: Spec 343 added acknowledgement and tightened accepted-risk visibility; the page is v1-usable but now too dense for daily MSP/operator workflows. Productization polish improves operator velocity and reduces false calmness by making the next action unavoidable.
|
||||
- **Why not local**: Small cosmetic changes cannot fix the decision hierarchy problem; this must be an intentional “Operator Summary first” re-layout to prevent drift and duplicated truth across cards.
|
||||
- **Approval class**: Workflow Compression.
|
||||
- **Red flags triggered**: Customer-safe strategic surface changes and risk of “false calmness” if hierarchy is wrong. Defense: preserve truth sources and semantics; keep diagnostics/proof as supporting details; require browser screenshots + targeted tests.
|
||||
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexität: 1 | Produktnähe: 2 | Wiederverwendung: 1 | **Gesamt: 10/12**
|
||||
- **Decision**: approve.
|
||||
|
||||
## Candidate Source And Completed-Spec Guardrail
|
||||
|
||||
- **Candidate source**: Directly user-provided as “Spec 344 — Customer Review Workspace Density & Audience Mode Polish” as the next slice after Spec 343.
|
||||
- **Completed-spec check**: No `specs/344-*` package existed before this prep. Related Specs 337, 342, and 343 contain completed-task, validation, and/or close-out signals and are treated as historical context only (do not rewrite or normalize them).
|
||||
- **Roadmap alignment**: Stays inside the “Customer Review Workspace v1 completion / productization” lane by improving operator decision hierarchy and customer-safe disclosure without creating a parallel customer portal.
|
||||
- **Close alternatives deferred**:
|
||||
- Decision-based Governance Inbox follow-through (separate strategic workflow surface; defer to keep this slice UI-only on UI-038).
|
||||
- Customer-facing localization adoption (needs stable hierarchy first).
|
||||
- Provider readiness / monitoring maturity (unrelated).
|
||||
- External support desk / PSA handoff (unrelated).
|
||||
|
||||
## Spec Scope Fields *(mandatory)*
|
||||
|
||||
- **Scope**: workspace hub (existing strategic surface UI-038) with page-level `environment_id` filter.
|
||||
- **Primary Routes**:
|
||||
- `/admin/reviews/workspace` (Customer Review Workspace)
|
||||
- **Data Ownership**: no new persistence; no migrations.
|
||||
- **RBAC**:
|
||||
- Preserve existing membership + capability gates for view and for acknowledgement write (Spec 343).
|
||||
- Diagnostics remain capability-gated (`support_diagnostics.view`).
|
||||
|
||||
## UI Surface Impact *(mandatory — UI-COV-001)*
|
||||
|
||||
- [ ] No UI surface impact
|
||||
- [x] Existing page changed
|
||||
- [ ] New page/route added
|
||||
- [ ] Navigation changed
|
||||
- [ ] Filament panel/provider surface changed
|
||||
- [ ] New modal/drawer/wizard/action added
|
||||
- [x] New table/form/state added
|
||||
- [x] Customer-facing surface changed
|
||||
- [ ] Dangerous action changed
|
||||
- [x] Status/evidence/review presentation changed
|
||||
- [x] Workspace/environment context presentation changed
|
||||
|
||||
## UI/Productization Coverage *(mandatory)*
|
||||
|
||||
- **Route/page/surface**: `/admin/reviews/workspace` (`apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php` + `apps/platform/resources/views/filament/pages/reviews/customer-review-workspace.blade.php`)
|
||||
- **Current page archetype**: `docs/ui-ux-enterprise-audit/route-inventory.md` → UI-038 (Strategic Surface, repo-verified)
|
||||
- **Design depth**: Strategic Surface (operator/MSP surface that produces customer-safe output)
|
||||
- **Repo-truth level**: repo-verified surface; no new truth sources
|
||||
- **Existing pattern reused**: Spec 342 decision-first hierarchy + Spec 343 acknowledgement card/action + existing customer-safe disclosure + existing evidence/proof layout primitives
|
||||
- **New pattern required**: none; only regrouping and de-duplication + one explicit “Operator Summary” layer marker
|
||||
- **Screenshot required**: yes (`specs/344-customer-review-workspace-density-audience-polish/artifacts/screenshots/`)
|
||||
- **Customer-safe review required**: yes (copy + disclosure + prevent false “ready/available” duplication)
|
||||
- **Dangerous-action review required**: no (no new destructive/high-impact actions; acknowledgement remains confirmed + capability-gated per Spec 343)
|
||||
- **Coverage artifacts**: decide post-diff whether `docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md` needs an update; route inventory entry should remain stable.
|
||||
|
||||
## Cross-Cutting / Shared Pattern Reuse *(mandatory)*
|
||||
|
||||
- **Cross-cutting feature?**: yes.
|
||||
- **Interaction classes**: status messaging, action hierarchy, proof/evidence disclosure, diagnostics collapse, accepted-risk summary.
|
||||
- **Shared paths reused**:
|
||||
- Existing customer-safe disclosure language and copy discipline from Specs 342–343 (no legal/compliance certification wording).
|
||||
- Existing badge/status primitives (no ad-hoc “ready/available” duplication).
|
||||
- Existing acknowledgement action semantics from Spec 343 (capability + confirmation + audit).
|
||||
- **Deviations**: none intended. If a new presenter/state helper is introduced to reduce duplicated UI logic, it must remain page-local and derived-only.
|
||||
|
||||
## OperationRun UX Impact *(mandatory)*
|
||||
|
||||
N/A - no OperationRun creation/completion/dedupe semantics are introduced or changed in this slice.
|
||||
|
||||
## Provider Boundary / Platform Core Check *(mandatory)*
|
||||
|
||||
N/A - no provider/platform shared boundary is changed.
|
||||
|
||||
## Proportionality Review *(mandatory when structural complexity is introduced)*
|
||||
|
||||
- **New source of truth?**: no.
|
||||
- **New persisted entity/table/artifact?**: no.
|
||||
- **New abstraction?**: no new framework. Optional: extract a page-local derived “summary view state” helper only if it reduces duplicated truth and keeps copy/status consistent.
|
||||
- **New enum/status family?**: no. All displayed states remain derived from existing review/pack/evidence/acknowledgement/accepted-risk truth.
|
||||
- **Alternative intentionally rejected**: audience-mode toggle framework, separate customer portal page, new review readiness engine, new accepted-risk workflow surface.
|
||||
|
||||
## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)*
|
||||
|
||||
- **Test purpose / classification**: Feature/Livewire for action visibility + RBAC + disclosure defaults; Browser for scan-first hierarchy and “acknowledgement is visible before supporting details” on a strategic surface.
|
||||
- **Validation lane(s)**: confidence + browser.
|
||||
- **Planned validation commands** (later implementation):
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test tests/Feature/Filament/Spec343CustomerReviewAttestationAcceptedRiskTest.php --compact`
|
||||
- `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec343CustomerReviewAttestationAcceptedRiskSmokeTest.php --compact`
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test tests/Feature/Filament/Spec344CustomerReviewWorkspaceDensityTest.php --compact`
|
||||
- `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec344CustomerReviewWorkspaceDensitySmokeTest.php --compact`
|
||||
- `cd apps/platform && ./vendor/bin/sail pint --dirty`
|
||||
- `git diff --check`
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 — Understand the main decision quickly (Priority: P1)
|
||||
|
||||
An MSP/operator opens Customer Review Workspace and immediately sees shareability status, acknowledgement status, and the one primary next action without scanning multiple equal-weight cards.
|
||||
|
||||
**Independent proof**: browser smoke + screenshots showing the Operator Summary zone in realistic fixtures.
|
||||
|
||||
### User Story 2 — Acknowledgement appears before supporting details (Priority: P1)
|
||||
|
||||
If acknowledgement is required, it appears in the primary decision area (or directly beneath it) and never below secondary proof/flow sections.
|
||||
|
||||
**Independent proof**: Livewire test asserts acknowledgement action is present and visible in the Operator Summary; browser smoke confirms hierarchy.
|
||||
|
||||
### User Story 3 — Supporting details remain available but secondary (Priority: P2)
|
||||
|
||||
Evidence path, review pack state, accepted-risk detail lists, disclosure rules, diagnostics, and the package index remain accessible but are visually secondary and/or progressively disclosed.
|
||||
|
||||
**Independent proof**: browser smoke shows details as secondary/collapsed; Livewire test confirms diagnostics remain capability-gated.
|
||||
|
||||
## Functional Requirements
|
||||
|
||||
- **FR-001**: The page must have two clear layers:
|
||||
1) **Operator Summary** (decision + acknowledgement + risks/findings + primary next action)
|
||||
2) **Supporting Details** (proof/evidence path + pack state + accepted risks + diagnostics + index)
|
||||
- **FR-002**: Acknowledgement must appear in (1) when required.
|
||||
- **FR-003**: Repetitive “ready/available/no action needed” blocks must be reduced, grouped, or demoted so they do not compete with the true pending action.
|
||||
- **FR-004**: Accepted-risk and findings signals must not be duplicated in multiple equal-weight regions without distinct purpose.
|
||||
- **FR-005**: Diagnostics remain collapsed/secondary by default and capability-gated.
|
||||
- **FR-006**: Environment filter remains a local page filter and stays visible; no new “topbar-as-filter” guidance is introduced.
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
- Page render remains DB-only: no Graph/provider calls during UI render.
|
||||
- Preserve Filament v5 + Livewire v4.0+ compliance.
|
||||
- Keep panel provider registration unchanged in `apps/platform/bootstrap/providers.php`.
|
||||
- Avoid new frontend assets; if any asset registration becomes necessary, document `php artisan filament:assets`.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- No new portal routes, no audience-mode toggle framework, no new persisted preferences.
|
||||
- No new review readiness engine, no new accepted-risk workflow surface, no lifecycle or taxonomy rewrite.
|
||||
- No changes to acknowledgement semantics introduced by Spec 343 (capability/confirmation/audit behavior stays intact).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] AC-001 Operator Summary answers (in order): shareability, acknowledgement, primary next action, blockers/risks, and latest review context.
|
||||
- [ ] AC-002 If acknowledgement is required, the action is visible before supporting details (no scroll needed).
|
||||
- [ ] AC-003 Duplicate “ready/available/no action needed” cards are reduced or grouped so the pending action stands out.
|
||||
- [ ] AC-004 Evidence path remains visible but secondary; diagnostics remain collapsed/secondary.
|
||||
- [ ] AC-005 Environment filter remains visible and local; the page does not imply global context.
|
||||
- [ ] AC-006 Spec 343 acknowledgement behavior is preserved end-to-end.
|
||||
- [ ] AC-007 Tests pass (targeted Spec 343 + Spec 344 feature + browser lanes).
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- An operator can answer “what do I do now?” within a few seconds on first load.
|
||||
- Customer-safe claims remain truthful and do not become “green noise”.
|
||||
- Supporting proof paths remain accessible without competing as primary decision content.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The current strategic surface remains `UI-038 /admin/reviews/workspace` and uses the existing page class + Blade view.
|
||||
- Spec 343 acknowledgement is already repo-real and covered by targeted tests.
|
||||
- No additional backend truth is needed to improve hierarchy (UI-only slice).
|
||||
|
||||
## Open Questions
|
||||
|
||||
- OQ-001: Should the “Supporting Details” layer be purely collapsed-by-default, or should it remain visible but visually demoted? (Decision belongs to implementation with screenshots; either is acceptable if diagnostics remain secondary.)
|
||||
|
||||
## Spec Artifacts *(required for this package)*
|
||||
|
||||
- `specs/344-customer-review-workspace-density-audience-polish/spec.md`
|
||||
- `specs/344-customer-review-workspace-density-audience-polish/plan.md`
|
||||
- `specs/344-customer-review-workspace-density-audience-polish/tasks.md`
|
||||
- `specs/344-customer-review-workspace-density-audience-polish/checklists/requirements.md`
|
||||
- `specs/344-customer-review-workspace-density-audience-polish/artifacts/screenshots/`
|
||||
|
||||
## Follow-Up Spec Candidates
|
||||
|
||||
- Decision-based Governance Inbox follow-through (separate surface; keep numbering flexible).
|
||||
- Customer Review External Portal v1 (separate customer-facing surface, not the operator workspace).
|
||||
- Customer Review Mode Toggle (only if one page cannot serve operator vs customer-summary needs).
|
||||
- Review Package Index productization (timeline/history focus).
|
||||
- Accepted Risk lifecycle detail surface (only if accepted-risk records need their own workflow).
|
||||
@ -0,0 +1,78 @@
|
||||
# Tasks: Spec 344 - Customer Review Workspace Density & Audience Mode Polish
|
||||
|
||||
**Branch**: `344-customer-review-workspace-density-audience-polish` | **Date**: 2026-06-01
|
||||
**Spec**: `specs/344-customer-review-workspace-density-audience-polish/spec.md`
|
||||
**Plan**: `specs/344-customer-review-workspace-density-audience-polish/plan.md`
|
||||
|
||||
## Test Governance (TEST-GOV-001)
|
||||
|
||||
- **Test purpose / classification**: Feature/Livewire + Browser (strategic surface UI-038).
|
||||
- **Validation lanes**: confidence + browser.
|
||||
- **Why sufficient**: this slice changes scan-first hierarchy on a strategic surface; browser proof is required while Feature/Livewire tests protect RBAC + disclosure defaults + action availability.
|
||||
|
||||
## Phase 0 — Guardrails + Baseline
|
||||
|
||||
- [x] T010 Confirm this slice is UI-only: no persistence, no new status family, no new routes.
|
||||
- [x] T011 Capture representative screenshots for UI-038 hierarchy under `specs/344-customer-review-workspace-density-audience-polish/artifacts/screenshots/` (pre-change baseline optional; post-change required).
|
||||
- [x] T012 Re-read Specs 342–343 close-out signals to avoid reopening semantics and to preserve acknowledgement behavior.
|
||||
|
||||
## Phase 1 — Repo-Truth Inventory (before code changes)
|
||||
|
||||
- [x] T020 Inventory current visible regions/cards on Customer Review Workspace and identify:
|
||||
- which regions are true blockers vs redundant “green noise”
|
||||
- which regions are operator decision vs supporting proof vs diagnostics/support
|
||||
- [x] T021 Record the intended before/after hierarchy in the PR description (later) and keep the change bounded to UI-038.
|
||||
|
||||
## Phase 2 — UI Refactor (Customer Review Workspace)
|
||||
|
||||
- [x] T030 Implement an explicit **Operator Summary** zone on `/admin/reviews/workspace`.
|
||||
- [x] T031 Move acknowledgement (Spec 343) into the primary decision hierarchy when required (no behavior change; placement only).
|
||||
- [x] T032 Reduce or group duplicate “ready/available/no action needed” blocks so pending actions dominate.
|
||||
- [x] T033 Keep Evidence Path visible but secondary; avoid competing primary card weight.
|
||||
- [x] T034 Keep diagnostics collapsed/secondary and capability-gated (`support_diagnostics.view`).
|
||||
- [x] T035 Keep `environment_id` filter visible and page-local; avoid “topbar as filter” copy or implied global context.
|
||||
|
||||
## Phase 3 — Feature/Livewire Tests (Pest)
|
||||
|
||||
- [x] T040 Add `apps/platform/tests/Feature/Filament/Spec344CustomerReviewWorkspaceDensityTest.php` covering:
|
||||
- Operator Summary markers present
|
||||
- acknowledgement action visible in the summary zone when required (and disabled when unauthorized)
|
||||
- diagnostics remain capability-gated and not default-prominent
|
||||
- environment filter visibility and canonical query behavior
|
||||
- [x] T041 Keep Spec 343 tests passing; update only assertions that intentionally depend on hierarchy markers.
|
||||
|
||||
## Phase 4 — Browser Smoke + Screenshots
|
||||
|
||||
- [x] T050 Add `apps/platform/tests/Browser/Spec344CustomerReviewWorkspaceDensitySmokeTest.php` proving:
|
||||
- Operator Summary is first-screen
|
||||
- acknowledgement appears before supporting details when required
|
||||
- diagnostics are collapsed/secondary by default
|
||||
- [x] T051 Capture screenshots under `specs/344-customer-review-workspace-density-audience-polish/artifacts/screenshots/`:
|
||||
- `01-operator-summary.png`
|
||||
- `02-acknowledgement-prominent.png`
|
||||
- `03-supporting-details-demoted.png`
|
||||
- `04-diagnostics-collapsed.png`
|
||||
- `05-dark-mode.png` (if practical)
|
||||
- [x] T052 If a screenshot state is unreachable, document why in the spec package instead of inventing backend truth.
|
||||
|
||||
## Phase 5 — UI Coverage Artifacts (post-diff decision)
|
||||
|
||||
- [x] T060 Decide whether `docs/ui-ux-enterprise-audit/page-reports/ui-006-customer-review-workspace.md` requires an update due to hierarchy changes.
|
||||
- [x] T061 If required, update that page report; otherwise record a concise “no update needed” note in the active feature PR close-out entry.
|
||||
|
||||
## Phase 6 — Validation
|
||||
|
||||
- [x] T070 Run:
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test tests/Feature/Filament/Spec343CustomerReviewAttestationAcceptedRiskTest.php --compact`
|
||||
- `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec343CustomerReviewAttestationAcceptedRiskSmokeTest.php --compact`
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test tests/Feature/Filament/Spec344CustomerReviewWorkspaceDensityTest.php --compact`
|
||||
- `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec344CustomerReviewWorkspaceDensitySmokeTest.php --compact`
|
||||
- `cd apps/platform && ./vendor/bin/sail pint --dirty`
|
||||
- `git diff --check`
|
||||
|
||||
## Explicit Non-Goals
|
||||
|
||||
- [x] NT001 Do not introduce a new domain model, new persisted view state, or new status family.
|
||||
- [x] NT002 Do not change acknowledgement semantics (capability/confirmation/audit) from Spec 343.
|
||||
- [x] NT003 Do not add a customer portal, audience-mode toggle framework, or new navigation shell.
|
||||
- [x] NT004 Do not add new global frontend assets unless explicitly justified and documented.
|
||||
Loading…
Reference in New Issue
Block a user