schema([ Forms\Components\TextInput::make('name') ->label('Backup name') ->default(fn () => now()->format('Y-m-d H:i:s').' backup') ->required(), ]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('name')->searchable(), Tables\Columns\TextColumn::make('status')->badge(), Tables\Columns\TextColumn::make('item_count')->label('Items'), Tables\Columns\TextColumn::make('created_by')->label('Created by'), Tables\Columns\TextColumn::make('completed_at')->dateTime()->since(), Tables\Columns\TextColumn::make('created_at')->dateTime()->since(), ]) ->filters([ Tables\Filters\TrashedFilter::make(), ]) ->actions([ Actions\ViewAction::make() ->url(fn (BackupSet $record) => static::getUrl('view', ['record' => $record])) ->openUrlInNewTab(false), ActionGroup::make([ Actions\Action::make('archive') ->label('Archive') ->color('danger') ->icon('heroicon-o-archive-box-x-mark') ->requiresConfirmation() ->visible(fn (BackupSet $record) => ! $record->trashed()) ->action(function (BackupSet $record, AuditLogger $auditLogger) { if ($record->restoreRuns()->withTrashed()->exists()) { Notification::make() ->title('Cannot archive backup set') ->body('Backup sets used by restore runs cannot be archived.') ->danger() ->send(); return; } $record->delete(); if ($record->tenant) { $auditLogger->log( tenant: $record->tenant, action: 'backup.deleted', resourceType: 'backup_set', resourceId: (string) $record->id, status: 'success', context: ['metadata' => ['name' => $record->name]] ); } Notification::make() ->title('Backup set archived') ->success() ->send(); }), Actions\Action::make('forceDelete') ->label('Force delete') ->color('danger') ->icon('heroicon-o-trash') ->requiresConfirmation() ->visible(fn (BackupSet $record) => $record->trashed()) ->action(function (BackupSet $record, AuditLogger $auditLogger) { if ($record->restoreRuns()->withTrashed()->exists()) { Notification::make() ->title('Cannot force delete backup set') ->body('Backup sets referenced by restore runs cannot be removed.') ->danger() ->send(); return; } if ($record->tenant) { $auditLogger->log( tenant: $record->tenant, action: 'backup.force_deleted', resourceType: 'backup_set', resourceId: (string) $record->id, status: 'success', context: ['metadata' => ['name' => $record->name]] ); } $record->items()->withTrashed()->forceDelete(); $record->forceDelete(); Notification::make() ->title('Backup set permanently deleted') ->success() ->send(); }), ])->icon('heroicon-o-ellipsis-vertical'), ]) ->bulkActions([]); } public static function infolist(Schema $schema): Schema { return $schema ->schema([ Infolists\Components\TextEntry::make('name'), Infolists\Components\TextEntry::make('status')->badge(), Infolists\Components\TextEntry::make('item_count')->label('Items'), Infolists\Components\TextEntry::make('created_by')->label('Created by'), Infolists\Components\TextEntry::make('completed_at')->dateTime(), Infolists\Components\TextEntry::make('metadata') ->label('Metadata') ->formatStateUsing(fn ($state) => json_encode($state ?? [], JSON_PRETTY_PRINT)) ->copyable() ->copyMessage('Metadata copied'), ]); } public static function getRelations(): array { return [ BackupItemsRelationManager::class, ]; } public static function getPages(): array { return [ 'index' => Pages\ListBackupSets::route('/'), 'create' => Pages\CreateBackupSet::route('/create'), 'view' => Pages\ViewBackupSet::route('/{record}'), ]; } /** * @return array{label:?string,category:?string,restore:?string,risk:?string}|array */ private static function typeMeta(?string $type): array { if ($type === null) { return []; } return collect(config('tenantpilot.supported_policy_types', [])) ->firstWhere('type', $type) ?? []; } /** * Create a backup set via the domain service instead of direct model mass-assignment. */ public static function createBackupSet(array $data): BackupSet { /** @var Tenant $tenant */ $tenant = Tenant::current(); /** @var BackupService $service */ $service = app(BackupService::class); return $service->createBackupSet( tenant: $tenant, policyIds: $data['policy_ids'] ?? [], name: $data['name'] ?? null, actorEmail: auth()->user()?->email, actorName: auth()->user()?->name, ); } }