Fix Event & EventType resource issues and apply formatting

- Fix EventType deletion error handling (constraint violations)
- Fix Event update error (package_id column missing)
- Fix Event Type dropdown options (JSON display issue)
- Fix EventPackagesRelationManager query error
- Add missing translations for deletion errors
- Apply Pint formatting
This commit is contained in:
Codex Agent
2026-01-21 10:34:06 +01:00
parent 198fbf6751
commit fa33e7cbcf
112 changed files with 334 additions and 280 deletions

View File

@@ -7,7 +7,6 @@ use App\Models\Tenant;
use App\Models\User; use App\Models\User;
use Illuminate\Console\Attributes\AsCommand; use Illuminate\Console\Attributes\AsCommand;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
#[AsCommand(name: 'tenant:attach-demo-event')] #[AsCommand(name: 'tenant:attach-demo-event')]
class AttachDemoEvent extends Command class AttachDemoEvent extends Command
@@ -25,10 +24,12 @@ class AttachDemoEvent extends Command
{ {
if (! \Illuminate\Support\Facades\Schema::hasTable('events')) { if (! \Illuminate\Support\Facades\Schema::hasTable('events')) {
$this->error("Table 'events' does not exist. Run: php artisan migrate"); $this->error("Table 'events' does not exist. Run: php artisan migrate");
return self::FAILURE; return self::FAILURE;
} }
if (! \Illuminate\Support\Facades\Schema::hasColumn('events', 'tenant_id')) { if (! \Illuminate\Support\Facades\Schema::hasColumn('events', 'tenant_id')) {
$this->error("Column 'events.tenant_id' does not exist. Add it and rerun. Suggested: create a migration to add a nullable foreignId to tenants."); $this->error("Column 'events.tenant_id' does not exist. Add it and rerun. Suggested: create a migration to add a nullable foreignId to tenants.");
return self::FAILURE; return self::FAILURE;
} }
$tenant = null; $tenant = null;
@@ -45,6 +46,7 @@ class AttachDemoEvent extends Command
} }
if (! $tenant) { if (! $tenant) {
$this->error('Tenant not found. Provide --tenant-slug or a user with tenant_id via --tenant-email.'); $this->error('Tenant not found. Provide --tenant-slug or a user with tenant_id via --tenant-email.');
return self::FAILURE; return self::FAILURE;
} }
@@ -67,12 +69,14 @@ class AttachDemoEvent extends Command
if (! $event) { if (! $event) {
$this->error('Event not found. Provide --event-id or --event-slug.'); $this->error('Event not found. Provide --event-id or --event-slug.');
return self::FAILURE; return self::FAILURE;
} }
// Idempotent update // Idempotent update
if ((int) $event->tenant_id === (int) $tenant->id) { if ((int) $event->tenant_id === (int) $tenant->id) {
$this->info("Event #{$event->id} already attached to tenant #{$tenant->id} ({$tenant->slug})."); $this->info("Event #{$event->id} already attached to tenant #{$tenant->id} ({$tenant->slug}).");
return self::SUCCESS; return self::SUCCESS;
} }
@@ -80,6 +84,7 @@ class AttachDemoEvent extends Command
$event->save(); $event->save();
$this->info("Attached event #{$event->id} ({$event->slug}) to tenant #{$tenant->id} ({$tenant->slug})."); $this->info("Attached event #{$event->id} ({$event->slug}) to tenant #{$tenant->id} ({$tenant->slug}).");
return self::SUCCESS; return self::SUCCESS;
} }
} }

View File

@@ -10,22 +10,27 @@ use Illuminate\Support\Facades\Storage;
class BackfillThumbnails extends Command class BackfillThumbnails extends Command
{ {
protected $signature = 'media:backfill-thumbnails {--limit=500}'; protected $signature = 'media:backfill-thumbnails {--limit=500}';
protected $description = 'Generate thumbnails for photos missing thumbnail_path or where thumbnail equals original.'; protected $description = 'Generate thumbnails for photos missing thumbnail_path or where thumbnail equals original.';
public function handle(): int public function handle(): int
{ {
$limit = (int) $this->option('limit'); $limit = (int) $this->option('limit');
$rows = DB::table('photos') $rows = DB::table('photos')
->select(['id','event_id','file_path','thumbnail_path']) ->select(['id', 'event_id', 'file_path', 'thumbnail_path'])
->orderBy('id') ->orderBy('id')
->limit($limit) ->limit($limit)
->get(); ->get();
$count = 0; $count = 0;
foreach ($rows as $r) { foreach ($rows as $r) {
$orig = $this->relativeFromUrl((string)$r->file_path); $orig = $this->relativeFromUrl((string) $r->file_path);
$thumb = (string)($r->thumbnail_path ?? ''); $thumb = (string) ($r->thumbnail_path ?? '');
if ($thumb && $thumb !== $r->file_path) continue; // already set to different thumb if ($thumb && $thumb !== $r->file_path) {
if (! $orig) continue; continue;
} // already set to different thumb
if (! $orig) {
continue;
}
$baseName = pathinfo($orig, PATHINFO_FILENAME); $baseName = pathinfo($orig, PATHINFO_FILENAME);
$destRel = "events/{$r->event_id}/photos/thumbs/{$baseName}_thumb.jpg"; $destRel = "events/{$r->event_id}/photos/thumbs/{$baseName}_thumb.jpg";
$made = ImageHelper::makeThumbnailOnDisk('public', $orig, $destRel, 640, 82); $made = ImageHelper::makeThumbnailOnDisk('public', $orig, $destRel, 640, 82);
@@ -39,6 +44,7 @@ class BackfillThumbnails extends Command
} }
} }
$this->info("Done. Thumbnails generated: {$count}"); $this->info("Done. Thumbnails generated: {$count}");
return self::SUCCESS; return self::SUCCESS;
} }
@@ -49,6 +55,7 @@ class BackfillThumbnails extends Command
if (str_starts_with($p, '/storage/')) { if (str_starts_with($p, '/storage/')) {
return substr($p, strlen('/storage/')); return substr($p, strlen('/storage/'));
} }
return null; return null;
} }
} }

View File

@@ -4,15 +4,15 @@ namespace App\Console\Commands;
use App\Models\PackagePurchase; use App\Models\PackagePurchase;
use App\Models\Tenant; use App\Models\Tenant;
use App\Models\User;
use App\Models\TenantPackage; use App\Models\TenantPackage;
use App\Models\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
class MigrateLegacyPurchases extends Command class MigrateLegacyPurchases extends Command
{ {
protected $signature = 'packages:migrate-legacy'; protected $signature = 'packages:migrate-legacy';
protected $description = 'Migrate legacy purchases to new system with temp tenants'; protected $description = 'Migrate legacy purchases to new system with temp tenants';
public function handle() public function handle()
@@ -21,19 +21,20 @@ class MigrateLegacyPurchases extends Command
if ($legacyPurchases->isEmpty()) { if ($legacyPurchases->isEmpty()) {
$this->info('No legacy purchases found.'); $this->info('No legacy purchases found.');
return 0; return 0;
} }
$this->info("Found {$legacyPurchases->count()} legacy purchases."); $this->info("Found {$legacyPurchases->count()} legacy purchases.");
foreach ($legacyPurchases as $purchase) { foreach ($legacyPurchases as $purchase) {
if (!$purchase->user_id) { if (! $purchase->user_id) {
// Create temp user if no user // Create temp user if no user
$tempUser = User::create([ $tempUser = User::create([
'name' => 'Legacy User ' . $purchase->id, 'name' => 'Legacy User '.$purchase->id,
'email' => 'legacy' . $purchase->id . '@fotospiel.local', 'email' => 'legacy'.$purchase->id.'@fotospiel.local',
'password' => Hash::make('legacy'), 'password' => Hash::make('legacy'),
'username' => 'legacy' . $purchase->id, 'username' => 'legacy'.$purchase->id,
'first_name' => 'Legacy', 'first_name' => 'Legacy',
'last_name' => 'User', 'last_name' => 'User',
'address' => 'Legacy Address', 'address' => 'Legacy Address',
@@ -43,7 +44,7 @@ class MigrateLegacyPurchases extends Command
$tempTenant = Tenant::create([ $tempTenant = Tenant::create([
'user_id' => $tempUser->id, 'user_id' => $tempUser->id,
'name' => 'Legacy Tenant ' . $purchase->id, 'name' => 'Legacy Tenant '.$purchase->id,
'status' => 'active', 'status' => 'active',
]); ]);
@@ -73,6 +74,7 @@ class MigrateLegacyPurchases extends Command
} }
$this->info('Legacy migration completed.'); $this->info('Legacy migration completed.');
return 0; return 0;
} }
} }

View File

@@ -62,7 +62,7 @@ class SendAbandonedCheckoutReminders extends Command
if ($this->shouldSendReminder($checkout, $stage)) { if ($this->shouldSendReminder($checkout, $stage)) {
$resumeUrl = $this->generateResumeUrl($checkout); $resumeUrl = $this->generateResumeUrl($checkout);
if (!$isDryRun) { if (! $isDryRun) {
$mailLocale = $checkout->user->preferred_locale ?? config('app.locale'); $mailLocale = $checkout->user->preferred_locale ?? config('app.locale');
Mail::to($checkout->user) Mail::to($checkout->user)
@@ -86,8 +86,8 @@ class SendAbandonedCheckoutReminders extends Command
$totalProcessed++; $totalProcessed++;
} }
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error("Failed to send {$stage} reminder for checkout {$checkout->id}: " . $e->getMessage()); Log::error("Failed to send {$stage} reminder for checkout {$checkout->id}: ".$e->getMessage());
$this->error(" ❌ Failed to process checkout {$checkout->id}: " . $e->getMessage()); $this->error(" ❌ Failed to process checkout {$checkout->id}: ".$e->getMessage());
} }
} }
} }
@@ -98,7 +98,7 @@ class SendAbandonedCheckoutReminders extends Command
->count(); ->count();
if ($oldCheckouts > 0) { if ($oldCheckouts > 0) {
if (!$isDryRun) { if (! $isDryRun) {
AbandonedCheckoutModel::where('abandoned_at', '<', now()->subDays(30)) AbandonedCheckoutModel::where('abandoned_at', '<', now()->subDays(30))
->where('converted', false) ->where('converted', false)
->delete(); ->delete();
@@ -108,10 +108,10 @@ class SendAbandonedCheckoutReminders extends Command
} }
} }
$this->info("✅ Reminder process completed!"); $this->info('✅ Reminder process completed!');
$this->info(" Processed: {$totalProcessed} checkouts"); $this->info(" Processed: {$totalProcessed} checkouts");
if (!$isDryRun) { if (! $isDryRun) {
$this->info(" Sent: {$totalSent} reminder emails"); $this->info(" Sent: {$totalSent} reminder emails");
} else { } else {
$this->info(" Would send: {$totalSent} reminder emails"); $this->info(" Would send: {$totalSent} reminder emails");
@@ -131,12 +131,12 @@ class SendAbandonedCheckoutReminders extends Command
} }
// User existiert noch? // User existiert noch?
if (!$checkout->user) { if (! $checkout->user) {
return false; return false;
} }
// Package existiert noch? // Package existiert noch?
if (!$checkout->package) { if (! $checkout->package) {
return false; return false;
} }

View File

@@ -5,8 +5,6 @@ namespace App\Exports;
use App\Models\EventPurchase; use App\Models\EventPurchase;
use Filament\Actions\Exports\Exporter; use Filament\Actions\Exports\Exporter;
use Filament\Actions\Exports\Models\Export; use Filament\Actions\Exports\Models\Export;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
class EventPurchaseExporter extends Exporter class EventPurchaseExporter extends Exporter
{ {
@@ -28,7 +26,6 @@ class EventPurchaseExporter extends Exporter
]; ];
} }
public static function getCompletedNotificationBody(Export $export): string public static function getCompletedNotificationBody(Export $export): string
{ {
$body = "Your Event Purchases export has completed and is ready for download. {$export->successful_rows} purchases were exported."; $body = "Your Event Purchases export has completed and is ready for download. {$export->successful_rows} purchases were exported.";

View File

@@ -13,7 +13,9 @@ use Illuminate\Support\Facades\Storage;
class ImportEmotions extends Page class ImportEmotions extends Page
{ {
protected static string $resource = EmotionResource::class; protected static string $resource = EmotionResource::class;
protected string $view = 'filament.resources.emotion-resource.pages.import-emotions'; protected string $view = 'filament.resources.emotion-resource.pages.import-emotions';
protected ?string $heading = null; protected ?string $heading = null;
public ?string $file = null; public ?string $file = null;
@@ -36,6 +38,7 @@ class ImportEmotions extends Page
$path = $this->form->getState()['file'] ?? null; $path = $this->form->getState()['file'] ?? null;
if (! $path || ! Storage::disk('public')->exists($path)) { if (! $path || ! Storage::disk('public')->exists($path)) {
Notification::make()->danger()->title(__('admin.notifications.file_not_found'))->send(); Notification::make()->danger()->title(__('admin.notifications.file_not_found'))->send();
return; return;
} }

View File

@@ -65,14 +65,15 @@ class EventResource extends Resource
->required(), ->required(),
Select::make('event_type_id') Select::make('event_type_id')
->label(__('admin.events.fields.type')) ->label(__('admin.events.fields.type'))
->options(EventType::query()->pluck('name', 'id')) ->options(fn () => EventType::all()->pluck('name.de', 'id'))
->searchable(), ->searchable(),
Select::make('package_id') Select::make('package_id')
->label(__('admin.events.fields.package')) ->label(__('admin.events.fields.package'))
->options(\App\Models\Package::query()->where('type', 'endcustomer')->pluck('name', 'id')) ->options(\App\Models\Package::query()->where('type', 'endcustomer')->pluck('name', 'id'))
->searchable() ->searchable()
->preload() ->preload()
->required(), ->required()
->visibleOn('create'),
TextInput::make('default_locale') TextInput::make('default_locale')
->label(__('admin.events.fields.default_locale')) ->label(__('admin.events.fields.default_locale'))
->default('de') ->default('de')

View File

@@ -8,4 +8,25 @@ use App\Filament\Resources\Pages\AuditedCreateRecord;
class CreateEvent extends AuditedCreateRecord class CreateEvent extends AuditedCreateRecord
{ {
protected static string $resource = EventResource::class; protected static string $resource = EventResource::class;
public ?int $packageId = null;
protected function mutateFormDataBeforeCreate(array $data): array
{
$this->packageId = $data['package_id'] ?? null;
unset($data['package_id']);
return $data;
}
protected function afterCreate(): void
{
if ($this->packageId) {
$this->record->eventPackages()->create([
'package_id' => $this->packageId,
]);
}
parent::afterCreate();
}
} }

View File

@@ -19,7 +19,6 @@ use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
class EventPackagesRelationManager extends RelationManager class EventPackagesRelationManager extends RelationManager
{ {
@@ -59,6 +58,7 @@ class EventPackagesRelationManager extends RelationManager
public function table(Table $table): Table public function table(Table $table): Table
{ {
return $table return $table
->modifyQueryUsing(fn (Builder $query) => $query->with('package'))
->recordTitleAttribute('package.name') ->recordTitleAttribute('package.name')
->columns([ ->columns([
TextColumn::make('package.name') TextColumn::make('package.name')
@@ -147,9 +147,4 @@ class EventPackagesRelationManager extends RelationManager
{ {
return __('admin.events.relation_managers.event_packages.title'); return __('admin.events.relation_managers.event_packages.title');
} }
public function getTableQuery(): Builder|Relation
{
return parent::getTableQuery()->with('package');
}
} }

View File

@@ -113,18 +113,64 @@ class EventTypeResource extends Resource
SuperAdminAuditLogger::fieldsMetadata($data), SuperAdminAuditLogger::fieldsMetadata($data),
static::class static::class
)), )),
]) Actions\DeleteAction::make()
->bulkActions([ ->action(function (EventType $record, Actions\DeleteAction $action) {
Actions\DeleteBulkAction::make() try {
->after(function (Collection $records): void { $record->delete();
$logger = app(SuperAdminAuditLogger::class); } catch (\Exception $e) {
$isConstraint = ($e instanceof \Illuminate\Database\QueryException && ($e->getCode() == 23000 || ($e->errorInfo[0] ?? '') == 23000));
foreach ($records as $record) { if ($isConstraint) {
$logger->recordModelMutation( \Filament\Notifications\Notification::make()
->title(__('admin.common.error'))
->body(__('admin.event_types.messages.delete_constraint_error'))
->danger()
->send();
$action->halt();
}
throw $e;
}
})
->after(fn (EventType $record) => app(SuperAdminAuditLogger::class)->recordModelMutation(
'deleted', 'deleted',
$record, $record,
source: static::class source: static::class
); )),
])
->bulkActions([
Actions\DeleteBulkAction::make()
->action(function (Collection $records, Actions\DeleteBulkAction $action) {
$logger = app(SuperAdminAuditLogger::class);
$deletedCount = 0;
$failedCount = 0;
foreach ($records as $record) {
try {
$record->delete();
$logger->recordModelMutation('deleted', $record, source: static::class);
$deletedCount++;
} catch (\Exception $e) {
$isConstraint = ($e instanceof \Illuminate\Database\QueryException && ($e->getCode() == 23000 || ($e->errorInfo[0] ?? '') == 23000));
if ($isConstraint) {
$failedCount++;
} else {
throw $e;
}
}
}
if ($failedCount > 0) {
\Filament\Notifications\Notification::make()
->title(__('admin.common.error'))
->body(__('admin.event_types.messages.delete_constraint_error')." ($failedCount failed, $deletedCount deleted)")
->danger()
->send();
if ($deletedCount === 0) {
$action->halt();
}
} }
}), }),
]); ]);

View File

@@ -17,4 +17,3 @@ class ListMediaStorageTargets extends ListRecords
]; ];
} }
} }

View File

@@ -14,4 +14,3 @@ class ListPurchaseHistories extends ListRecords
return []; return [];
} }
} }

View File

@@ -14,4 +14,3 @@ class ViewPurchaseHistory extends ViewRecord
return []; return [];
} }
} }

View File

@@ -3,8 +3,8 @@
namespace App\Filament\SuperAdmin\Pages\Auth; namespace App\Filament\SuperAdmin\Pages\Auth;
use Filament\Auth\Pages\EditProfile as BaseEditProfile; use Filament\Auth\Pages\EditProfile as BaseEditProfile;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema; use Filament\Schemas\Schema;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;

View File

@@ -4,8 +4,8 @@ namespace App\Filament\Widgets;
use Filament\Widgets\StatsOverviewWidget as BaseWidget; use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat; use Filament\Widgets\StatsOverviewWidget\Stat;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
class PlatformStatsWidget extends BaseWidget class PlatformStatsWidget extends BaseWidget
{ {

View File

@@ -7,7 +7,6 @@ use Filament\Widgets\LineChartWidget;
class RevenueTrendWidget extends LineChartWidget class RevenueTrendWidget extends LineChartWidget
{ {
protected static ?int $sort = 1; protected static ?int $sort = 1;
protected int|string|array $columnSpan = 'full'; protected int|string|array $columnSpan = 'full';

View File

@@ -2,9 +2,9 @@
namespace App\Filament\Widgets; namespace App\Filament\Widgets;
use App\Models\Tenant;
use Filament\Tables; use Filament\Tables;
use Filament\Widgets\TableWidget as BaseWidget; use Filament\Widgets\TableWidget as BaseWidget;
use App\Models\Tenant;
class TopTenantsByUploads extends BaseWidget class TopTenantsByUploads extends BaseWidget
{ {
@@ -14,6 +14,7 @@ class TopTenantsByUploads extends BaseWidget
{ {
return __('admin.widgets.top_tenants_by_uploads.heading'); return __('admin.widgets.top_tenants_by_uploads.heading');
} }
protected ?string $pollingInterval = '60s'; protected ?string $pollingInterval = '60s';
public function table(Tables\Table $table): Tables\Table public function table(Tables\Table $table): Tables\Table
@@ -33,4 +34,3 @@ class TopTenantsByUploads extends BaseWidget
->paginated(false); ->paginated(false);
} }
} }

View File

@@ -2,8 +2,8 @@
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use SimpleSoftwareIO\QrCode\Facades\QrCode; use SimpleSoftwareIO\QrCode\Facades\QrCode;
class QrController extends BaseController class QrController extends BaseController
@@ -15,7 +15,7 @@ class QrController extends BaseController
return response('missing data', 400); return response('missing data', 400);
} }
$png = QrCode::format('png')->size(300)->generate($data); $png = QrCode::format('png')->size(300)->generate($data);
return response($png, 200, ['Content-Type' => 'image/png']); return response($png, 200, ['Content-Type' => 'image/png']);
} }
} }

View File

@@ -26,11 +26,11 @@ class LegalController extends BaseController
'allow_unsafe_links' => false, 'allow_unsafe_links' => false,
]); ]);
$environment->addExtension(new CommonMarkCoreExtension()); $environment->addExtension(new CommonMarkCoreExtension);
$environment->addExtension(new TableExtension()); $environment->addExtension(new TableExtension);
$environment->addExtension(new AutolinkExtension()); $environment->addExtension(new AutolinkExtension);
$environment->addExtension(new StrikethroughExtension()); $environment->addExtension(new StrikethroughExtension);
$environment->addExtension(new TaskListExtension()); $environment->addExtension(new TaskListExtension);
$this->markdown = new MarkdownConverter($environment); $this->markdown = new MarkdownConverter($environment);
} }

View File

@@ -7,7 +7,6 @@ use App\Models\Event;
use App\Services\Analytics\EventAnalyticsService; use App\Services\Analytics\EventAnalyticsService;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Arr;
class EventAnalyticsController extends Controller class EventAnalyticsController extends Controller
{ {
@@ -26,10 +25,10 @@ class EventAnalyticsController extends Controller
$hasAccess = in_array('advanced_analytics', $packageFeatures, true); $hasAccess = in_array('advanced_analytics', $packageFeatures, true);
if (!$hasAccess) { if (! $hasAccess) {
return response()->json([ return response()->json([
'message' => 'This feature is only available in the Premium package.', 'message' => 'This feature is only available in the Premium package.',
'code' => 'feature_locked' 'code' => 'feature_locked',
], 403); ], 403);
} }

View File

@@ -112,4 +112,3 @@ class FontController extends Controller
return $fonts; return $fonts;
} }
} }

View File

@@ -69,7 +69,7 @@ class LegalPageController extends Controller
$effectiveFrom = optional($page->effective_from); $effectiveFrom = optional($page->effective_from);
return Inertia::render('legal/Show', [ return Inertia::render('legal/Show', [
'seoTitle' => $title . ' - ' . config('app.name', 'Fotospiel'), 'seoTitle' => $title.' - '.config('app.name', 'Fotospiel'),
'title' => $title, 'title' => $title,
'content' => $this->convertMarkdownToHtml($bodyMarkdown), 'content' => $this->convertMarkdownToHtml($bodyMarkdown),
'effectiveFrom' => $effectiveFrom ? $effectiveFrom->toDateString() : null, 'effectiveFrom' => $effectiveFrom ? $effectiveFrom->toDateString() : null,
@@ -112,11 +112,11 @@ class LegalPageController extends Controller
'allow_unsafe_links' => false, 'allow_unsafe_links' => false,
]); ]);
$environment->addExtension(new CommonMarkCoreExtension()); $environment->addExtension(new CommonMarkCoreExtension);
$environment->addExtension(new TableExtension()); $environment->addExtension(new TableExtension);
$environment->addExtension(new AutolinkExtension()); $environment->addExtension(new AutolinkExtension);
$environment->addExtension(new StrikethroughExtension()); $environment->addExtension(new StrikethroughExtension);
$environment->addExtension(new TaskListExtension()); $environment->addExtension(new TaskListExtension);
$converter = new MarkdownConverter($environment); $converter = new MarkdownConverter($environment);

View File

@@ -35,7 +35,7 @@ class EventPhotoArchiveController extends Controller
abort(404, 'No approved photos available for this event.'); abort(404, 'No approved photos available for this event.');
} }
$zip = new ZipArchive(); $zip = new ZipArchive;
$tempPath = tempnam(sys_get_temp_dir(), 'fotospiel-photos-'); $tempPath = tempnam(sys_get_temp_dir(), 'fotospiel-photos-');
if ($tempPath === false || $zip->open($tempPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { if ($tempPath === false || $zip->open($tempPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
@@ -129,4 +129,3 @@ class EventPhotoArchiveController extends Controller
return false; return false;
} }
} }

View File

@@ -21,7 +21,7 @@ class SetLocale
$sessionLocale = Session::get('locale', 'de'); $sessionLocale = Session::get('locale', 'de');
// Fallback to Accept-Language header if no session // Fallback to Accept-Language header if no session
if (!in_array($sessionLocale, $supportedLocales)) { if (! in_array($sessionLocale, $supportedLocales)) {
$acceptLanguage = $request->header('Accept-Language', 'de'); $acceptLanguage = $request->header('Accept-Language', 'de');
$localeFromHeader = substr($acceptLanguage, 0, 2); $localeFromHeader = substr($acceptLanguage, 0, 2);
$sessionLocale = in_array($localeFromHeader, $supportedLocales) ? $localeFromHeader : 'de'; $sessionLocale = in_array($localeFromHeader, $supportedLocales) ? $localeFromHeader : 'de';

View File

@@ -2,11 +2,11 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use App\Support\LocaleConfig;
use Closure; use Closure;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use App\Support\LocaleConfig;
class SetLocaleFromRequest class SetLocaleFromRequest
{ {

View File

@@ -19,4 +19,3 @@ class SetLocaleFromUser
return $next($request); return $next($request);
} }
} }

View File

@@ -57,8 +57,3 @@ class ProfileUpdateRequest extends FormRequest
]; ];
} }
} }

View File

@@ -3,7 +3,6 @@
namespace App\Http\Requests\Tenant; namespace App\Http\Requests\Tenant;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class PhotoStoreRequest extends FormRequest class PhotoStoreRequest extends FormRequest
{ {

View File

@@ -88,4 +88,3 @@ class SettingsStoreRequest extends FormRequest
]); ]);
} }
} }

View File

@@ -59,6 +59,7 @@ class EmotionResource extends JsonResource
} }
$first = reset($value); $first = reset($value);
return $first !== false ? (string) $first : $fallback; return $first !== false ? (string) $first : $fallback;
} }
} }

View File

@@ -31,6 +31,7 @@ class ArchiveEventMediaAssets implements ShouldQueue
if (! $event) { if (! $event) {
Log::warning('Archive job aborted: event missing', ['event_id' => $this->eventId]); Log::warning('Archive job aborted: event missing', ['event_id' => $this->eventId]);
return; return;
} }
@@ -38,6 +39,7 @@ class ArchiveEventMediaAssets implements ShouldQueue
if (! $archiveDisk) { if (! $archiveDisk) {
Log::warning('Archive job aborted: no archive disk configured', ['event_id' => $event->id]); Log::warning('Archive job aborted: no archive disk configured', ['event_id' => $event->id]);
return; return;
} }

View File

@@ -2,12 +2,12 @@
namespace App\Jobs\Concerns; namespace App\Jobs\Concerns;
use App\Models\Tenant;
use App\Models\TenantNotificationLog; use App\Models\TenantNotificationLog;
use App\Models\TenantNotificationReceipt; use App\Models\TenantNotificationReceipt;
use App\Models\Tenant;
use App\Services\Packages\TenantNotificationLogger; use App\Services\Packages\TenantNotificationLogger;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
trait LogsTenantNotifications trait LogsTenantNotifications
{ {

View File

@@ -26,7 +26,7 @@ class AbandonedCheckout extends Mailable
public function envelope(): Envelope public function envelope(): Envelope
{ {
return new Envelope( return new Envelope(
subject: __('emails.abandoned_checkout.subject_' . $this->timing, [ subject: __('emails.abandoned_checkout.subject_'.$this->timing, [
'package' => $this->localizedPackageName(), 'package' => $this->localizedPackageName(),
]), ]),
); );

View File

@@ -13,9 +13,7 @@ class ContactConfirmation extends Mailable
use Queueable; use Queueable;
use SerializesModels; use SerializesModels;
public function __construct(public string $name) public function __construct(public string $name) {}
{
}
public function envelope(): Envelope public function envelope(): Envelope
{ {

View File

@@ -7,7 +7,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Log;
class BlogCategory extends Model class BlogCategory extends Model
{ {

View File

@@ -8,19 +8,18 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use Spatie\Translatable\HasTranslations;
use League\CommonMark\Environment\Environment; use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Table\TableExtension;
use League\CommonMark\Extension\Autolink\AutolinkExtension; use League\CommonMark\Extension\Autolink\AutolinkExtension;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Strikethrough\StrikethroughExtension; use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
use League\CommonMark\Extension\Table\TableExtension;
use League\CommonMark\Extension\TaskList\TaskListExtension; use League\CommonMark\Extension\TaskList\TaskListExtension;
use League\CommonMark\MarkdownConverter; use League\CommonMark\MarkdownConverter;
use Spatie\Translatable\HasTranslations;
class BlogPost extends Model class BlogPost extends Model
{ {
use HasFactory, SoftDeletes, HasTranslations; use HasFactory, HasTranslations, SoftDeletes;
protected $table = 'blog_posts'; protected $table = 'blog_posts';
@@ -78,14 +77,15 @@ class BlogPost extends Model
return Attribute::get(function () { return Attribute::get(function () {
$markdown = $this->getTranslation('content', app()->getLocale()); $markdown = $this->getTranslation('content', app()->getLocale());
$environment = new Environment(); $environment = new Environment;
$environment->addExtension(new CommonMarkCoreExtension()); $environment->addExtension(new CommonMarkCoreExtension);
$environment->addExtension(new TableExtension()); $environment->addExtension(new TableExtension);
$environment->addExtension(new AutolinkExtension()); $environment->addExtension(new AutolinkExtension);
$environment->addExtension(new StrikethroughExtension()); $environment->addExtension(new StrikethroughExtension);
$environment->addExtension(new TaskListExtension()); $environment->addExtension(new TaskListExtension);
$converter = new MarkdownConverter($environment); $converter = new MarkdownConverter($environment);
return (string) $converter->convert($markdown); return (string) $converter->convert($markdown);
}); });
} }

View File

@@ -3,14 +3,13 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Tags\Tag; use Spatie\Tags\Tag;
use Spatie\Translatable\HasTranslations; use Spatie\Translatable\HasTranslations;
class BlogTag extends Tag class BlogTag extends Tag
{ {
use HasFactory, SoftDeletes, HasTranslations; use HasFactory, HasTranslations, SoftDeletes;
protected $translatable = [ protected $translatable = [
'name', 'name',

View File

@@ -13,7 +13,9 @@ class Emotion extends Model
use HasFactory; use HasFactory;
protected $table = 'emotions'; protected $table = 'emotions';
protected $guarded = []; protected $guarded = [];
protected $casts = [ protected $casts = [
'name' => 'array', 'name' => 'array',
'description' => 'array', 'description' => 'array',
@@ -34,4 +36,3 @@ class Emotion extends Model
return $this->hasMany(Task::class); return $this->hasMany(Task::class);
} }
} }

View File

@@ -47,4 +47,3 @@ class EventJoinTokenEvent extends Model
return $this->belongsTo(Tenant::class); return $this->belongsTo(Tenant::class);
} }
} }

View File

@@ -51,4 +51,3 @@ class EventMediaAsset extends Model
return $this->belongsTo(Photo::class); return $this->belongsTo(Photo::class);
} }
} }

View File

@@ -11,7 +11,9 @@ class EventPurchase extends Model
use HasFactory; use HasFactory;
protected $table = 'event_purchases'; protected $table = 'event_purchases';
protected $guarded = []; protected $guarded = [];
protected $casts = [ protected $casts = [
'purchased_at' => 'datetime', 'purchased_at' => 'datetime',
'amount' => 'decimal:2', 'amount' => 'decimal:2',

View File

@@ -36,4 +36,3 @@ class EventStorageAssignment extends Model
return $this->belongsTo(MediaStorageTarget::class, 'media_storage_target_id'); return $this->belongsTo(MediaStorageTarget::class, 'media_storage_target_id');
} }
} }

View File

@@ -12,7 +12,9 @@ class EventType extends Model
use HasFactory; use HasFactory;
protected $table = 'event_types'; protected $table = 'event_types';
protected $guarded = []; protected $guarded = [];
protected $casts = [ protected $casts = [
'name' => 'array', 'name' => 'array',
'settings' => 'array', 'settings' => 'array',
@@ -28,4 +30,3 @@ class EventType extends Model
return $this->hasMany(Event::class); return $this->hasMany(Event::class);
} }
} }

View File

@@ -7,7 +7,9 @@ use Illuminate\Database\Eloquent\Model;
class LegalPage extends Model class LegalPage extends Model
{ {
protected $table = 'legal_pages'; protected $table = 'legal_pages';
protected $guarded = []; protected $guarded = [];
protected $casts = [ protected $casts = [
'title' => 'array', 'title' => 'array',
'body_markdown' => 'array', 'body_markdown' => 'array',
@@ -15,4 +17,3 @@ class LegalPage extends Model
'effective_from' => 'datetime', 'effective_from' => 'datetime',
]; ];
} }

View File

@@ -63,4 +63,3 @@ class MediaStorageTarget extends Model
return array_merge($base, $config); return array_merge($base, $config);
} }
} }

View File

@@ -8,7 +8,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PhotoLike extends Model class PhotoLike extends Model
{ {
protected $table = 'photo_likes'; protected $table = 'photo_likes';
protected $guarded = []; protected $guarded = [];
public $timestamps = false; public $timestamps = false;
public function photo(): BelongsTo public function photo(): BelongsTo

View File

@@ -11,8 +11,11 @@ class PurchaseHistory extends Model
use HasFactory; use HasFactory;
protected $table = 'purchase_history'; protected $table = 'purchase_history';
public $timestamps = false; public $timestamps = false;
public $incrementing = false; public $incrementing = false;
protected $keyType = 'string'; protected $keyType = 'string';
protected $guarded = []; protected $guarded = [];
@@ -28,4 +31,3 @@ class PurchaseHistory extends Model
return $this->belongsTo(Tenant::class); return $this->belongsTo(Tenant::class);
} }
} }

View File

@@ -3,7 +3,6 @@
namespace App\Services\Analytics; namespace App\Services\Analytics;
use App\Models\Event; use App\Models\Event;
use App\Models\Photo;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
class EventAnalyticsService class EventAnalyticsService

View File

@@ -5,7 +5,6 @@ namespace App\Services\Analytics;
use App\Models\EventJoinToken; use App\Models\EventJoinToken;
use App\Models\EventJoinTokenEvent; use App\Models\EventJoinTokenEvent;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Str; use Illuminate\Support\Str;
class JoinTokenAnalyticsRecorder class JoinTokenAnalyticsRecorder
@@ -109,4 +108,3 @@ class JoinTokenAnalyticsRecorder
return Str::substr($token, 0, 6).'…'.Str::substr($token, -4); return Str::substr($token, 0, 6).'…'.Str::substr($token, -4);
} }
} }

View File

@@ -19,6 +19,7 @@ class EmotionImportService
$headers = fgetcsv($handle, 0, ','); $headers = fgetcsv($handle, 0, ',');
if (! $headers) { if (! $headers) {
fclose($handle); fclose($handle);
return [0, 0]; return [0, 0];
} }

View File

@@ -192,4 +192,3 @@ class PhotoSecurityScanner
} }
} }
} }

View File

@@ -8,8 +8,6 @@ use App\Models\EventStorageAssignment;
use App\Models\MediaStorageTarget; use App\Models\MediaStorageTarget;
use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class EventStorageManager class EventStorageManager
{ {

View File

@@ -60,4 +60,3 @@ class StorageHealthService
} }
} }
} }

View File

@@ -9,6 +9,7 @@ class LocaleConfig
{ {
/** /**
* Return configured locales from env/config as provided (may include region codes). * Return configured locales from env/config as provided (may include region codes).
*
* @return array<int, string> * @return array<int, string>
*/ */
public static function configured(): array public static function configured(): array
@@ -34,6 +35,7 @@ class LocaleConfig
/** /**
* Return normalized short codes (language only, lowercase). * Return normalized short codes (language only, lowercase).
*
* @return array<int, string> * @return array<int, string>
*/ */
public static function normalized(): array public static function normalized(): array

View File

@@ -66,8 +66,8 @@ return [
'h1', 'h1',
'h2', 'h2',
'h3', 'h3',
//'hr', // 'hr',
//'image', // 'image',
'italic', 'italic',
'link', 'link',
'orderedList', 'orderedList',

View File

@@ -14,4 +14,3 @@ return [
'name' => env('SECURITY_SCAN_QUEUE', 'media-security'), 'name' => env('SECURITY_SCAN_QUEUE', 'media-security'),
], ],
]; ];

View File

@@ -29,4 +29,3 @@ class EmotionFactory extends Factory
]; ];
} }
} }

View File

@@ -64,4 +64,3 @@ class EventFactory extends Factory
]); ]);
} }
} }

View File

@@ -28,4 +28,3 @@ class EventTypeFactory extends Factory
]; ];
} }
} }

View File

@@ -25,4 +25,3 @@ class PurchaseHistoryFactory extends Factory
]; ];
} }
} }

View File

@@ -3,7 +3,6 @@
namespace Database\Factories; namespace Database\Factories;
use App\Models\EventType; use App\Models\EventType;
use App\Models\Task;
use App\Models\TaskCollection; use App\Models\TaskCollection;
use App\Models\Tenant; use App\Models\Tenant;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
@@ -21,7 +20,7 @@ class TaskCollectionFactory extends Factory
return [ return [
'tenant_id' => Tenant::factory(), 'tenant_id' => Tenant::factory(),
'event_type_id' => EventType::factory(), 'event_type_id' => EventType::factory(),
'slug' => Str::slug($label . '-' . $this->faker->unique()->numberBetween(1, 9999)), 'slug' => Str::slug($label.'-'.$this->faker->unique()->numberBetween(1, 9999)),
'name_translations' => [ 'name_translations' => [
'de' => $label, 'de' => $label,
'en' => $label, 'en' => $label,

View File

@@ -19,7 +19,7 @@ class TaskFactory extends Factory
return [ return [
'tenant_id' => Tenant::factory(), 'tenant_id' => Tenant::factory(),
'slug' => Str::slug($title . '-' . $this->faker->unique()->numberBetween(1, 9999)), 'slug' => Str::slug($title.'-'.$this->faker->unique()->numberBetween(1, 9999)),
'title' => [ 'title' => [
'de' => $title, 'de' => $title,
'en' => $title, 'en' => $title,

View File

@@ -69,13 +69,13 @@ return new class extends Migration
] ]
: null; : null;
$slugBase = Str::slug($name ?: ('collection-' . $row->id)); $slugBase = Str::slug($name ?: ('collection-'.$row->id));
if (empty($slugBase)) { if (empty($slugBase)) {
$slugBase = 'collection-' . $row->id; $slugBase = 'collection-'.$row->id;
} }
$slug = $row->slug ?: ($slugBase . '-' . $row->id); $slug = $row->slug ?: ($slugBase.'-'.$row->id);
DB::table('task_collections') DB::table('task_collections')
->where('id', $row->id) ->where('id', $row->id)
@@ -146,18 +146,18 @@ return new class extends Migration
$base = $json['de'] $base = $json['de']
?? $json['en'] ?? $json['en']
?? ('task-' . $row->id); ?? ('task-'.$row->id);
$slug = Str::slug($base); $slug = Str::slug($base);
if (empty($slug)) { if (empty($slug)) {
$slug = 'task-' . $row->id; $slug = 'task-'.$row->id;
} }
DB::table('tasks') DB::table('tasks')
->where('id', $row->id) ->where('id', $row->id)
->update([ ->update([
'slug' => $slug . '-' . $row->id, 'slug' => $slug.'-'.$row->id,
]); ]);
} }
}); });
@@ -232,7 +232,7 @@ return new class extends Migration
DB::table('task_collections') DB::table('task_collections')
->where('id', $row->id) ->where('id', $row->id)
->update([ ->update([
'name' => $names['de'] ?? $names['en'] ?? 'Collection ' . $row->id, 'name' => $names['de'] ?? $names['en'] ?? 'Collection '.$row->id,
'description' => $descriptions['de'] ?? $descriptions['en'] ?? null, 'description' => $descriptions['de'] ?? $descriptions['en'] ?? null,
]); ]);
} }

View File

@@ -9,7 +9,7 @@ return new class extends Migration
public function up(): void public function up(): void
{ {
// Create tenants table if not exists // Create tenants table if not exists
if (!Schema::hasTable('tenants')) { if (! Schema::hasTable('tenants')) {
Schema::create('tenants', function (Blueprint $table) { Schema::create('tenants', function (Blueprint $table) {
$table->id(); $table->id();
$table->string('name'); $table->string('name');
@@ -32,7 +32,7 @@ return new class extends Migration
}); });
} else { } else {
// Add missing columns to existing tenants table // Add missing columns to existing tenants table
if (!Schema::hasColumn('tenants', 'email')) { if (! Schema::hasColumn('tenants', 'email')) {
Schema::table('tenants', function (Blueprint $table) { Schema::table('tenants', function (Blueprint $table) {
$table->string('email')->nullable()->after('contact_phone'); $table->string('email')->nullable()->after('contact_phone');
}); });
@@ -42,17 +42,17 @@ return new class extends Migration
$table->dropColumn(['event_credits_balance', 'free_event_granted_at']); $table->dropColumn(['event_credits_balance', 'free_event_granted_at']);
}); });
} }
if (!Schema::hasColumn('tenants', 'stripe_account_id')) { if (! Schema::hasColumn('tenants', 'stripe_account_id')) {
Schema::table('tenants', function (Blueprint $table) { Schema::table('tenants', function (Blueprint $table) {
$table->string('stripe_account_id')->nullable()->after('features'); $table->string('stripe_account_id')->nullable()->after('features');
}); });
} }
if (!Schema::hasColumn('tenants', 'custom_domain')) { if (! Schema::hasColumn('tenants', 'custom_domain')) {
Schema::table('tenants', function (Blueprint $table) { Schema::table('tenants', function (Blueprint $table) {
$table->string('custom_domain')->nullable()->after('domain'); $table->string('custom_domain')->nullable()->after('domain');
}); });
} }
if (!Schema::hasColumn('tenants', 'user_id')) { if (! Schema::hasColumn('tenants', 'user_id')) {
Schema::table('tenants', function (Blueprint $table) { Schema::table('tenants', function (Blueprint $table) {
$table->foreignId('user_id')->nullable()->constrained('users')->onDelete('cascade')->after('id'); $table->foreignId('user_id')->nullable()->constrained('users')->onDelete('cascade')->after('id');
}); });
@@ -68,7 +68,7 @@ return new class extends Migration
}); });
} }
// Add subscription fields (from add_subscription_fields_to_tenants_table) // Add subscription fields (from add_subscription_fields_to_tenants_table)
if (!Schema::hasColumn('tenants', 'subscription_status')) { if (! Schema::hasColumn('tenants', 'subscription_status')) {
Schema::table('tenants', function (Blueprint $table) { Schema::table('tenants', function (Blueprint $table) {
$table->string('subscription_status')->default('active')->after('event_credits_balance'); $table->string('subscription_status')->default('active')->after('event_credits_balance');
$table->timestamp('subscription_ends_at')->nullable()->after('subscription_status'); $table->timestamp('subscription_ends_at')->nullable()->after('subscription_status');
@@ -77,14 +77,14 @@ return new class extends Migration
} }
// Add tenant_id to users if not exists // Add tenant_id to users if not exists
if (Schema::hasTable('users') && !Schema::hasColumn('users', 'tenant_id')) { if (Schema::hasTable('users') && ! Schema::hasColumn('users', 'tenant_id')) {
Schema::table('users', function (Blueprint $table) { Schema::table('users', function (Blueprint $table) {
$table->foreignId('tenant_id')->nullable()->after('id')->constrained('tenants')->nullOnDelete(); $table->foreignId('tenant_id')->nullable()->after('id')->constrained('tenants')->nullOnDelete();
}); });
} }
// Add tenant_id to events if not exists // Add tenant_id to events if not exists
if (Schema::hasTable('events') && !Schema::hasColumn('events', 'tenant_id')) { if (Schema::hasTable('events') && ! Schema::hasColumn('events', 'tenant_id')) {
Schema::table('events', function (Blueprint $table) { Schema::table('events', function (Blueprint $table) {
$table->foreignId('tenant_id')->nullable()->after('id')->constrained('tenants')->nullOnDelete(); $table->foreignId('tenant_id')->nullable()->after('id')->constrained('tenants')->nullOnDelete();
}); });

View File

@@ -9,7 +9,7 @@ return new class extends Migration
public function up(): void public function up(): void
{ {
// Event Types // Event Types
if (!Schema::hasTable('event_types')) { if (! Schema::hasTable('event_types')) {
Schema::create('event_types', function (Blueprint $table) { Schema::create('event_types', function (Blueprint $table) {
$table->id(); $table->id();
$table->json('name'); $table->json('name');
@@ -21,7 +21,7 @@ return new class extends Migration
} }
// Emotions // Emotions
if (!Schema::hasTable('emotions')) { if (! Schema::hasTable('emotions')) {
Schema::create('emotions', function (Blueprint $table) { Schema::create('emotions', function (Blueprint $table) {
$table->id(); $table->id();
$table->json('name'); $table->json('name');
@@ -35,7 +35,7 @@ return new class extends Migration
} }
// Pivot table for emotions and event types // Pivot table for emotions and event types
if (!Schema::hasTable('emotion_event_type')) { if (! Schema::hasTable('emotion_event_type')) {
Schema::create('emotion_event_type', function (Blueprint $table) { Schema::create('emotion_event_type', function (Blueprint $table) {
$table->unsignedBigInteger('emotion_id'); $table->unsignedBigInteger('emotion_id');
$table->unsignedBigInteger('event_type_id'); $table->unsignedBigInteger('event_type_id');

View File

@@ -10,7 +10,7 @@ return new class extends Migration
public function up(): void public function up(): void
{ {
// Legal Pages table // Legal Pages table
if (!Schema::hasTable('legal_pages')) { if (! Schema::hasTable('legal_pages')) {
Schema::create('legal_pages', function (Blueprint $table) { Schema::create('legal_pages', function (Blueprint $table) {
$table->id(); $table->id();
$table->string('slug', 32); $table->string('slug', 32);
@@ -86,7 +86,7 @@ return new class extends Migration
private function impressumDe(): string private function impressumDe(): string
{ {
return <<<MD return <<<'MD'
# Impressum # Impressum
Anbieter dieser Seiten: Anbieter dieser Seiten:

View File

@@ -9,7 +9,7 @@ return new class extends Migration
public function up(): void public function up(): void
{ {
// Blog Categories // Blog Categories
if (!Schema::hasTable('blog_categories')) { if (! Schema::hasTable('blog_categories')) {
Schema::create('blog_categories', function (Blueprint $table) { Schema::create('blog_categories', function (Blueprint $table) {
$table->id(); $table->id();
$table->string('name')->nullable(); $table->string('name')->nullable();
@@ -21,13 +21,13 @@ return new class extends Migration
$table->timestamps(); $table->timestamps();
}); });
} else { } else {
if (!Schema::hasColumn('blog_categories', 'name')) { if (! Schema::hasColumn('blog_categories', 'name')) {
Schema::table('blog_categories', function (Blueprint $table) { Schema::table('blog_categories', function (Blueprint $table) {
$table->string('name')->nullable()->after('id'); $table->string('name')->nullable()->after('id');
$table->longText('description')->nullable()->after('name'); $table->longText('description')->nullable()->after('name');
}); });
} }
if (!Schema::hasColumn('blog_categories', 'translations')) { if (! Schema::hasColumn('blog_categories', 'translations')) {
Schema::table('blog_categories', function (Blueprint $table) { Schema::table('blog_categories', function (Blueprint $table) {
$table->json('translations')->nullable()->after('description'); $table->json('translations')->nullable()->after('description');
}); });
@@ -35,7 +35,7 @@ return new class extends Migration
} }
// Blog Authors // Blog Authors
if (!Schema::hasTable('blog_authors')) { if (! Schema::hasTable('blog_authors')) {
Schema::create('blog_authors', function (Blueprint $table) { Schema::create('blog_authors', function (Blueprint $table) {
$table->id(); $table->id();
$table->string('name'); $table->string('name');
@@ -49,7 +49,7 @@ return new class extends Migration
} }
// Blog Posts // Blog Posts
if (!Schema::hasTable('blog_posts')) { if (! Schema::hasTable('blog_posts')) {
Schema::create('blog_posts', function (Blueprint $table) { Schema::create('blog_posts', function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignId('blog_author_id')->nullable()->constrained()->cascadeOnDelete(); $table->foreignId('blog_author_id')->nullable()->constrained()->cascadeOnDelete();
@@ -65,7 +65,7 @@ return new class extends Migration
$table->timestamps(); $table->timestamps();
}); });
} else { } else {
if (!Schema::hasColumn('blog_posts', 'translations')) { if (! Schema::hasColumn('blog_posts', 'translations')) {
Schema::table('blog_posts', function (Blueprint $table) { Schema::table('blog_posts', function (Blueprint $table) {
$table->json('translations')->nullable()->after('content'); $table->json('translations')->nullable()->after('content');
}); });
@@ -73,7 +73,7 @@ return new class extends Migration
} }
// Tags // Tags
if (!Schema::hasTable('tags')) { if (! Schema::hasTable('tags')) {
Schema::create('tags', function (Blueprint $table) { Schema::create('tags', function (Blueprint $table) {
$table->id(); $table->id();
$table->json('name'); $table->json('name');
@@ -85,7 +85,7 @@ return new class extends Migration
} }
// Taggables (polymorphic) // Taggables (polymorphic)
if (!Schema::hasTable('taggables')) { if (! Schema::hasTable('taggables')) {
Schema::create('taggables', function (Blueprint $table) { Schema::create('taggables', function (Blueprint $table) {
$table->foreignId('tag_id')->constrained()->cascadeOnDelete(); $table->foreignId('tag_id')->constrained()->cascadeOnDelete();
$table->morphs('taggable'); $table->morphs('taggable');

View File

@@ -12,16 +12,16 @@ return new class extends Migration
public function up(): void public function up(): void
{ {
Schema::table('users', function (Blueprint $table) { Schema::table('users', function (Blueprint $table) {
if (!Schema::hasColumn('tenants', 'first_name')) { if (! Schema::hasColumn('tenants', 'first_name')) {
$table->string('first_name')->default('')->after('name'); $table->string('first_name')->default('')->after('name');
} }
if (!Schema::hasColumn('tenants', 'last_name')) { if (! Schema::hasColumn('tenants', 'last_name')) {
$table->string('last_name')->default('')->after('first_name'); $table->string('last_name')->default('')->after('first_name');
} }
if (!Schema::hasColumn('tenants', 'address')) { if (! Schema::hasColumn('tenants', 'address')) {
$table->string('address')->nullable()->after('last_name'); $table->string('address')->nullable()->after('last_name');
} }
if (!Schema::hasColumn('tenants', 'phone')) { if (! Schema::hasColumn('tenants', 'phone')) {
$table->string('phone')->nullable()->after('address'); $table->string('phone')->nullable()->after('address');
} }
}); });

View File

@@ -12,13 +12,13 @@ return new class extends Migration
public function up(): void public function up(): void
{ {
Schema::table('tenants', function (Blueprint $table) { Schema::table('tenants', function (Blueprint $table) {
if (!Schema::hasColumn('tenants', 'is_suspended')) { if (! Schema::hasColumn('tenants', 'is_suspended')) {
$table->boolean('is_suspended')->default(false)->after('is_active'); $table->boolean('is_suspended')->default(false)->after('is_active');
} }
if (!Schema::hasColumn('tenants', 'settings')) { if (! Schema::hasColumn('tenants', 'settings')) {
$table->json('settings')->nullable()->after('subscription_expires_at'); $table->json('settings')->nullable()->after('subscription_expires_at');
} }
if (!Schema::hasColumn('tenants', 'settings_updated_at')) { if (! Schema::hasColumn('tenants', 'settings_updated_at')) {
$table->timestamp('settings_updated_at')->nullable()->after('settings'); $table->timestamp('settings_updated_at')->nullable()->after('settings');
} }
}); });

View File

@@ -2,8 +2,8 @@
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str; use Illuminate\Support\Str;
return new class extends Migration return new class extends Migration
@@ -14,10 +14,10 @@ return new class extends Migration
public function up(): void public function up(): void
{ {
Schema::table('packages', function (Blueprint $table) { Schema::table('packages', function (Blueprint $table) {
if (!Schema::hasColumn('packages', 'slug')) { if (! Schema::hasColumn('packages', 'slug')) {
$table->string('slug')->default('')->after('name'); $table->string('slug')->default('')->after('name');
} }
if (!Schema::hasColumn('packages', 'description')) { if (! Schema::hasColumn('packages', 'description')) {
$table->text('description')->nullable()->after('slug'); $table->text('description')->nullable()->after('slug');
} }
}); });
@@ -26,7 +26,7 @@ return new class extends Migration
if (Schema::hasTable('packages')) { if (Schema::hasTable('packages')) {
$packages = DB::table('packages')->get(); $packages = DB::table('packages')->get();
foreach ($packages as $package) { foreach ($packages as $package) {
$slug = Str::slug($package->name . '-' . $package->id); $slug = Str::slug($package->name.'-'.$package->id);
DB::table('packages')->where('id', $package->id)->update(['slug' => $slug]); DB::table('packages')->where('id', $package->id)->update(['slug' => $slug]);
} }
} }

View File

@@ -40,7 +40,7 @@ return new class extends Migration
} }
$translations = $this->decodeTranslations($row->name_translations); $translations = $this->decodeTranslations($row->name_translations);
$base = $translations['en'] ?? $translations['de'] ?? reset($translations) ?? ('collection-' . $row->id); $base = $translations['en'] ?? $translations['de'] ?? reset($translations) ?? ('collection-'.$row->id);
$slug = $this->buildUniqueSlug($base, 'collection-', function ($candidate) { $slug = $this->buildUniqueSlug($base, 'collection-', function ($candidate) {
return DB::table('task_collections')->where('slug', $candidate)->exists(); return DB::table('task_collections')->where('slug', $candidate)->exists();
}); });
@@ -76,7 +76,7 @@ return new class extends Migration
} }
$translations = $this->decodeTranslations($row->title); $translations = $this->decodeTranslations($row->title);
$base = $translations['en'] ?? $translations['de'] ?? reset($translations) ?? ('task-' . $row->id); $base = $translations['en'] ?? $translations['de'] ?? reset($translations) ?? ('task-'.$row->id);
$slug = $this->buildUniqueSlug($base, 'task-', function ($candidate) { $slug = $this->buildUniqueSlug($base, 'task-', function ($candidate) {
return DB::table('tasks')->where('slug', $candidate)->exists(); return DB::table('tasks')->where('slug', $candidate)->exists();
}); });
@@ -139,10 +139,10 @@ return new class extends Migration
protected function buildUniqueSlug(string $base, string $prefix, callable $exists): string protected function buildUniqueSlug(string $base, string $prefix, callable $exists): string
{ {
$slugBase = Str::slug($base) ?: ($prefix . Str::random(4)); $slugBase = Str::slug($base) ?: ($prefix.Str::random(4));
do { do {
$candidate = $slugBase . '-' . Str::random(4); $candidate = $slugBase.'-'.Str::random(4);
} while ($exists($candidate)); } while ($exists($candidate));
return $candidate; return $candidate;

View File

@@ -9,7 +9,7 @@ return new class extends Migration
public function up(): void public function up(): void
{ {
Schema::table('packages', function (Blueprint $table) { Schema::table('packages', function (Blueprint $table) {
if (!Schema::hasColumn('packages', 'description_table')) { if (! Schema::hasColumn('packages', 'description_table')) {
$table->json('description_table')->nullable()->after('description'); $table->json('description_table')->nullable()->after('description');
} }
}); });

View File

@@ -2,8 +2,8 @@
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration return new class extends Migration
{ {

View File

@@ -33,4 +33,3 @@ return new class extends Migration
Schema::dropIfExists('media_storage_targets'); Schema::dropIfExists('media_storage_targets');
} }
}; };

View File

@@ -34,4 +34,3 @@ return new class extends Migration
Schema::dropIfExists('event_storage_assignments'); Schema::dropIfExists('event_storage_assignments');
} }
}; };

View File

@@ -43,4 +43,3 @@ return new class extends Migration
Schema::dropIfExists('event_media_assets'); Schema::dropIfExists('event_media_assets');
} }
}; };

View File

@@ -24,4 +24,3 @@ return new class extends Migration
}); });
} }
}; };

View File

@@ -13,17 +13,17 @@ return new class extends Migration
$addedTokenHashColumn = false; $addedTokenHashColumn = false;
Schema::table('event_join_tokens', function (Blueprint $table) use (&$addedTokenHashColumn) { Schema::table('event_join_tokens', function (Blueprint $table) use (&$addedTokenHashColumn) {
if (!Schema::hasColumn('event_join_tokens', 'token_hash')) { if (! Schema::hasColumn('event_join_tokens', 'token_hash')) {
$table->string('token_hash', 128)->nullable()->after('token'); $table->string('token_hash', 128)->nullable()->after('token');
$table->index('token_hash', 'event_join_tokens_token_hash_index'); $table->index('token_hash', 'event_join_tokens_token_hash_index');
$addedTokenHashColumn = true; $addedTokenHashColumn = true;
} }
if (!Schema::hasColumn('event_join_tokens', 'token_encrypted')) { if (! Schema::hasColumn('event_join_tokens', 'token_encrypted')) {
$table->text('token_encrypted')->nullable()->after('token_hash'); $table->text('token_encrypted')->nullable()->after('token_hash');
} }
if (!Schema::hasColumn('event_join_tokens', 'token_preview')) { if (! Schema::hasColumn('event_join_tokens', 'token_preview')) {
$table->string('token_preview', 32)->nullable()->after('token_encrypted'); $table->string('token_preview', 32)->nullable()->after('token_encrypted');
} }
}); });
@@ -76,7 +76,7 @@ return new class extends Migration
->filter(fn ($column) => Schema::hasColumn('event_join_tokens', $column)) ->filter(fn ($column) => Schema::hasColumn('event_join_tokens', $column))
->all(); ->all();
if (!empty($columns)) { if (! empty($columns)) {
$table->dropColumn($columns); $table->dropColumn($columns);
} }
}); });

View File

@@ -23,4 +23,3 @@ return new class extends Migration
}); });
} }
}; };

View File

@@ -35,4 +35,3 @@ return new class extends Migration
Schema::dropIfExists('event_join_token_events'); Schema::dropIfExists('event_join_token_events');
} }
}; };

View File

@@ -1,7 +1,6 @@
<?php <?php
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
return new class extends Migration return new class extends Migration

View File

@@ -2,9 +2,9 @@
namespace Database\Seeders; namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Emotion; use App\Models\Emotion;
use App\Models\EventType; use App\Models\EventType;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
class EmotionsSeeder extends Seeder class EmotionsSeeder extends Seeder
@@ -12,16 +12,16 @@ class EmotionsSeeder extends Seeder
public function run(): void public function run(): void
{ {
$emotionData = [ $emotionData = [
['name'=>['de'=>'Liebe','en'=>'Love'], 'icon'=>'💖', 'color'=>'#ff6b9d', 'description'=>['de'=>'Romantische Momente','en'=>'Romantic moments'], 'sort_order'=>1], ['name' => ['de' => 'Liebe', 'en' => 'Love'], 'icon' => '💖', 'color' => '#ff6b9d', 'description' => ['de' => 'Romantische Momente', 'en' => 'Romantic moments'], 'sort_order' => 1],
['name'=>['de'=>'Freude','en'=>'Joy'], 'icon'=>'😊', 'color'=>'#ffd93d', 'description'=>['de'=>'Fröhliche Augenblicke','en'=>'Happy moments'], 'sort_order'=>2], ['name' => ['de' => 'Freude', 'en' => 'Joy'], 'icon' => '😊', 'color' => '#ffd93d', 'description' => ['de' => 'Fröhliche Augenblicke', 'en' => 'Happy moments'], 'sort_order' => 2],
['name'=>['de'=>'Rührung','en'=>'Touched'], 'icon'=>'🥹', 'color'=>'#6bcf7f', 'description'=>['de'=>'Berührende Szenen','en'=>'Touching scenes'], 'sort_order'=>3], ['name' => ['de' => 'Rührung', 'en' => 'Touched'], 'icon' => '🥹', 'color' => '#6bcf7f', 'description' => ['de' => 'Berührende Szenen', 'en' => 'Touching scenes'], 'sort_order' => 3],
['name'=>['de'=>'Nostalgie','en'=>'Nostalgia'], 'icon'=>'🕰️', 'color'=>'#a78bfa', 'description'=>['de'=>'Erinnerungen','en'=>'Memories'], 'sort_order'=>4], ['name' => ['de' => 'Nostalgie', 'en' => 'Nostalgia'], 'icon' => '🕰️', 'color' => '#a78bfa', 'description' => ['de' => 'Erinnerungen', 'en' => 'Memories'], 'sort_order' => 4],
['name'=>['de'=>'Überraschung','en'=>'Surprise'], 'icon'=>'😲', 'color'=>'#fb7185', 'description'=>['de'=>'Unerwartete Momente','en'=>'Unexpected moments'], 'sort_order'=>5], ['name' => ['de' => 'Überraschung', 'en' => 'Surprise'], 'icon' => '😲', 'color' => '#fb7185', 'description' => ['de' => 'Unerwartete Momente', 'en' => 'Unexpected moments'], 'sort_order' => 5],
['name'=>['de'=>'Stolz','en'=>'Pride'], 'icon'=>'🏆', 'color'=>'#34d399', 'description'=>['de'=>'Triumphale Augenblicke','en'=>'Triumphal moments'], 'sort_order'=>6], ['name' => ['de' => 'Stolz', 'en' => 'Pride'], 'icon' => '🏆', 'color' => '#34d399', 'description' => ['de' => 'Triumphale Augenblicke', 'en' => 'Triumphal moments'], 'sort_order' => 6],
['name'=>['de'=>'Teamgeist','en'=>'Team Spirit'], 'icon'=>'🤝', 'color'=>'#38bdf8', 'description'=>['de'=>'Zusammenhalt','en'=>'Team bonding'], 'sort_order'=>7], ['name' => ['de' => 'Teamgeist', 'en' => 'Team Spirit'], 'icon' => '🤝', 'color' => '#38bdf8', 'description' => ['de' => 'Zusammenhalt', 'en' => 'Team bonding'], 'sort_order' => 7],
['name'=>['de'=>'Besinnlichkeit','en'=>'Contemplation'], 'icon'=>'🕯️', 'color'=>'#22c55e', 'description'=>['de'=>'Feierliche Stimmung','en'=>'Festive calm'], 'sort_order'=>8], ['name' => ['de' => 'Besinnlichkeit', 'en' => 'Contemplation'], 'icon' => '🕯️', 'color' => '#22c55e', 'description' => ['de' => 'Feierliche Stimmung', 'en' => 'Festive calm'], 'sort_order' => 8],
['name'=>['de'=>'Romantik','en'=>'Romance'], 'icon'=>'🌹', 'color'=>'#e11d48', 'description'=>['de'=>'Romantische Stimmung','en'=>'Romantic mood'], 'sort_order'=>9], ['name' => ['de' => 'Romantik', 'en' => 'Romance'], 'icon' => '🌹', 'color' => '#e11d48', 'description' => ['de' => 'Romantische Stimmung', 'en' => 'Romantic mood'], 'sort_order' => 9],
['name'=>['de'=>'Ekstase','en'=>'Ecstasy'], 'icon'=>'🎉', 'color'=>'#f59e0b', 'description'=>['de'=>'Pure Lebensfreude','en'=>'Pure zest for life'], 'sort_order'=>10], ['name' => ['de' => 'Ekstase', 'en' => 'Ecstasy'], 'icon' => '🎉', 'color' => '#f59e0b', 'description' => ['de' => 'Pure Lebensfreude', 'en' => 'Pure zest for life'], 'sort_order' => 10],
]; ];
$typeIds = EventType::pluck('id')->toArray(); $typeIds = EventType::pluck('id')->toArray();
@@ -38,4 +38,3 @@ class EmotionsSeeder extends Seeder
} }
} }
} }

View File

@@ -2,24 +2,23 @@
namespace Database\Seeders; namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\EventType; use App\Models\EventType;
use Illuminate\Database\Seeder;
class EventTypesSeeder extends Seeder class EventTypesSeeder extends Seeder
{ {
public function run(): void public function run(): void
{ {
$types = [ $types = [
['name' => ['de'=>'Hochzeit','en'=>'Wedding'], 'slug'=>'wedding', 'icon'=>'💍'], ['name' => ['de' => 'Hochzeit', 'en' => 'Wedding'], 'slug' => 'wedding', 'icon' => '💍'],
['name' => ['de'=>'Geburtstag','en'=>'Birthday'], 'slug'=>'birthday', 'icon'=>'🎂'], ['name' => ['de' => 'Geburtstag', 'en' => 'Birthday'], 'slug' => 'birthday', 'icon' => '🎂'],
['name' => ['de'=>'Weihnachten','en'=>'Christmas'], 'slug'=>'christmas', 'icon'=>'🎄'], ['name' => ['de' => 'Weihnachten', 'en' => 'Christmas'], 'slug' => 'christmas', 'icon' => '🎄'],
['name' => ['de'=>'Konfirmation / Jugendweihe','en'=>'Confirmation'], 'slug'=>'confirmation', 'icon'=>'🕊️'], ['name' => ['de' => 'Konfirmation / Jugendweihe', 'en' => 'Confirmation'], 'slug' => 'confirmation', 'icon' => '🕊️'],
['name' => ['de'=>'Schulabschluss','en'=>'Graduation'], 'slug'=>'graduation', 'icon'=>'🎓'], ['name' => ['de' => 'Schulabschluss', 'en' => 'Graduation'], 'slug' => 'graduation', 'icon' => '🎓'],
['name' => ['de'=>'Firmenfeier','en'=>'Corporate'], 'slug'=>'corporate', 'icon'=>'🏢'], ['name' => ['de' => 'Firmenfeier', 'en' => 'Corporate'], 'slug' => 'corporate', 'icon' => '🏢'],
]; ];
foreach ($types as $t) { foreach ($types as $t) {
EventType::updateOrCreate(['slug'=>$t['slug']], $t); EventType::updateOrCreate(['slug' => $t['slug']], $t);
} }
} }
} }

View File

@@ -90,4 +90,3 @@ class LegalPagesSeeder extends Seeder
); );
} }
} }

View File

@@ -25,6 +25,7 @@ return [
], ],
'common' => [ 'common' => [
'error' => 'Fehler',
'key' => 'Schlüssel', 'key' => 'Schlüssel',
'value' => 'Wert', 'value' => 'Wert',
'locale' => 'Sprache', 'locale' => 'Sprache',
@@ -466,6 +467,9 @@ return [
'icon' => 'Icon', 'icon' => 'Icon',
'created_at' => 'Erstellt', 'created_at' => 'Erstellt',
], ],
'messages' => [
'delete_constraint_error' => 'Dieser Eventtyp wird derzeit verwendet und kann nicht gelöscht werden.',
],
], ],
'tasks' => [ 'tasks' => [

View File

@@ -25,6 +25,7 @@ return [
], ],
'common' => [ 'common' => [
'error' => 'Error',
'key' => 'Key', 'key' => 'Key',
'value' => 'Value', 'value' => 'Value',
'locale' => 'Locale', 'locale' => 'Locale',
@@ -459,6 +460,9 @@ return [
'settings' => 'Settings', 'settings' => 'Settings',
'emotions' => 'Emotions', 'emotions' => 'Emotions',
], ],
'messages' => [
'delete_constraint_error' => 'This event type is currently in use and cannot be deleted.',
],
], ],
'tasks' => [ 'tasks' => [

View File

@@ -2,11 +2,11 @@
namespace Tests\Feature\Api; namespace Tests\Feature\Api;
use App\Mail\GiftVoucherIssued;
use App\Models\GiftVoucher; use App\Models\GiftVoucher;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Tests\TestCase; use Tests\TestCase;
use App\Mail\GiftVoucherIssued;
class GiftVoucherResendTest extends TestCase class GiftVoucherResendTest extends TestCase
{ {

View File

@@ -142,5 +142,4 @@ class CheckEventPackagesCommandTest extends TestCase
Carbon::setTestNow(); Carbon::setTestNow();
} }
} }
} }

View File

@@ -24,7 +24,8 @@ class ProcessPhotoSecurityScanTest extends TestCase
{ {
[$photo, $asset] = $this->seedPhotoWithAsset(); [$photo, $asset] = $this->seedPhotoWithAsset();
$scanner = new class extends PhotoSecurityScanner { $scanner = new class extends PhotoSecurityScanner
{
public function scan(string $disk, ?string $relativePath): array public function scan(string $disk, ?string $relativePath): array
{ {
return ['status' => 'clean', 'message' => 'ok']; return ['status' => 'clean', 'message' => 'ok'];
@@ -49,7 +50,8 @@ class ProcessPhotoSecurityScanTest extends TestCase
{ {
[$photo] = $this->seedPhotoWithAsset(); [$photo] = $this->seedPhotoWithAsset();
$scanner = new class extends PhotoSecurityScanner { $scanner = new class extends PhotoSecurityScanner
{
public function scan(string $disk, ?string $relativePath): array public function scan(string $disk, ?string $relativePath): array
{ {
return ['status' => 'skipped', 'message' => 'disabled']; return ['status' => 'skipped', 'message' => 'disabled'];
@@ -73,7 +75,8 @@ class ProcessPhotoSecurityScanTest extends TestCase
{ {
[$photo, $asset] = $this->seedPhotoWithAsset(); [$photo, $asset] = $this->seedPhotoWithAsset();
$scanner = new class extends PhotoSecurityScanner { $scanner = new class extends PhotoSecurityScanner
{
public function scan(string $disk, ?string $relativePath): array public function scan(string $disk, ?string $relativePath): array
{ {
return ['status' => 'infected', 'message' => 'bad']; return ['status' => 'infected', 'message' => 'bad'];

View File

@@ -2,13 +2,14 @@
namespace Tests\Feature\Marketing; namespace Tests\Feature\Marketing;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Inertia\Testing\AssertableInertia as Assert; use Inertia\Testing\AssertableInertia as Assert;
use Tests\TestCase; use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class MarketingLocaleRoutingTest extends TestCase class MarketingLocaleRoutingTest extends TestCase
{ {
use RefreshDatabase; use RefreshDatabase;
public function test_home_route_accepts_german_locale_prefix(): void public function test_home_route_accepts_german_locale_prefix(): void
{ {
$response = $this->get('/de'); $response = $this->get('/de');

View File

@@ -11,7 +11,7 @@ class PackageSeederTest extends TestCase
{ {
use RefreshDatabase; use RefreshDatabase;
public function testStarterPackageHasSingleEventQuota(): void public function test_starter_package_has_single_event_quota(): void
{ {
$this->seed(PackageSeeder::class); $this->seed(PackageSeeder::class);

View File

@@ -64,15 +64,14 @@ KEY;
mkdir($storagePath, 0755, true); mkdir($storagePath, 0755, true);
} }
$publicKeyPath = $storagePath . DIRECTORY_SEPARATOR . 'public.key'; $publicKeyPath = $storagePath.DIRECTORY_SEPARATOR.'public.key';
if (! file_exists($publicKeyPath)) { if (! file_exists($publicKeyPath)) {
file_put_contents($publicKeyPath, self::JWT_PUBLIC_KEY); file_put_contents($publicKeyPath, self::JWT_PUBLIC_KEY);
} }
$privateKeyPath = $storagePath . DIRECTORY_SEPARATOR . 'private.key'; $privateKeyPath = $storagePath.DIRECTORY_SEPARATOR.'private.key';
if (! file_exists($privateKeyPath)) { if (! file_exists($privateKeyPath)) {
file_put_contents($privateKeyPath, self::JWT_PRIVATE_KEY); file_put_contents($privateKeyPath, self::JWT_PRIVATE_KEY);
} }
} }
} }

View File

@@ -50,7 +50,7 @@ class AdminDashboardWidgetsTest extends TestCase
'created_at' => now()->subMonth(), 'created_at' => now()->subMonth(),
]); ]);
$widget = new RevenueTrendWidget(); $widget = new RevenueTrendWidget;
$data = $this->invokeProtectedMethod($widget, 'getData'); $data = $this->invokeProtectedMethod($widget, 'getData');
$this->assertArrayHasKey('datasets', $data); $this->assertArrayHasKey('datasets', $data);
@@ -68,8 +68,6 @@ class AdminDashboardWidgetsTest extends TestCase
/** /**
* @template T * @template T
* *
* @param object $object
* @param string $method
* @return mixed * @return mixed
*/ */
private function invokeProtectedMethod(object $object, string $method) private function invokeProtectedMethod(object $object, string $method)