TenantAtlas/app/Support/Baselines/TenantGovernanceAggregate.php
ahmido 807d574d31 feat: add tenant governance aggregate contract and action surface follow-ups (#199)
## Summary
- amend the operator UI constitution and related SpecKit templates for the new UI/UX governance rules
- add Spec 168 artifacts plus the tenant governance aggregate implementation used by the tenant dashboard, banner, and baseline compare landing surfaces
- normalize Filament action surfaces around clickable-row inspection, grouped secondary actions, and explicit action-surface declarations across enrolled resources and pages
- fix post-suite regressions in membership cache priming, finding workflow state refresh, tenant review derived-state invalidation, and tenant-bound backup-set related navigation

## Commit Series
- `docs: amend operator UI constitution`
- `spec: add tenant governance aggregate contract`
- `feat: add tenant governance aggregate contract`
- `refactor: normalize filament action surfaces`
- `fix: resolve post-suite state regressions`

## Testing
- `vendor/bin/sail artisan test --compact`
- Result: `3176 passed, 8 skipped (17384 assertions)`

## Notes
- Livewire v4 / Filament v5 stack remains unchanged
- no provider registration changes; `bootstrap/providers.php` remains the relevant location
- no new global-search resources or asset-registration changes in this branch

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #199
2026-03-29 21:14:17 +00:00

112 lines
4.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support\Baselines;
use InvalidArgumentException;
final readonly class TenantGovernanceAggregate
{
public function __construct(
public int $tenantId,
public int $workspaceId,
public ?string $profileName,
public string $compareState,
public string $stateFamily,
public string $tone,
public string $headline,
public ?string $supportingMessage,
public ?string $reasonCode,
public ?string $lastComparedLabel,
public int $visibleDriftFindingsCount,
public int $overdueOpenFindingsCount,
public int $expiringGovernanceCount,
public int $lapsedGovernanceCount,
public int $activeNonNewFindingsCount,
public int $highSeverityActiveFindingsCount,
public string $nextActionLabel,
public string $nextActionTarget,
public bool $positiveClaimAllowed,
public BaselineCompareStats $stats,
public BaselineCompareSummaryAssessment $summaryAssessment,
) {
if ($this->tenantId <= 0) {
throw new InvalidArgumentException('Tenant governance aggregates require a positive tenant id.');
}
if ($this->workspaceId <= 0) {
throw new InvalidArgumentException('Tenant governance aggregates require a positive workspace id.');
}
if (trim($this->compareState) === '') {
throw new InvalidArgumentException('Tenant governance aggregates require a compare state.');
}
if (trim($this->headline) === '') {
throw new InvalidArgumentException('Tenant governance aggregates require a headline.');
}
if (trim($this->nextActionLabel) === '') {
throw new InvalidArgumentException('Tenant governance aggregates require a next-action label.');
}
if (! in_array($this->nextActionTarget, [
BaselineCompareSummaryAssessment::NEXT_TARGET_FINDINGS,
BaselineCompareSummaryAssessment::NEXT_TARGET_LANDING,
BaselineCompareSummaryAssessment::NEXT_TARGET_RUN,
BaselineCompareSummaryAssessment::NEXT_TARGET_NONE,
], true)) {
throw new InvalidArgumentException('Tenant governance aggregates require a supported next-action target.');
}
}
/**
* @return array{
* tenantId: int,
* workspaceId: int,
* profileName: ?string,
* compareState: string,
* stateFamily: string,
* tone: string,
* headline: string,
* supportingMessage: ?string,
* reasonCode: ?string,
* lastComparedLabel: ?string,
* visibleDriftFindingsCount: int,
* overdueOpenFindingsCount: int,
* expiringGovernanceCount: int,
* lapsedGovernanceCount: int,
* activeNonNewFindingsCount: int,
* highSeverityActiveFindingsCount: int,
* nextActionLabel: string,
* nextActionTarget: string,
* positiveClaimAllowed: bool
* }
*/
public function toArray(): array
{
return [
'tenantId' => $this->tenantId,
'workspaceId' => $this->workspaceId,
'profileName' => $this->profileName,
'compareState' => $this->compareState,
'stateFamily' => $this->stateFamily,
'tone' => $this->tone,
'headline' => $this->headline,
'supportingMessage' => $this->supportingMessage,
'reasonCode' => $this->reasonCode,
'lastComparedLabel' => $this->lastComparedLabel,
'visibleDriftFindingsCount' => $this->visibleDriftFindingsCount,
'overdueOpenFindingsCount' => $this->overdueOpenFindingsCount,
'expiringGovernanceCount' => $this->expiringGovernanceCount,
'lapsedGovernanceCount' => $this->lapsedGovernanceCount,
'activeNonNewFindingsCount' => $this->activeNonNewFindingsCount,
'highSeverityActiveFindingsCount' => $this->highSeverityActiveFindingsCount,
'nextActionLabel' => $this->nextActionLabel,
'nextActionTarget' => $this->nextActionTarget,
'positiveClaimAllowed' => $this->positiveClaimAllowed,
];
}
}