presseportale/routes/customer.php
Kevin Adametz 2cde7284a1 livewire: Volt entfernt — Komponenten als Livewire-4-Single-File-Components, Route::livewire(), Livewire::test(); livewire/volt deinstalliert
- 83 Komponenten: Livewire\Volt\Component → Livewire\Component
- 73 Routen (admin, auth, customer): Volt::route() → Route::livewire()
- 88 Testdateien: Volt::test/actingAs → Livewire::test/actingAs
- VoltServiceProvider gelöscht; composer remove livewire/volt
- config/livewire.php auf v4-Schlüssel (component_layout, component_placeholder,
  component_locations, make_command: SFC ohne ⚡). Der alte Schlüssel layout war
  wirkungslos; alle gerouteten Komponenten setzen ihr Layout selbst.
- CLAUDE.md, AGENTS.md, boost.json, .cursorrules, Skill volt-development
- Roadmap: erledigt; Larastan erfasst SFCs weiterhin nicht (Blade-Dateien)

Suite 2.325 grün, einer übersprungen; Larastan und Pint ohne Meldung;
route:cache und view:cache laufen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:59:36 +00:00

128 lines
7.3 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
use App\Http\Controllers\CheckoutController;
use App\Http\Controllers\EmailUnsubscribeController;
use App\Http\Controllers\InvoicePdfController;
use App\Http\Controllers\LegacyInvoicePdfController;
use App\Http\Controllers\ProofPdfController;
use App\Http\Controllers\StripeInvoiceRedirectController;
use App\Http\Controllers\Web\PressReleaseReportController;
use App\Http\Middleware\EnsureUserIsCustomer;
use App\Http\Middleware\LogSlowAdminRequests;
use Illuminate\Support\Facades\Route;
// ============================================================
// "Mein Bereich" – Eigentümer-Sicht im gemeinsamen Admin-Panel
// URL-Prefix: /admin/me/*
// Routen-Name: me.*
// Zugriff: Rollen admin, editor, customer (alle eingeloggten Panel-User)
// ============================================================
Route::middleware(['auth', 'verified', EnsureUserIsCustomer::class, LogSlowAdminRequests::class])
->prefix('admin/me')
->name('me.')
->group(function () {
Route::livewire('/', 'customer.dashboard')->name('dashboard');
Route::livewire('press-releases', 'customer.press-releases.index')->name('press-releases.index');
Route::livewire('press-releases/create', 'customer.press-releases.create')->name('press-releases.create');
Route::livewire('press-releases/{id}', 'customer.press-releases.show')->name('press-releases.show');
Route::livewire('press-releases/{id}/edit', 'customer.press-releases.edit')->name('press-releases.edit');
Route::get('press-releases/{id}/nachweis', ProofPdfController::class)
->where('id', '[0-9]+')
->name('press-releases.proof');
// Meldeverfahren (Phase 3): Eigentümer-Einstieg ohne Magic-Link —
// die Zuordnung der PM ersetzt die Verifikation (logged_in).
Route::post('press-releases/{pressReleaseId}/melden', [PressReleaseReportController::class, 'startFromCustomer'])
->where('pressReleaseId', '[0-9]+')
->name('press-releases.report');
Route::livewire('firmen', 'customer.press-kits.index')->name('press-kits.index');
Route::livewire('firmen/anlegen', 'customer.press-kits.create')->name('press-kits.create');
Route::livewire('firmen/{id}', 'customer.press-kits.show')->name('press-kits.show');
Route::redirect('pressemappen', '/admin/me/firmen', 301);
Route::get('pressemappen/{id}', fn (string $id) => redirect("/admin/me/firmen/{$id}", 301))
->where('id', '[0-9]+');
Route::livewire('buchungen-add-ons', 'customer.bookings')->name('bookings.index');
Route::get('checkout/abo/{planSlug}/{interval}', [CheckoutController::class, 'subscription'])
->whereIn('interval', ['monthly', 'yearly'])
->name('checkout.subscription');
Route::post('checkout/tarifwechsel/{planSlug}/{interval}', [CheckoutController::class, 'swap'])
->whereIn('interval', ['monthly', 'yearly'])
->name('checkout.swap');
Route::get('checkout/einzel-pm', [CheckoutController::class, 'singlePm'])
->name('checkout.single-pm');
Route::get('checkout/credits/{pack}', [CheckoutController::class, 'creditTopup'])
->name('checkout.credit-topup');
Route::get('checkout/depublikation/{pressReleaseId}', [CheckoutController::class, 'depublication'])
->where('pressReleaseId', '[0-9]+')
->name('checkout.depublication');
Route::get('checkout/aenderung/{pressReleaseId}', [CheckoutController::class, 'change'])
->where('pressReleaseId', '[0-9]+')
->name('checkout.change');
Route::get('checkout/abo-verwalten', [CheckoutController::class, 'billingPortal'])
->name('checkout.billing-portal');
Route::livewire('invoices', 'customer.invoices')->name('invoices.index');
Route::get('legacy-invoices/{legacyInvoice}/pdf', LegacyInvoicePdfController::class)->name('invoices.pdf');
Route::get('invoices/{invoice}/stripe', StripeInvoiceRedirectController::class)->name('invoices.stripe');
Route::get('invoices/{invoice}/pdf', InvoicePdfController::class)->name('invoices.manual-pdf');
Route::livewire('tokens', 'customer.tokens')->name('tokens.index');
Route::livewire('profile', 'customer.profile')->name('profile');
Route::livewire('security', 'customer.security')->name('security');
});
// ============================================================
// E-Mail-Benachrichtigungen: One-Click-Abmeldung ohne Login.
// Signierte Links aus den Benachrichtigungs-Mails (kein Ablauf, damit
// auch alte Mails funktionieren). Der POST auf die Abmelde-URL bedient
// den List-Unsubscribe-Header (RFC 8058, ohne CSRF — Ausnahme in
// bootstrap/app.php); der Wieder-Anmelden-POST kommt von der
// Bestätigungsseite selbst.
// ============================================================
Route::prefix('e-mail-benachrichtigungen')
->name('email-preferences.')
->middleware('throttle:30,1')
->group(function () {
Route::get('abmelden/{user}/{type}', [EmailUnsubscribeController::class, 'show'])
->name('unsubscribe');
Route::post('abmelden/{user}/{type}', [EmailUnsubscribeController::class, 'oneClickUnsubscribe'])
->name('unsubscribe.one-click');
Route::post('anmelden/{user}/{type}', [EmailUnsubscribeController::class, 'resubscribe'])
->name('resubscribe');
// Roadmap 1d.3: Abmeldung für Empfänger ohne Konto (Pressekontakt,
// Firmen-E-Mail einer API-Meldung) — adressbezogen, in der
// Sperrliste geführt. Signiert, damit nur ein Link aus unserer Mail
// eine Adresse abmelden kann.
Route::get('abmelden-adresse', [EmailUnsubscribeController::class, 'showAddress'])
->name('unsubscribe-address');
Route::post('abmelden-adresse', [EmailUnsubscribeController::class, 'oneClickUnsubscribeAddress'])
->name('unsubscribe-address.one-click');
Route::post('anmelden-adresse', [EmailUnsubscribeController::class, 'resubscribeAddress'])
->name('resubscribe-address');
});
// ============================================================
// Legacy /customer/*-Pfade als 301-Redirect erhalten,
// damit Bookmarks und alte Mails weiter funktionieren.
// ============================================================
Route::prefix('customer')->group(function () {
Route::redirect('/', '/admin/me', 301);
Route::redirect('press-releases', '/admin/me/press-releases', 301);
Route::redirect('press-releases/create', '/admin/me/press-releases/create', 301);
Route::get('press-releases/{id}', fn (string $id) => redirect("/admin/me/press-releases/{$id}", 301))
->where('id', '[0-9]+');
Route::get('press-releases/{id}/edit', fn (string $id) => redirect("/admin/me/press-releases/{$id}/edit", 301))
->where('id', '[0-9]+');
Route::redirect('pressemappen', '/admin/me/firmen', 301);
Route::get('pressemappen/{id}', fn (string $id) => redirect("/admin/me/firmen/{$id}", 301))
->where('id', '[0-9]+');
Route::redirect('buchungen-add-ons', '/admin/me/buchungen-add-ons', 301);
Route::redirect('invoices', '/admin/me/invoices', 301);
Route::redirect('tokens', '/admin/me/tokens', 301);
Route::redirect('profile', '/admin/me/profile', 301);
Route::redirect('security', '/admin/me/security', 301);
});