merge: agent session work

This commit is contained in:
Ahmed Darrazi 2025-12-21 23:54:10 +01:00
commit 028fa817d1
6 changed files with 91 additions and 30 deletions

View File

@ -52,8 +52,25 @@ public static function infolist(Schema $schema): Schema
// For Settings Catalog policies: Tabs with Settings table + JSON viewer // For Settings Catalog policies: Tabs with Settings table + JSON viewer
Tabs::make('policy_content') Tabs::make('policy_content')
->activeTab(1)
->persistTabInQueryString()
->tabs([ ->tabs([
Tab::make('General')
->id('general')
->schema([
ViewEntry::make('policy_general')
->label('')
->view('filament.infolists.entries.policy-general')
->state(function (Policy $record) {
$normalized = static::normalizedPolicyState($record);
$split = static::splitGeneralBlock($normalized);
return $split['general'];
}),
])
->visible(fn (Policy $record) => $record->versions()->exists()),
Tab::make('Settings') Tab::make('Settings')
->id('settings')
->schema([ ->schema([
ViewEntry::make('settings_catalog') ViewEntry::make('settings_catalog')
->label('') ->label('')
@ -87,20 +104,8 @@ public static function infolist(Schema $schema): Schema
->helperText('This policy has been inventoried but no configuration snapshot has been captured yet.') ->helperText('This policy has been inventoried but no configuration snapshot has been captured yet.')
->visible(fn (Policy $record) => ! $record->versions()->exists()), ->visible(fn (Policy $record) => ! $record->versions()->exists()),
]), ]),
Tab::make('General')
->schema([
ViewEntry::make('policy_general')
->label('')
->view('filament.infolists.entries.policy-general')
->state(function (Policy $record) {
$normalized = static::normalizedPolicyState($record);
$split = static::splitGeneralBlock($normalized);
return $split['general'];
}),
])
->visible(fn (Policy $record) => $record->versions()->exists()),
Tab::make('JSON') Tab::make('JSON')
->id('json')
->schema([ ->schema([
ViewEntry::make('snapshot_json') ViewEntry::make('snapshot_json')
->view('filament.infolists.entries.snapshot-json') ->view('filament.infolists.entries.snapshot-json')
@ -336,12 +341,15 @@ private static function latestSnapshot(Policy $record): array
*/ */
private static function normalizedPolicyState(Policy $record): array private static function normalizedPolicyState(Policy $record): array
{ {
static $cache = []; $cacheKey = 'tenantpilot.normalizedPolicyState.'.(string) $record->getKey();
$request = request();
$cacheKey = (string) $record->getKey(); if ($request->attributes->has($cacheKey)) {
$cached = $request->attributes->get($cacheKey);
if (isset($cache[$cacheKey])) { if (is_array($cached)) {
return $cache[$cacheKey]; return $cached;
}
} }
$snapshot = static::latestSnapshot($record); $snapshot = static::latestSnapshot($record);
@ -355,7 +363,7 @@ private static function normalizedPolicyState(Policy $record): array
$normalized['context'] = 'policy'; $normalized['context'] = 'policy';
$normalized['record_id'] = (string) $record->getKey(); $normalized['record_id'] = (string) $record->getKey();
$cache[$cacheKey] = $normalized; $request->attributes->set($cacheKey, $normalized);
return $normalized; return $normalized;
} }

View File

@ -25,8 +25,18 @@
@endif @endif
@if (! empty($settingsTableRows)) @if (! empty($settingsTableRows))
<div class="space-y-2 rounded-md border border-gray-200 bg-white p-3 shadow-sm"> @php
<div class="text-sm font-semibold text-gray-800">{{ is_array($settingsTable) ? ($settingsTable['title'] ?? 'Settings') : 'Settings' }}</div> $settingsTableTitle = is_array($settingsTable) ? ($settingsTable['title'] ?? null) : null;
$shouldShowTitle = is_string($settingsTableTitle)
&& $settingsTableTitle !== ''
&& ! ($context === 'policy' && strtolower($settingsTableTitle) === 'settings');
@endphp
<div class="space-y-2">
@if ($shouldShowTitle)
<div class="text-sm font-semibold text-gray-800">{{ $settingsTableTitle }}</div>
@endif
<livewire:settings-catalog-settings-table <livewire:settings-catalog-settings-table
:settings-rows="$settingsTableRows" :settings-rows="$settingsTableRows"
:context="$context" :context="$context"

View File

@ -90,16 +90,16 @@
$isNumericValue = is_numeric($value); $isNumericValue = is_numeric($value);
@endphp @endphp
<div class="group relative overflow-hidden rounded-xl border border-gray-200/70 bg-gradient-to-br from-white via-amber-50/40 to-amber-100/60 p-4 shadow-sm transition duration-200 hover:-translate-y-0.5 hover:border-amber-200/70 hover:shadow-md dark:border-gray-700/60 dark:from-gray-950 dark:via-gray-900 dark:to-amber-950/30 dark:hover:border-amber-700/60"> <div class="tp-policy-general-card group relative overflow-hidden rounded-xl border border-gray-200/70 bg-white p-4 shadow-sm transition duration-200 hover:-translate-y-0.5 hover:border-gray-300/70 hover:shadow-md dark:border-gray-700/60 dark:bg-gray-900 dark:hover:border-gray-600">
<div class="flex items-start gap-3"> <div class="flex items-start gap-3">
<div class="flex h-10 w-10 items-center justify-center rounded-lg ring-1 {{ $tone['ring'] ?? '' }} {{ $toneClass }}"> <div class="flex h-10 w-10 items-center justify-center rounded-lg ring-1 {{ $tone['ring'] ?? '' }} {{ $toneClass }}">
<x-filament::icon icon="{{ $tone['icon'] ?? 'heroicon-o-document-text' }}" class="h-5 w-5" /> <x-filament::icon icon="{{ $tone['icon'] ?? 'heroicon-o-document-text' }}" class="h-5 w-5" />
</div> </div>
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
<dt class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400"> <dt class="text-xs font-semibold tracking-wide text-gray-500 dark:text-gray-400">
{{ $entry['key'] ?? '-' }} {{ $entry['key'] ?? '-' }}
</dt> </dt>
<dd class="mt-2"> <dd class="mt-2 text-left">
@if ($isListValue) @if ($isListValue)
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
@foreach ($value as $item) @foreach ($value as $item)
@ -121,11 +121,11 @@
{{ $boolLabel }} {{ $boolLabel }}
</x-filament::badge> </x-filament::badge>
@elseif ($isNumericValue) @elseif ($isNumericValue)
<div class="text-lg font-semibold text-gray-900 dark:text-white tabular-nums"> <div class="text-sm font-semibold text-gray-900 dark:text-white tabular-nums">
{{ number_format((float) $value) }} {{ number_format((float) $value) }}
</div> </div>
@else @else
<div class="text-sm text-gray-900 dark:text-white whitespace-pre-wrap break-words"> <div class="text-sm text-gray-900 dark:text-white whitespace-pre-wrap break-words text-left">
{{ is_string($value) ? $value : json_encode($value, JSON_PRETTY_PRINT) }} {{ is_string($value) ? $value : json_encode($value, JSON_PRETTY_PRINT) }}
</div> </div>
@endif @endif

View File

@ -4,6 +4,7 @@
// Normalize payload to array for the JSON viewer // Normalize payload to array for the JSON viewer
$payloadArray = is_string($payload) ? (json_decode($payload, true) ?? []) : ($payload ?? []); $payloadArray = is_string($payload) ? (json_decode($payload, true) ?? []) : ($payload ?? []);
$rawJson = json_encode($payloadArray, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// Provide the small set of helpers the pepperfm json view expects // Provide the small set of helpers the pepperfm json view expects
$getState = fn () => $payloadArray; $getState = fn () => $payloadArray;
@ -17,10 +18,51 @@
$getRenderMode = fn () => \PepperFM\FilamentJson\Enums\RenderModeEnum::Tree; $getRenderMode = fn () => \PepperFM\FilamentJson\Enums\RenderModeEnum::Tree;
$getInitiallyCollapsed = fn () => 1; $getInitiallyCollapsed = fn () => 1;
$getExpandAllToggle = fn () => false; $getExpandAllToggle = fn () => false;
$getCopyJsonAction = fn () => true; $getCopyJsonAction = fn () => false;
$getMaxDepth = fn () => 3; $getMaxDepth = fn () => 3;
$applyLimit = fn ($v) => $v; $applyLimit = fn ($v) => $v;
@endphp @endphp
{{-- Render pepperfm filament-json viewer --}} <div
@include('filament-json::json') class="space-y-2"
x-data="{
text: @js($rawJson),
async copyJson() {
try {
if (navigator.clipboard && (location.protocol === 'https:' || location.hostname === 'localhost')) {
await navigator.clipboard.writeText(this.text);
} else {
const ta = document.createElement('textarea');
ta.value = this.text;
ta.style.position = 'fixed';
ta.style.inset = '0';
document.body.appendChild(ta);
ta.focus();
ta.select();
document.execCommand('copy');
ta.remove();
}
new FilamentNotification()
.title('Copied!')
.icon('heroicon-o-clipboard-document-check')
.success()
.send();
} catch (e) {
new FilamentNotification()
.title('Copy failed!')
.danger()
.send();
}
}
}"
>
<div class="flex items-center justify-end">
<x-filament::button size="xs" color="gray" x-on:click="copyJson()">
Copy JSON
</x-filament::button>
</div>
{{-- Render pepperfm filament-json viewer --}}
@include('filament-json::json')
</div>

View File

@ -80,7 +80,8 @@
$response->assertSee('Settings'); // Settings tab should appear for Settings Catalog $response->assertSee('Settings'); // Settings tab should appear for Settings Catalog
$response->assertSee('General'); $response->assertSee('General');
$response->assertSee('JSON'); $response->assertSee('JSON');
$response->assertSee('bg-gradient-to-br'); $response->assertSee('tp-policy-general-card');
$response->assertSee('Copy JSON');
}); });
it('shows display names instead of definition IDs', function () { it('shows display names instead of definition IDs', function () {

View File

@ -59,7 +59,7 @@
$user = User::factory()->create(); $user = User::factory()->create();
$policyResponse = $this->actingAs($user) $policyResponse = $this->actingAs($user)
->get(PolicyResource::getUrl('view', ['record' => $policy])); ->get(PolicyResource::getUrl('view', ['record' => $policy]).'?tab=settings');
$policyResponse->assertOk(); $policyResponse->assertOk();
$policyResponse->assertSee('fi-width-full'); $policyResponse->assertSee('fi-width-full');