diff --git a/app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php b/app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php index ad2188f..414c1f0 100644 --- a/app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php +++ b/app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php @@ -7,6 +7,9 @@ use Filament\Actions\Action; use Filament\Notifications\Notification; use Filament\Resources\Pages\ViewRecord; +use Filament\Schemas\Components\Section; +use Filament\Schemas\Components\TextEntry; +use Filament\Schemas\Schema; use Filament\Support\Enums\Width; class ViewPolicy extends ViewRecord @@ -56,4 +59,105 @@ protected function getActions(): array ->color('primary'), ]; } + + public function infolist(Schema $schema): Schema + { + $latestBackupItem = $this->record->backupItems() + ->whereNotNull('assignments') + ->latest('created_at') + ->first(); + + if (! $latestBackupItem || ! $latestBackupItem->hasAssignments()) { + return $schema + ->schema([ + Section::make('Policy Information') + ->schema([ + TextEntry::make('display_name')->label('Name'), + TextEntry::make('policy_type')->label('Type'), + TextEntry::make('platform')->label('Platform'), + TextEntry::make('external_id')->label('Policy ID')->copyable(), + ]), + Section::make('Assignments') + ->schema([ + TextEntry::make('no_assignments') + ->label('') + ->default('No assignments captured yet. Create a backup with "Include Assignments" enabled to view assignment data.') + ->columnSpanFull(), + ]) + ->collapsible(), + ]); + } + + return $schema + ->schema([ + Section::make('Policy Information') + ->schema([ + TextEntry::make('display_name')->label('Name'), + TextEntry::make('policy_type')->label('Type'), + TextEntry::make('platform')->label('Platform'), + TextEntry::make('external_id')->label('Policy ID')->copyable(), + ]), + Section::make('Assignments') + ->description('Captured from backup on '.$latestBackupItem->created_at->format('M d, Y H:i')) + ->schema([ + TextEntry::make('assignment_summary') + ->label('Summary') + ->default(function () use ($latestBackupItem) { + $count = $latestBackupItem->assignment_count; + $orphaned = $latestBackupItem->hasOrphanedAssignments() ? ' (includes orphaned groups)' : ''; + + return "{$count} assignment(s){$orphaned}"; + }), + TextEntry::make('scope_tags') + ->label('Scope Tags') + ->badge() + ->separator(',') + ->default(fn () => $latestBackupItem->scope_tag_names), + TextEntry::make('assignments_detail') + ->label('Assignments') + ->columnSpanFull() + ->default(function () use ($latestBackupItem) { + if (empty($latestBackupItem->assignments)) { + return 'No assignments'; + } + + $lines = []; + foreach ($latestBackupItem->assignments as $assignment) { + $target = $assignment['target'] ?? []; + $type = $target['@odata.type'] ?? 'unknown'; + $intent = $assignment['intent'] ?? 'apply'; + + $typeName = match ($type) { + '#microsoft.graph.groupAssignmentTarget' => 'Group', + '#microsoft.graph.allLicensedUsersAssignmentTarget' => 'All Users', + '#microsoft.graph.allDevicesAssignmentTarget' => 'All Devices', + default => 'Unknown', + }; + + if ($type === '#microsoft.graph.groupAssignmentTarget') { + $groupId = $target['groupId'] ?? 'unknown'; + $groupName = $this->resolveGroupName($groupId, $latestBackupItem); + $lines[] = "• {$typeName}: {$groupName} ({$intent})"; + } else { + $lines[] = "• {$typeName} ({$intent})"; + } + } + + return implode("\n", $lines); + }) + ->markdown(), + ]) + ->collapsible(), + ]); + } + + private function resolveGroupName(string $groupId, $backupItem): string + { + // Try to find group name in backup metadata or show as orphaned + if ($backupItem->hasOrphanedAssignments()) { + return "⚠️ Unknown Group (ID: {$groupId})"; + } + + return "Group ID: {$groupId}"; + } }