TenantAtlas/apps/platform/resources/views/filament/partials/context-bar.blade.php
ahmido b159dacd36 feat: clean up legacy tenant environment context (#372)
## Summary
- remove legacy tenant-scoped routing and middleware paths in favor of the current environment/workspace context flow
- update Filament pages and resources to use the cleaned-up admin surface and environment filter context
- add the related spec 317 artifacts and targeted tests for environment filter state and legacy context cleanup

## Testing
- not run as part of this commit/push/PR workflow

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #372
2026-05-16 18:25:36 +00:00

229 lines
14 KiB
PHP

@php
use App\Filament\Pages\ChooseEnvironment;
use App\Filament\Pages\ChooseWorkspace;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Support\OperateHub\OperateHubShell;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
/** @var WorkspaceContext $workspaceContext */
$workspaceContext = app(WorkspaceContext::class);
$resolvedContext = app(OperateHubShell::class)->resolvedContext(request());
$workspace = $resolvedContext->workspace;
$user = auth()->user();
$environments = collect();
if ($user instanceof User && $workspace) {
$environments = collect($user->getTenants(Filament::getCurrentOrDefaultPanel()))
->filter(fn ($environment): bool => $environment instanceof ManagedEnvironment && (int) $environment->workspace_id === (int) $workspace->getKey())
->values();
}
$currentEnvironment = $resolvedContext->tenant;
$currentEnvironmentId = $currentEnvironment instanceof ManagedEnvironment ? (int) $currentEnvironment->getKey() : null;
$currentEnvironmentName = $currentEnvironment instanceof ManagedEnvironment ? $currentEnvironment->getFilamentName() : null;
$lastEnvironmentId = $workspaceContext->lastEnvironmentId(request());
$canClearEnvironmentContext = $currentEnvironment instanceof ManagedEnvironment || $lastEnvironmentId !== null;
@endphp
@php
$environmentLabel = $currentEnvironmentName ?? __('localization.shell.no_environment_selected');
$workspaceLabel = $workspace?->name ?? __('localization.shell.choose_workspace');
$hasActiveEnvironment = $currentEnvironmentName !== null;
$managedEnvironmentsUrl = $workspace
? route('admin.workspace.managed-environments.index', ['workspace' => $workspace])
: route('admin.onboarding');
$workspaceUrl = $workspace
? route('admin.home')
: ChooseWorkspace::getUrl(panel: 'admin');
$environmentTriggerLabel = $workspace ? $environmentLabel : __('localization.shell.choose_workspace');
$localePlane = 'admin';
@endphp
<div class="inline-flex items-center gap-0 rounded-lg border border-gray-200 bg-white text-sm dark:border-white/10 dark:bg-white/5">
{{-- Workspace label: standalone link --}}
<a
href="{{ $workspaceUrl }}"
wire:navigate
class="inline-flex items-center gap-1.5 rounded-l-lg px-2.5 py-1.5 font-medium text-gray-700 transition hover:bg-gray-50 hover:text-primary-600 dark:text-gray-200 dark:hover:bg-white/10 dark:hover:text-primary-400"
>
<x-filament::icon icon="heroicon-o-squares-2x2" class="h-4 w-4 text-gray-400 dark:text-gray-500" />
{{ $workspaceLabel }}
</a>
@if ($workspace)
<x-filament::icon icon="heroicon-m-chevron-right" class="h-3 w-3 shrink-0 text-gray-300 dark:text-gray-600" />
@endif
{{-- Dropdown trigger: environment label + chevron --}}
<x-filament::dropdown placement="bottom-start" teleport width="xs">
<x-slot name="trigger">
<button
type="button"
aria-label="{{ $workspace ? __('localization.shell.environment_scope') : __('localization.shell.select_environment') }}"
class="inline-flex items-center gap-1.5 rounded-r-lg px-2 py-1.5 transition hover:bg-gray-50 dark:hover:bg-white/10"
>
<span class="{{ $workspace && $hasActiveEnvironment ? 'font-medium text-primary-600 dark:text-primary-400' : 'text-gray-500 dark:text-gray-400' }}">
{{ $environmentTriggerLabel }}
</span>
<x-filament::icon icon="heroicon-m-chevron-down" class="h-3.5 w-3.5 text-gray-400 dark:text-gray-500" />
</button>
</x-slot>
<x-filament::dropdown.list>
<div class="space-y-3 px-3 py-2" x-data="{ query: '' }">
@if ($resolvedContext->showsRecoveryNotice())
<div class="rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200">
<div class="font-semibold">{{ __('localization.shell.context_unavailable') }}</div>
@if ($workspace)
<div>{{ __('localization.shell.context_unavailable_workspace') }}</div>
@else
<div>{{ __('localization.shell.context_unavailable_no_workspace') }}</div>
@endif
</div>
@endif
{{-- Workspace section --}}
<div class="space-y-1">
<div class="text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">
{{ __('localization.shell.workspace') }}
</div>
<div class="flex items-center justify-between rounded-lg bg-gray-50 px-3 py-2 dark:bg-white/5">
<div class="flex items-center gap-2">
<x-filament::icon icon="heroicon-o-squares-2x2" class="h-4 w-4 text-gray-500 dark:text-gray-400" />
<span class="text-sm font-medium text-gray-950 dark:text-white">{{ $workspaceLabel }}</span>
</div>
<a
href="{{ ChooseWorkspace::getUrl(panel: 'admin').'?choose=1' }}"
class="text-xs font-medium text-primary-600 hover:text-primary-500 dark:text-primary-400 dark:hover:text-primary-300"
>
{{ __('localization.shell.switch_workspace') }}
</a>
</div>
<a
href="{{ route('admin.home') }}"
class="flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-gray-700 transition hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-white/5"
>
<x-filament::icon icon="heroicon-o-home" class="h-4 w-4 text-gray-400 dark:text-gray-500" />
{{ __('localization.shell.workspace_home') }}
</a>
</div>
@if ($workspace)
<div class="border-t border-gray-200 dark:border-white/10"></div>
{{-- Managed Environment section --}}
<div class="space-y-2">
<div class="flex items-center justify-between">
<div class="text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">
{{ __('localization.shell.selected_environment') }}
</div>
</div>
@if ($resolvedContext->pageCategory->requiresExplicitEnvironment() && $hasActiveEnvironment)
<div class="space-y-2">
<div class="flex items-center gap-2 rounded-lg bg-primary-50 px-3 py-2 dark:bg-primary-950/30">
<x-filament::icon icon="heroicon-o-building-office-2" class="h-4 w-4 text-primary-600 dark:text-primary-400" />
<span class="text-sm font-medium text-primary-700 dark:text-primary-300">{{ $currentEnvironmentName }}</span>
<a
href="{{ ChooseEnvironment::getUrl(panel: 'admin') }}"
class="ml-auto text-xs font-medium text-primary-600 hover:text-primary-500 dark:text-primary-400 dark:hover:text-primary-300"
>
{{ __('localization.shell.switch_environment') }}
</a>
</div>
@if ($canClearEnvironmentContext)
<form method="POST" action="{{ route('admin.clear-environment-context') }}">
@csrf
<button type="submit" class="w-full rounded-lg px-3 py-1.5 text-left text-xs font-medium text-gray-500 transition hover:bg-gray-50 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-200">
{{ __('localization.shell.clear_environment_scope') }}
</button>
</form>
@endif
</div>
@else
@if ($environments->isEmpty())
<div class="space-y-2 rounded-lg border border-dashed border-gray-300 px-3 py-3 text-center text-xs text-gray-500 dark:border-gray-600 dark:text-gray-400">
<div>{{ __('localization.shell.no_active_environments') }}</div>
<a href="{{ $managedEnvironmentsUrl }}" class="inline-flex items-center gap-1 text-primary-600 hover:text-primary-500 dark:text-primary-400 dark:hover:text-primary-300">
<x-filament::icon icon="heroicon-o-arrow-top-right-on-square" class="h-3.5 w-3.5" />
{{ __('localization.shell.view_managed_environments') }}
</a>
</div>
@else
@if (! $hasActiveEnvironment)
<div class="rounded-lg bg-gray-50 px-3 py-2 text-xs text-gray-600 dark:bg-white/5 dark:text-gray-400">
{{ __('localization.shell.workspace_wide_available_without_environment') }}
</div>
@endif
<input
type="text"
class="fi-input fi-text-input w-full"
placeholder="{{ __('localization.shell.search_environments') }}"
x-model="query"
/>
<div class="max-h-48 overflow-auto rounded-lg border border-gray-200 dark:border-gray-700">
@foreach ($environments as $environment)
@php
$isActive = $currentEnvironmentId !== null && (int) $environment->getKey() === $currentEnvironmentId;
@endphp
<form method="POST" action="{{ route('admin.select-environment') }}">
@csrf
<input type="hidden" name="managed_environment_id" value="{{ (int) $environment->getKey() }}" />
<button
type="submit"
class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition {{ $isActive ? 'bg-primary-50 font-medium text-primary-700 dark:bg-primary-950/30 dark:text-primary-300' : 'text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-white/5' }}"
data-search="{{ (string) str($environment->getFilamentName())->lower() }}"
x-show="query === '' || ($el.dataset.search ?? '').includes(query.toLowerCase())"
>
@if ($isActive)
<x-filament::icon icon="heroicon-s-check-circle" class="h-4 w-4 shrink-0 text-primary-600 dark:text-primary-400" />
@else
<x-filament::icon icon="heroicon-o-building-office-2" class="h-4 w-4 shrink-0 text-gray-400 dark:text-gray-500" />
@endif
{{ $environment->getFilamentName() }}
</button>
</form>
@endforeach
</div>
@if ($canClearEnvironmentContext)
<form method="POST" action="{{ route('admin.clear-environment-context') }}">
@csrf
<button type="submit" class="w-full rounded-lg px-3 py-1.5 text-left text-xs font-medium text-gray-500 transition hover:bg-gray-50 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-200">
{{ __('localization.shell.clear_environment_scope') }}
</button>
</form>
@endif
@endif
@endif
</div>
@else
<div class="rounded-lg border border-dashed border-gray-300 px-3 py-3 text-center text-xs text-gray-500 dark:border-gray-600 dark:text-gray-400">
{{ __('localization.shell.choose_workspace_first') }}
</div>
@endif
</div>
</x-filament::dropdown.list>
</x-filament::dropdown>
@include('filament.partials.locale-switcher', ['plane' => $localePlane, 'showPreference' => true, 'embedded' => true])
</div>