components([ Grid::make() ->schema([ Section::make() ->schema([ TextInput::make('name') ->label(__('filament-shield::filament-shield.field.name')) ->unique( ignoreRecord: true, modifyRuleUsing: fn (Unique $rule): Unique => Utils::isTenancyEnabled() ? $rule->where(Utils::getTenantModelForeignKey(), Filament::getTenant()?->id) : $rule ) ->required() ->maxLength(255), TextInput::make('guard_name') ->label(__('filament-shield::filament-shield.field.guard_name')) ->default(Utils::getFilamentAuthGuard()) ->nullable() ->maxLength(255), Select::make(config('permission.column_names.team_foreign_key')) ->label(__('filament-shield::filament-shield.field.team')) ->placeholder(__('filament-shield::filament-shield.field.team.placeholder')) ->default(Filament::getTenant()?->id) ->options(fn (): array => in_array(Utils::getTenantModel(), [null, '', '0'], true) ? [] : Utils::getTenantModel()::pluck('name', 'id')->toArray()) ->visible(fn (): bool => static::shield()->isCentralApp() && Utils::isTenancyEnabled()) ->dehydrated(fn (): bool => static::shield()->isCentralApp() && Utils::isTenancyEnabled()), static::getSelectAllFormComponent(), ]) ->columns(['sm' => 2, 'lg' => 3]) ->columnSpanFull() ->footerActions([ fn (string $operation) => Action::make('create') ->label(__('filament-panels::resources/pages/create-record.form.actions.create.label')) ->submit('create') ->keyBindings(['mod+s']) ->visible($operation === 'create'), fn (string $operation) => Action::make('createAnother') ->label(__('filament-panels::resources/pages/create-record.form.actions.create_another.label')) ->action('createAnother') ->keyBindings(['mod+shift+s']) ->color('gray') ->visible($operation === 'create'), fn (string $operation) => Action::make('cancelCreate') ->label(__('filament-panels::resources/pages/create-record.form.actions.cancel.label')) ->url(static::getUrl('index')) ->color('gray') ->visible($operation === 'create'), fn (string $operation) => Action::make('save') ->label(__('filament-panels::resources/pages/edit-record.form.actions.save.label')) ->submit('save') ->keyBindings(['mod+s']) ->visible($operation === 'edit'), fn (string $operation) => Action::make('cancelEdit') ->label(__('filament-panels::resources/pages/edit-record.form.actions.cancel.label')) ->url(static::getUrl('index')) ->color('gray') ->visible($operation === 'edit'), ]), ]) ->columnSpanFull(), static::getShieldFormComponents(), ]); } public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('name') ->weight(FontWeight::Medium) ->label(__('filament-shield::filament-shield.column.name')) ->formatStateUsing(fn (string $state): string => Str::headline($state)) ->searchable(), TextColumn::make('guard_name') ->badge() ->color('warning') ->label(__('filament-shield::filament-shield.column.guard_name')), TextColumn::make('team.name') ->default('Global') ->badge() ->color(fn (mixed $state): string => str($state)->contains('Global') ? 'gray' : 'primary') ->label(__('filament-shield::filament-shield.column.team')) ->searchable() ->visible(fn (): bool => static::shield()->isCentralApp() && Utils::isTenancyEnabled()), TextColumn::make('permissions_count') ->badge() ->label(__('filament-shield::filament-shield.column.permissions')) ->counts('permissions') ->color('primary'), TextColumn::make('updated_at') ->label(__('filament-shield::filament-shield.column.updated_at')) ->formatStateUsing(function ($state) { if (! $state) { return '-'; } try { $date = \Carbon\Carbon::parse($state); return Jalalian::fromCarbon($date)->format('Y/m/d - H:i'); } catch (\Exception $e) { return $state; } }), ]) ->filters([ // ]) ->recordActions([ EditAction::make(), DeleteAction::make(), ]) ->paginationPageOptions([5, 10, 25, 50, 100]) ->defaultPaginationPageOption(25) ->toolbarActions([ BulkActionGroup::make([ DeleteBulkAction::make(), BulkAction::make('export_excel_selected') ->label(__('table.export_excel_selected')) ->icon('heroicon-o-table-cells') ->color('success') ->action(function (Collection $records) { $tempPath = tempnam(sys_get_temp_dir(), 'export_') . '.xlsx'; $writer = new Writer(); $writer->openToFile($tempPath); $writer->addRow(Row::fromValues([ 'ID', __('filament-shield::filament-shield.column.name'), __('filament-shield::filament-shield.column.guard_name'), __('filament-shield::filament-shield.column.permissions'), __('filament-shield::filament-shield.column.updated_at'), ])); foreach ($records as $record) { $writer->addRow(Row::fromValues([ $record->id, $record->name, $record->guard_name, $record->permissions_count ?? $record->permissions()->count(), $record->updated_at?->format('Y-m-d H:i:s'), ])); } $writer->close(); return response()->streamDownload(function () use ($tempPath) { readfile($tempPath); @unlink($tempPath); }, 'roles-' . now()->format('Y-m-d') . '.xlsx', [ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ]); }), ]), ActionGroup::make([ Action::make('export_excel') ->label(__('table.export_excel')) ->color('success') ->icon('heroicon-o-table-cells') ->action(function () { $records = Role::with('permissions')->withCount('permissions')->get(); $tempPath = tempnam(sys_get_temp_dir(), 'export_') . '.xlsx'; $writer = new Writer(); $writer->openToFile($tempPath); $writer->addRow(Row::fromValues([ 'ID', __('filament-shield::filament-shield.column.name'), __('filament-shield::filament-shield.column.guard_name'), __('filament-shield::filament-shield.column.permissions'), __('filament-shield::filament-shield.column.updated_at'), ])); foreach ($records as $record) { $writer->addRow(Row::fromValues([ $record->id, $record->name, $record->guard_name, $record->permissions_count, $record->updated_at?->format('Y-m-d H:i:s'), ])); } $writer->close(); return response()->streamDownload(function () use ($tempPath) { readfile($tempPath); @unlink($tempPath); }, 'roles-' . now()->format('Y-m-d') . '.xlsx', [ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ]); }), Action::make('export_pdf') ->label(__('table.export_pdf')) ->color('danger') ->icon('heroicon-o-document-arrow-down') ->action(function () { $records = Role::with('permissions')->get(); return PdfService::download( 'exports.roles-pdf', ['records' => $records], 'roles-' . now()->format('Y-m-d') . '.pdf', ); }), ]) ->label(__('table.export')) ->color('orange') ->button(), ]); } public static function getPages(): array { return [ 'index' => Pages\ListRoles::route('/'), 'create' => Pages\CreateRole::route('/create'), 'view' => Pages\ViewRole::route('/{record}'), 'edit' => Pages\EditRole::route('/{record}/edit'), ]; } public static function toggleSelectAllViaEntities(Livewire $livewire, Set $set): void { /** @phpstan-ignore-next-line */ $entitiesStates = collect($livewire->form->getFlatComponents()) ->filter(fn (mixed $component): bool => $component instanceof Component) ->reduce(function (mixed $counts, Component $component) { if ($component instanceof CheckboxList) { $counts[$component->getName()] = count(array_keys($component->getOptions())) === count(collect($component->getState())->values()->unique()->toArray()); } return $counts; }, collect()) ->values(); if ($entitiesStates->containsStrict(false)) { $set('select_all', false); } else { $set('select_all', true); } } public static function toggleEntitiesViaSelectAll(Livewire $livewire, Set $set, bool $state): void { /** @phpstan-ignore-next-line */ $entitiesComponents = collect($livewire->form->getFlatComponents()) ->filter(fn (mixed $component): bool => $component instanceof CheckboxList); if ($state) { $entitiesComponents->each(function (CheckboxList $component) use ($set): void { $set($component->getName(), array_keys($component->getOptions())); }); } else { $entitiesComponents->each(fn (CheckboxList $component) => $component->state([])); } } }