From 58d9ef34ab0030d901e01915a3203e192141aadf Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Wed, 15 Oct 2025 21:43:24 +0200 Subject: [PATCH] fixed errors in event and tenant resources --- app/Filament/Blog/Resources/PostResource.php | 153 +++++++----------- .../Resources/PostResource/Pages/EditPost.php | 4 +- app/Filament/Resources/EventResource.php | 11 +- app/Filament/Resources/TenantResource.php | 8 +- .../PackagePurchasesRelationManager.php | 2 +- app/Models/BlogPost.php | 9 +- app/Models/Event.php | 31 +++- app/Models/EventPackage.php | 4 +- 8 files changed, 116 insertions(+), 106 deletions(-) diff --git a/app/Filament/Blog/Resources/PostResource.php b/app/Filament/Blog/Resources/PostResource.php index 57d402c..e7b3292 100644 --- a/app/Filament/Blog/Resources/PostResource.php +++ b/app/Filament/Blog/Resources/PostResource.php @@ -33,6 +33,7 @@ use Filament\Tables\Filters\TernaryFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use Illuminate\Support\Arr; use Illuminate\Support\Str; use Filament\Schemas\Components\Tabs as SchemaTabs; use Filament\Schemas\Components\Tabs\Tab as SchemaTab; @@ -58,7 +59,7 @@ class PostResource extends Resource ->tabs([ SchemaTab::make('Deutsch') ->schema([ - TextInput::make('title_de') + TextInput::make('title.de') ->label('Titel') ->required() ->maxLength(255) @@ -70,17 +71,17 @@ class PostResource extends Resource $set('slug', Str::slug($state)); }), - MarkdownEditor::make('content_de') + MarkdownEditor::make('content.de') ->label('Inhalt') ->required() ->columnSpanFull(), - TextInput::make('excerpt_de') + TextInput::make('excerpt.de') ->label('Auszug') ->maxLength(255), - TextInput::make('meta_title_de') + TextInput::make('meta_title.de') ->label('Meta-Titel') ->maxLength(255), - Textarea::make('meta_description_de') + Textarea::make('meta_description.de') ->label('Meta-Beschreibung') ->maxLength(65535) ->columnSpanFull(), @@ -88,19 +89,19 @@ class PostResource extends Resource ->columns(2), SchemaTab::make('Englisch') ->schema([ - TextInput::make('title_en') + TextInput::make('title.en') ->label('Titel') ->maxLength(255), - MarkdownEditor::make('content_en') + MarkdownEditor::make('content.en') ->label('Inhalt') ->columnSpanFull(), - TextInput::make('excerpt_en') + TextInput::make('excerpt.en') ->label('Auszug') ->maxLength(255), - TextInput::make('meta_title_en') + TextInput::make('meta_title.en') ->label('Meta-Titel') ->maxLength(255), - Textarea::make('meta_description_en') + Textarea::make('meta_description.en') ->label('Meta-Beschreibung') ->maxLength(65535) ->columnSpanFull(), @@ -119,9 +120,15 @@ class PostResource extends Resource ->image() ->directory('blog') ->visibility('public'), - Select::make('category_id') + Select::make('blog_category_id') ->label('Kategorie') - ->relationship('category', 'name_de') + ->options(fn () => BlogCategory::query() + ->orderBy('slug') + ->get() + ->mapWithKeys(fn (BlogCategory $category) => [ + $category->getKey() => $category->name['de'] ?? $category->name['en'] ?? $category->slug, + ])->toArray()) + ->searchable() ->required() ->preload() ->createOptionForm([ @@ -130,12 +137,31 @@ class PostResource extends Resource ->required() ->maxLength(255) ->afterStateUpdated(fn (Set $set, $state) => $set('name_en', $state)), + TextInput::make('name_en') + ->label('Name (EN)') + ->maxLength(255), TextInput::make('slug') ->label('Slug') ->required() ->unique(\App\Models\BlogCategory::class, 'slug', ignoreRecord: true) ->maxLength(255), - ]), + ]) + ->createOptionUsing(function (array $data) { + $nameDe = $data['name_de'] ?? null; + $nameEn = $data['name_en'] ?? null; + + $category = BlogCategory::create([ + 'slug' => $data['slug'], + 'is_visible' => true, + 'name' => [ + 'de' => $nameDe ?: ($nameEn ?: $data['slug']), + 'en' => $nameEn ?: ($nameDe ?: $data['slug']), + ], + 'description' => null, + ]); + + return $category->getKey(); + }), ]) ->columns(2), Section::make('Veröffentlichung') @@ -152,83 +178,6 @@ class PostResource extends Resource public static function mutateFormDataBeforeCreate(array $data): array { - $data['translations'] = [ - 'title' => [ - 'de' => $data['title_de'] ?? '', - 'en' => $data['title_en'] ?? '', - ], - 'content' => [ - 'de' => $data['content_de'] ?? '', - 'en' => $data['content_en'] ?? '', - ], - 'excerpt' => [ - 'de' => $data['excerpt_de'] ?? '', - 'en' => $data['excerpt_en'] ?? '', - ], - 'meta_title' => [ - 'de' => $data['meta_title_de'] ?? '', - 'en' => $data['meta_title_en'] ?? '', - ], - 'meta_description' => [ - 'de' => $data['meta_description_de'] ?? '', - 'en' => $data['meta_description_en'] ?? '', - ], - ]; - - unset($data['title_de'], $data['title_en'], $data['content_de'], $data['content_en'], $data['excerpt_de'], $data['excerpt_en'], $data['meta_title_de'], $data['meta_title_en'], $data['meta_description_de'], $data['meta_description_en']); - - return $data; - } - - public static function mutateFormDataBeforeFill(array $data): array - { - $record = static::getModel()::find(request()?->route()?->parameter('record') ?? request()?->input('record_id') ?? null); - - if (!$record) { - return $data; - } - - $data['title_de'] = $record->getTranslation('title', 'de'); - $data['title_en'] = $record->getTranslation('title', 'en'); - $data['content_de'] = $record->getTranslation('content', 'de'); - $data['content_en'] = $record->getTranslation('content', 'en'); - $data['excerpt_de'] = $record->getTranslation('excerpt', 'de'); - $data['excerpt_en'] = $record->getTranslation('excerpt', 'en'); - $data['meta_title_de'] = $record->getTranslation('meta_title', 'de'); - $data['meta_title_en'] = $record->getTranslation('meta_title', 'en'); - $data['meta_description_de'] = $record->getTranslation('meta_description', 'de'); - $data['meta_description_en'] = $record->getTranslation('meta_description', 'en'); - - return $data; - } - - public static function mutateFormDataBeforeSave(array $data): array - { - $data['translations'] = [ - 'title' => [ - 'de' => $data['title_de'] ?? '', - 'en' => $data['title_en'] ?? '', - ], - 'content' => [ - 'de' => $data['content_de'] ?? '', - 'en' => $data['content_en'] ?? '', - ], - 'excerpt' => [ - 'de' => $data['excerpt_de'] ?? '', - 'en' => $data['excerpt_en'] ?? '', - ], - 'meta_title' => [ - 'de' => $data['meta_title_de'] ?? '', - 'en' => $data['meta_title_en'] ?? '', - ], - 'meta_description' => [ - 'de' => $data['meta_description_de'] ?? '', - 'en' => $data['meta_description_en'] ?? '', - ], - ]; - - unset($data['title_de'], $data['title_en'], $data['content_de'], $data['content_en'], $data['excerpt_de'], $data['excerpt_en'], $data['meta_title_de'], $data['meta_title_en'], $data['meta_description_de'], $data['meta_description_en']); - return $data; } @@ -241,9 +190,29 @@ class PostResource extends Resource ->getStateUsing(fn ($record) => $record->getTranslation('title', 'de')) ->searchable() ->sortable(), - TextColumn::make('category.name_de') + TextColumn::make('category_label') ->label('Kategorie') ->badge() + ->getStateUsing(function ($record) { + $raw = $record->category?->name ?? null; + + if (is_string($raw) && $raw !== '') { + $decoded = json_decode($raw, true); + if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { + $raw = $decoded; + } + } + + if (is_array($raw)) { + return $raw['de'] ?? $raw['en'] ?? '—'; + } + + if (is_string($raw) && $raw !== '') { + return $raw; + } + + return '—'; + }) ->color('primary'), IconColumn::make('is_published') ->label('Veröffentlicht') @@ -301,4 +270,4 @@ class PostResource extends Resource SoftDeletingScope::class, ]); } -} \ No newline at end of file +} diff --git a/app/Filament/Blog/Resources/PostResource/Pages/EditPost.php b/app/Filament/Blog/Resources/PostResource/Pages/EditPost.php index d5c24c3..5091c01 100644 --- a/app/Filament/Blog/Resources/PostResource/Pages/EditPost.php +++ b/app/Filament/Blog/Resources/PostResource/Pages/EditPost.php @@ -17,4 +17,6 @@ class EditPost extends EditRecord Actions\DeleteAction::make(), ]; } -} \ No newline at end of file + + // default behaviour genügt jetzt – Daten liegen direkt in den JSON-Feldern +} diff --git a/app/Filament/Resources/EventResource.php b/app/Filament/Resources/EventResource.php index 031070a..41c9935 100644 --- a/app/Filament/Resources/EventResource.php +++ b/app/Filament/Resources/EventResource.php @@ -45,7 +45,7 @@ class EventResource extends Resource ->options(Tenant::query()->pluck('name', 'id')) ->searchable() ->required(), - TextInput::make('name') + TextInput::make('name.de') ->label(__('admin.events.fields.name')) ->required() ->maxLength(255), @@ -77,7 +77,10 @@ class EventResource extends Resource KeyValue::make('settings') ->label(__('admin.events.fields.settings')) ->keyLabel(__('admin.common.key')) - ->valueLabel(__('admin.common.value')), + ->valueLabel(__('admin.common.value')) + ->addButtonLabel(__('admin.common.add')) + ->reorderable() + ->columnSpanFull(), ])->columns(2); } @@ -87,7 +90,9 @@ class EventResource extends Resource ->columns([ Tables\Columns\TextColumn::make('id')->sortable(), Tables\Columns\TextColumn::make('tenant.name')->label(__('admin.events.table.tenant'))->searchable(), - Tables\Columns\TextColumn::make('name')->limit(30), + Tables\Columns\TextColumn::make('name.de') + ->label(__('admin.events.fields.name')) + ->limit(30), Tables\Columns\TextColumn::make('slug')->searchable(), Tables\Columns\TextColumn::make('date')->date(), Tables\Columns\IconColumn::make('is_active')->boolean(), diff --git a/app/Filament/Resources/TenantResource.php b/app/Filament/Resources/TenantResource.php index 536bb07..755d672 100644 --- a/app/Filament/Resources/TenantResource.php +++ b/app/Filament/Resources/TenantResource.php @@ -45,7 +45,7 @@ class TenantResource extends Resource ->required() ->readOnly() ->dehydrated(false) - ->getStateUsing(fn (Tenant $record) => $record->user->full_name), + ->default(fn (Tenant $record) => $record->user?->full_name ?? 'Unbekannt'), TextInput::make('slug') ->label(__('admin.tenants.fields.slug')) ->required() @@ -64,7 +64,7 @@ class TenantResource extends Resource ->readOnly(), Select::make('active_reseller_package_id') ->label(__('admin.tenants.fields.active_reseller_package')) - ->relationship('activeResellerPackage', 'name') + ->relationship('activeResellerPackage.package', 'name') ->searchable() ->preload() ->nullable(), @@ -72,7 +72,7 @@ class TenantResource extends Resource ->label(__('admin.tenants.fields.remaining_events')) ->readOnly() ->dehydrated(false) - ->getStateUsing(fn (Tenant $record) => $record->activeResellerPackage?->remaining_events ?? 0), + ->default(fn (Tenant $record) => $record->activeResellerPackage?->remaining_events ?? 0), Toggle::make('is_active') ->label(__('admin.tenants.fields.is_active')) ->default(true), @@ -96,7 +96,7 @@ class TenantResource extends Resource ->label(__('admin.tenants.fields.name')) ->searchable() ->sortable() - ->getStateUsing(fn (Tenant $record) => $record->user->full_name), + ->getStateUsing(fn (Tenant $record) => $record->user?->full_name ?? 'Unbekannt'), Tables\Columns\TextColumn::make('slug')->searchable(), Tables\Columns\TextColumn::make('contact_email'), Tables\Columns\TextColumn::make('active_reseller_package_id') diff --git a/app/Filament/Resources/TenantResource/RelationManagers/PackagePurchasesRelationManager.php b/app/Filament/Resources/TenantResource/RelationManagers/PackagePurchasesRelationManager.php index ec6daee..cc3708c 100644 --- a/app/Filament/Resources/TenantResource/RelationManagers/PackagePurchasesRelationManager.php +++ b/app/Filament/Resources/TenantResource/RelationManagers/PackagePurchasesRelationManager.php @@ -22,7 +22,7 @@ use Illuminate\Database\Eloquent\Relations\Relation; class PackagePurchasesRelationManager extends RelationManager { - protected static string $relationship = 'packagePurchases'; + protected static string $relationship = 'purchases'; protected static ?string $title = 'Package-Käufe'; diff --git a/app/Models/BlogPost.php b/app/Models/BlogPost.php index a592daf..d69b11e 100644 --- a/app/Models/BlogPost.php +++ b/app/Models/BlogPost.php @@ -38,6 +38,11 @@ class BlogPost extends Model 'banner', 'published_at', 'is_published', + 'title', + 'content', + 'excerpt', + 'meta_title', + 'meta_description', 'translations', ]; @@ -69,7 +74,7 @@ class BlogPost extends Model $environment->addExtension(new TaskListExtension()); $converter = new MarkdownConverter($environment); - return $converter->convert($markdown); + return (string) $converter->convert($markdown); }); } @@ -92,4 +97,4 @@ class BlogPost extends Model { return $this->belongsTo(BlogAuthor::class, 'blog_author_id'); } -} \ No newline at end of file +} diff --git a/app/Models/Event.php b/app/Models/Event.php index 2b96d62..f5a16d6 100644 --- a/app/Models/Event.php +++ b/app/Models/Event.php @@ -16,7 +16,6 @@ class Event extends Model protected $guarded = []; protected $casts = [ 'date' => 'datetime', - 'settings' => 'array', 'is_active' => 'boolean', 'name' => 'array', 'description' => 'array', @@ -58,6 +57,11 @@ class Event extends Model return $this->belongsTo(EventPackage::class); } + public function eventPackages(): HasMany + { + return $this->hasMany(EventPackage::class); + } + public function joinTokens(): HasMany { return $this->hasMany(EventJoinToken::class); @@ -85,4 +89,29 @@ class Event extends Model return $this->eventPackage->canUploadPhoto(); } + + public function getSettingsAttribute($value): array + { + if (is_array($value)) { + return $value; + } + + if (is_string($value) && $value !== '') { + $decoded = json_decode($value, true); + + return is_array($decoded) ? $decoded : []; + } + + return []; + } + + public function setSettingsAttribute($value): void + { + if (is_string($value)) { + $decoded = json_decode($value, true); + $value = is_array($decoded) ? $decoded : []; + } + + $this->attributes['settings'] = json_encode($value ?? []); + } } diff --git a/app/Models/EventPackage.php b/app/Models/EventPackage.php index 4b279e0..74a533e 100644 --- a/app/Models/EventPackage.php +++ b/app/Models/EventPackage.php @@ -69,13 +69,13 @@ class EventPackage extends Model public function getRemainingPhotosAttribute(): int { $max = $this->package->max_photos ?? 0; - return max(0, $this->max_photos - $this->used_photos); + return max(0, $max - $this->used_photos); } public function getRemainingGuestsAttribute(): int { $max = $this->package->max_guests ?? 0; - return max(0, $this->max_guests - $this->used_guests); + return max(0, $max - $this->used_guests); } protected static function boot()