schema([ TextInput::make('slug') ->label(__('admin.common.slug')) ->maxLength(255) ->unique(ignoreRecord: true) ->required(), Select::make('event_type_id') ->relationship('eventType', 'name') ->getOptionLabelFromRecordUsing(fn (EventType $record) => is_array($record->name) ? ($record->name['de'] ?? $record->name['en'] ?? __('admin.common.unnamed')) : $record->name) ->searchable() ->preload() ->label(__('admin.task_collections.fields.event_type_optional')), SchemaTabs::make('content_tabs') ->label(__('admin.task_collections.fields.content_localization')) ->tabs([ SchemaTab::make(__('admin.common.german')) ->icon('heroicon-o-language') ->schema([ TextInput::make('name_translations.de') ->label(__('admin.task_collections.fields.name_de')) ->required(), MarkdownEditor::make('description_translations.de') ->label(__('admin.task_collections.fields.description_de')) ->columnSpanFull(), ]), SchemaTab::make(__('admin.common.english')) ->icon('heroicon-o-language') ->schema([ TextInput::make('name_translations.en') ->label(__('admin.task_collections.fields.name_en')) ->required(), MarkdownEditor::make('description_translations.en') ->label(__('admin.task_collections.fields.description_en')) ->columnSpanFull(), ]), ]) ->columnSpanFull(), Toggle::make('is_default') ->label(__('admin.task_collections.fields.is_default')) ->default(false), TextInput::make('position') ->label(__('admin.task_collections.fields.position')) ->numeric() ->default(0), ]) ->columns(2); } public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('id') ->label('#') ->sortable(), TextColumn::make('name') ->label(__('admin.task_collections.table.name')) ->getStateUsing(fn (TaskCollection $record) => static::formatTranslation($record->name_translations)) ->searchable(['name_translations->de', 'name_translations->en']) ->limit(60), TextColumn::make('eventType.name') ->label(__('admin.task_collections.table.event_type')) ->getStateUsing(function (TaskCollection $record) { $value = optional($record->eventType)->name; if (is_array($value)) { $locale = app()->getLocale(); return $value[$locale] ?? ($value['de'] ?? ($value['en'] ?? '')); } return (string) ($value ?? ''); }) ->toggleable(), TextColumn::make('slug') ->label(__('admin.task_collections.table.slug')) ->toggleable() ->searchable(), IconColumn::make('is_default') ->label(__('admin.task_collections.table.is_default')) ->boolean(), TextColumn::make('position') ->label(__('admin.task_collections.table.position')) ->sortable(), TextColumn::make('tasks_count') ->label(__('admin.task_collections.table.tasks')) ->sortable(), TextColumn::make('events_count') ->label(__('admin.task_collections.table.events')) ->sortable(), ]) ->filters([ SelectFilter::make('event_type_id') ->label(__('admin.task_collections.table.event_type')) ->relationship( 'eventType', 'name', fn (Builder $query): Builder => $query->orderBy('name->de') ) ->getOptionLabelFromRecordUsing(fn (EventType $record) => is_array($record->name) ? ($record->name['de'] ?? $record->name['en'] ?? __('admin.common.unnamed')) : $record->name), SelectFilter::make('is_default') ->label(__('admin.task_collections.table.is_default')) ->options([ '1' => __('admin.common.yes'), '0' => __('admin.common.no'), ]), ]) ->recordActions([ Actions\EditAction::make() ->mutateDataUsing(fn (array $data, TaskCollection $record): array => static::normalizeData($data, $record)) ->after(fn (array $data, TaskCollection $record) => app(SuperAdminAuditLogger::class)->recordModelMutation( 'updated', $record, SuperAdminAuditLogger::fieldsMetadata($data), static::class )), Actions\DeleteAction::make() ->after(fn (TaskCollection $record) => app(SuperAdminAuditLogger::class)->recordModelMutation( 'deleted', $record, source: static::class )), ]) ->bulkActions([ Actions\DeleteBulkAction::make() ->after(function (Collection $records): void { $logger = app(SuperAdminAuditLogger::class); foreach ($records as $record) { $logger->recordModelMutation( 'deleted', $record, source: static::class ); } }), ]); } public static function getNavigationLabel(): string { return __('admin.task_collections.menu'); } public static function getNavigationGroup(): UnitEnum|string|null { return __('admin.nav.curation'); } public static function getEloquentQuery(): Builder { return parent::getEloquentQuery() ->whereNull('tenant_id') ->with('eventType') ->withCount(['tasks', 'events']); } /** * @param array $data */ public static function normalizeData(array $data, ?TaskCollection $record = null): array { $data['tenant_id'] = null; $data['slug'] = static::resolveSlug($data, $record); return $data; } /** * @param array $data */ protected static function resolveSlug(array $data, ?TaskCollection $record = null): string { $rawSlug = trim((string) ($data['slug'] ?? '')); $translations = Arr::wrap($data['name_translations'] ?? []); $fallbackName = (string) ($translations['en'] ?? $translations['de'] ?? ''); $base = $rawSlug !== '' ? $rawSlug : $fallbackName; $slugBase = Str::slug($base) ?: 'collection'; $query = TaskCollection::query()->where('slug', $slugBase); if ($record) { $query->whereKeyNot($record->getKey()); } if (! $query->exists()) { return $slugBase; } do { $candidate = $slugBase.'-'.Str::random(4); $candidateQuery = TaskCollection::query()->where('slug', $candidate); if ($record) { $candidateQuery->whereKeyNot($record->getKey()); } } while ($candidateQuery->exists()); return $candidate; } /** * @param array|null $translations */ protected static function formatTranslation(?array $translations): string { if (! is_array($translations)) { return ''; } $locale = app()->getLocale(); return $translations[$locale] ?? ($translations['de'] ?? ($translations['en'] ?? Arr::first($translations) ?? '')); } public static function getPages(): array { return [ 'index' => ListTaskCollections::route('/'), 'create' => CreateTaskCollection::route('/create'), 'edit' => EditTaskCollection::route('/{record}/edit'), ]; } public static function getRelations(): array { return [ TasksRelationManager::class, ]; } }