TenantAtlas/apps/platform/app/Support/Navigation/WorkspaceSidebarNavigation.php
ahmido 7ce066dd00 Spec 329: productize evidence and audit log disclosure (#390)
## Summary
- productize the Monitoring audit log disclosure flow with richer detail inspection and updated disclosure UI
- expand the evidence overview disclosure experience, including filtering and presentation updates
- wire the monitoring pages into the Filament admin panel and workspace sidebar navigation
- add English and German disclosure copy for the new audit and evidence surfaces
- include Spec 329 implementation artifacts and supporting presenter/route updates

## Tests
- added/updated monitoring acceptance and feature coverage for the disclosure flow
- touched tests include `Spec329EvidenceAuditDisclosureSmokeTest`, `Spec329EvidenceAuditDisclosureProductizationTest`, `AuditLogPageTest`, `AuditLogDetailInspectionTest`, `AuditLogInspectFlowTest`, and related monitoring/navigation coverage
- no additional test run was performed as part of this commit/push/PR workflow

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #390
2026-05-19 21:34:23 +00:00

176 lines
9.1 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.evidence_overview'))
->url(fn (): string => $this->workspaceHubUrl(route('admin.evidence.overview')))
->icon('heroicon-o-shield-check'),
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);
}
}