TenantAtlas/app/Filament/Widgets/Inventory/InventoryKpiHeader.php

132 lines
4.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\Widgets\Inventory;
use App\Models\InventoryItem;
use App\Models\InventorySyncRun;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Services\Inventory\CoverageCapabilitiesResolver;
use App\Support\Inventory\InventoryPolicyTypeMeta;
use Filament\Facades\Filament;
use Filament\Widgets\Widget;
class InventoryKpiHeader extends Widget
{
protected static bool $isLazy = false;
protected string $view = 'filament.widgets.inventory.inventory-kpi-header';
protected int|string|array $columnSpan = 'full';
/**
* Inventory KPI aggregation source-of-truth:
* - `inventory_items.policy_type`
* - `config('tenantpilot.supported_policy_types')` + `config('tenantpilot.foundation_types')` meta (`restore`, `risk`)
* - dependency capability via `CoverageCapabilitiesResolver`
*
* @return array<string, mixed>
*/
protected function getViewData(): array
{
$tenant = Filament::getTenant();
if (! $tenant instanceof Tenant) {
return [
'totalItems' => 0,
'coveragePercent' => 0,
'lastInventorySyncLabel' => '—',
'activeOps' => 0,
'inventoryOps' => 0,
'dependenciesItems' => 0,
'partialItems' => 0,
'restorableItems' => 0,
'riskItems' => 0,
];
}
$tenantId = (int) $tenant->getKey();
/** @var array<string, int> $countsByPolicyType */
$countsByPolicyType = InventoryItem::query()
->where('tenant_id', $tenantId)
->selectRaw('policy_type, COUNT(*) as aggregate')
->groupBy('policy_type')
->pluck('aggregate', 'policy_type')
->map(fn ($value): int => (int) $value)
->all();
$totalItems = array_sum($countsByPolicyType);
$restorableItems = 0;
$partialItems = 0;
$riskItems = 0;
foreach ($countsByPolicyType as $policyType => $count) {
if (InventoryPolicyTypeMeta::isRestorable($policyType)) {
$restorableItems += $count;
} elseif (InventoryPolicyTypeMeta::isPartial($policyType)) {
$partialItems += $count;
}
if (InventoryPolicyTypeMeta::isHighRisk($policyType)) {
$riskItems += $count;
}
}
$coveragePercent = $totalItems > 0
? (int) round(($restorableItems / $totalItems) * 100)
: 0;
$lastRun = InventorySyncRun::query()
->where('tenant_id', $tenantId)
->latest('id')
->first();
$lastInventorySyncLabel = '—';
if ($lastRun instanceof InventorySyncRun) {
$timestamp = $lastRun->finished_at ?? $lastRun->started_at;
$lastInventorySyncLabel = trim(sprintf(
'%s%s',
(string) ($lastRun->status ?? '—'),
$timestamp ? ' • '.$timestamp->diffForHumans() : ''
));
}
$activeOps = (int) OperationRun::query()
->where('tenant_id', $tenantId)
->active()
->count();
$inventoryOps = (int) OperationRun::query()
->where('tenant_id', $tenantId)
->where('type', 'inventory.sync')
->active()
->count();
$resolver = app(CoverageCapabilitiesResolver::class);
$dependenciesItems = 0;
foreach ($countsByPolicyType as $policyType => $count) {
if ($policyType !== '' && $resolver->supportsDependencies($policyType)) {
$dependenciesItems += $count;
}
}
return [
'totalItems' => $totalItems,
'coveragePercent' => $coveragePercent,
'lastInventorySyncLabel' => $lastInventorySyncLabel,
'activeOps' => $activeOps,
'inventoryOps' => $inventoryOps,
'dependenciesItems' => $dependenciesItems,
'partialItems' => $partialItems,
'restorableItems' => $restorableItems,
'riskItems' => $riskItems,
];
}
}