presseportale/resources/views/components/layouts/auth/pressekonto.blade.php
Kevin Adametz ee267c622e web: Doppelveröffentlichung — Prüfsumme, kanonische Zuordnung (aus bis E9), Quellenauskunft, noindex im Portal (Anhang 2)
Über die API kommt praktisch jede Meldung zweimal, je Portal eine Zeile
mit gleichem Text: 9.092 Paare, 53 % des Archivs, bisher jede Fassung
self-canonical und in beiden Sitemaps — Google wählte.

1c.7: press_releases.content_hash (SHA-256 über Titel + Text ohne Markup),
gesetzt beim Speichern, Bestand per press-releases:hash-content
(Query-Builder, Blöcke). Migration in zwei Statements — ADD COLUMN INSTANT,
ADD INDEX INPLACE, kein Rebuild. press-releases:duplicates-report zählt
Paare je Hauptkategorie.

1c.8: canonical_press_release_id + CanonicalAssigner. Erstquelle folgt der
Kategorie (config/canonical.php, Vorschlag zu E9), sonst die zuerst
veröffentlichte Fassung. Zuordnung bei jeder Veröffentlichung und über den
Bestand (press-releases:assign-canonicals). Die Kopie trägt rel=canonical
und mainEntityOfPage auf die Erstquelle; Sitemap und News-Sitemap nennen
nur sie; Leser sehen weiterhin beide. Alles hinter
CANONICAL_ASSIGNMENT_ENABLED (false), bis E9 entschieden ist.

1c.9: user_canonical in den Index-Prüfungen, sourceVerdict() unterscheidet
eigene Adresse / andere eigene Fassung / fremdes Portal; Anzeige im Block
„Google-Index" hinter GSC_CUSTOMER_VISIBLE, ohne Aussage über das Warum.

Dazu: Abschnitt zur einen Quelle auf /gute-pressemitteilung (Verteiler →
viele Kopien, eigene Website zuerst, keine Garantie), noindex in Admin-,
Kunden- und Auth-Layouts, UmamiSnippetTest von der lokalen .env gelöst.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 16:09:16 +00:00

169 lines
7.1 KiB
PHP
Raw 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.

@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() }}" />
{{-- Login/Registrierung gehören nicht in den Index (Anhang 2, Abschnitt 7). --}}
<meta name="robots" content="noindex, nofollow" />
{{-- 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()) }}" type="image/svg+xml" />
<link rel="apple-touch-icon" href="{{ asset(\App\Helpers\ThemeHelper::getAppIconPath()) }}" />
@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)
{{-- Heading/Eyebrow kommen als deutsche Strings aus dem #[Layout]-
Attribut (dort sind keine __()-Aufrufe erlaubt); hier am
Ausgabepunkt übersetzt, damit sie als Schlüssel wirken. --}}
<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>
{{-- Schwebendes Kontakt-Widget (unten rechts, Auth-Bereich) --}}
<x-web.contact-launcher />
@livewireScripts
</body>
</html>