Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m0s
Implemented the output contract and readiness semantics for review packs. Also added spec 348. Includes changes to ChooseEnvironment, CustomerReviewWorkspace, GenerateReviewPackJob and related blade views. Added comprehensive tests.
206 lines
14 KiB
PHP
206 lines
14 KiB
PHP
<x-filament-panels::page>
|
|
@php
|
|
$allTenants = $this->getTenants();
|
|
$tenants = $this->getVisibleTenants($allTenants);
|
|
$workspace = app(\App\Support\Workspaces\WorkspaceContext::class)->currentWorkspace();
|
|
$environmentCount = $allTenants->count();
|
|
$visibleEnvironmentCount = $tenants->count();
|
|
$hasSearch = trim($this->search) !== '';
|
|
@endphp
|
|
|
|
@if ($allTenants->isEmpty())
|
|
{{-- Empty state --}}
|
|
<div class="mx-auto max-w-md">
|
|
<div class="rounded-xl border border-gray-200 bg-white p-8 text-center shadow-sm dark:border-white/10 dark:bg-white/5">
|
|
@if ($workspace)
|
|
<div class="mb-5 inline-flex items-center gap-1.5 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-400">
|
|
<x-filament::icon icon="heroicon-m-building-office-2" class="h-3.5 w-3.5" />
|
|
{{ $workspace->name }}
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-primary-50 dark:bg-primary-950/30">
|
|
<x-filament::icon
|
|
icon="heroicon-o-server-stack"
|
|
class="h-7 w-7 text-primary-500 dark:text-primary-400"
|
|
/>
|
|
</div>
|
|
|
|
<h3 class="text-base font-semibold text-gray-900 dark:text-white">{{ __('localization.shell.no_active_environments') }}</h3>
|
|
<p class="mx-auto mt-2 max-w-xs text-sm text-gray-500 dark:text-gray-400">
|
|
{{ __('localization.shell.no_active_environments_description') }}
|
|
</p>
|
|
|
|
<div class="mt-6 flex flex-col items-center gap-3">
|
|
<x-filament::button
|
|
tag="a"
|
|
href="{{ $workspace ? route('admin.workspace.managed-environments.index', ['workspace' => $workspace]) : route('admin.onboarding') }}"
|
|
icon="heroicon-m-arrow-right"
|
|
size="lg"
|
|
>
|
|
{{ __('localization.shell.view_managed_environments') }}
|
|
</x-filament::button>
|
|
|
|
<a href="{{ route('filament.admin.pages.choose-workspace') }}"
|
|
class="inline-flex items-center gap-1.5 text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
|
<x-filament::icon icon="heroicon-m-arrows-right-left" class="h-4 w-4" />
|
|
{{ __('localization.shell.switch_workspace') }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@else
|
|
{{-- ManagedEnvironment list --}}
|
|
<div class="mx-auto max-w-xl" data-testid="choose-environment-selector">
|
|
{{-- Header row --}}
|
|
<div class="mb-5 space-y-3">
|
|
<div class="flex min-w-0 flex-wrap items-center gap-2">
|
|
@if ($workspace)
|
|
<div class="inline-flex max-w-full min-w-0 items-center gap-1.5 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-400">
|
|
<x-filament::icon icon="heroicon-m-building-office-2" class="h-3.5 w-3.5 shrink-0" />
|
|
<span class="min-w-0 truncate" title="{{ $workspace->name }}">{{ $workspace->name }}</span>
|
|
</div>
|
|
@endif
|
|
<span class="text-sm text-gray-500 dark:text-gray-400">
|
|
{{ trans_choice('localization.shell.environment_count', $environmentCount, ['count' => $environmentCount]) }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
|
<a href="{{ route('admin.onboarding') }}"
|
|
data-testid="choose-environment-add"
|
|
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm font-medium text-gray-600 shadow-sm transition-colors hover:bg-gray-50 hover:text-gray-800 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:border-white/10 dark:bg-white/5 dark:text-gray-300 dark:hover:bg-white/10 dark:hover:text-white dark:focus:ring-offset-gray-900">
|
|
<x-filament::icon icon="heroicon-m-plus" class="h-4 w-4" />
|
|
{{ __('localization.shell.add_environment') }}
|
|
</a>
|
|
<a href="{{ route('filament.admin.pages.choose-workspace') }}"
|
|
data-testid="choose-environment-switch-workspace"
|
|
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm font-medium text-gray-600 shadow-sm transition-colors hover:bg-gray-50 hover:text-gray-800 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:border-white/10 dark:bg-white/5 dark:text-gray-300 dark:hover:bg-white/10 dark:hover:text-white dark:focus:ring-offset-gray-900">
|
|
<x-filament::icon icon="heroicon-m-arrows-right-left" class="h-4 w-4" />
|
|
{{ __('localization.shell.switch_workspace') }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">{{ __('localization.shell.choose_environment_description') }}</p>
|
|
<p class="mb-4 text-xs text-gray-500 dark:text-gray-400">{{ __('localization.shell.workspace_wide_available_without_environment') }}</p>
|
|
|
|
<div class="mb-4 space-y-2" data-testid="choose-environment-filter">
|
|
<label for="choose-environment-search" class="fi-sr-only">{{ __('localization.shell.search_environments') }}</label>
|
|
<x-filament::input.wrapper
|
|
:prefix-icon="\Filament\Support\Icons\Heroicon::MagnifyingGlass"
|
|
inline-prefix
|
|
class="w-full"
|
|
wire:target="search"
|
|
>
|
|
<x-filament::input
|
|
id="choose-environment-search"
|
|
type="search"
|
|
wire:model.live.debounce.250ms="search"
|
|
placeholder="{{ __('localization.shell.search_environments') }}"
|
|
data-testid="choose-environment-search"
|
|
inline-prefix
|
|
/>
|
|
</x-filament::input.wrapper>
|
|
|
|
@if ($hasSearch)
|
|
<div class="mt-2 flex flex-wrap items-center justify-between gap-2 text-xs text-gray-500 dark:text-gray-400">
|
|
<span>{{ __('localization.shell.environment_search_results_count', ['visible' => $visibleEnvironmentCount, 'total' => $environmentCount]) }}</span>
|
|
<button
|
|
type="button"
|
|
wire:click="$set('search', '')"
|
|
class="font-medium text-gray-600 transition hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:text-gray-300 dark:hover:text-white dark:focus:ring-offset-gray-900"
|
|
>
|
|
{{ __('localization.shell.clear_search') }}
|
|
</button>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- ManagedEnvironment cards --}}
|
|
@if ($tenants->isEmpty())
|
|
<div class="rounded-xl border border-gray-200 bg-white p-6 text-center shadow-sm dark:border-white/10 dark:bg-white/5" data-testid="choose-environment-no-results">
|
|
<h3 class="text-sm font-semibold text-gray-900 dark:text-white">{{ __('localization.shell.no_environment_search_results') }}</h3>
|
|
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">{{ __('localization.shell.no_environment_search_results_description') }}</p>
|
|
<button
|
|
type="button"
|
|
wire:click="$set('search', '')"
|
|
class="mt-4 inline-flex items-center gap-1.5 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm font-medium text-gray-600 shadow-sm transition hover:bg-gray-50 hover:text-gray-800 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:border-white/10 dark:bg-white/5 dark:text-gray-300 dark:hover:bg-white/10 dark:hover:text-white dark:focus:ring-offset-gray-900"
|
|
>
|
|
{{ __('localization.shell.clear_search') }}
|
|
</button>
|
|
</div>
|
|
@else
|
|
<div class="grid grid-cols-1 gap-3" data-testid="choose-environment-results">
|
|
@foreach ($tenants as $tenant)
|
|
@php
|
|
$presentation = $this->tenantLifecyclePresentation($tenant);
|
|
$badgeClasses = match ($presentation->badgeColor) {
|
|
'success' => 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-200',
|
|
'warning' => 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-500/20 dark:bg-amber-500/10 dark:text-amber-200',
|
|
'danger' => 'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-500/20 dark:bg-rose-500/10 dark:text-rose-200',
|
|
default => 'border-gray-200 bg-gray-100 text-gray-700 dark:border-white/10 dark:bg-white/10 dark:text-gray-300',
|
|
};
|
|
$environmentLabel = $tenant->environment && $tenant->environment !== 'managed_environment'
|
|
? strtoupper($tenant->environment)
|
|
: null;
|
|
@endphp
|
|
<button
|
|
type="button"
|
|
wire:key="tenant-{{ $tenant->id }}"
|
|
wire:click="selectEnvironment({{ (int) $tenant->id }})"
|
|
aria-label="{{ __('localization.shell.select_environment') }}: {{ $tenant->name }}"
|
|
data-testid="choose-environment-card"
|
|
class="group relative w-full overflow-hidden rounded-xl border border-gray-200 bg-white p-4 text-left shadow-sm transition-all duration-150 hover:border-gray-300 hover:shadow-md focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:border-white/10 dark:bg-white/5 dark:hover:border-white/20 dark:focus:ring-offset-gray-900"
|
|
>
|
|
{{-- Loading overlay --}}
|
|
<div wire:loading wire:target="selectEnvironment({{ (int) $tenant->id }})"
|
|
class="absolute inset-0 z-10 flex items-center justify-center rounded-xl bg-white/80 dark:bg-gray-900/80">
|
|
<x-filament::loading-indicator class="h-5 w-5 text-primary-500" />
|
|
</div>
|
|
|
|
<div class="flex min-w-0 items-start gap-3">
|
|
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-gray-100 transition-colors group-hover:bg-gray-200 dark:bg-white/10 dark:group-hover:bg-white/15">
|
|
<x-filament::icon
|
|
icon="heroicon-o-server-stack"
|
|
class="h-5 w-5 text-gray-500 group-hover:text-gray-600 dark:text-gray-400 dark:group-hover:text-gray-300"
|
|
/>
|
|
</div>
|
|
<div class="min-w-0 flex-1">
|
|
<div class="flex min-w-0 flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
|
|
<div class="min-w-0">
|
|
<h3 class="truncate text-sm font-semibold text-gray-900 dark:text-white">
|
|
{{ $tenant->name }}
|
|
</h3>
|
|
@if ($tenant->domain)
|
|
<p class="mt-0.5 truncate text-xs text-gray-500 dark:text-gray-400">
|
|
{{ $tenant->domain }}
|
|
</p>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="flex max-w-full shrink-0 flex-wrap items-center gap-2 sm:max-w-48 sm:justify-end">
|
|
@if ($environmentLabel)
|
|
<span class="inline-flex max-w-full items-center truncate rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600 dark:bg-white/10 dark:text-gray-400">
|
|
{{ $environmentLabel }}
|
|
</span>
|
|
@endif
|
|
<span class="inline-flex max-w-full items-center truncate rounded-full border px-2 py-0.5 text-xs font-medium {{ $badgeClasses }}">
|
|
{{ $presentation->label }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="mt-2 text-xs leading-5 text-gray-500 dark:text-gray-400">
|
|
{{ $presentation->shortDescription }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
</x-filament-panels::page>
|