## 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
176 lines
9.1 KiB
PHP
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);
|
|
}
|
|
}
|