Magic-Link und Pressekontakt-Zugang zu einer Seite (/anmeldelink) zusammengeführt; altes Login-Modal entfernt, /pressekontakt-zugang leitet weiter. - ContactAccessService deckt jetzt Firmen-E-Mail UND Pressekontakt-E-Mail ab, portalübergreifend (ohne PortalScope). Eine E-Mail mehrfach hinterlegt → genau ein Account, dem alle Firmen + Kontakte zugeordnet werden. - Zugeordnete Firmen erhalten Pivot-Rolle 'responsible' (Schreibzugriff auf Stammdaten, Kontakte, Pressemitteilungen) statt nur 'member'; bestehende Lese-Pivots werden hochgestuft, Owner bleiben unangetastet. - Neuer Login-Listener (SyncCompanyMembershipsOnLogin) frischt die Zuordnungen bei JEDEM Login (Magic-Link, Passwort, Google) auf – auch nachträglich (API) hinzugekommene Firmen/Kontakte mit gleicher E-Mail greifen. - Auth-Bereich erzwingt Hellmodus: aus dem Portal übernommene .dark-Klasse wird am <html> entfernt (Login war im Dark Mode hängengeblieben). - Tests: Firmen-E-Mail-Login, Multi-Firmen-Aggregation, Schreibzugriff/Upgrade, Per-Login-Re-Sync, Auth-Hellmodus. Sicherheits-Doku aktualisiert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
160 lines
6.4 KiB
PHP
160 lines
6.4 KiB
PHP
@props([
|
||
'title' => null,
|
||
'eyebrow' => 'Publisher-Hub',
|
||
'heading' => null,
|
||
'topRightLabel' => null,
|
||
'topRightLinkText' => null,
|
||
'topRightLinkHref' => null,
|
||
'showFromBanner' => true,
|
||
])
|
||
|
||
@php
|
||
$brand = config('domains.domains.pressekonto.brand', []);
|
||
$from = request()->query('from');
|
||
|
||
$brandLabelMap = [
|
||
'presseecho' => 'presseecho.de',
|
||
'businessportal24' => 'businessportal24.com',
|
||
];
|
||
$fromBrandLabel = $brandLabelMap[$from] ?? null;
|
||
|
||
$pageTitle = $title ?? ($brand['meta_title'] ?? 'pressekonto – Publisher-Hub');
|
||
|
||
config([
|
||
'app.theme' => 'pressekonto',
|
||
'app.view_prefix' => 'web',
|
||
]);
|
||
$themeCssPath = \App\Helpers\ThemeHelper::getThemeCssPath();
|
||
$assetsDir = config('domains.domains.pressekonto.assets_dir', 'build/web');
|
||
\App\Support\DomainAssetContext::configureVite([
|
||
'assets_dir' => $assetsDir,
|
||
]);
|
||
@endphp
|
||
|
||
<!DOCTYPE html>
|
||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<meta name="csrf-token" content="{{ csrf_token() }}" />
|
||
|
||
{{-- Der Auth-Bereich ist bewusst immer im Hellmodus; Dark/Light gibt es nur
|
||
im eingeloggten Portal. Eine aus dem Portal übernommene .dark-Klasse
|
||
(flux_appearance-Cookie / DOM-Übernahme) wird hier sofort entfernt,
|
||
bevor die Styles greifen – sonst erscheint der Login dunkel. --}}
|
||
<script>document.documentElement.classList.remove('dark');</script>
|
||
|
||
<title>{{ $pageTitle }}</title>
|
||
|
||
<link rel="icon" href="{{ asset(\App\Helpers\ThemeHelper::getFaviconPath()) }}" />
|
||
|
||
@include('partials.local-fonts')
|
||
|
||
{{-- Nur CSS aus dem Web-Build laden. Alpine bringt @livewireScripts mit;
|
||
würden wir hier zusätzlich resources/js/app.js mit Alpine.start()
|
||
laden, gäbe es zwei Alpine-Instanzen und wire:submit, x-data,
|
||
wire:model würden brechen (siehe Browser-Logs vor diesem Fix). --}}
|
||
@vite([$themeCssPath], $assetsDir)
|
||
@livewireStyles
|
||
</head>
|
||
|
||
<body class="font-sans text-ink antialiased" style="background-color: #f6f4ef;">
|
||
|
||
<div class="relative min-h-screen flex flex-col overflow-hidden bg-bg">
|
||
|
||
{{-- Atmosphäre: subtiles Raster --}}
|
||
<div class="auth-grid" aria-hidden="true"></div>
|
||
|
||
{{-- Atmosphäre: konzentrische Kreise um die Bildmitte --}}
|
||
<svg class="absolute inset-0 w-full h-full pointer-events-none" preserveAspectRatio="xMidYMid slice" viewBox="0 0 1280 880" aria-hidden="true">
|
||
<g opacity="0.09" stroke="#1A2540" fill="none" stroke-width="1">
|
||
<circle cx="640" cy="470" r="160" />
|
||
<circle cx="640" cy="470" r="260" />
|
||
<circle cx="640" cy="470" r="380" />
|
||
<circle cx="640" cy="470" r="510" />
|
||
<circle cx="640" cy="470" r="660" />
|
||
</g>
|
||
</svg>
|
||
|
||
{{-- 3px Hub-Blau-Streifen --}}
|
||
<div class="relative h-[3px] bg-hub z-10"></div>
|
||
|
||
{{-- Header --}}
|
||
<header class="relative z-10 px-6 sm:px-10 py-[22px] flex items-center justify-between gap-4">
|
||
<a href="{{ route('home') }}" class="flex items-baseline gap-2.5 no-underline" wire:navigate>
|
||
<span class="text-[19px] font-bold tracking-[-0.4px] leading-none">
|
||
<x-web.brand-mark brand="pressekonto" :serif="false" />
|
||
</span>
|
||
<span class="hidden sm:inline-block w-px h-[14px] bg-bg-rule"></span>
|
||
<span class="hidden sm:inline-block text-[9.5px] font-bold tracking-[0.22em] uppercase text-ink-3">
|
||
{{ $brand['tagline_short'] ?? 'Publisher · Hub' }}
|
||
</span>
|
||
</a>
|
||
|
||
@if ($topRightLinkText && $topRightLinkHref)
|
||
<span class="text-[13px] text-ink-3">
|
||
@if ($topRightLabel)
|
||
{{ $topRightLabel }}
|
||
@endif
|
||
<a href="{{ $topRightLinkHref }}" class="link-hub" wire:navigate>{{ $topRightLinkText }}</a>
|
||
</span>
|
||
@endif
|
||
</header>
|
||
|
||
{{-- Auth-Card --}}
|
||
<main class="relative z-10 flex-1 flex items-center justify-center px-6 sm:px-10 py-5">
|
||
<div class="w-full max-w-[440px]">
|
||
|
||
@if ($showFromBanner && $fromBrandLabel)
|
||
<div class="from-banner mb-3.5">
|
||
Sie kommen von <strong class="font-semibold text-ink">{{ $fromBrandLabel }}</strong>.
|
||
Ihr Konto funktioniert für <strong class="font-semibold text-ink">beide Portale</strong> –
|
||
presseecho.de und businessportal24.com.
|
||
</div>
|
||
@elseif ($showFromBanner)
|
||
<div class="from-banner mb-3.5">
|
||
Ihr Konto funktioniert auch für
|
||
<strong class="font-semibold text-ink">presseecho.de</strong>
|
||
und
|
||
<strong class="font-semibold text-ink">businessportal24.com</strong>.
|
||
</div>
|
||
@endif
|
||
|
||
<div class="auth-card">
|
||
@if ($eyebrow)
|
||
<div class="eyebrow-hub mb-2.5">{{ $eyebrow }}</div>
|
||
@endif
|
||
|
||
@if ($heading)
|
||
<h1 class="text-[26px] font-bold tracking-[-0.5px] leading-[1.2] m-0 mb-7 text-ink">
|
||
{{ $heading }}
|
||
</h1>
|
||
@endif
|
||
|
||
{{ $slot }}
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
{{-- Micro-Footer --}}
|
||
<footer class="relative z-10 px-6 sm:px-10 pt-[18px] pb-[26px] flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 text-[11.5px] text-ink-3">
|
||
<span>
|
||
SSL <span class="text-ink-4 mx-2">·</span>
|
||
Daten in DE <span class="text-ink-4 mx-2">·</span>
|
||
2-Faktor verfügbar
|
||
</span>
|
||
<span class="flex flex-wrap items-center gap-x-1 gap-y-1">
|
||
<a href="{{ route('impressum') }}" class="text-ink-3 no-underline hover:text-ink">Impressum</a>
|
||
<span class="text-ink-4 mx-2">·</span>
|
||
<a href="{{ route('datenschutz') }}" class="text-ink-3 no-underline hover:text-ink">Datenschutz</a>
|
||
<span class="text-ink-4 mx-2">·</span>
|
||
<a href="{{ route('agb') }}" class="text-ink-3 no-underline hover:text-ink">AGB</a>
|
||
<span class="text-ink-4 mx-2">·</span>
|
||
<a href="{{ route('hilfe') }}" class="text-ink-3 no-underline hover:text-ink">Support</a>
|
||
</span>
|
||
</footer>
|
||
</div>
|
||
|
||
@livewireScripts
|
||
</body>
|
||
</html>
|