- Fix display_name → name column on Tenant model (3 files) - Rename 'Soll vs Ist' to 'Baseline Compare' (English consistency) - Redesign landing page: stats overview grid, critical drift banner, severity breakdown section, proper empty states with icons - Upgrade dashboard widget: severity badges, inline critical alert, last compared timestamp, compliance status indicator - Move Findings + DriftLanding from 'Drift' to 'Governance' nav group
143 lines
4.5 KiB
PHP
143 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Resources\BaselineProfileResource\Pages;
|
|
|
|
use App\Filament\Resources\BaselineProfileResource;
|
|
use App\Models\BaselineProfile;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Services\Baselines\BaselineCaptureService;
|
|
use App\Support\Auth\Capabilities;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Filament\Actions\Action;
|
|
use Filament\Actions\EditAction;
|
|
use Filament\Forms\Components\Select;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Resources\Pages\ViewRecord;
|
|
|
|
class ViewBaselineProfile extends ViewRecord
|
|
{
|
|
protected static string $resource = BaselineProfileResource::class;
|
|
|
|
protected function getHeaderActions(): array
|
|
{
|
|
return [
|
|
$this->captureAction(),
|
|
EditAction::make()
|
|
->visible(fn (): bool => $this->hasManageCapability()),
|
|
];
|
|
}
|
|
|
|
private function captureAction(): Action
|
|
{
|
|
return Action::make('capture')
|
|
->label('Capture Snapshot')
|
|
->icon('heroicon-o-camera')
|
|
->color('primary')
|
|
->visible(fn (): bool => $this->hasManageCapability())
|
|
->disabled(fn (): bool => ! $this->hasManageCapability())
|
|
->tooltip(fn (): ?string => ! $this->hasManageCapability() ? 'You need manage permission to capture snapshots.' : null)
|
|
->requiresConfirmation()
|
|
->modalHeading('Capture Baseline Snapshot')
|
|
->modalDescription('Select the source tenant whose current inventory will be captured as the baseline snapshot.')
|
|
->form([
|
|
Select::make('source_tenant_id')
|
|
->label('Source Tenant')
|
|
->options(fn (): array => $this->getWorkspaceTenantOptions())
|
|
->required()
|
|
->searchable(),
|
|
])
|
|
->action(function (array $data): void {
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User || ! $this->hasManageCapability()) {
|
|
Notification::make()
|
|
->title('Permission denied')
|
|
->danger()
|
|
->send();
|
|
|
|
return;
|
|
}
|
|
|
|
/** @var BaselineProfile $profile */
|
|
$profile = $this->getRecord();
|
|
$sourceTenant = Tenant::query()->find((int) $data['source_tenant_id']);
|
|
|
|
if (! $sourceTenant instanceof Tenant) {
|
|
Notification::make()
|
|
->title('Source tenant not found')
|
|
->danger()
|
|
->send();
|
|
|
|
return;
|
|
}
|
|
|
|
$service = app(BaselineCaptureService::class);
|
|
$result = $service->startCapture($profile, $sourceTenant, $user);
|
|
|
|
if (! $result['ok']) {
|
|
Notification::make()
|
|
->title('Cannot start capture')
|
|
->body('Reason: '.str_replace('.', ' ', (string) ($result['reason_code'] ?? 'unknown')))
|
|
->danger()
|
|
->send();
|
|
|
|
return;
|
|
}
|
|
|
|
Notification::make()
|
|
->title('Capture enqueued')
|
|
->body('Baseline snapshot capture has been started.')
|
|
->success()
|
|
->send();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
private function getWorkspaceTenantOptions(): array
|
|
{
|
|
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
|
|
|
|
if ($workspaceId === null) {
|
|
return [];
|
|
}
|
|
|
|
return Tenant::query()
|
|
->where('workspace_id', $workspaceId)
|
|
->orderBy('name')
|
|
->pluck('name', 'id')
|
|
->all();
|
|
}
|
|
|
|
private function hasManageCapability(): bool
|
|
{
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User) {
|
|
return false;
|
|
}
|
|
|
|
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
|
|
|
|
if ($workspaceId === null) {
|
|
return false;
|
|
}
|
|
|
|
$workspace = Workspace::query()->whereKey($workspaceId)->first();
|
|
|
|
if (! $workspace instanceof Workspace) {
|
|
return false;
|
|
}
|
|
|
|
$resolver = app(\App\Services\Auth\WorkspaceCapabilityResolver::class);
|
|
|
|
return $resolver->isMember($user, $workspace)
|
|
&& $resolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_MANAGE);
|
|
}
|
|
}
|