12-05-2026 Frontend dev
This commit is contained in:
parent
405df0a122
commit
5b8bdf4182
779 changed files with 480564 additions and 6241 deletions
77
routes/ADMIN_ROUTES.md
Normal file
77
routes/ADMIN_ROUTES.md
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# Admin-Routes Übersicht
|
||||
|
||||
**Datei:** `routes/admin.php`
|
||||
**Stand:** 23. April 2026
|
||||
|
||||
---
|
||||
|
||||
## Aktueller Status
|
||||
|
||||
- **31 Routen** sind aktiv deklariert.
|
||||
- Alle aktiven Volt-Routen zeigen auf vorhandene Komponenten.
|
||||
- Nicht vorhandene Volt-Ziele wurden aus dem Routing entfernt und bleiben als Backlog bestehen.
|
||||
|
||||
---
|
||||
|
||||
## Aktive Routen
|
||||
|
||||
### Basis
|
||||
- `GET /` → `admin.home`
|
||||
- `GET /dashboard` → `dashboard`
|
||||
- `GET /settings` (Redirect) → `settings/profile`
|
||||
|
||||
### Settings
|
||||
- `GET /settings/profile` → `settings.profile`
|
||||
- `GET /settings/password` → `settings.password`
|
||||
- `GET /settings/appearance` → `settings.appearance`
|
||||
|
||||
### Content
|
||||
- `GET /admin/press-releases` → `admin.press-releases.index`
|
||||
- `GET /admin/press-releases/create` → `admin.press-releases.create`
|
||||
- `GET /admin/press-releases/{id}` → `admin.press-releases.show`
|
||||
- `GET /admin/press-releases/{id}/edit` → `admin.press-releases.edit`
|
||||
- `GET /admin/categories` → `admin.categories.index`
|
||||
|
||||
### CRM
|
||||
- `GET /admin/companies` → `admin.companies.index`
|
||||
- `GET /admin/companies/create` → `admin.companies.create`
|
||||
- `GET /admin/companies/{id}` → `admin.companies.show`
|
||||
- `GET /admin/companies/{id}/edit` → `admin.companies.edit`
|
||||
- `GET /admin/companies/{companyId}/contacts/create` → `admin.companies.contacts.create`
|
||||
- `GET /admin/contacts` → `admin.contacts.index`
|
||||
- `GET /admin/contacts/create` → `admin.contacts.create`
|
||||
- `GET /admin/contacts/{id}/edit` → `admin.contacts.edit`
|
||||
|
||||
### Billing
|
||||
- `GET /admin/invoices` → `admin.invoices.index`
|
||||
- `GET /admin/payments` → `admin.payments.index`
|
||||
- `GET /admin/coupons` → `admin.coupons.index`
|
||||
- `GET /admin/newsletter-sync` → `admin.newsletter.sync`
|
||||
|
||||
### Administration
|
||||
- `GET /admin/users` → `admin.users.index` (gemappt auf Volt-Komponente `admin.users`)
|
||||
- `GET /admin/users/create` → `admin.users.create`
|
||||
- `GET /admin/users/{id}` → `admin.users.show`
|
||||
- `GET /admin/users/{id}/edit` → `admin.users.edit`
|
||||
- `GET /admin/users/table` → `admin.users.table`
|
||||
- `GET /admin/roles` → `admin.roles.index`
|
||||
- `GET /admin/roles/create` → `admin.roles.create`
|
||||
- `GET /admin/roles/{id}/edit` → `admin.roles.edit`
|
||||
|
||||
---
|
||||
|
||||
## Backlog (derzeit nicht geroutet)
|
||||
|
||||
- Kategorien: `create`, `edit`
|
||||
- Kontakte: —
|
||||
- Rechnungen: `show`
|
||||
- Zahlungen: `show`
|
||||
- Gutscheine: `create`, `edit`
|
||||
- Benutzer: —
|
||||
- System: `scheduler`, `newsletter/*` (Kampagnen/Subscriber), `settings`
|
||||
|
||||
---
|
||||
|
||||
## Hinweis
|
||||
|
||||
Bei neuen Volt-Komponenten können die Backlog-Routen gezielt wieder aktiviert werden.
|
||||
|
|
@ -1,29 +1,88 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Admin\DashboardController;
|
||||
use App\Http\Controllers\Admin\LeaveImpersonationController;
|
||||
use App\Http\Controllers\LegacyInvoicePdfController;
|
||||
use App\Http\Middleware\EnsureUserIsAdmin;
|
||||
use App\Http\Middleware\LogSlowAdminRequests;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Livewire\Volt\Volt;
|
||||
|
||||
// Admin-Routes
|
||||
|
||||
// Admin-Dashboard
|
||||
// Admin-Startseite → Dashboard
|
||||
Route::get('/', function () {
|
||||
return redirect()->route('dashboard');
|
||||
})->name('home');
|
||||
})->name('admin.home');
|
||||
|
||||
Route::view('dashboard', 'admin.dashboard')->middleware(['auth', 'verified'])->name('dashboard');
|
||||
Route::get('dashboard', DashboardController::class)
|
||||
->middleware(['auth', 'verified', EnsureUserIsAdmin::class, LogSlowAdminRequests::class])
|
||||
->name('dashboard');
|
||||
|
||||
// Admin-Einstellungen
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
// ========================================
|
||||
// Nutzer-eigene Einstellungen (kein Admin erforderlich)
|
||||
// ========================================
|
||||
Route::middleware(['auth', 'verified'])->group(function () {
|
||||
Route::redirect('settings', 'settings/profile');
|
||||
|
||||
Volt::route('settings/profile', 'settings.profile')->name('settings.profile');
|
||||
Volt::route('settings/password', 'settings.password')->name('settings.password');
|
||||
Volt::route('settings/appearance', 'settings.appearance')->name('settings.appearance');
|
||||
|
||||
// Weitere Admin-Routen hier...
|
||||
|
||||
Volt::route('admin/users', 'admin.users')->name('admin.users');
|
||||
Volt::route('admin/users/table', 'admin.users.table')->name('admin.users.table');
|
||||
});
|
||||
|
||||
// Admin-Authentication wird bereits in domains.php geladen
|
||||
Route::post('admin/impersonate/leave', LeaveImpersonationController::class)
|
||||
->middleware(['auth', 'throttle:10,1'])
|
||||
->name('admin.impersonate.leave');
|
||||
|
||||
// ========================================
|
||||
// Admin-Bereich (nur Rollen admin/editor)
|
||||
// ========================================
|
||||
Route::middleware(['auth', 'verified', EnsureUserIsAdmin::class, LogSlowAdminRequests::class])->group(function () {
|
||||
|
||||
// Content Management
|
||||
Volt::route('admin/press-releases', 'admin.press-releases.index')->name('admin.press-releases.index');
|
||||
Volt::route('admin/press-releases/create', 'admin.press-releases.create')->name('admin.press-releases.create');
|
||||
Volt::route('admin/press-releases/{id}', 'admin.press-releases.show')->name('admin.press-releases.show');
|
||||
Volt::route('admin/press-releases/{id}/edit', 'admin.press-releases.edit')->name('admin.press-releases.edit');
|
||||
|
||||
Volt::route('admin/categories', 'admin.categories.index')->name('admin.categories.index');
|
||||
Volt::route('admin/categories/create', 'admin.categories.create')->name('admin.categories.create');
|
||||
Volt::route('admin/categories/{id}/edit', 'admin.categories.edit')->name('admin.categories.edit');
|
||||
|
||||
Volt::route('admin/footer-codes', 'admin.footer-codes.index')->name('admin.footer-codes.index');
|
||||
Volt::route('admin/footer-codes/create', 'admin.footer-codes.create')->name('admin.footer-codes.create');
|
||||
Volt::route('admin/footer-codes/{id}/edit', 'admin.footer-codes.edit')->name('admin.footer-codes.edit');
|
||||
|
||||
// CRM
|
||||
Volt::route('admin/companies', 'admin.companies.index')->name('admin.companies.index');
|
||||
Volt::route('admin/companies/create', 'admin.companies.create')->name('admin.companies.create');
|
||||
Volt::route('admin/companies/{id}', 'admin.companies.show')->name('admin.companies.show');
|
||||
Volt::route('admin/companies/{id}/edit', 'admin.companies.edit')->name('admin.companies.edit');
|
||||
Volt::route('admin/companies/{companyId}/contacts/create', 'admin.contacts.create')->name('admin.companies.contacts.create');
|
||||
|
||||
Volt::route('admin/contacts', 'admin.contacts.index')->name('admin.contacts.index');
|
||||
Volt::route('admin/contacts/create', 'admin.contacts.create')->name('admin.contacts.create');
|
||||
Volt::route('admin/contacts/{id}/edit', 'admin.contacts.edit')->name('admin.contacts.edit');
|
||||
|
||||
// Billing
|
||||
Volt::route('admin/invoices', 'admin.invoices.index')->name('admin.invoices.index');
|
||||
Route::get('admin/legacy-invoices/{legacyInvoice}/pdf', LegacyInvoicePdfController::class)->name('admin.legacy-invoices.pdf');
|
||||
Volt::route('admin/payments', 'admin.payments.index')->name('admin.payments.index');
|
||||
Volt::route('admin/coupons', 'admin.coupons.index')->name('admin.coupons.index');
|
||||
Volt::route('admin/newsletter-sync', 'admin.newsletter.sync')->name('admin.newsletter.sync');
|
||||
|
||||
// Administration
|
||||
Volt::route('admin/presets', 'admin.presets.index')->name('admin.presets.index');
|
||||
Volt::route('admin/presets/create', 'admin.presets.create')->name('admin.presets.create');
|
||||
Volt::route('admin/presets/{id}/edit', 'admin.presets.edit')->name('admin.presets.edit');
|
||||
|
||||
Volt::route('admin/users', 'admin.users')->name('admin.users.index');
|
||||
Volt::route('admin/users/create', 'admin.users.create')->name('admin.users.create');
|
||||
Volt::route('admin/users/{id}', 'admin.users.show')->name('admin.users.show');
|
||||
Volt::route('admin/users/{id}/edit', 'admin.users.edit')->name('admin.users.edit');
|
||||
Volt::route('admin/users/table', 'admin.users.table')->name('admin.users.table');
|
||||
|
||||
Volt::route('admin/roles', 'admin.roles.index')->name('admin.roles.index');
|
||||
Volt::route('admin/roles/create', 'admin.roles.create')->name('admin.roles.create');
|
||||
Volt::route('admin/roles/{id}/edit', 'admin.roles.edit')->name('admin.roles.edit');
|
||||
|
||||
// Reports
|
||||
Volt::route('admin/reports/slow-requests', 'admin.reports.slow-requests')->name('admin.reports.slow-requests');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,58 +1,27 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Http\Controllers\Api\V1\CategoryController;
|
||||
use App\Http\Controllers\Api\V1\CompanyController;
|
||||
use App\Http\Controllers\Api\V1\NewsletterSubscriptionController;
|
||||
use App\Http\Controllers\Api\V1\PressReleaseController;
|
||||
use App\Http\Controllers\Api\V1\PressReleaseImageController;
|
||||
use App\Http\Middleware\EnsureApiTokenRateLimit;
|
||||
use App\Http\Middleware\EnsureApiUserIsActive;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| API Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register API routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider and all of them will
|
||||
| be assigned to the "api" middleware group. Make something great!
|
||||
|
|
||||
*/
|
||||
|
||||
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
|
||||
return $request->user();
|
||||
});
|
||||
|
||||
// Geschützte API-Routen
|
||||
Route::middleware('auth:sanctum')->group(function () {
|
||||
// Beispiel-API-Route
|
||||
Route::get('/profile', function (Request $request) {
|
||||
return response()->json([
|
||||
'user' => $request->user(),
|
||||
'message' => 'Profil erfolgreich abgerufen'
|
||||
]);
|
||||
Route::prefix('v1')
|
||||
->middleware(['auth:sanctum', EnsureApiUserIsActive::class, EnsureApiTokenRateLimit::class])
|
||||
->group(function (): void {
|
||||
Route::apiResource('press-releases', PressReleaseController::class)
|
||||
->parameters(['press-releases' => 'pressRelease']);
|
||||
Route::get('press-releases/{pressRelease}/images', [PressReleaseImageController::class, 'index'])
|
||||
->name('press-releases.images.index');
|
||||
Route::post('press-releases/{pressRelease}/images', [PressReleaseImageController::class, 'store'])
|
||||
->name('press-releases.images.store');
|
||||
Route::delete('press-release-images/{pressReleaseImage}', [PressReleaseImageController::class, 'destroy'])
|
||||
->name('press-release-images.destroy');
|
||||
Route::apiResource('companies', CompanyController::class)->only(['index', 'show']);
|
||||
Route::get('categories', [CategoryController::class, 'index'])->name('categories.index');
|
||||
Route::post('newsletter/subscribe', [NewsletterSubscriptionController::class, 'store'])
|
||||
->name('newsletter.subscribe');
|
||||
});
|
||||
|
||||
// Token-Erstellung (bereits durch Sanctum bereitgestellt)
|
||||
// POST /api/tokens/create
|
||||
// POST /api/tokens/revoke
|
||||
});
|
||||
|
||||
// Öffentliche API-Routen
|
||||
Route::post('/login', function (Request $request) {
|
||||
$credentials = $request->validate([
|
||||
'email' => 'required|email',
|
||||
'password' => 'required',
|
||||
]);
|
||||
|
||||
if (Auth::attempt($credentials)) {
|
||||
$user = Auth::user();
|
||||
$token = $user->createToken('api-token')->plainTextToken;
|
||||
|
||||
return response()->json([
|
||||
'token' => $token,
|
||||
'user' => $user,
|
||||
'message' => 'Erfolgreich angemeldet'
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Ungültige Anmeldedaten'
|
||||
], 401);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Auth\MagicLinkConsumeController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Laravel\Fortify\Http\Controllers\AuthenticatedSessionController;
|
||||
use Livewire\Volt\Volt;
|
||||
|
||||
// Fortify-Routen für Authentifizierung mit Livewire
|
||||
|
|
@ -10,6 +12,10 @@ Route::group(['middleware' => config('fortify.middleware', ['web'])], function (
|
|||
->middleware(['guest:'.config('fortify.guard')])
|
||||
->name('login');
|
||||
|
||||
Route::get('/magic-login/{token}', MagicLinkConsumeController::class)
|
||||
->middleware(['guest:'.config('fortify.guard')])
|
||||
->name('magic-links.consume');
|
||||
|
||||
// Registrierung mit Livewire
|
||||
Volt::route('/register', 'auth.register')
|
||||
->middleware(['guest:'.config('fortify.guard')])
|
||||
|
|
@ -36,6 +42,6 @@ Route::group(['middleware' => config('fortify.middleware', ['web'])], function (
|
|||
->name('password.confirm');
|
||||
|
||||
// Logout-Route
|
||||
Route::post('/logout', [Laravel\Fortify\Http\Controllers\AuthenticatedSessionController::class, 'destroy'])
|
||||
->name('logout');
|
||||
Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])
|
||||
->name('logout1');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,29 @@
|
|||
<?php
|
||||
|
||||
use App\Console\Commands\PurgeExpiredPressReleaseDrafts;
|
||||
use App\Console\Commands\PurgeMagicLinks;
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Schedule;
|
||||
|
||||
Artisan::command('inspire', function () {
|
||||
$this->comment(Inspiring::quote());
|
||||
})->purpose('Display an inspiring quote');
|
||||
|
||||
// ========================================
|
||||
// Tägliche Bereinigungen
|
||||
// ========================================
|
||||
|
||||
// Magic-Links: Abgelaufene / verbrauchte Tokens entfernen (täglich 03:00)
|
||||
Schedule::command(PurgeMagicLinks::class, ['--days=30'])
|
||||
->dailyAt('03:00')
|
||||
->withoutOverlapping()
|
||||
->runInBackground();
|
||||
|
||||
// PM-Entwürfe: Zombie-Drafts nach 180 Tagen Inaktivität archivieren (wöchentlich)
|
||||
Schedule::command(PurgeExpiredPressReleaseDrafts::class, ['--days=180'])
|
||||
->weekly()
|
||||
->sundays()
|
||||
->at('04:00')
|
||||
->withoutOverlapping()
|
||||
->runInBackground();
|
||||
|
|
|
|||
62
routes/customer.php
Normal file
62
routes/customer.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\LegacyInvoicePdfController;
|
||||
use App\Http\Middleware\EnsureUserIsCustomer;
|
||||
use App\Http\Middleware\LogSlowAdminRequests;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Livewire\Volt\Volt;
|
||||
|
||||
// ============================================================
|
||||
// "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 () {
|
||||
|
||||
Volt::route('/', 'customer.dashboard')->name('dashboard');
|
||||
|
||||
Volt::route('press-releases', 'customer.press-releases.index')->name('press-releases.index');
|
||||
Volt::route('press-releases/create', 'customer.press-releases.create')->name('press-releases.create');
|
||||
Volt::route('press-releases/{id}', 'customer.press-releases.show')->name('press-releases.show');
|
||||
Volt::route('press-releases/{id}/edit', 'customer.press-releases.edit')->name('press-releases.edit');
|
||||
|
||||
Volt::route('firmen', 'customer.press-kits.index')->name('press-kits.index');
|
||||
Volt::route('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]+');
|
||||
|
||||
Volt::route('buchungen-add-ons', 'customer.bookings')->name('bookings.index');
|
||||
Volt::route('invoices', 'customer.invoices')->name('invoices.index');
|
||||
Route::get('legacy-invoices/{legacyInvoice}/pdf', LegacyInvoicePdfController::class)->name('invoices.pdf');
|
||||
Volt::route('tokens', 'customer.tokens')->name('tokens.index');
|
||||
|
||||
Volt::route('profile', 'customer.profile')->name('profile');
|
||||
Volt::route('security', 'customer.security')->name('security');
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 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);
|
||||
});
|
||||
|
|
@ -1,48 +1,49 @@
|
|||
<?php
|
||||
|
||||
use Flux\AssetManager;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
|
||||
// Portal-Bereich auf https://pr-copilot.test
|
||||
Route::domain('pr-copilot.test')->group(function () {
|
||||
$domainPortal = config('domains.domain_portal');
|
||||
$domainPresseecho = config('domains.domain_presseecho');
|
||||
$domainBusinessportal = config('domains.domain_businessportal');
|
||||
|
||||
// Portal-Bereich (lokal: presseportale.test, live: presseportale.com – via .env)
|
||||
Route::domain($domainPortal)->group(function () {
|
||||
// Auth-Routen laden
|
||||
require __DIR__ . '/auth.php';
|
||||
require __DIR__.'/auth.php';
|
||||
|
||||
// Admin-Routes laden
|
||||
require __DIR__ . '/admin.php';
|
||||
require __DIR__.'/admin.php';
|
||||
|
||||
// Customer-Portal laden
|
||||
require __DIR__.'/customer.php';
|
||||
|
||||
// Display-API-Route (öffentlich zugänglich)
|
||||
// Route::get('/api/display/config', [\App\Http\Controllers\Api\DisplayConfigController::class, 'index']);
|
||||
|
||||
// FluxUI Asset-Routen explizit für Portal-Domain registrieren
|
||||
// (Notwendig weil Route-Cache die globalen Flux-Routen nicht für alle Domains enthält)
|
||||
Route::get('/flux/flux.js', [AssetManager::class, 'fluxJs']);
|
||||
Route::get('/flux/flux.min.js', [AssetManager::class, 'fluxMinJs']);
|
||||
Route::get('/flux/editor.css', [AssetManager::class, 'editorCss']);
|
||||
Route::get('/flux/editor.js', [AssetManager::class, 'editorJs']);
|
||||
Route::get('/flux/editor.min.js', [AssetManager::class, 'editorMinJs']);
|
||||
|
||||
// Portal-Assets (Vite) - Port 5177
|
||||
Vite::useBuildDirectory('build/portal');
|
||||
});
|
||||
|
||||
// API-Routen für alle Domains
|
||||
Route::domain('api.pr-copilot.test')->group(function () {
|
||||
/*Route::domain('api.presseportale.test')->group(function () {
|
||||
require __DIR__ . '/api.php';
|
||||
});
|
||||
});*/
|
||||
|
||||
require __DIR__.'/web.php';
|
||||
|
||||
// Theme 1 für presseecho.test
|
||||
Route::domain('presseecho.test')->group(function () {
|
||||
Route::domain($domainPresseecho)->group(function () {
|
||||
// Web-Routes laden
|
||||
require __DIR__ . '/web.php';
|
||||
|
||||
// Theme 1 Assets (Vite)
|
||||
Vite::macro('themeCss', function () {
|
||||
return $this->asset('resources/css/web/theme-presseecho.css');
|
||||
});
|
||||
|
||||
Vite::useBuildDirectory('build/web');
|
||||
});
|
||||
|
||||
// Theme 2 für businessportal24.test
|
||||
Route::domain('businessportal24.test')->group(function () {
|
||||
Route::domain($domainBusinessportal)->group(function () {
|
||||
// Web-Routes laden
|
||||
require __DIR__ . '/web.php';
|
||||
|
||||
// Theme 2 Assets (Vite)
|
||||
Vite::macro('themeCss', function () {
|
||||
return $this->asset('resources/css/web/theme-businessportal24.css');
|
||||
});
|
||||
|
||||
Vite::useBuildDirectory('build/web');
|
||||
});
|
||||
|
|
|
|||
214
routes/web.php
214
routes/web.php
|
|
@ -1,25 +1,221 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Enums\PressReleaseStatus;
|
||||
use App\Http\Controllers\PressReleasePreviewController;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\PressRelease;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
// Gemeinsame Web-Routes für alle Landingpages
|
||||
Route::get('/pm-vorschau/{token}', PressReleasePreviewController::class)
|
||||
->where('token', '[A-Za-z0-9]{40,}')
|
||||
->name('press-releases.preview');
|
||||
|
||||
// Gemeinsame Web-Routes für alle Landingpages
|
||||
// Jede Landing-Page hat das gleiche Gerüst, aber unterschiedliches Styling
|
||||
|
||||
$applyWebDomainConfig = static function (string $domainKey): array {
|
||||
$domainConfig = config("domains.domains.{$domainKey}", []);
|
||||
|
||||
config([
|
||||
'app.theme' => $domainConfig['theme'] ?? $domainKey,
|
||||
'app.view_prefix' => $domainConfig['view_prefix'] ?? 'web',
|
||||
'app.domain_name' => $domainConfig['domain_name'] ?? request()->getHost(),
|
||||
'app.url' => $domainConfig['url'] ?? config('app.url'),
|
||||
]);
|
||||
|
||||
View::share('theme', $domainConfig['theme'] ?? $domainKey);
|
||||
View::share('viewPrefix', $domainConfig['view_prefix'] ?? 'web');
|
||||
View::share('domainName', $domainConfig['domain_name'] ?? request()->getHost());
|
||||
View::share('domainConfig', $domainConfig);
|
||||
View::share('domainUrl', $domainConfig['url'] ?? config('app.url'));
|
||||
View::share('assetUrl', $domainConfig['url'] ?? config('app.url'));
|
||||
|
||||
return $domainConfig;
|
||||
};
|
||||
|
||||
$webHomeData = static function (Portal $primaryPortal): array {
|
||||
$portalValues = [$primaryPortal->value, Portal::Both->value];
|
||||
|
||||
$publishedQuery = static fn (): Builder => PressRelease::query()
|
||||
->whereIn('portal', $portalValues)
|
||||
->where('status', PressReleaseStatus::Published)
|
||||
->where('language', 'de')
|
||||
->whereNotNull('published_at')
|
||||
->where('published_at', '<=', now());
|
||||
|
||||
$with = [
|
||||
'company',
|
||||
'category.translations' => fn ($query) => $query->where('locale', 'de'),
|
||||
'images' => fn ($query) => $query
|
||||
->orderByDesc('is_preview')
|
||||
->orderBy('sort_order')
|
||||
->limit(1),
|
||||
];
|
||||
|
||||
$oldestPublishedAt = $publishedQuery()
|
||||
->oldest('published_at')
|
||||
->value('published_at');
|
||||
|
||||
$leadRelease = $publishedQuery()
|
||||
->with($with)
|
||||
->orderByDesc('published_at')
|
||||
->first();
|
||||
|
||||
$sideReleases = $publishedQuery()
|
||||
->with($with)
|
||||
->when($leadRelease, fn (Builder $query) => $query->where('id', '!=', $leadRelease->id))
|
||||
->orderByDesc('published_at')
|
||||
->limit(4)
|
||||
->get();
|
||||
|
||||
$mostReadReleases = $publishedQuery()
|
||||
->orderByDesc('hits')
|
||||
->orderByDesc('published_at')
|
||||
->limit(4)
|
||||
->get(['id', 'slug', 'title', 'hits', 'portal', 'language']);
|
||||
|
||||
$activeNewsrooms = Company::query()
|
||||
->whereIn('portal', $portalValues)
|
||||
->where('is_active', true)
|
||||
->whereHas('pressReleases', function (Builder $query) use ($portalValues): void {
|
||||
$query
|
||||
->whereIn('portal', $portalValues)
|
||||
->where('status', PressReleaseStatus::Published)
|
||||
->where('language', 'de')
|
||||
->whereNotNull('published_at')
|
||||
->where('published_at', '>=', now()->subDays(7));
|
||||
})
|
||||
->withCount([
|
||||
'pressReleases as recent_releases_count' => function (Builder $query) use ($portalValues): void {
|
||||
$query
|
||||
->whereIn('portal', $portalValues)
|
||||
->where('status', PressReleaseStatus::Published)
|
||||
->where('language', 'de')
|
||||
->whereNotNull('published_at')
|
||||
->where('published_at', '>=', now()->subDays(7));
|
||||
},
|
||||
'pressReleases as today_releases_count' => function (Builder $query) use ($portalValues): void {
|
||||
$query
|
||||
->whereIn('portal', $portalValues)
|
||||
->where('status', PressReleaseStatus::Published)
|
||||
->where('language', 'de')
|
||||
->whereDate('published_at', today());
|
||||
},
|
||||
])
|
||||
->orderByDesc('today_releases_count')
|
||||
->orderByDesc('recent_releases_count')
|
||||
->limit(6)
|
||||
->get()
|
||||
->map(fn (Company $company): array => [
|
||||
'name' => $company->name,
|
||||
'slug' => $company->slug,
|
||||
'initial' => mb_strtoupper(mb_substr((string) $company->name, 0, 1)),
|
||||
'count' => (int) $company->recent_releases_count,
|
||||
'today' => (int) $company->today_releases_count > 0,
|
||||
]);
|
||||
|
||||
$industryIndex = Category::query()
|
||||
->with(['translations' => fn ($query) => $query->where('locale', 'de')])
|
||||
->withCount([
|
||||
'pressReleases as recent_count' => function (Builder $query) use ($portalValues): void {
|
||||
$query
|
||||
->whereIn('portal', $portalValues)
|
||||
->where('status', PressReleaseStatus::Published)
|
||||
->where('language', 'de')
|
||||
->where('published_at', '>=', now()->subDays(7))
|
||||
->whereNotNull('published_at');
|
||||
},
|
||||
'pressReleases as previous_count' => function (Builder $query) use ($portalValues): void {
|
||||
$query
|
||||
->whereIn('portal', $portalValues)
|
||||
->where('status', PressReleaseStatus::Published)
|
||||
->where('language', 'de')
|
||||
->where('published_at', '>=', now()->subDays(14))
|
||||
->where('published_at', '<', now()->subDays(7));
|
||||
},
|
||||
])
|
||||
->whereIn('portal', $portalValues)
|
||||
->where('is_active', true)
|
||||
->whereNull('parent_id')
|
||||
->orderByDesc('recent_count')
|
||||
->limit(7)
|
||||
->get()
|
||||
->map(function (Category $category): ?array {
|
||||
$translation = $category->translations->first();
|
||||
|
||||
if (! $translation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $translation->name,
|
||||
'href' => $translation->slug ? route('kategorie', ['slug' => $translation->slug]) : route('kategorien'),
|
||||
'count' => (int) ($category->recent_count ?? 0),
|
||||
'delta' => (int) ($category->recent_count ?? 0) - (int) ($category->previous_count ?? 0),
|
||||
];
|
||||
})
|
||||
->filter()
|
||||
->values();
|
||||
|
||||
return [
|
||||
'leadRelease' => $leadRelease,
|
||||
'sideReleases' => $sideReleases,
|
||||
'mostReadReleases' => $mostReadReleases,
|
||||
'activeNewsrooms' => $activeNewsrooms,
|
||||
'industryIndex' => $industryIndex,
|
||||
'homeStats' => [
|
||||
'publishedCount' => $publishedQuery()->count(),
|
||||
'publishedToday' => $publishedQuery()->whereDate('published_at', today())->count(),
|
||||
'archiveSince' => $oldestPublishedAt ? (int) Carbon::parse($oldestPublishedAt)->format('Y') : null,
|
||||
],
|
||||
];
|
||||
};
|
||||
|
||||
// Hauptseite - dynamisch basierend auf Domain
|
||||
Route::get('/', function () {
|
||||
Route::get('/', function () use ($applyWebDomainConfig, $webHomeData) {
|
||||
$domain = request()->getHost();
|
||||
|
||||
if (str_contains($domain, 'presseecho')) {
|
||||
$applyWebDomainConfig('presseecho');
|
||||
|
||||
return view('web.presseecho', $webHomeData(Portal::Presseecho));
|
||||
} elseif (str_contains($domain, 'businessportal24')) {
|
||||
$applyWebDomainConfig('businessportal24');
|
||||
|
||||
return view('web.businessportal24', $webHomeData(Portal::Businessportal24));
|
||||
}
|
||||
|
||||
$applyWebDomainConfig('businessportal24');
|
||||
|
||||
return view('web.businessportal24', $webHomeData(Portal::Businessportal24));
|
||||
})->name('home');
|
||||
|
||||
Route::get('/variant-1', function () {
|
||||
$domain = request()->getHost();
|
||||
// Domain-basierte View-Auswahl
|
||||
if (str_contains($domain, 'presseecho')) {
|
||||
return view('web.presseecho');
|
||||
} elseif (str_contains($domain, 'businessportal24')) {
|
||||
return view('web.businessportal24');
|
||||
return view('web.businessportal24-variant-float-glow');
|
||||
}
|
||||
|
||||
// Fallback für andere Domains oder lokale Entwicklung
|
||||
return view('web.businessportal24');
|
||||
})->name('home');
|
||||
return view('web.businessportal24-variant-float-glow');
|
||||
})->name('variant-1');
|
||||
|
||||
Route::get('/variant-2', function () {
|
||||
$domain = request()->getHost();
|
||||
// Domain-basierte View-Auswahl
|
||||
if (str_contains($domain, 'presseecho')) {
|
||||
return view('web.presseecho');
|
||||
} elseif (str_contains($domain, 'businessportal24')) {
|
||||
return view('web.businessportal24-variant-glass-gradient');
|
||||
}
|
||||
});
|
||||
// Preise & Leistungen
|
||||
Route::get('/preise', function () {
|
||||
return view('web.preise');
|
||||
|
|
@ -64,6 +260,12 @@ Route::get('/api', function () {
|
|||
return view('web.api');
|
||||
})->name('api');
|
||||
|
||||
Route::get('/docs/api/v1', function () {
|
||||
return response((string) file_get_contents(base_path('docs/api/v1.yml')), 200, [
|
||||
'Content-Type' => 'application/yaml; charset=UTF-8',
|
||||
]);
|
||||
})->name('docs.api.v1');
|
||||
|
||||
// Über uns - Unterseiten
|
||||
Route::get('/team', function () {
|
||||
return view('web.team');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue