TenantAtlas/apps/platform/app/Support/Navigation/WorkspaceSidebarNavigation.php
Ahmed Darrazi b0253cabc2
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m45s
feat: enforce workspace hub navigation context contract
2026-05-16 11:41:07 +02:00

173 lines
8.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support\Navigation;
use App\Filament\Pages\Governance\DecisionRegister;
use App\Filament\Pages\Governance\GovernanceInbox;
use App\Filament\Pages\Monitoring\Alerts;
use App\Filament\Pages\Monitoring\FindingExceptionsQueue;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Pages\Reviews\ReviewRegister;
use App\Filament\Pages\Settings\WorkspaceSettings;
use App\Filament\Pages\WorkspaceOverview;
use App\Filament\Resources\AlertDeliveryResource;
use App\Filament\Resources\AlertDestinationResource;
use App\Filament\Resources\AlertRuleResource;
use App\Filament\Resources\ProviderConnectionResource;
use App\Filament\Resources\Workspaces\WorkspaceResource;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Services\Auth\WorkspaceRoleCapabilityMap;
use App\Support\Auth\Capabilities;
use App\Support\OperationRunLinks;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Navigation\NavigationBuilder;
use Filament\Navigation\NavigationGroup;
use Filament\Navigation\NavigationItem;
final class WorkspaceSidebarNavigation
{
public function build(): NavigationBuilder
{
return app(NavigationBuilder::class)
->item(WorkspaceOverview::navigationItem())
->groups([
NavigationGroup::make(__('localization.navigation.monitoring'))
->items($this->visibleItems([
NavigationItem::make(FindingExceptionsQueue::getNavigationLabel())
->url(fn (): string => $this->workspaceHubUrl(FindingExceptionsQueue::getUrl(panel: 'admin')))
->icon(FindingExceptionsQueue::getNavigationIcon())
->visible(fn (): bool => FindingExceptionsQueue::canAccess()),
NavigationItem::make(__('localization.navigation.operations'))
->url(fn (): string => $this->workspaceHubUrl(OperationRunLinks::index()))
->icon('heroicon-o-queue-list')
->visible(fn (): bool => true),
NavigationItem::make(__('localization.navigation.alerts'))
->url(fn (): string => $this->workspaceHubUrl(route('filament.admin.alerts')))
->icon('heroicon-o-bell-alert')
->visible(fn (): bool => Alerts::canAccess())
->childItems($this->visibleItems([
NavigationItem::make(AlertDestinationResource::getNavigationLabel())
->url(fn (): string => $this->workspaceHubUrl(AlertDestinationResource::getUrl(panel: 'admin')))
->icon(AlertDestinationResource::getNavigationIcon())
->visible(fn (): bool => AlertDestinationResource::canViewAny()),
NavigationItem::make(AlertRuleResource::getNavigationLabel())
->url(fn (): string => $this->workspaceHubUrl(AlertRuleResource::getUrl(panel: 'admin')))
->icon(AlertRuleResource::getNavigationIcon())
->visible(fn (): bool => AlertRuleResource::canViewAny()),
NavigationItem::make(AlertDeliveryResource::getNavigationLabel())
->url(fn (): string => $this->workspaceHubUrl(AlertDeliveryResource::getUrl(panel: 'admin')))
->icon(AlertDeliveryResource::getNavigationIcon())
->visible(fn (): bool => AlertDeliveryResource::canViewAny()),
])),
NavigationItem::make(__('localization.navigation.audit_log'))
->url(fn (): string => $this->workspaceHubUrl(route('admin.monitoring.audit-log')))
->icon('heroicon-o-clipboard-document-list'),
])),
NavigationGroup::make(__('localization.review.reporting'))
->items($this->visibleItems([
NavigationItem::make(ReviewRegister::getNavigationLabel())
->url(fn (): string => $this->workspaceHubUrl(ReviewRegister::getUrl(panel: 'admin')))
->icon(ReviewRegister::getNavigationIcon()),
NavigationItem::make(CustomerReviewWorkspace::getNavigationLabel())
->url(fn (): string => $this->workspaceHubUrl(CustomerReviewWorkspace::getUrl(panel: 'admin')))
->icon(CustomerReviewWorkspace::getNavigationIcon()),
])),
NavigationGroup::make(__('localization.navigation.settings'))
->items($this->visibleItems([
NavigationItem::make(__('localization.navigation.manage_workspaces'))
->url(fn (): string => $this->workspaceHubUrl(route('filament.admin.resources.workspaces.index')))
->icon(WorkspaceResource::getNavigationIcon())
->visible(fn (): bool => $this->canManageWorkspaces()),
NavigationItem::make(__('localization.navigation.integrations'))
->url(fn (): string => $this->workspaceHubUrl(ProviderConnectionResource::getUrl('index', panel: 'admin')))
->icon(ProviderConnectionResource::getNavigationIcon())
->visible(fn (): bool => ProviderConnectionResource::canViewAny())
->childItems($this->visibleItems([
NavigationItem::make(ProviderConnectionResource::getNavigationLabel())
->url(fn (): string => $this->workspaceHubUrl(ProviderConnectionResource::getUrl('index', panel: 'admin')))
->icon(ProviderConnectionResource::getNavigationIcon())
->visible(fn (): bool => ProviderConnectionResource::canViewAny()),
])),
NavigationItem::make(__('localization.navigation.settings'))
->url(fn (): string => $this->workspaceHubUrl(WorkspaceSettings::getUrl(panel: 'admin')))
->icon('heroicon-o-cog-6-tooth')
->visible(fn (): bool => $this->canViewWorkspaceSettings()),
])),
NavigationGroup::make(__('localization.navigation.governance'))
->items($this->visibleItems([
NavigationItem::make(GovernanceInbox::getNavigationLabel())
->url(fn (): string => $this->workspaceHubUrl(GovernanceInbox::getUrl(panel: 'admin')))
->icon(GovernanceInbox::getNavigationIcon()),
NavigationItem::make(DecisionRegister::getNavigationLabel())
->url(fn (): string => $this->workspaceHubUrl(DecisionRegister::getUrl(panel: 'admin')))
->icon(DecisionRegister::getNavigationIcon())
->visible(fn (): bool => DecisionRegister::canAccess()),
])),
]);
}
/**
* @param array<int, NavigationItem> $items
* @return array<int, NavigationItem>
*/
private function visibleItems(array $items): array
{
return array_values(array_filter(
$items,
static fn (NavigationItem $item): bool => $item->isVisible(),
));
}
private function workspaceHubUrl(string $url): string
{
return WorkspaceHubRegistry::cleanUrl($url);
}
private function canManageWorkspaces(): bool
{
$user = auth()->user();
if (! $user instanceof User) {
return false;
}
$roles = WorkspaceRoleCapabilityMap::rolesWithCapability(Capabilities::WORKSPACE_MEMBERSHIP_MANAGE);
return WorkspaceMembership::query()
->where('user_id', (int) $user->getKey())
->whereIn('role', $roles)
->exists();
}
private function canViewWorkspaceSettings(): bool
{
$user = auth()->user();
if (! $user instanceof User) {
return false;
}
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
if (! is_int($workspaceId)) {
return false;
}
$workspace = Workspace::query()->whereKey($workspaceId)->first();
if (! $workspace instanceof Workspace) {
return false;
}
$resolver = app(WorkspaceCapabilityResolver::class);
return $resolver->isMember($user, $workspace)
&& $resolver->can($user, $workspace, Capabilities::WORKSPACE_SETTINGS_VIEW);
}
}