TenantAtlas/apps/platform/app/Filament/Support/NormalizedDiffSurface.php

156 lines
5.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\Support;
final class NormalizedDiffSurface
{
/**
* @param array<string, mixed> $diff
* @return array<string, mixed>
*/
public static function build(array $diff, string $hostKind): array
{
$summary = is_array($diff['summary'] ?? null) ? $diff['summary'] : [];
$added = is_array($diff['added'] ?? null) ? $diff['added'] : [];
$removed = is_array($diff['removed'] ?? null) ? $diff['removed'] : [];
$changed = is_array($diff['changed'] ?? null) ? $diff['changed'] : [];
$message = is_string($summary['message'] ?? null) && trim((string) $summary['message']) !== ''
? trim((string) $summary['message'])
: null;
$addedCount = is_numeric($summary['added'] ?? null) ? (int) $summary['added'] : count($added);
$removedCount = is_numeric($summary['removed'] ?? null) ? (int) $summary['removed'] : count($removed);
$changedCount = is_numeric($summary['changed'] ?? null) ? (int) $summary['changed'] : count($changed);
$availabilityState = self::availabilityState($message, $addedCount, $removedCount, $changedCount);
return [
'hostKind' => $hostKind,
'availabilityState' => $availabilityState,
'summary' => [
'added' => $addedCount,
'removed' => $removedCount,
'changed' => $changedCount,
'message' => $message,
],
'viewModes' => [
['key' => 'grouped', 'label' => 'Grouped diff', 'default' => true],
],
'sectionBehavior' => [
'preservesGroupOrder' => true,
'supportsExpansion' => true,
'supportsFullscreen' => true,
],
'renderExpectations' => [
'ownsAvailabilityState' => true,
'ownsZeroDiffMessaging' => true,
'keepsHostFramingOutsideCore' => true,
],
'groups' => [
self::group('changed', 'Changed', $changed, false),
self::group('added', 'Added', $added, true),
self::group('removed', 'Removed', $removed, true),
],
'scriptRendering' => [
'policyType' => $diff['policy_type'] ?? null,
'showScriptContent' => (bool) config('tenantpilot.display.show_script_content', false),
],
'emptyState' => self::emptyState($availabilityState, $message, $addedCount, $removedCount, $changedCount),
'raw' => [
'added' => $added,
'removed' => $removed,
'changed' => $changed,
],
];
}
/**
* @param array<string, mixed> $items
* @return array<string, mixed>
*/
private static function group(string $key, string $label, array $items, bool $collapsed): array
{
return [
'key' => $key,
'label' => $label,
'collapsed' => $collapsed,
'count' => count($items),
'items' => self::groupByBlock($items),
];
}
/**
* @param array<string, mixed> $items
* @return array<string, array<string, mixed>>
*/
private static function groupByBlock(array $items): array
{
$groups = [];
foreach ($items as $path => $value) {
if (! is_string($path) || $path === '') {
continue;
}
$parts = explode(' > ', $path, 2);
$group = count($parts) === 2 ? $parts[0] : 'Other';
$label = count($parts) === 2 ? $parts[1] : $path;
$groups[$group][$label] = $value;
}
ksort($groups);
return $groups;
}
private static function availabilityState(?string $message, int $addedCount, int $removedCount, int $changedCount): string
{
if ($message !== null && str_contains(strtolower($message), 'unavailable')) {
return 'unavailable';
}
if ($message !== null && str_contains(strtolower($message), 'partial')) {
return 'partial';
}
return 'available';
}
/**
* @return array{title: string, message: string}|null
*/
private static function emptyState(
string $availabilityState,
?string $message,
int $addedCount,
int $removedCount,
int $changedCount,
): ?array
{
if ($availabilityState === 'unavailable' && $message !== null) {
return [
'title' => 'Diff unavailable',
'message' => $message,
];
}
if ($availabilityState === 'partial' && $message !== null) {
return [
'title' => 'Diff partially available',
'message' => $message,
];
}
if ($availabilityState === 'available' && ($addedCount + $removedCount + $changedCount) === 0) {
return [
'title' => 'No normalized changes',
'message' => $message ?? 'No normalized changes were found.',
];
}
return null;
}
}