orderBy('price') ->get() ->map(fn (Package $package) => $this->presentPackage($package)) ->values() ->all(); return Inertia::render('marketing/Home', compact('packages')); } public function contact(Request $request) { $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|max:255', 'message' => 'required|string|max:1000', ]); $locale = app()->getLocale(); $contactAddress = config('mail.contact_address', config('mail.from.address')) ?: 'admin@fotospiel.de'; Mail::raw( __('emails.contact.body', [ 'name' => $request->name, 'email' => $request->email, 'message' => $request->message, ], $locale), function ($message) use ($contactAddress, $locale) { $message->to($contactAddress) ->subject(__('emails.contact.subject', [], $locale)); } ); Mail::to($request->email) ->locale($locale) ->queue(new ContactConfirmation($request->name)); return redirect() ->back() ->with('success', __('marketing.contact.success', [], $locale)); } public function contactView(Request $request) { $locale = app()->getLocale(); $secondSegment = $request->segment(2); $slug = $secondSegment ? '/'.trim((string) $secondSegment, '/') : '/'; if ($locale === 'en' && $slug === '/kontakt') { return redirect()->route('marketing.contact', [ 'locale' => $request->route('locale') ?? $locale, ], 301); } if ($locale === 'de' && $slug === '/contact') { return redirect()->route('kontakt', [ 'locale' => $request->route('locale') ?? $locale, ], 301); } return Inertia::render('marketing/Kontakt'); } /** * Handle package purchase flow. */ public function buyPackages(Request $request, string $locale, $packageId) { Log::info('Buy packages called', ['auth' => Auth::check(), 'locale' => $locale, 'package_id' => $packageId]); $package = Package::findOrFail($packageId); if (! Auth::check()) { return redirect()->route('register', ['package_id' => $package->id]) ->with('message', __('marketing.packages.register_required')); } $user = Auth::user(); if (! $user->email_verified_at) { return redirect()->route('verification.notice') ->with('message', __('auth.verification_required')); } $tenant = $user->tenant; if (! $tenant) { abort(500, 'Tenant not found'); } if ($package->price == 0) { TenantPackage::updateOrCreate( [ 'tenant_id' => $tenant->id, 'package_id' => $package->id, ], [ 'price' => $package->price, 'active' => true, 'purchased_at' => now(), 'expires_at' => now()->addYear(), ] ); PackagePurchase::create([ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'provider' => 'free', 'provider_id' => 'free', 'price' => $package->price, 'type' => $package->type === 'endcustomer' ? 'endcustomer_event' : 'reseller_subscription', 'purchased_at' => now(), 'refunded' => false, ]); return redirect('/event-admin')->with('success', __('marketing.packages.free_assigned')); } if (! $package->paddle_price_id) { Log::warning('Package missing Paddle price id', ['package_id' => $package->id]); return redirect()->route('packages', [ 'locale' => app()->getLocale(), 'highlight' => $package->slug, ]) ->with('error', __('marketing.packages.paddle_not_configured')); } $session = $this->checkoutSessions->createOrResume($user, $package, [ 'tenant' => $tenant, ]); $this->checkoutSessions->selectProvider($session, CheckoutSession::PROVIDER_PADDLE); $checkout = $this->paddleCheckout->createCheckout($tenant, $package, [ 'success_url' => route('marketing.success', [ 'locale' => app()->getLocale(), 'packageId' => $package->id, ]), 'return_url' => route('packages', [ 'locale' => app()->getLocale(), 'highlight' => $package->slug, ]), 'metadata' => [ 'checkout_session_id' => $session->id, ], ]); $session->forceFill([ 'paddle_checkout_id' => $checkout['id'] ?? $session->paddle_checkout_id, 'provider_metadata' => array_merge($session->provider_metadata ?? [], array_filter([ 'paddle_checkout_id' => $checkout['id'] ?? null, 'paddle_checkout_url' => $checkout['checkout_url'] ?? null, 'paddle_expires_at' => $checkout['expires_at'] ?? null, ])), ])->save(); $redirectUrl = $checkout['checkout_url'] ?? null; if (! $redirectUrl) { throw ValidationException::withMessages([ 'paddle' => __('marketing.packages.paddle_checkout_failed'), ]); } return redirect()->away($redirectUrl); } public function success(Request $request, $packageId = null) { if (Auth::check() && Auth::user()->email_verified_at) { return redirect('/event-admin')->with('success', __('marketing.success.welcome')); } return Inertia::render('marketing/Success', compact('packageId')); } public function blogIndex(Request $request) { $locale = $request->get('locale', app()->getLocale()); Log::info('Blog Index Debug - Initial', [ 'locale' => $locale, 'full_url' => $request->fullUrl(), ]); $query = BlogPost::query() ->with('author') ->whereHas('category', function ($query) { $query->where('slug', 'blog'); }); $totalWithCategory = $query->count(); Log::info('Blog Index Debug - With Category', ['count' => $totalWithCategory]); $query->where('is_published', true) ->whereNotNull('published_at') ->where('published_at', '<=', now()); $totalPublished = $query->count(); Log::info('Blog Index Debug - Published', ['count' => $totalPublished]); // Removed translation filter for now $totalWithTranslation = $query->count(); Log::info('Blog Index Debug - With Translation', ['count' => $totalWithTranslation, 'locale' => $locale]); $posts = $query->orderBy('published_at', 'desc') ->paginate(8); // Transform posts to include translated strings for the current locale $posts->getCollection()->transform(function ($post) use ($locale) { $post->title = $post->getTranslation('title', $locale) ?? $post->getTranslation('title', 'de') ?? ''; $post->excerpt = $post->getTranslation('excerpt', $locale) ?? $post->getTranslation('excerpt', 'de') ?? ''; $post->content = $post->getTranslation('content', $locale) ?? $post->getTranslation('content', 'de') ?? ''; // Author name is a string, no translation needed; author is loaded via with('author') return $post; }); Log::info('Blog Index Debug - Final Posts', [ 'count' => $posts->count(), 'total' => $posts->total(), 'posts_data' => $posts->toArray(), 'first_post_title' => $posts->count() > 0 ? $posts->first()->title : 'No posts', ]); return Inertia::render('marketing/Blog', compact('posts')); } public function blogShow($slug) { $locale = app()->getLocale(); $postModel = BlogPost::query() ->with('author') ->whereHas('category', function ($query) { $query->where('slug', 'blog'); }) ->where('slug', $slug) ->where('is_published', true) ->whereNotNull('published_at') ->where('published_at', '<=', now()) // Removed translation filter for now ->firstOrFail(); // Transform to array with translated strings for the current locale $markdown = $postModel->getTranslation('content', $locale) ?? $postModel->getTranslation('content', 'de') ?? ''; $environment = new Environment; $environment->addExtension(new CommonMarkCoreExtension); $environment->addExtension(new TableExtension); $environment->addExtension(new AutolinkExtension); $environment->addExtension(new StrikethroughExtension); $environment->addExtension(new TaskListExtension); $converter = new MarkdownConverter($environment); $contentHtml = (string) $converter->convert($markdown); $post = [ 'id' => $postModel->id, 'title' => $postModel->getTranslation('title', $locale) ?? $postModel->getTranslation('title', 'de') ?? '', 'excerpt' => $postModel->getTranslation('excerpt', $locale) ?? $postModel->getTranslation('excerpt', 'de') ?? '', 'content' => $markdown, 'content_html' => $contentHtml, 'featured_image' => $postModel->featured_image ?? $postModel->banner_url ?? null, 'published_at' => $postModel->published_at->toDateString(), 'slug' => $postModel->slug, 'author' => $postModel->author ? [ 'name' => $postModel->author->name, ] : null, ]; return Inertia::render('marketing/BlogShow', compact('post')); } public function howItWorks() { return Inertia::render('marketing/HowItWorks'); } public function demo() { return Inertia::render('marketing/Demo'); } public function packagesIndex() { $endcustomerPackages = Package::where('type', 'endcustomer') ->orderBy('price') ->get() ->map(fn (Package $package) => $this->presentPackage($package)) ->values() ->all(); $resellerPackages = Package::where('type', 'reseller') ->orderBy('price') ->get() ->map(fn (Package $package) => $this->presentPackage($package)) ->values() ->all(); return Inertia::render('marketing/Packages', [ 'endcustomerPackages' => $endcustomerPackages, 'resellerPackages' => $resellerPackages, ]); } public function occasionsType(Request $request, string $locale, string $type) { Log::info('OccasionsType hit', [ 'type' => $type, 'locale' => $locale, 'url' => request()->fullUrl(), 'route' => request()->route()->getName(), 'isInertia' => request()->header('X-Inertia'), ]); $normalized = strtolower($type); $typeMap = [ 'hochzeit' => 'hochzeit', 'wedding' => 'hochzeit', 'geburtstag' => 'geburtstag', 'birthday' => 'geburtstag', 'firmenevent' => 'firmenevent', 'corporate-event' => 'firmenevent', 'konfirmation' => 'konfirmation', 'confirmation' => 'konfirmation', ]; if (! array_key_exists($normalized, $typeMap)) { Log::warning('Invalid occasion type accessed', ['type' => $type]); abort(404, 'Invalid occasion type'); } $baseSlug = $typeMap[$normalized]; $canonical = [ 'hochzeit' => [ 'de' => 'hochzeit', 'en' => 'wedding', ], 'geburtstag' => [ 'de' => 'geburtstag', 'en' => 'birthday', ], 'firmenevent' => [ 'de' => 'firmenevent', 'en' => 'corporate-event', ], 'konfirmation' => [ 'de' => 'konfirmation', 'en' => 'confirmation', ], ]; $canonicalSlug = $canonical[$baseSlug][$locale] ?? $baseSlug; $currentSlug = strtolower($type); if ($currentSlug !== $canonicalSlug) { $routeName = $locale === 'en' ? 'occasions.type' : 'anlaesse.type'; return redirect()->route($routeName, [ 'locale' => $locale, 'type' => $canonicalSlug, ], 301); } return Inertia::render('marketing/Occasions', [ 'type' => $baseSlug, 'requestedType' => $normalized, ]); } }