Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m0s
## Summary - productize the customer review workspace and released-review drilldown into a calmer customer-safe governance flow - make review-pack and evidence-proof access explicit, capability-aware, and auditable in the shared Filament resources - add focused Pest coverage, browser smoke coverage, and the full Spec 258 artifact package ## Notes - Filament stays on v5 with Livewire v4 surfaces; no provider registration changes were introduced - no new global-search scope, destructive action surface, or asset registration was added - bounded additive audit action IDs were added for workspace open and evidence proof open events ## Validation - focused Pest feature suites for workspace, review detail, review-pack, and evidence flows - bounded browser smoke: `tests/Browser/Reviews/CustomerReviewWorkspaceSmokeTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #310
147 lines
5.2 KiB
PHP
147 lines
5.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Resources\EvidenceSnapshotResource\Pages;
|
|
|
|
use App\Filament\Resources\EvidenceSnapshotResource;
|
|
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
|
use App\Models\EvidenceSnapshot;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Services\Audit\WorkspaceAuditLogger;
|
|
use App\Services\Evidence\EvidenceSnapshotService;
|
|
use App\Support\Audit\AuditActionId;
|
|
use App\Support\Auth\Capabilities;
|
|
use App\Support\Rbac\UiEnforcement;
|
|
use App\Support\Ui\GovernanceActions\GovernanceActionCatalog;
|
|
use Filament\Actions;
|
|
use Filament\Forms\Components\Textarea;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Resources\Pages\ViewRecord;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
class ViewEvidenceSnapshot extends ViewRecord
|
|
{
|
|
protected static string $resource = EvidenceSnapshotResource::class;
|
|
|
|
public function mount(int|string $record): void
|
|
{
|
|
parent::mount($record);
|
|
|
|
$this->auditCustomerWorkspaceProofOpen();
|
|
}
|
|
|
|
protected function resolveRecord(int|string $key): Model
|
|
{
|
|
return EvidenceSnapshotResource::resolveScopedRecordOrFail($key);
|
|
}
|
|
|
|
protected function getHeaderActions(): array
|
|
{
|
|
if (EvidenceSnapshotResource::isCustomerWorkspaceFlow()) {
|
|
return [];
|
|
}
|
|
|
|
$refreshRule = GovernanceActionCatalog::rule('refresh_evidence');
|
|
$expireRule = GovernanceActionCatalog::rule('expire_snapshot');
|
|
|
|
return [
|
|
UiEnforcement::forAction(
|
|
Actions\Action::make('refresh_evidence')
|
|
->label($refreshRule->canonicalLabel)
|
|
->icon('heroicon-o-arrow-path')
|
|
->color('primary')
|
|
->requiresConfirmation()
|
|
->modalHeading($refreshRule->modalHeading)
|
|
->modalDescription($refreshRule->modalDescription)
|
|
->action(function () use ($refreshRule): void {
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User) {
|
|
abort(403);
|
|
}
|
|
|
|
app(EvidenceSnapshotService::class)->refresh($this->record, $user);
|
|
|
|
Notification::make()->success()->title($refreshRule->successTitle)->send();
|
|
}),
|
|
)
|
|
->requireCapability(Capabilities::EVIDENCE_MANAGE)
|
|
->apply(),
|
|
UiEnforcement::forAction(
|
|
Actions\Action::make('expire_snapshot')
|
|
->label($expireRule->canonicalLabel)
|
|
->icon('heroicon-o-x-circle')
|
|
->color('danger')
|
|
->hidden(fn (): bool => ! EvidenceSnapshotResource::canExpireRecord($this->record))
|
|
->requiresConfirmation()
|
|
->modalHeading($expireRule->modalHeading)
|
|
->modalDescription($expireRule->modalDescription)
|
|
->form([
|
|
Textarea::make('expiration_reason')
|
|
->label('Expiry reason')
|
|
->rows(4)
|
|
->required()
|
|
->maxLength(2000),
|
|
])
|
|
->action(function (array $data) use ($expireRule): void {
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User) {
|
|
abort(403);
|
|
}
|
|
|
|
app(EvidenceSnapshotService::class)->expire(
|
|
$this->record,
|
|
$user,
|
|
(string) ($data['expiration_reason'] ?? ''),
|
|
);
|
|
$this->refreshFormData(['status', 'expires_at']);
|
|
|
|
Notification::make()->success()->title($expireRule->successTitle)->send();
|
|
}),
|
|
)
|
|
->requireCapability(Capabilities::EVIDENCE_MANAGE)
|
|
->apply(),
|
|
];
|
|
}
|
|
|
|
private function auditCustomerWorkspaceProofOpen(): void
|
|
{
|
|
if (! EvidenceSnapshotResource::isCustomerWorkspaceFlow()) {
|
|
return;
|
|
}
|
|
|
|
$record = $this->record;
|
|
$user = auth()->user();
|
|
|
|
if (! $record instanceof EvidenceSnapshot || ! $user instanceof User) {
|
|
return;
|
|
}
|
|
|
|
$tenant = $record->tenant;
|
|
|
|
if (! $tenant instanceof Tenant) {
|
|
return;
|
|
}
|
|
|
|
app(WorkspaceAuditLogger::class)->log(
|
|
workspace: $tenant->workspace,
|
|
action: AuditActionId::EvidenceSnapshotOpened,
|
|
context: [
|
|
'metadata' => [
|
|
'evidence_snapshot_id' => (int) $record->getKey(),
|
|
'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
|
|
],
|
|
],
|
|
actor: $user,
|
|
resourceType: 'evidence_snapshot',
|
|
resourceId: (string) $record->getKey(),
|
|
targetLabel: sprintf('Evidence snapshot #%d', (int) $record->getKey()),
|
|
tenant: $tenant,
|
|
operationRunId: $record->operation_run_id !== null ? (int) $record->operation_run_id : null,
|
|
);
|
|
}
|
|
}
|