Spoke-Frontend-Audit: Fake-Inhalte raus, Newsletter-Rueckbau, echte Boersendaten, EN-Lokalisierung
Frontend-Umbau (Kevin, 06.07.2026), Ergebnis des businessportal24-Audits: - Fake-Anzeigen und Mock-/Fallback-Daten entfernt (focus-ad, feed-ad, highlight-card, press-release-card, featured-releases, press-releases-grid); Sektionen rendern nur noch mit echten Daten oder gar nicht - Newsletter-Strip und Newsletter-Box zurueckgebaut (Feature nachgelagert, vor Reaktivierung DOI-Flow noetig) - Echte Boersendaten: MarketDataService (Yahoo-Finance-Quotes, config/market.php, Cache) + market:refresh alle 15 Min. im Scheduler; live-ticker blendet sich ohne Daten aus - EN-Lokalisierung der Kern-Seiten via lang/de/web.php + lang/en/web.php; Carbon-Locale folgt app()->getLocale() - Web-Tests auf den neuen Frontend-Stand nachgezogen (Suite: 993 gruen) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3320aaaa85
commit
bfad13af7d
67 changed files with 1735 additions and 1521 deletions
36
app/Console/Commands/RefreshMarketData.php
Normal file
36
app/Console/Commands/RefreshMarketData.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Web\MarketDataService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RefreshMarketData extends Command
|
||||
{
|
||||
protected $signature = 'market:refresh';
|
||||
|
||||
protected $description = 'Kursdaten (DAX, EUR/USD, Brent) für den Web-Ticker abrufen und cachen';
|
||||
|
||||
public function handle(MarketDataService $marketData): int
|
||||
{
|
||||
if (! config('market.enabled')) {
|
||||
$this->info('Marktdaten sind deaktiviert (MARKET_DATA_ENABLED=false).');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$slides = $marketData->refresh();
|
||||
|
||||
if ($slides === []) {
|
||||
$this->warn('Keine Kursdaten erhalten — Cache unverändert.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
foreach ($slides as $slide) {
|
||||
$this->line(sprintf('%s: %s (%s)', $slide['label'], $slide['value'], $slide['delta']));
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -9,12 +9,11 @@ use App\Models\PressRelease;
|
|||
use App\Support\EditorialClock;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Datenaufbereitung der öffentlichen Startseite einer Ausgabe: Lead-/Seiten-/
|
||||
* meistgelesene Meldungen, aktive Newsrooms, Branchen-Index, „Heute im Fokus"
|
||||
* (Spotlight), Ticker und Kennzahlen.
|
||||
* (Spotlight) und Kennzahlen. Der Ad-Hoc-Ticker kommt zentral aus TickerData.
|
||||
*/
|
||||
class HomePageData
|
||||
{
|
||||
|
|
@ -207,26 +206,15 @@ class HomePageData
|
|||
$spotlight = [
|
||||
'industry' => $spotlightTranslation->name,
|
||||
'stats' => [
|
||||
['label' => 'Meldungen (7 Tage)', 'value' => number_format($windowCount, 0, ',', '.'), 'sub' => 'Stand '.$referenceNow->translatedFormat('j. M Y')],
|
||||
['label' => 'Aktive Unternehmen', 'value' => number_format($activeCompanies, 0, ',', '.'), 'sub' => 'in dieser Branche'],
|
||||
['label' => 'Im Archiv', 'value' => number_format($archiveCount, 0, ',', '.'), 'sub' => 'Meldungen gesamt'],
|
||||
['label' => __('web.spotlight.stat_window'), 'value' => number_format($windowCount, 0, ',', '.'), 'sub' => __('web.spotlight.stat_window_sub', ['date' => $referenceNow->translatedFormat('j. M Y')])],
|
||||
['label' => __('web.spotlight.stat_companies'), 'value' => number_format($activeCompanies, 0, ',', '.'), 'sub' => __('web.spotlight.stat_companies_sub')],
|
||||
['label' => __('web.spotlight.stat_archive'), 'value' => number_format($archiveCount, 0, ',', '.'), 'sub' => __('web.spotlight.stat_archive_sub')],
|
||||
],
|
||||
'releases' => $spotlightReleases->all(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Ad-Hoc-Ticker aus den jüngsten echten Pressemitteilungen.
|
||||
$tickerItems = $publishedQuery()
|
||||
->orderByDesc('published_at')
|
||||
->limit(8)
|
||||
->get(['id', 'title', 'published_at'])
|
||||
->map(static fn (PressRelease $release): array => [
|
||||
'time' => $release->published_at?->format('H:i') ?? '',
|
||||
'text' => Str::limit((string) $release->title, 70),
|
||||
])
|
||||
->all();
|
||||
|
||||
return [
|
||||
'leadRelease' => $leadRelease,
|
||||
'sideReleases' => $sideReleases,
|
||||
|
|
@ -234,7 +222,6 @@ class HomePageData
|
|||
'activeNewsrooms' => $activeNewsrooms,
|
||||
'industryIndex' => $industryIndex,
|
||||
'spotlight' => $spotlight,
|
||||
'tickerItems' => $tickerItems,
|
||||
'homeStats' => [
|
||||
'publishedCount' => $publishedQuery()->count(),
|
||||
'publishedToday' => $publishedQuery()->whereDate('published_at', $referenceNow->toDateString())->count(),
|
||||
|
|
|
|||
109
app/Services/Web/MarketDataService.php
Normal file
109
app/Services/Web/MarketDataService.php
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Web;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Kursdaten für den Ad-Hoc-Ticker der Web-Frontends.
|
||||
*
|
||||
* refresh() fragt die konfigurierten Symbole (config/market.php) über die
|
||||
* Yahoo-Finance-Chart-API ab und legt sie in den Cache; die Views lesen nur
|
||||
* aus dem Cache (slides()). Schlägt der Abruf für alle Symbole fehl, bleibt
|
||||
* der bestehende Cache-Stand erhalten, damit ein API-Ausfall die Anzeige
|
||||
* nicht sofort leert.
|
||||
*/
|
||||
class MarketDataService
|
||||
{
|
||||
/**
|
||||
* @return list<array{label: string, value: string, delta: string, positive: bool}>
|
||||
*/
|
||||
public function slides(): array
|
||||
{
|
||||
if (! config('market.enabled')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var list<array{label: string, value: string, delta: string, positive: bool}> $slides */
|
||||
$slides = Cache::get((string) config('market.cache_key'), []);
|
||||
|
||||
return is_array($slides) ? $slides : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{label: string, value: string, delta: string, positive: bool}>
|
||||
*/
|
||||
public function refresh(): array
|
||||
{
|
||||
$slides = [];
|
||||
|
||||
foreach ((array) config('market.symbols', []) as $config) {
|
||||
$quote = $this->fetchQuote((string) $config['symbol']);
|
||||
|
||||
if ($quote === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[$price, $previousClose] = $quote;
|
||||
|
||||
$deltaPercent = $previousClose > 0.0
|
||||
? ($price - $previousClose) / $previousClose * 100
|
||||
: 0.0;
|
||||
|
||||
$slides[] = [
|
||||
'label' => (string) $config['label'],
|
||||
'value' => number_format($price, (int) ($config['precision'] ?? 2), ',', '.').($config['suffix'] ?? ''),
|
||||
'delta' => ($deltaPercent >= 0 ? '+' : '').number_format($deltaPercent, 1, ',', '.').'%',
|
||||
'positive' => $deltaPercent >= 0,
|
||||
];
|
||||
}
|
||||
|
||||
if ($slides === []) {
|
||||
Log::warning('market:refresh lieferte keine Kursdaten — bestehender Cache-Stand bleibt erhalten.');
|
||||
|
||||
return $this->slides();
|
||||
}
|
||||
|
||||
Cache::put((string) config('market.cache_key'), $slides, (int) config('market.cache_ttl', 86400));
|
||||
|
||||
return $slides;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0: float, 1: float}|null [Kurs, Vortagesschluss]
|
||||
*/
|
||||
private function fetchQuote(string $symbol): ?array
|
||||
{
|
||||
try {
|
||||
$response = Http::timeout(10)
|
||||
->withHeaders(['User-Agent' => 'Mozilla/5.0 (compatible; PressekontoTicker/1.0)'])
|
||||
->get(config('market.endpoint').rawurlencode($symbol), [
|
||||
'range' => '1d',
|
||||
'interval' => '1d',
|
||||
]);
|
||||
} catch (\Throwable $exception) {
|
||||
Log::warning('Kursabruf fehlgeschlagen.', ['symbol' => $symbol, 'error' => $exception->getMessage()]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $response->successful()) {
|
||||
Log::warning('Kursabruf fehlgeschlagen.', ['symbol' => $symbol, 'status' => $response->status()]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$meta = $response->json('chart.result.0.meta');
|
||||
|
||||
$price = $meta['regularMarketPrice'] ?? null;
|
||||
$previousClose = $meta['chartPreviousClose'] ?? $meta['previousClose'] ?? null;
|
||||
|
||||
if (! is_numeric($price) || ! is_numeric($previousClose)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [(float) $price, (float) $previousClose];
|
||||
}
|
||||
}
|
||||
42
app/Services/Web/TickerData.php
Normal file
42
app/Services/Web/TickerData.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Web;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Models\PressRelease;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Ad-Hoc-Ticker-Meldungen der Web-Frontends: die jüngsten veröffentlichten
|
||||
* Pressemitteilungen des Portals. Zentral hier statt je Seite, damit der
|
||||
* Ticker auf allen Unterseiten identisch gefüllt ist; kurz gecacht, weil
|
||||
* der Header auf jeder Seite rendert.
|
||||
*/
|
||||
class TickerData
|
||||
{
|
||||
private const CACHE_TTL_SECONDS = 300;
|
||||
|
||||
/**
|
||||
* @return list<array{time: string, text: string}>
|
||||
*/
|
||||
public function items(Portal $portal, string $language): array
|
||||
{
|
||||
$cacheKey = sprintf('web.ticker.%s.%s', $portal->value, $language);
|
||||
|
||||
return Cache::remember($cacheKey, self::CACHE_TTL_SECONDS, function () use ($portal, $language): array {
|
||||
return PressRelease::query()
|
||||
->forPortalValues([$portal->value, Portal::Both->value])
|
||||
->forLanguage($language)
|
||||
->visibleInArchive()
|
||||
->orderByDesc('published_at')
|
||||
->limit(8)
|
||||
->get(['id', 'title', 'published_at'])
|
||||
->map(static fn (PressRelease $release): array => [
|
||||
'time' => $release->published_at?->format('H:i') ?? '',
|
||||
'text' => Str::limit((string) $release->title, 70),
|
||||
])
|
||||
->all();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -23,13 +23,21 @@ class WebPortalContext
|
|||
public readonly array $domainConfig,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Portal-Ableitung ohne Seiteneffekte (kein Anwenden der Domain-Config) —
|
||||
* für Komponenten, die nur wissen müssen, auf welchem Portal sie rendern.
|
||||
*/
|
||||
public static function portalFromHost(): Portal
|
||||
{
|
||||
return str_contains(request()->getHost(), 'presseecho')
|
||||
? Portal::Presseecho
|
||||
: Portal::Businessportal24;
|
||||
}
|
||||
|
||||
public static function fromRequest(): self
|
||||
{
|
||||
$host = request()->getHost();
|
||||
|
||||
[$domainKey, $portal] = str_contains($host, 'presseecho')
|
||||
? ['presseecho', Portal::Presseecho]
|
||||
: ['businessportal24', Portal::Businessportal24];
|
||||
$portal = self::portalFromHost();
|
||||
$domainKey = $portal === Portal::Presseecho ? 'presseecho' : 'businessportal24';
|
||||
|
||||
return new self(
|
||||
domainKey: $domainKey,
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ return [
|
|||
'report_email' => env('REPORT_EMAIL_PRESSEKONTO'),
|
||||
'umami_website_id' => 'ca440b65-a5bd-42c1-8cdb-96f34734003c',
|
||||
'tagline_short' => 'Publisher · Hub',
|
||||
'tagline_long' => 'Der gemeinsame Publisher-Bereich für presseecho und businessportal24 – Pressemitteilungen schreiben, redaktionell prüfen lassen, auf beiden Reichweiten veröffentlichen.',
|
||||
'tagline_long' => 'Der gemeinsame Publisher-Bereich für presseecho und businessportal24 – Pressemitteilungen schreiben, automatisiert prüfen lassen, auf beiden Reichweiten veröffentlichen.',
|
||||
'footer_legal' => '© :year pressekonto · Alle Rechte vorbehalten',
|
||||
'about_label' => 'Über pressekonto',
|
||||
'meta_title' => 'pressekonto – Publisher-Hub für presseecho und businessportal24',
|
||||
'meta_description' => 'Ein Konto, zwei Reichweiten: Pressemitteilungen redaktionell geprüft auf presseecho und businessportal24 gleichzeitig veröffentlichen.',
|
||||
'meta_description' => 'Ein Konto, zwei Reichweiten: Pressemitteilungen qualitätsgeprüft auf presseecho und businessportal24 gleichzeitig veröffentlichen.',
|
||||
],
|
||||
],
|
||||
|
||||
|
|
@ -85,16 +85,16 @@ return [
|
|||
'report_email' => env('REPORT_EMAIL_PRESSEECHO'),
|
||||
'umami_website_id' => '3ea8a7b0-105d-4519-b200-138cadc0fbdc',
|
||||
'tagline_short' => 'Pressemitteilungen · DACH',
|
||||
'tagline_long' => 'Fachmeldungen aus Wirtschaft und Industrie. Redaktionell geprüft. Strukturiert distribuiert.',
|
||||
'tagline_long' => 'Fachmeldungen aus Wirtschaft und Industrie. Qualitätsgeprüft. Strukturiert distribuiert.',
|
||||
'footer_legal' => '© :year presseecho · Alle Rechte vorbehalten',
|
||||
'about_label' => 'Über presseecho',
|
||||
'meta_title' => 'Aktuelle Pressemitteilungen aus Wirtschaft & Industrie – presseecho',
|
||||
'meta_description' => 'Redaktionell kuratierte Pressemitteilungen aus Wirtschaft und Industrie im DACH-Raum.',
|
||||
'meta_description' => 'Qualitätsgeprüfte Pressemitteilungen aus Wirtschaft und Industrie im DACH-Raum.',
|
||||
'newsletter_topics' => [
|
||||
['name' => 'Tageszusammenfassung', 'meta' => 'Mo–Fr · 07:00 Uhr', 'active' => true],
|
||||
['name' => 'Wochenrückblick', 'meta' => 'Sonntags · 18:00 Uhr', 'active' => false],
|
||||
['name' => 'Energie & Klima', 'meta' => 'ab Content-Score 80 · max. 2× pro Woche', 'active' => true],
|
||||
['name' => 'Industrie & Forschung', 'meta' => 'ausgewählt von der Redaktion · ca. 3–5 Mails/Monat', 'active' => false],
|
||||
['name' => 'Industrie & Forschung', 'meta' => 'kuratierte Auswahl · ca. 3–5 Mails/Monat', 'active' => false],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
@ -119,16 +119,16 @@ return [
|
|||
'report_email' => env('REPORT_EMAIL_BUSINESSPORTAL'),
|
||||
'umami_website_id' => 'f03a5fc3-ac44-488f-9118-84004861099f',
|
||||
'tagline_short' => 'Pressemitteilungen · DACH',
|
||||
'tagline_long' => 'Veröffentlichungs-Portal für redaktionell geprüfte Pressemitteilungen aus Deutschland, Österreich und der Schweiz.',
|
||||
'tagline_long' => 'Veröffentlichungs-Portal für qualitätsgeprüfte Pressemitteilungen aus Deutschland, Österreich und der Schweiz.',
|
||||
'footer_legal' => '© :year businessportal24 · Alle Rechte vorbehalten',
|
||||
'about_label' => 'Über businessportal24',
|
||||
'meta_title' => 'Aktuelle Pressemitteilungen aus der deutschen Wirtschaft – businessportal24',
|
||||
'meta_description' => 'Pressemitteilungen aus Deutschland, Österreich und der Schweiz. Redaktionell geprüft. Strukturiert distribuiert.',
|
||||
'meta_description' => 'Pressemitteilungen aus Deutschland, Österreich und der Schweiz. Qualitätsgeprüft. Strukturiert distribuiert.',
|
||||
'newsletter_topics' => [
|
||||
['name' => 'Tageszusammenfassung', 'meta' => 'Mo–Fr · 07:00 Uhr', 'active' => true],
|
||||
['name' => 'Wochenrückblick', 'meta' => 'Sonntags · 18:00 Uhr', 'active' => false],
|
||||
['name' => 'Energie & Klima', 'meta' => 'ab Content-Score 80 · max. 2× pro Woche', 'active' => true],
|
||||
['name' => 'IPO & M&A', 'meta' => 'ausgewählt von der Redaktion · ca. 3–5 Mails/Monat', 'active' => false],
|
||||
['name' => 'IPO & M&A', 'meta' => 'kuratierte Auswahl · ca. 3–5 Mails/Monat', 'active' => false],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
|
|||
40
config/market.php
Normal file
40
config/market.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Börsendaten im Ad-Hoc-Ticker
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Kursdaten für den Ticker der Web-Frontends. Abgerufen per Scheduler
|
||||
| (market:refresh) über die Yahoo-Finance-Chart-API und gecacht — die
|
||||
| Views lesen ausschließlich aus dem Cache. Ohne Daten (API down, noch
|
||||
| kein Abruf) wird der Markt-Slot im Ticker ausgeblendet.
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => env('MARKET_DATA_ENABLED', true),
|
||||
|
||||
'cache_key' => 'web.market-data',
|
||||
|
||||
// Cache-Lebensdauer in Sekunden. Bewusst großzügig (24 h), damit ein
|
||||
// vorübergehender API-Ausfall die Anzeige nicht sofort leert; der
|
||||
// Scheduler aktualisiert deutlich häufiger.
|
||||
'cache_ttl' => 86400,
|
||||
|
||||
'endpoint' => env('MARKET_DATA_ENDPOINT', 'https://query1.finance.yahoo.com/v8/finance/chart/'),
|
||||
|
||||
/*
|
||||
| label — Anzeigename im Ticker
|
||||
| symbol — Yahoo-Finance-Symbol
|
||||
| precision — Nachkommastellen der Kursanzeige
|
||||
| suffix — optionales Einheiten-Suffix (z. B. " $")
|
||||
*/
|
||||
'symbols' => [
|
||||
['label' => 'DAX', 'symbol' => '^GDAXI', 'precision' => 0, 'suffix' => ''],
|
||||
['label' => 'EUR/USD', 'symbol' => 'EURUSD=X', 'precision' => 2, 'suffix' => ''],
|
||||
['label' => 'Brent Öl', 'symbol' => 'BZ=F', 'precision' => 2, 'suffix' => ' $'],
|
||||
],
|
||||
|
||||
];
|
||||
|
|
@ -110,7 +110,7 @@ return [
|
|||
'notes' => [
|
||||
'slots' => [
|
||||
'title' => 'Slots und Kontingente',
|
||||
'text' => 'Ihr Monatskontingent (Slots) gilt für den jeweiligen Abrechnungsmonat; nicht genutzte Slots verfallen mit Monatsende. Slots sind kein Geldguthaben. Ein Slot wird nur durch eine tatsächliche Veröffentlichung verbraucht — abgelehnte Mitteilungen kosten keinen Slot; schlägt eine Veröffentlichung durch einen technischen Fehler bei uns fehl, wird der Slot automatisch zurückgebucht. Tageslimits schützen die redaktionelle Qualität der Portale; darüber hinaus eingereichte Mitteilungen können für den Folgetag eingeplant werden.',
|
||||
'text' => 'Ihr Monatskontingent (Slots) gilt für den jeweiligen Abrechnungsmonat; nicht genutzte Slots verfallen mit Monatsende. Slots sind kein Geldguthaben. Ein Slot wird nur durch eine tatsächliche Veröffentlichung verbraucht — abgelehnte Mitteilungen kosten keinen Slot; schlägt eine Veröffentlichung durch einen technischen Fehler bei uns fehl, wird der Slot automatisch zurückgebucht. Tageslimits schützen die inhaltliche Qualität der Portale; darüber hinaus eingereichte Mitteilungen können für den Folgetag eingeplant werden.',
|
||||
],
|
||||
'credits' => [
|
||||
'title' => 'Credits',
|
||||
|
|
|
|||
307
lang/de/web.php
Normal file
307
lang/de/web.php
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Web-Frontend (presseecho / businessportal24) — Deutsche Ausgabe
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Alle UI-Texte der öffentlichen Web-Frontends. Die englische Ausgabe
|
||||
| liegt in lang/en/web.php. Lange Inhaltsseiten (FAQ, Hilfe,
|
||||
| Rechtstexte) sind separat; Rechtstexte bleiben bewusst deutsch.
|
||||
|
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
'meta' => [
|
||||
'home_title' => 'Aktuelle Pressemitteilungen aus Wirtschaft & Industrie – :brand',
|
||||
'home_description' => 'Pressemitteilungen aus Deutschland, Österreich und der Schweiz. Qualitätsgeprüft. Strukturiert distribuiert.',
|
||||
],
|
||||
|
||||
'shared' => [
|
||||
'yesterday' => 'Gestern',
|
||||
'read_time' => ':minutes Min. Lesezeit',
|
||||
'minutes_short' => ':minutes Min.',
|
||||
'recommendation' => 'Empfehlung',
|
||||
],
|
||||
|
||||
'countries' => [
|
||||
'de' => 'Deutschland',
|
||||
'at' => 'Österreich',
|
||||
'ch' => 'Schweiz',
|
||||
],
|
||||
|
||||
'header' => [
|
||||
'edition' => 'Ausgabe',
|
||||
'pricing' => 'Preise',
|
||||
'homepage' => 'Startseite',
|
||||
'login' => 'Anmelden',
|
||||
'publish' => 'Veröffentlichen',
|
||||
'publish_aria' => 'Pressemitteilung veröffentlichen',
|
||||
'search_label' => 'Pressemitteilungen, Unternehmen, Branchen, ISIN durchsuchen',
|
||||
'search_placeholder' => 'Pressemitteilungen, Unternehmen, Branchen, ISIN…',
|
||||
'search_open' => 'Suche öffnen',
|
||||
'search_close' => 'Suche schließen',
|
||||
'search_eyebrow' => 'Suche',
|
||||
'search_title' => 'Pressemitteilungen durchsuchen',
|
||||
'search_submit' => 'Suchen',
|
||||
'advanced_search' => 'Erweiterte Suche',
|
||||
'press' => 'Drücken Sie',
|
||||
'to_close' => 'zum Schließen',
|
||||
],
|
||||
|
||||
'nav' => [
|
||||
'home' => 'Startseite',
|
||||
'menu' => 'Menü',
|
||||
'aria_label' => 'Hauptnavigation',
|
||||
'open_menu' => 'Hauptmenü öffnen',
|
||||
'section' => 'Rubrik',
|
||||
],
|
||||
|
||||
'ticker' => [
|
||||
'aria_label' => 'Ad-Hoc-Ticker',
|
||||
'sr_intro' => 'Aktuelle Ad-Hoc-Meldungen:',
|
||||
],
|
||||
|
||||
'hero' => [
|
||||
'title' => 'Im Fokus',
|
||||
'clock_format' => 'dddd, HH:mm [Uhr]',
|
||||
'editorial_pick' => 'Top-Auswahl',
|
||||
'top_story' => 'Top-Meldung',
|
||||
'sidebar_title' => 'Was sonst zählt',
|
||||
],
|
||||
|
||||
'feed' => [
|
||||
'title' => 'Aktuelle Meldungen',
|
||||
'filter_all' => 'Alle',
|
||||
'filter_today' => 'Heute',
|
||||
'filter_week' => 'Diese Woche',
|
||||
'empty' => 'Aktuell liegen keine Meldungen vor. Sobald Pressemitteilungen veröffentlicht werden, erscheinen sie hier.',
|
||||
'more' => 'Weitere :count Meldungen anzeigen',
|
||||
],
|
||||
|
||||
'most_read' => [
|
||||
'title' => 'Meistgelesen',
|
||||
],
|
||||
|
||||
'newsrooms' => [
|
||||
'title' => 'Aktive Newsrooms',
|
||||
'today' => 'Heute',
|
||||
'count_suffix' => 'Meldungen letzte 7 Tage',
|
||||
'active_today' => 'heute aktiv',
|
||||
'all' => 'Alle Newsrooms anzeigen',
|
||||
],
|
||||
|
||||
'spotlight' => [
|
||||
'title' => 'Heute im Fokus',
|
||||
'stat_window' => 'Meldungen (7 Tage)',
|
||||
'stat_window_sub' => 'Stand :date',
|
||||
'stat_companies' => 'Aktive Unternehmen',
|
||||
'stat_companies_sub' => 'in dieser Branche',
|
||||
'stat_archive' => 'Im Archiv',
|
||||
'stat_archive_sub' => 'Meldungen gesamt',
|
||||
],
|
||||
|
||||
'industry_index' => [
|
||||
'title' => 'Branchen-Index',
|
||||
'window' => 'Letzte 7 Tage',
|
||||
'releases' => 'Meldungen',
|
||||
],
|
||||
|
||||
'publisher_cta' => [
|
||||
'eyebrow' => 'Für Unternehmen',
|
||||
'title' => 'Veröffentlichen Sie Ihre Pressemitteilung',
|
||||
'subline' => 'Reichweite in DACH · Inhaltsprüfung · Strukturierte Distribution',
|
||||
'submit' => 'Jetzt einreichen',
|
||||
'pricing' => 'Tarife & Pakete ansehen',
|
||||
],
|
||||
|
||||
'publish_band' => [
|
||||
'eyebrow' => 'Für Unternehmen',
|
||||
'title' => 'Ihre Pressemitteilung, qualitätsgeprüft veröffentlicht',
|
||||
'subline' => 'Anmelden, Meldung einreichen, veröffentlichen — mit Reichweite im DACH-Raum, eigenem Newsroom und dauerhaft auffindbarem Archiv.',
|
||||
'cta_submit' => 'Pressemitteilung veröffentlichen',
|
||||
'cta_pricing' => 'Tarife & Pakete',
|
||||
'footnote' => 'Ohne Abo-Zwang · Inhaltsprüfung werktags innerhalb 24 h',
|
||||
'step1_title' => 'Konto anlegen',
|
||||
'step1_text' => 'In rund zwei Minuten — ein Konto für alle Portale, Anmeldung wahlweise passwortlos per Magic-Link.',
|
||||
'step2_title' => 'Meldung einreichen',
|
||||
'step2_text' => 'Text, Bild und Ansprechpartner hochladen — die automatisierte Inhaltsprüfung läuft direkt mit.',
|
||||
'step3_title' => 'Veröffentlichen',
|
||||
'step3_text' => 'Nach bestandener Inhaltsprüfung live — inklusive Newsroom, Kategorien-Zuordnung und Archiv.',
|
||||
],
|
||||
|
||||
'pricing_teaser' => [
|
||||
'title' => 'Tarife & Pakete',
|
||||
'details' => 'Alle Details',
|
||||
'popular' => 'Beliebt',
|
||||
'month' => 'Monat',
|
||||
'year' => 'Jahr',
|
||||
'two_months_free' => '2 Monate gratis',
|
||||
'quota' => 'Pressemitteilungen / Monat',
|
||||
'daily_limit' => 'max. pro Tag',
|
||||
'no_daily_limit' => 'kein Tageslimit',
|
||||
'select' => 'Auswählen',
|
||||
'footnote' => 'Alle Preise zzgl. MwSt. · Jederzeit kündbar · Auch ohne Abo als Einzelveröffentlichung möglich.',
|
||||
'publish_direct' => 'Direkt veröffentlichen',
|
||||
],
|
||||
|
||||
'quality' => [
|
||||
'eyebrow' => 'Unser Qualitätsstandard',
|
||||
'claim' => 'Alle Pressemitteilungen durchlaufen eine automatisierte Inhaltsprüfung.',
|
||||
'detail' => 'Mindestqualität gewährleistet durch unseren Content-Score — eine automatische Bewertung von Quellenqualität, Verifizierungsstatus und inhaltlicher Relevanz.',
|
||||
'guidelines' => 'Veröffentlichungsrichtlinien',
|
||||
],
|
||||
|
||||
'footer' => [
|
||||
'sections' => 'Rubriken',
|
||||
'newsrooms' => 'Newsrooms',
|
||||
'for_companies' => 'Für Unternehmen',
|
||||
'publish' => 'Veröffentlichen',
|
||||
'pricing' => 'Tarife & Pakete',
|
||||
'company' => 'Unternehmen',
|
||||
'contact' => 'Kontakt',
|
||||
'help' => 'Hilfe-Center',
|
||||
'cancellation' => 'Verträge hier kündigen',
|
||||
'imprint' => 'Impressum',
|
||||
'privacy' => 'Datenschutz',
|
||||
'terms' => 'AGB',
|
||||
'guidelines' => 'Richtlinien',
|
||||
'report' => 'Inhalte melden',
|
||||
],
|
||||
|
||||
'search' => [
|
||||
'meta_title' => 'Pressemitteilungen durchsuchen – :brand',
|
||||
'meta_description' => 'Durchsuchen Sie das geprüfte Pressemitteilungs-Archiv nach Stichwort, Unternehmen und Branche – qualitätsgeprüft und dauerhaft auffindbar im DACH-Raum.',
|
||||
'eyebrow' => 'Archiv-Suche',
|
||||
'intro' => 'Stichwort, Unternehmen oder Branche – durchsuchen Sie das vollständige, qualitätsgeprüfte Archiv.',
|
||||
'placeholder' => 'Suchbegriff, Unternehmen oder Branche…',
|
||||
'searching' => 'sucht…',
|
||||
'reset' => 'Suche zurücksetzen',
|
||||
'all_categories' => 'Alle Rubriken',
|
||||
'results' => 'Ergebnis|Ergebnisse',
|
||||
'for_term' => 'für „:term"',
|
||||
'sort' => 'Sortierung',
|
||||
'sort_newest' => 'Neueste zuerst',
|
||||
'sort_oldest' => 'Älteste zuerst',
|
||||
'no_results' => 'Keine Treffer',
|
||||
'no_results_text' => 'Für Ihre Suche gibt es derzeit keine Pressemitteilungen. Versuchen Sie einen anderen Begriff oder entfernen Sie Filter.',
|
||||
'pagination' => 'Seitennummerierung',
|
||||
'previous_page' => 'Vorherige Seite',
|
||||
'next_page' => 'Nächste Seite',
|
||||
'discover_title' => 'Durchsuchen Sie das Archiv',
|
||||
'discover_text' => ':count geprüfte Pressemitteilungen — durchsuchbar nach Stichwort, Unternehmen und Branche. Geben Sie oben einen Suchbegriff ein oder wählen Sie eine Rubrik.',
|
||||
'popular_categories' => 'Beliebte Rubriken',
|
||||
],
|
||||
|
||||
'newsrooms_page' => [
|
||||
'meta_title' => 'Newsrooms – :brand',
|
||||
'meta_description' => 'Alle aktiven Unternehmens-Newsrooms im Überblick – qualitätsgeprüfte Pressemitteilungen je Unternehmen, gebündelt und dauerhaft auffindbar im DACH-Raum.',
|
||||
'eyebrow' => 'Unternehmens-Newsrooms',
|
||||
'intro' => 'Alle aktiven Unternehmen mit ihren qualitätsgeprüften Pressemitteilungen – gebündelt an einem Ort.',
|
||||
'stat_total_sub' => 'mit veröffentlichten Meldungen',
|
||||
'stat_active' => 'Aktiv (30 Tage)',
|
||||
'stat_archive' => 'Meldungen im Archiv',
|
||||
'stat_archive_sub' => 'qualitätsgeprüft',
|
||||
'search_label' => 'Newsroom suchen',
|
||||
'search_placeholder' => 'Unternehmen suchen…',
|
||||
'reset_filter' => 'Filter zurücksetzen',
|
||||
'latest_release' => 'Letzte Meldung :date',
|
||||
'days_30' => '30 Tage',
|
||||
'none_found' => 'Keine Newsrooms gefunden',
|
||||
'none_found_term' => 'Für „:term" gibt es derzeit keinen passenden Newsroom. Versuchen Sie einen anderen Suchbegriff.',
|
||||
'none_available' => 'Aktuell sind keine aktiven Newsrooms verfügbar.',
|
||||
'cta_eyebrow' => 'Eigener Newsroom',
|
||||
'cta_title' => 'Bündeln Sie Ihre Pressemitteilungen im eigenen Newsroom.',
|
||||
'cta_text' => 'Eigene Marken-URL · alle Meldungen zentral · SEO-Vorteil für Ihr Unternehmen · qualitätsgeprüft.',
|
||||
],
|
||||
|
||||
'newsroom_page' => [
|
||||
'meta_description' => 'Newsroom von :company: alle qualitätsgeprüften Pressemitteilungen gebündelt an einem Ort.',
|
||||
'back_aria' => 'Zurück zu allen Newsrooms',
|
||||
'all_newsrooms' => 'Alle Newsrooms',
|
||||
'recent_stat' => ':count Meldungen in den letzten 30 Tagen',
|
||||
'since' => 'Newsroom seit :year',
|
||||
'industries' => 'Branchen',
|
||||
'last_30_days' => 'Letzte 30 Tage',
|
||||
'published_releases' => 'veröffentlichte Meldungen',
|
||||
'total_hits' => 'Aufrufe gesamt',
|
||||
'total_hits_sub' => 'über alle Meldungen',
|
||||
'latest' => 'Aktuellste Meldung',
|
||||
'empty' => 'Aktuell keine weiteren Meldungen in diesem Newsroom.',
|
||||
'own_title' => 'Eigenen Newsroom starten',
|
||||
'own_text' => 'Bündeln Sie alle Ihre Pressemitteilungen unter einer eigenen Marken-URL – mit Logo, Branchen-Zuordnung und SEO-Vorteil.',
|
||||
'own_cta' => 'Newsroom anlegen',
|
||||
],
|
||||
|
||||
'category' => [
|
||||
'fallback_name' => 'Branche',
|
||||
'default_description' => 'Aktuelle Pressemitteilungen aus dem Bereich :category – qualitätsgeprüft und strukturiert distribuiert im DACH-Raum.',
|
||||
'meta_title' => 'Pressemitteilungen :category – :brand',
|
||||
'eyebrow' => 'Branche',
|
||||
'back_aria' => 'Zurück zu allen Meldungen',
|
||||
'all_releases' => 'Alle Meldungen',
|
||||
'month_stat' => ':count Meldungen in den letzten 30 Tagen · :today am :date',
|
||||
'sub_topics' => 'Sub-Themen',
|
||||
'stat_today' => 'Meldungen aktueller Tag',
|
||||
'vs_previous_week' => 'ggü. Vorwoche',
|
||||
'last_7_days' => 'in den letzten 7 Tagen',
|
||||
'more_top' => 'Weitere Top-Meldungen',
|
||||
'no_more_top' => 'Aktuell keine weiteren Top-Meldungen.',
|
||||
'no_more_releases' => 'Aktuell keine weiteren Meldungen in dieser Branche.',
|
||||
'week_stat' => ':count Meldungen in den letzten 7 Tagen',
|
||||
'search_archive' => 'Im Archiv suchen',
|
||||
'empty_title' => 'Noch keine Meldungen in :category',
|
||||
'empty_text' => 'Sobald Pressemitteilungen in dieser Branche veröffentlicht werden, erscheinen sie hier.',
|
||||
'sub_industries' => 'Sub-Branchen',
|
||||
'volume_30_days' => 'Volumen letzte 30 Tage',
|
||||
'trend_7_days' => '7-Tage-Trend',
|
||||
'related' => 'Verwandte Branchen',
|
||||
'cta_eyebrow' => 'Für Unternehmen aus :category',
|
||||
'cta_title' => 'Veröffentlichen Sie Ihre Pressemitteilung gezielt in :category.',
|
||||
'cta_text' => 'Reichweite über aktive Newsrooms · Strukturierte Distribution im DACH-Raum · Qualitätsgeprüft.',
|
||||
'cta_submit' => 'Pressemitteilung einreichen',
|
||||
'cta_pricing' => 'Tarife & Branchen-Pakete ansehen',
|
||||
],
|
||||
|
||||
'detail' => [
|
||||
'breadcrumbs' => 'Brotkrumen',
|
||||
'press_release' => 'Pressemitteilung',
|
||||
'embargo_lifted' => 'Embargo aufgehoben',
|
||||
'audience' => 'Für Wirtschaftsjournalisten',
|
||||
'time_at' => ':time Uhr',
|
||||
'words' => ':count Wörter',
|
||||
'recommended' => 'Empfehlung',
|
||||
'recommended_hint' => 'Diese Pressemitteilung erreicht einen hohen Content-Score — basierend auf Quellenqualität, Verifizierungsstatus und inhaltlicher Relevanz.',
|
||||
'image_placeholder' => 'Bild · Pressefoto',
|
||||
'image_placeholder_company' => 'Bild · Pressefoto :company',
|
||||
'photo' => 'Foto',
|
||||
'about_company' => 'Über das Unternehmen',
|
||||
'press_contact' => 'Pressekontakt',
|
||||
'verified_hint' => 'Verifizierter Publisher — Identität und Domain bestätigt',
|
||||
'verified_by' => 'Identität und Domain durch :brand bestätigt',
|
||||
'verified_publisher' => 'Verifizierter Publisher',
|
||||
'published_by' => 'Veröffentlicht von',
|
||||
'to_newsroom' => 'Zum Newsroom',
|
||||
'share' => 'Teilen',
|
||||
'email' => 'E-Mail',
|
||||
'copy_link' => 'Link kopieren',
|
||||
'copied' => 'Kopiert!',
|
||||
'keywords' => 'Schlagwörter',
|
||||
'further' => 'Weiterführend',
|
||||
'print' => 'Drucken',
|
||||
'report_release' => 'Pressemitteilung melden',
|
||||
'privacy_rights' => 'Datenschutz & Persönlichkeitsrecht',
|
||||
'legal_question' => 'Sie haben ein rechtliches Anliegen zu dieser Mitteilung?',
|
||||
'report_content' => 'Inhalt melden',
|
||||
'remove_data' => 'Eigene Daten entfernen',
|
||||
'reports_free' => 'Meldungen sind kostenlos.',
|
||||
'more_from' => 'Mehr von :company',
|
||||
'more_from_sub' => 'Aktuelle Mitteilungen aus diesem Newsroom',
|
||||
'all_from' => 'Alle Mitteilungen von :company',
|
||||
'related' => 'Verwandte Meldungen',
|
||||
'related_sub' => 'Reaktionen, ähnliche Themen, Branchenkontext',
|
||||
'all_from_category' => 'Alle Meldungen aus :category',
|
||||
],
|
||||
|
||||
];
|
||||
302
lang/en/web.php
Normal file
302
lang/en/web.php
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Web frontend (presseecho / businessportal24) — English edition
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
'meta' => [
|
||||
'home_title' => 'Latest press releases from business & industry – :brand',
|
||||
'home_description' => 'Press releases from Germany, Austria and Switzerland. Quality-checked. Distributed in a structured way.',
|
||||
],
|
||||
|
||||
'shared' => [
|
||||
'yesterday' => 'Yesterday',
|
||||
'read_time' => ':minutes min read',
|
||||
'minutes_short' => ':minutes min',
|
||||
'recommendation' => 'Recommended',
|
||||
],
|
||||
|
||||
'countries' => [
|
||||
'de' => 'Germany',
|
||||
'at' => 'Austria',
|
||||
'ch' => 'Switzerland',
|
||||
],
|
||||
|
||||
'header' => [
|
||||
'edition' => 'Edition',
|
||||
'pricing' => 'Pricing',
|
||||
'homepage' => 'Homepage',
|
||||
'login' => 'Sign in',
|
||||
'publish' => 'Publish',
|
||||
'publish_aria' => 'Publish a press release',
|
||||
'search_label' => 'Search press releases, companies, industries, ISIN',
|
||||
'search_placeholder' => 'Press releases, companies, industries, ISIN…',
|
||||
'search_open' => 'Open search',
|
||||
'search_close' => 'Close search',
|
||||
'search_eyebrow' => 'Search',
|
||||
'search_title' => 'Search press releases',
|
||||
'search_submit' => 'Search',
|
||||
'advanced_search' => 'Advanced search',
|
||||
'press' => 'Press',
|
||||
'to_close' => 'to close',
|
||||
],
|
||||
|
||||
'nav' => [
|
||||
'home' => 'Home',
|
||||
'menu' => 'Menu',
|
||||
'aria_label' => 'Main navigation',
|
||||
'open_menu' => 'Open main menu',
|
||||
'section' => 'Section',
|
||||
],
|
||||
|
||||
'ticker' => [
|
||||
'aria_label' => 'Ad-hoc ticker',
|
||||
'sr_intro' => 'Latest ad-hoc announcements:',
|
||||
],
|
||||
|
||||
'hero' => [
|
||||
'title' => 'In Focus',
|
||||
'clock_format' => 'dddd, HH:mm',
|
||||
'editorial_pick' => 'Top selection',
|
||||
'top_story' => 'Top story',
|
||||
'sidebar_title' => 'Also worth noting',
|
||||
],
|
||||
|
||||
'feed' => [
|
||||
'title' => 'Latest releases',
|
||||
'filter_all' => 'All',
|
||||
'filter_today' => 'Today',
|
||||
'filter_week' => 'This week',
|
||||
'empty' => 'There are no releases at the moment. As soon as press releases are published, they will appear here.',
|
||||
'more' => 'Show :count more releases',
|
||||
],
|
||||
|
||||
'most_read' => [
|
||||
'title' => 'Most read',
|
||||
],
|
||||
|
||||
'newsrooms' => [
|
||||
'title' => 'Active newsrooms',
|
||||
'today' => 'Today',
|
||||
'count_suffix' => 'releases in the last 7 days',
|
||||
'active_today' => 'active today',
|
||||
'all' => 'View all newsrooms',
|
||||
],
|
||||
|
||||
'spotlight' => [
|
||||
'title' => 'In focus today',
|
||||
'stat_window' => 'Releases (7 days)',
|
||||
'stat_window_sub' => 'As of :date',
|
||||
'stat_companies' => 'Active companies',
|
||||
'stat_companies_sub' => 'in this industry',
|
||||
'stat_archive' => 'In the archive',
|
||||
'stat_archive_sub' => 'releases in total',
|
||||
],
|
||||
|
||||
'industry_index' => [
|
||||
'title' => 'Industry index',
|
||||
'window' => 'Last 7 days',
|
||||
'releases' => 'releases',
|
||||
],
|
||||
|
||||
'publisher_cta' => [
|
||||
'eyebrow' => 'For companies',
|
||||
'title' => 'Publish your press release',
|
||||
'subline' => 'Reach across the DACH region · Quality check · Structured distribution',
|
||||
'submit' => 'Submit now',
|
||||
'pricing' => 'View plans & packages',
|
||||
],
|
||||
|
||||
'publish_band' => [
|
||||
'eyebrow' => 'For companies',
|
||||
'title' => 'Your press release, quality-checked and published',
|
||||
'subline' => 'Sign up, submit your release, publish — with reach across the DACH region, your own newsroom and a permanently searchable archive.',
|
||||
'cta_submit' => 'Publish a press release',
|
||||
'cta_pricing' => 'Plans & pricing',
|
||||
'footnote' => 'No subscription required · Quality check within 24 h on business days',
|
||||
'step1_title' => 'Create an account',
|
||||
'step1_text' => 'Takes about two minutes — one account for all portals, optionally passwordless via magic link.',
|
||||
'step2_title' => 'Submit your release',
|
||||
'step2_text' => 'Upload text, image and press contact — an automated quality check runs alongside.',
|
||||
'step3_title' => 'Publish',
|
||||
'step3_text' => 'Live after passing the quality check — including newsroom, category mapping and archive.',
|
||||
],
|
||||
|
||||
'pricing_teaser' => [
|
||||
'title' => 'Plans & packages',
|
||||
'details' => 'All details',
|
||||
'popular' => 'Popular',
|
||||
'month' => 'month',
|
||||
'year' => 'year',
|
||||
'two_months_free' => '2 months free',
|
||||
'quota' => 'press releases / month',
|
||||
'daily_limit' => 'max. per day',
|
||||
'no_daily_limit' => 'no daily limit',
|
||||
'select' => 'Select',
|
||||
'footnote' => 'All prices excl. VAT · Cancel anytime · Single releases available without a subscription.',
|
||||
'publish_direct' => 'Publish directly',
|
||||
],
|
||||
|
||||
'quality' => [
|
||||
'eyebrow' => 'Our quality standard',
|
||||
'claim' => 'Every press release goes through a quality check.',
|
||||
'detail' => 'Minimum quality ensured by our content score — an automated assessment of source quality, verification status and content relevance.',
|
||||
'guidelines' => 'Publishing guidelines',
|
||||
],
|
||||
|
||||
'footer' => [
|
||||
'sections' => 'Sections',
|
||||
'newsrooms' => 'Newsrooms',
|
||||
'for_companies' => 'For companies',
|
||||
'publish' => 'Publish',
|
||||
'pricing' => 'Plans & packages',
|
||||
'company' => 'Company',
|
||||
'contact' => 'Contact',
|
||||
'help' => 'Help center',
|
||||
'cancellation' => 'Cancel contracts here',
|
||||
'imprint' => 'Imprint',
|
||||
'privacy' => 'Privacy',
|
||||
'terms' => 'Terms',
|
||||
'guidelines' => 'Guidelines',
|
||||
'report' => 'Report content',
|
||||
],
|
||||
|
||||
'search' => [
|
||||
'meta_title' => 'Search press releases – :brand',
|
||||
'meta_description' => 'Search the quality-checked press release archive by keyword, company and industry – permanently discoverable across the DACH region.',
|
||||
'eyebrow' => 'Archive search',
|
||||
'intro' => 'Keyword, company or industry – search the complete, quality-checked archive.',
|
||||
'placeholder' => 'Keyword, company or industry…',
|
||||
'searching' => 'searching…',
|
||||
'reset' => 'Reset search',
|
||||
'all_categories' => 'All sections',
|
||||
'results' => 'result|results',
|
||||
'for_term' => 'for ":term"',
|
||||
'sort' => 'Sort by',
|
||||
'sort_newest' => 'Newest first',
|
||||
'sort_oldest' => 'Oldest first',
|
||||
'no_results' => 'No results',
|
||||
'no_results_text' => 'There are currently no press releases matching your search. Try a different term or remove filters.',
|
||||
'pagination' => 'Pagination',
|
||||
'previous_page' => 'Previous page',
|
||||
'next_page' => 'Next page',
|
||||
'discover_title' => 'Search the archive',
|
||||
'discover_text' => ':count reviewed press releases — searchable by keyword, company and industry. Enter a search term above or choose a section.',
|
||||
'popular_categories' => 'Popular sections',
|
||||
],
|
||||
|
||||
'newsrooms_page' => [
|
||||
'meta_title' => 'Newsrooms – :brand',
|
||||
'meta_description' => 'All active company newsrooms at a glance – quality-checked press releases per company, bundled and permanently discoverable across the DACH region.',
|
||||
'eyebrow' => 'Company newsrooms',
|
||||
'intro' => 'All active companies with their quality-checked press releases – bundled in one place.',
|
||||
'stat_total_sub' => 'with published releases',
|
||||
'stat_active' => 'Active (30 days)',
|
||||
'stat_archive' => 'Releases in the archive',
|
||||
'stat_archive_sub' => 'quality-checked',
|
||||
'search_label' => 'Search newsrooms',
|
||||
'search_placeholder' => 'Search companies…',
|
||||
'reset_filter' => 'Reset filter',
|
||||
'latest_release' => 'Latest release :date',
|
||||
'days_30' => '30 days',
|
||||
'none_found' => 'No newsrooms found',
|
||||
'none_found_term' => 'There is currently no newsroom matching ":term". Try a different search term.',
|
||||
'none_available' => 'There are currently no active newsrooms.',
|
||||
'cta_eyebrow' => 'Your own newsroom',
|
||||
'cta_title' => 'Bundle your press releases in your own newsroom.',
|
||||
'cta_text' => 'Your own branded URL · all releases in one place · SEO benefit for your company · quality-checked.',
|
||||
],
|
||||
|
||||
'newsroom_page' => [
|
||||
'meta_description' => ':company newsroom: all quality-checked press releases bundled in one place.',
|
||||
'back_aria' => 'Back to all newsrooms',
|
||||
'all_newsrooms' => 'All newsrooms',
|
||||
'recent_stat' => ':count releases in the last 30 days',
|
||||
'since' => 'Newsroom since :year',
|
||||
'industries' => 'Industries',
|
||||
'last_30_days' => 'Last 30 days',
|
||||
'published_releases' => 'published releases',
|
||||
'total_hits' => 'Total views',
|
||||
'total_hits_sub' => 'across all releases',
|
||||
'latest' => 'Latest release',
|
||||
'empty' => 'No further releases in this newsroom at the moment.',
|
||||
'own_title' => 'Start your own newsroom',
|
||||
'own_text' => 'Bundle all your press releases under your own branded URL – with logo, industry mapping and SEO benefit.',
|
||||
'own_cta' => 'Create newsroom',
|
||||
],
|
||||
|
||||
'category' => [
|
||||
'fallback_name' => 'Industry',
|
||||
'default_description' => 'Latest press releases from the :category sector – quality-checked and distributed across the DACH region.',
|
||||
'meta_title' => ':category press releases – :brand',
|
||||
'eyebrow' => 'Industry',
|
||||
'back_aria' => 'Back to all releases',
|
||||
'all_releases' => 'All releases',
|
||||
'month_stat' => ':count releases in the last 30 days · :today on :date',
|
||||
'sub_topics' => 'Subtopics',
|
||||
'stat_today' => 'Releases today',
|
||||
'vs_previous_week' => 'vs. previous week',
|
||||
'last_7_days' => 'in the last 7 days',
|
||||
'more_top' => 'More top stories',
|
||||
'no_more_top' => 'No further top stories at the moment.',
|
||||
'no_more_releases' => 'No further releases in this industry at the moment.',
|
||||
'week_stat' => ':count releases in the last 7 days',
|
||||
'search_archive' => 'Search the archive',
|
||||
'empty_title' => 'No releases in :category yet',
|
||||
'empty_text' => 'As soon as press releases are published in this industry, they will appear here.',
|
||||
'sub_industries' => 'Sub-industries',
|
||||
'volume_30_days' => 'volume last 30 days',
|
||||
'trend_7_days' => '7-day trend',
|
||||
'related' => 'Related industries',
|
||||
'cta_eyebrow' => 'For companies in :category',
|
||||
'cta_title' => 'Publish your press release directly in :category.',
|
||||
'cta_text' => 'Reach via active newsrooms · Structured distribution across the DACH region · Quality-checked.',
|
||||
'cta_submit' => 'Submit press release',
|
||||
'cta_pricing' => 'View plans & industry packages',
|
||||
],
|
||||
|
||||
'detail' => [
|
||||
'breadcrumbs' => 'Breadcrumbs',
|
||||
'press_release' => 'Press release',
|
||||
'embargo_lifted' => 'Embargo lifted',
|
||||
'audience' => 'For business journalists',
|
||||
'time_at' => ':time',
|
||||
'words' => ':count words',
|
||||
'recommended' => 'Recommended',
|
||||
'recommended_hint' => 'This press release achieves a high content score — based on source quality, verification status and content relevance.',
|
||||
'image_placeholder' => 'Image · Press photo',
|
||||
'image_placeholder_company' => 'Image · Press photo :company',
|
||||
'photo' => 'Photo',
|
||||
'about_company' => 'About the company',
|
||||
'press_contact' => 'Press contact',
|
||||
'verified_hint' => 'Verified publisher — identity and domain confirmed',
|
||||
'verified_by' => 'Identity and domain confirmed by :brand',
|
||||
'verified_publisher' => 'Verified publisher',
|
||||
'published_by' => 'Published by',
|
||||
'to_newsroom' => 'Visit newsroom',
|
||||
'share' => 'Share',
|
||||
'email' => 'Email',
|
||||
'copy_link' => 'Copy link',
|
||||
'copied' => 'Copied!',
|
||||
'keywords' => 'Keywords',
|
||||
'further' => 'Further options',
|
||||
'print' => 'Print',
|
||||
'report_release' => 'Report press release',
|
||||
'privacy_rights' => 'Privacy & personal rights',
|
||||
'legal_question' => 'Do you have a legal concern about this release?',
|
||||
'report_content' => 'Report content',
|
||||
'remove_data' => 'Remove my data',
|
||||
'reports_free' => 'Reports are free of charge.',
|
||||
'more_from' => 'More from :company',
|
||||
'more_from_sub' => 'Latest releases from this newsroom',
|
||||
'all_from' => 'All releases from :company',
|
||||
'related' => 'Related releases',
|
||||
'related_sub' => 'Reactions, similar topics, industry context',
|
||||
'all_from_category' => 'All releases in :category',
|
||||
],
|
||||
|
||||
];
|
||||
|
|
@ -86,7 +86,7 @@
|
|||
<a href="{{ $presseechoUrl }}" style="font-weight: bold; text-decoration: none;"><span style="color: #1B2417;">presse</span><span style="color: #345636;">echo</span></a>
|
||||
und
|
||||
<a href="{{ $businessportalUrl }}" style="font-weight: bold; text-decoration: none;"><span style="color: #232A33;">businessportal</span><span style="color: #C84A1E;">24</span></a>
|
||||
— ein Konto, redaktionell geprüft, auf beiden Portalen veröffentlichen.
|
||||
— ein Konto, qualitätsgeprüft, auf beiden Portalen veröffentlichen.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
<li>{{ __('Aussagen entsprechen Ihrem Wissensstand und sind sachlich richtig.') }}</li>
|
||||
</ul>
|
||||
<p class="m-0 mt-2 text-[11.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ __('Sie stellen die Plattform von Ansprüchen Dritter frei, die aus einer unberechtigten Nutzung von Inhalten resultieren. Die endgültige Veröffentlichung erfolgt nach redaktioneller Prüfung.') }}
|
||||
{{ __('Sie stellen die Plattform von Ansprüchen Dritter frei, die aus einer unberechtigten Nutzung von Inhalten resultieren. Die endgültige Veröffentlichung erfolgt nach Abschluss der Inhaltsprüfung.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,23 +6,13 @@
|
|||
$palette = ['#0098A6', '#21A038', '#0018A8', '#1E1E1E', '#E20074', '#003781', '#C84A1E', '#5A6E2F'];
|
||||
|
||||
$companies = collect($companies)->take(6)->values();
|
||||
|
||||
if ($companies->isEmpty()) {
|
||||
$companies = collect([
|
||||
['initial' => 'S', 'name' => 'Siemens AG', 'count' => 18, 'color' => '#0098A6'],
|
||||
['initial' => 'B', 'name' => 'BASF SE', 'count' => 14, 'color' => '#21A038'],
|
||||
['initial' => 'D', 'name' => 'Deutsche Bank', 'count' => 11, 'color' => '#0018A8'],
|
||||
['initial' => 'V', 'name' => 'Volkswagen AG', 'count' => 16, 'color' => '#1E1E1E'],
|
||||
['initial' => 'T', 'name' => 'Deutsche Telekom', 'count' => 9, 'color' => '#E20074'],
|
||||
['initial' => 'A', 'name' => 'Allianz SE', 'count' => 8, 'color' => '#003781'],
|
||||
]);
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if ($companies->isNotEmpty())
|
||||
<section>
|
||||
<header class="flex items-baseline justify-between mb-3.5 min-h-[34px]">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] leading-[1.2] text-ink">Aktive Newsrooms</h2>
|
||||
<span class="eyebrow muted text-[10px]">Heute</span>
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] leading-[1.2] text-ink">{{ __('web.newsrooms.title') }}</h2>
|
||||
<span class="eyebrow muted text-[10px]">{{ __('web.newsrooms.today') }}</span>
|
||||
</header>
|
||||
<hr class="rule-strong mb-1">
|
||||
|
||||
|
|
@ -46,17 +36,22 @@
|
|||
<div class="min-w-0">
|
||||
<div class="text-[13.5px] font-semibold leading-[1.2] text-ink truncate group-hover:text-brand transition-colors">{{ $name }}</div>
|
||||
<div class="text-[11px] text-ink-3 mt-0.5">
|
||||
<span class="font-mono">{{ $count }}</span> Meldungen letzte 7 Tage
|
||||
<span class="font-mono">{{ $count }}</span> {{ __('web.newsrooms.count_suffix') }}
|
||||
</div>
|
||||
</div>
|
||||
<span class="inline-flex items-center gap-1.5 text-[10.5px] font-semibold text-ink-2 px-2 py-0.5 bg-brand/[0.06] border border-brand/20 whitespace-nowrap rounded-[2px]">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-brand pulse-dot"></span>
|
||||
heute aktiv
|
||||
</span>
|
||||
@if ($company['today'] ?? false)
|
||||
<span class="inline-flex items-center gap-1.5 text-[10.5px] font-semibold text-ink-2 px-2 py-0.5 bg-brand/[0.06] border border-brand/20 whitespace-nowrap rounded-[2px]">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-brand pulse-dot"></span>
|
||||
{{ __('web.newsrooms.active_today') }}
|
||||
</span>
|
||||
@else
|
||||
<span></span>
|
||||
@endif
|
||||
</a>
|
||||
@endforeach
|
||||
|
||||
<a href="{{ route('newsrooms') }}" class="block mt-3 text-[12px] text-brand font-semibold cursor-pointer hover:text-brand-deep transition-colors">
|
||||
Alle Newsrooms anzeigen →
|
||||
{{ __('web.newsrooms.all') }} →
|
||||
</a>
|
||||
</section>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
@props(['ad'])
|
||||
|
||||
@php
|
||||
$time = $ad['time'] ?? '';
|
||||
$date = $ad['date'] ?? '';
|
||||
$category = $ad['category'] ?? 'Anzeige';
|
||||
$title = $ad['title'] ?? '';
|
||||
$company = $ad['company'] ?? '';
|
||||
$href = $ad['href'] ?? '#';
|
||||
|
||||
$catClass = 'self-start whitespace-nowrap text-[9.5px] font-bold uppercase leading-snug tracking-[0.14em] text-card-warm-cat max-md:mt-1 max-md:text-[9px] max-md:tracking-[0.1em]';
|
||||
@endphp
|
||||
|
||||
<div class="-mx-4 my-2">
|
||||
<div class="flex items-center gap-2.5 px-4">
|
||||
<span class="h-px flex-1 bg-bg-card-warm-rule"></span>
|
||||
<span class="text-[9px] font-bold uppercase tracking-[0.22em] text-ink-on-dark-muted">Anzeige</span>
|
||||
<span class="h-px flex-1 bg-bg-card-warm-rule"></span>
|
||||
</div>
|
||||
|
||||
<a href="{{ $href }}" rel="sponsored nofollow" @class([
|
||||
'grid items-start bg-bg-card-warm py-4 pl-1.5 pr-4 sm:pl-2',
|
||||
'cursor-pointer transition-colors hover:bg-bg-card-warm-hover',
|
||||
'max-md:grid-cols-[auto_minmax(0,1fr)] max-md:gap-x-3',
|
||||
'md:grid-cols-[3.75rem_6.25rem_minmax(0,1fr)] md:gap-x-4',
|
||||
])>
|
||||
<div class="flex min-w-0 shrink-0 flex-col gap-0.5 tabular-nums pt-0.5">
|
||||
@if ($time !== '')
|
||||
<span class="font-mono text-[11px] leading-none text-card-warm-cat sm:text-[12px]">{{ $time }}</span>
|
||||
@endif
|
||||
@if ($date !== '')
|
||||
<span class="text-[9px] leading-tight text-ink-4 sm:text-[10px]">{{ $date }}</span>
|
||||
@endif
|
||||
<span class="{{ $catClass }} md:hidden">{{ $category }}</span>
|
||||
</div>
|
||||
<span class="{{ $catClass }} hidden md:inline">{{ $category }}</span>
|
||||
<div class="min-w-0">
|
||||
<div class="font-serif text-[15.5px] font-medium leading-[1.3] text-card-warm-title">
|
||||
{{ $title }}
|
||||
</div>
|
||||
@if ($company)
|
||||
<div class="mt-1 text-[11px] text-card-warm-cat">{{ $company }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="flex items-center gap-2.5 px-4">
|
||||
<span class="h-px flex-1 bg-bg-card-warm-rule"></span>
|
||||
<span class="text-[9px] font-bold uppercase tracking-[0.22em] text-ink-on-dark-muted">Ende Anzeige</span>
|
||||
<span class="h-px flex-1 bg-bg-card-warm-rule"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,49 +1,26 @@
|
|||
@props([
|
||||
'release' => null,
|
||||
'mock' => null,
|
||||
'recommended' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
\Carbon\Carbon::setLocale('de');
|
||||
$category = $release?->category?->translations?->firstWhere('locale', app()->getLocale())
|
||||
?? $release?->category?->translations?->first();
|
||||
$categoryName = $category?->name;
|
||||
$company = $release?->company?->name;
|
||||
$city = $release?->company?->city ?? null;
|
||||
$title = $release->title;
|
||||
$href = route('release.detail', ['slug' => $release->slug]);
|
||||
|
||||
if ($release) {
|
||||
$category = $release?->category?->translations?->firstWhere('locale', 'de')
|
||||
?? $release?->category?->translations?->first();
|
||||
$categoryName = $category?->name ?? 'Wirtschaft';
|
||||
$company = $release?->company?->name ?? 'Unternehmen';
|
||||
$city = $release?->company?->city ?? null;
|
||||
$title = $release->title;
|
||||
$href = route('release.detail', ['slug' => $release->slug]);
|
||||
$recommended = $recommended;
|
||||
|
||||
$published = $release->published_at;
|
||||
if ($published) {
|
||||
$timeLine = $published->format('H:i');
|
||||
if ($published->isToday()) {
|
||||
$dateLine = $published->translatedFormat('j. M');
|
||||
} elseif ($published->isYesterday()) {
|
||||
$dateLine = 'Gestern';
|
||||
} else {
|
||||
$dateLine = $published->translatedFormat('j. M');
|
||||
}
|
||||
$datetimeAttr = $published->toIso8601String();
|
||||
} else {
|
||||
$timeLine = '—';
|
||||
$dateLine = '';
|
||||
$datetimeAttr = null;
|
||||
}
|
||||
$published = $release->published_at;
|
||||
if ($published) {
|
||||
$timeLine = $published->format('H:i');
|
||||
$dateLine = $published->isYesterday() ? __('web.shared.yesterday') : $published->translatedFormat('j. M');
|
||||
$datetimeAttr = $published->toIso8601String();
|
||||
} else {
|
||||
$mock ??= [];
|
||||
$categoryName = $mock['category'] ?? 'Wirtschaft';
|
||||
$company = $mock['company'] ?? '';
|
||||
$city = $mock['city'] ?? null;
|
||||
$title = $mock['title'] ?? '';
|
||||
$href = $mock['href'] ?? '#';
|
||||
$timeLine = $mock['time'] ?? '';
|
||||
$dateLine = $mock['date'] ?? '';
|
||||
$timeLine = '—';
|
||||
$dateLine = '';
|
||||
$datetimeAttr = null;
|
||||
$recommended = $recommended || ($mock['recommended'] ?? false);
|
||||
}
|
||||
|
||||
$bpCatClass = 'bp-cat self-start whitespace-nowrap leading-snug max-md:mt-1 max-md:text-[9px] max-md:tracking-[0.1em]';
|
||||
|
|
@ -80,7 +57,7 @@
|
|||
<svg width="9" height="9" viewBox="0 0 12 12" class="flex-shrink-0" aria-hidden="true">
|
||||
<path d="M6 1l1.5 3.2 3.5.4-2.6 2.4.7 3.5L6 8.8l-3.1 1.7.7-3.5L1 4.6l3.5-.4z" fill="currentColor" />
|
||||
</svg>
|
||||
Empfehlung
|
||||
{{ __('web.shared.recommendation') }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,38 +1,22 @@
|
|||
@props([
|
||||
'release' => null,
|
||||
'mock' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
\Carbon\Carbon::setLocale('de');
|
||||
|
||||
if ($release) {
|
||||
$category = $release?->category?->translations?->firstWhere('locale', 'de')
|
||||
?? $release?->category?->translations?->first();
|
||||
$categoryName = $category?->name ?? 'Wirtschaft';
|
||||
$image = $release?->images?->first();
|
||||
$imageUrl = $image?->variantUrl('card') ?? $image?->url();
|
||||
$teaser = (string) \Illuminate\Support\Str::of(strip_tags((string) ($release?->text ?? '')))->squish()->limit(180);
|
||||
$company = $release?->company?->name ?? 'Unternehmen';
|
||||
$title = $release->title;
|
||||
$href = route('release.detail', ['slug' => $release->slug]);
|
||||
$time = $release?->published_at?->isToday()
|
||||
? $release->published_at->format('H:i')
|
||||
: ($release?->published_at?->isYesterday() ? 'Gestern' : $release?->published_at?->isoFormat('D. MMM'));
|
||||
$city = $release?->company?->city ?? null;
|
||||
$readTime = max(1, (int) round(str_word_count(strip_tags((string) ($release?->text ?? ''))) / 200));
|
||||
} else {
|
||||
$mock ??= [];
|
||||
$categoryName = $mock['category'] ?? 'Bauen · Immobilien';
|
||||
$imageUrl = $mock['image'] ?? null;
|
||||
$teaser = $mock['teaser'] ?? 'Nach 18 Monaten der Konsolidierung deuten Frühindikatoren auf eine Erholung hin. ImmoConsult-Studie analysiert 240 Märkte in DACH.';
|
||||
$company = $mock['company'] ?? 'ImmoConsult Deutschland';
|
||||
$title = $mock['title'] ?? 'Immobilienmarkt 2025: Experten prognostizieren Trendwende bei Kaufpreisen';
|
||||
$href = $mock['href'] ?? '#';
|
||||
$time = $mock['time'] ?? '14:18';
|
||||
$city = $mock['city'] ?? 'München';
|
||||
$readTime = $mock['readTime'] ?? 5;
|
||||
}
|
||||
$category = $release?->category?->translations?->firstWhere('locale', app()->getLocale())
|
||||
?? $release?->category?->translations?->first();
|
||||
$categoryName = $category?->name;
|
||||
$image = $release?->images?->first();
|
||||
$imageUrl = $image?->variantUrl('card') ?? $image?->url();
|
||||
$teaser = (string) \Illuminate\Support\Str::of(strip_tags((string) ($release?->text ?? '')))->squish()->limit(180);
|
||||
$company = $release?->company?->name;
|
||||
$title = $release->title;
|
||||
$href = route('release.detail', ['slug' => $release->slug]);
|
||||
$time = $release?->published_at?->isToday()
|
||||
? $release->published_at->format('H:i')
|
||||
: ($release?->published_at?->isYesterday() ? __('web.shared.yesterday') : $release?->published_at?->isoFormat('D. MMM'));
|
||||
$city = $release?->company?->city ?? null;
|
||||
$readTime = max(1, (int) round(str_word_count(strip_tags((string) ($release?->text ?? ''))) / 200));
|
||||
@endphp
|
||||
|
||||
<article class="py-6 border-b border-bg-rule grid gap-6 grid-cols-1 md:grid-cols-[240px_1fr] group">
|
||||
|
|
@ -59,7 +43,7 @@
|
|||
</g>
|
||||
</svg>
|
||||
@endif
|
||||
<span class="bp-tag absolute top-2.5 left-2.5">Top-Meldung</span>
|
||||
<span class="bp-tag absolute top-2.5 left-2.5">{{ __('web.hero.top_story') }}</span>
|
||||
</a>
|
||||
|
||||
<div>
|
||||
|
|
@ -86,7 +70,7 @@
|
|||
<span>{{ $city }}</span>
|
||||
@endif
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>{{ $readTime }} Min. Lesezeit</span>
|
||||
<span>{{ __('web.shared.read_time', ['minutes' => $readTime]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
@props(['ad' => null])
|
||||
|
||||
@php
|
||||
$ad ??= [
|
||||
'category' => 'Logistik & Transport',
|
||||
'title' => 'DHL Express erweitert Same-Day-Netz auf 24 deutsche Großstädte',
|
||||
'company' => 'Deutsche Post DHL Group',
|
||||
'gradient' => 'linear-gradient(135deg,#3A2818,#A88466)',
|
||||
'href' => '#',
|
||||
];
|
||||
|
||||
$category = $ad['category'] ?? 'Anzeige';
|
||||
$title = $ad['title'] ?? 'Werbeplatz';
|
||||
$company = $ad['company'] ?? '';
|
||||
$gradient = $ad['gradient'] ?? 'linear-gradient(135deg,#3A2818,#A88466)';
|
||||
$href = $ad['href'] ?? '#';
|
||||
@endphp
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="flex items-center gap-2.5 mb-2.5">
|
||||
<span class="h-px bg-bg-rule-strong flex-1"></span>
|
||||
<span class="text-[9px] font-bold tracking-[0.22em] text-ink-on-dark-muted uppercase">Anzeige</span>
|
||||
<span class="h-px bg-bg-rule-strong flex-1"></span>
|
||||
</div>
|
||||
|
||||
<a href="{{ $href }}" rel="sponsored nofollow" class="block px-3.5 py-4 bg-bg-card-warm border border-bg-card-warm-border cursor-pointer hover:border-brand/50 hover:bg-bg-card-warm-hover transition-colors">
|
||||
<div class="grid items-start gap-3" style="grid-template-columns: 64px 1fr;">
|
||||
<div class="w-16 h-16 flex-shrink-0 relative overflow-hidden opacity-85" style="background: {{ $gradient }};"></div>
|
||||
<div>
|
||||
<div class="text-[9.5px] font-bold tracking-[0.16em] text-card-warm-cat uppercase mb-1">
|
||||
{{ $category }}
|
||||
</div>
|
||||
<h3 class="font-serif text-[15.5px] leading-[1.3] m-0 font-medium text-card-warm-title">
|
||||
{{ $title }}
|
||||
</h3>
|
||||
@if ($company)
|
||||
<div class="text-[11.5px] mt-1.5 text-card-warm-cat">
|
||||
{{ $company }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="flex items-center gap-2.5 mt-2.5">
|
||||
<span class="h-px bg-bg-rule-strong flex-1"></span>
|
||||
<span class="text-[9px] font-bold tracking-[0.22em] text-ink-on-dark-muted uppercase">Ende Anzeige</span>
|
||||
<span class="h-px bg-bg-rule-strong flex-1"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,36 +1,33 @@
|
|||
@props([
|
||||
'leadRelease' => null,
|
||||
'sideReleases' => [],
|
||||
'advertisement' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
\Carbon\Carbon::setLocale('de');
|
||||
|
||||
$leadCategory = $leadRelease?->category?->translations?->firstWhere('locale', 'de')
|
||||
?? $leadRelease?->category?->translations?->first();
|
||||
$leadCategoryName = $leadCategory?->name ?? 'KI & Innovation';
|
||||
$leadImage = $leadRelease?->images?->first();
|
||||
$leadImageUrl = $leadImage?->variantUrl('card') ?? $leadImage?->url();
|
||||
$leadTitle = $leadRelease?->title ?? 'Die Zukunft der KI im deutschen Mittelstand';
|
||||
$leadTeaser = $leadRelease
|
||||
? (string) \Illuminate\Support\Str::of(strip_tags((string) $leadRelease->text))->squish()->limit(200)
|
||||
: 'Im exklusiven Interview spricht Dr. Maria Schmidt, Leiterin des KI-Instituts München, über Chancen und Herausforderungen der KI für mittelständische Unternehmen.';
|
||||
$leadCompany = $leadRelease?->company?->name ?? 'KI-Institut München';
|
||||
$leadHref = $leadRelease ? route('release.detail', ['slug' => $leadRelease->slug]) : '#';
|
||||
$leadReadTime = max(1, (int) round(str_word_count(strip_tags((string) ($leadRelease?->text ?? ''))) / 200)) ?: 5;
|
||||
$leadPublishedAt = $leadRelease?->published_at;
|
||||
|
||||
$sideReleases = collect($sideReleases)->take(4)->values();
|
||||
|
||||
if ($leadRelease) {
|
||||
$leadCategory = $leadRelease->category?->translations?->firstWhere('locale', app()->getLocale())
|
||||
?? $leadRelease->category?->translations?->first();
|
||||
$leadCategoryName = $leadCategory?->name;
|
||||
$leadImage = $leadRelease->images?->first();
|
||||
$leadImageUrl = $leadImage?->variantUrl('card') ?? $leadImage?->url();
|
||||
$leadTitle = $leadRelease->title;
|
||||
$leadTeaser = (string) \Illuminate\Support\Str::of(strip_tags((string) $leadRelease->text))->squish()->limit(200);
|
||||
$leadCompany = $leadRelease->company?->name;
|
||||
$leadHref = route('release.detail', ['slug' => $leadRelease->slug]);
|
||||
$leadReadTime = max(1, (int) round(str_word_count(strip_tags((string) $leadRelease->text)) / 200));
|
||||
$leadPublishedAt = $leadRelease->published_at;
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if ($leadRelease)
|
||||
<section class="max-w-layout mx-auto px-8 pt-8">
|
||||
<header class="flex items-baseline justify-between mb-4 flex-wrap gap-2">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">
|
||||
Im Fokus
|
||||
<span class="text-[16px] text-ink-3 font-normal ml-2">· 🇩🇪 Deutschland</span>
|
||||
{{ __('web.hero.title') }}
|
||||
</h2>
|
||||
<div class="eyebrow muted">{{ ucfirst(now()->isoFormat('dddd, HH:mm')) }} Uhr · Auswahl der Redaktion</div>
|
||||
<div class="eyebrow muted">{{ ucfirst(now()->isoFormat(__('web.hero.clock_format'))) }} · {{ __('web.hero.editorial_pick') }}</div>
|
||||
</header>
|
||||
<hr class="rule-strong mb-6">
|
||||
|
||||
|
|
@ -46,29 +43,19 @@
|
|||
loading="eager">
|
||||
<div class="absolute inset-0 bg-black/10"></div>
|
||||
@else
|
||||
<svg width="100%" height="100%" viewBox="0 0 760 500" class="absolute inset-0 opacity-40" aria-hidden="true">
|
||||
<defs>
|
||||
<pattern id="bp-hero-dots" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<circle cx="20" cy="20" r="1.5" fill="white" opacity="0.3" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="760" height="500" fill="url(#bp-hero-dots)" />
|
||||
<circle cx="600" cy="180" r="120" fill="white" opacity="0.05" />
|
||||
<circle cx="160" cy="380" r="80" fill="white" opacity="0.06" />
|
||||
</svg>
|
||||
<x-web.hero-fallback />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="absolute top-6 left-6 flex gap-2">
|
||||
<span class="bp-tag">TOP-MELDUNG</span>
|
||||
<span class="bp-tag" style="background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.3);color:white;backdrop-filter:blur(4px);">
|
||||
Audio · {{ $leadReadTime * 4 }} min
|
||||
</span>
|
||||
<span class="bp-tag">{{ __('web.hero.top_story') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="absolute left-0 right-0 bottom-0 px-9 pb-8 pt-[60px] text-white"
|
||||
style="background:linear-gradient(180deg,transparent,rgba(0,0,0,0.92));">
|
||||
<div class="eyebrow on-dark mb-3">{{ $leadCategoryName }}</div>
|
||||
@if ($leadCategoryName)
|
||||
<div class="eyebrow on-dark mb-3">{{ $leadCategoryName }}</div>
|
||||
@endif
|
||||
<h1 class="font-serif text-[38px] font-semibold m-0 leading-[1.1] tracking-[-0.7px] max-w-[680px]">
|
||||
{{ $leadTitle }}
|
||||
</h1>
|
||||
|
|
@ -76,54 +63,31 @@
|
|||
{{ $leadTeaser }}
|
||||
</p>
|
||||
<div class="mt-4 flex items-center gap-3.5 text-[12px] text-white/65 flex-wrap">
|
||||
<span>{{ $leadCompany }}</span>
|
||||
<span aria-hidden="true">·</span>
|
||||
@if ($leadCompany)
|
||||
<span>{{ $leadCompany }}</span>
|
||||
<span aria-hidden="true">·</span>
|
||||
@endif
|
||||
@if ($leadPublishedAt)
|
||||
<time datetime="{{ $leadPublishedAt->toIso8601String() }}" class="font-mono">
|
||||
{{ $leadPublishedAt->format('d.m.Y · H:i') }}
|
||||
</time>
|
||||
<span aria-hidden="true">·</span>
|
||||
@endif
|
||||
<span>{{ $leadReadTime }} Min. Lesezeit</span>
|
||||
<span>{{ __('web.shared.read_time', ['minutes' => $leadReadTime]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between pt-3.5 px-1 gap-3">
|
||||
<div class="flex gap-2 items-center" aria-label="Top-Meldung Paginierung">
|
||||
<span class="block h-[3px] w-9 bg-bg-rule-strong relative overflow-hidden">
|
||||
<span class="absolute inset-y-0 left-0 bg-brand" style="width:35%;"></span>
|
||||
</span>
|
||||
<span class="block h-[3px] w-[18px] bg-bg-rule-strong"></span>
|
||||
<span class="block h-[3px] w-[18px] bg-bg-rule-strong"></span>
|
||||
</div>
|
||||
<div class="text-[12px] text-ink-3">
|
||||
<span class="text-ink font-semibold">1</span> von 3 Top-Meldungen
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{{-- SIDEBAR: Was sonst zählt --}}
|
||||
<aside>
|
||||
<div class="eyebrow muted mb-3.5">Was sonst zählt</div>
|
||||
<div class="eyebrow muted mb-3.5">{{ __('web.hero.sidebar_title') }}</div>
|
||||
|
||||
@if ($sideReleases->isNotEmpty())
|
||||
@foreach ($sideReleases as $index => $release)
|
||||
<x-web.focus-side-item :release="$release" :index="$index + 1" :first="$loop->first" />
|
||||
@endforeach
|
||||
@else
|
||||
@foreach ([
|
||||
['n' => '01', 'cat' => 'Energie & Umwelt', 'time' => '10:42', 'title' => 'Energiewende beschleunigt sich: Neue Rekorde bei Erneuerbaren', 'company' => 'GreenPower Deutschland', 'gradient' => 'linear-gradient(135deg,#3A5A3A,#7BA876)'],
|
||||
['n' => '02', 'cat' => 'Finanzen', 'time' => '08:15', 'title' => 'FinTech-Startup PaymentFlow sichert sich 45 Mio. Euro', 'company' => 'PaymentFlow GmbH', 'gradient' => 'linear-gradient(135deg,#2A3548,#6B7C95)'],
|
||||
['n' => '03', 'cat' => 'Industrie', 'time' => '07:00', 'title' => 'Deutsche Unternehmen investieren 15 Mrd. in Automatisierung', 'company' => 'TechVision Analytics', 'gradient' => 'linear-gradient(135deg,#3A2A22,#9B6B52)'],
|
||||
['n' => '04', 'cat' => 'Mobilität', 'time' => 'Gestern', 'title' => 'VW eröffnet Batteriewerk in Salzgitter — 4.000 Arbeitsplätze', 'company' => 'Volkswagen AG', 'gradient' => 'linear-gradient(135deg,#1F2A3A,#5B7A99)'],
|
||||
] as $idx => $mock)
|
||||
<x-web.focus-side-item :mock="$mock" :first="$idx === 0" />
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
<x-web.focus-ad :ad="$advertisement" />
|
||||
@foreach ($sideReleases as $index => $release)
|
||||
<x-web.focus-side-item :release="$release" :index="$index + 1" :first="$loop->first" />
|
||||
@endforeach
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -1,35 +1,23 @@
|
|||
@props([
|
||||
'release' => null,
|
||||
'mock' => null,
|
||||
'index' => 1,
|
||||
'first' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
if ($release) {
|
||||
$category = $release?->category?->translations?->firstWhere('locale', 'de')
|
||||
?? $release?->category?->translations?->first();
|
||||
$categoryName = $category?->name ?? 'Wirtschaft';
|
||||
$time = $release?->published_at?->isToday()
|
||||
? $release->published_at->format('H:i')
|
||||
: ($release?->published_at?->isYesterday() ? 'Gestern' : $release?->published_at?->isoFormat('D. MMM'));
|
||||
$title = $release->title;
|
||||
$company = $release?->company?->name ?? 'Unternehmen';
|
||||
$href = route('release.detail', ['slug' => $release->slug]);
|
||||
$image = $release?->images?->first();
|
||||
$imageUrl = $image?->variantUrl('card') ?? $image?->url();
|
||||
$gradient = 'linear-gradient(135deg,#3A4A5A,#6B7C95)';
|
||||
$number = str_pad((string) $index, 2, '0', STR_PAD_LEFT);
|
||||
} else {
|
||||
$categoryName = $mock['cat'];
|
||||
$time = $mock['time'];
|
||||
$title = $mock['title'];
|
||||
$company = $mock['company'];
|
||||
$href = '#';
|
||||
$imageUrl = null;
|
||||
$gradient = $mock['gradient'] ?? 'linear-gradient(135deg,#3A4A5A,#6B7C95)';
|
||||
$number = $mock['n'];
|
||||
}
|
||||
$category = $release?->category?->translations?->firstWhere('locale', app()->getLocale())
|
||||
?? $release?->category?->translations?->first();
|
||||
$categoryName = $category?->name;
|
||||
$time = $release?->published_at?->isToday()
|
||||
? $release->published_at->format('H:i')
|
||||
: ($release?->published_at?->isYesterday() ? __('web.shared.yesterday') : $release?->published_at?->isoFormat('D. MMM'));
|
||||
$title = $release->title;
|
||||
$company = $release?->company?->name;
|
||||
$href = route('release.detail', ['slug' => $release->slug]);
|
||||
$image = $release?->images?->first();
|
||||
$imageUrl = $image?->variantUrl('card') ?? $image?->url();
|
||||
$gradient = 'linear-gradient(135deg,#3A4A5A,#6B7C95)';
|
||||
$number = str_pad((string) $index, 2, '0', STR_PAD_LEFT);
|
||||
@endphp
|
||||
|
||||
<article class="px-3.5 py-4 border-b border-bg-rule cursor-pointer transition-colors hover:bg-bg-elev group {{ $first ? 'border-t border-bg-rule-strong' : '' }}">
|
||||
|
|
|
|||
20
resources/views/components/web/hero-fallback.blade.php
Normal file
20
resources/views/components/web/hero-fallback.blade.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{{--
|
||||
Generierter Hintergrund für Top-Meldungen ohne Bild (Punktraster + Kreise),
|
||||
identisch zur Startseiten-Hero. Liegt absolut im übergeordneten Container
|
||||
(position: relative + bg-hero-grad erwartet).
|
||||
--}}
|
||||
@php
|
||||
// Eindeutige Pattern-ID, falls die Komponente mehrfach pro Seite rendert.
|
||||
$patternId = 'bp-hero-dots-'.\Illuminate\Support\Str::random(6);
|
||||
@endphp
|
||||
|
||||
<svg width="100%" height="100%" viewBox="0 0 760 500" preserveAspectRatio="xMidYMid slice" class="absolute inset-0 opacity-40" aria-hidden="true">
|
||||
<defs>
|
||||
<pattern id="{{ $patternId }}" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<circle cx="20" cy="20" r="1.5" fill="white" opacity="0.3" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="760" height="500" fill="url(#{{ $patternId }})" />
|
||||
<circle cx="600" cy="180" r="120" fill="white" opacity="0.05" />
|
||||
<circle cx="160" cy="380" r="80" fill="white" opacity="0.06" />
|
||||
</svg>
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
@props(['highlight'])
|
||||
|
||||
@php
|
||||
// Map industry to Heroicon
|
||||
$industryIcons = [
|
||||
'IT & Digitalisierung' => 'cpu-chip',
|
||||
'KI & Innovation' => 'cpu-chip',
|
||||
'Industrie & Technik' => 'cog-6-tooth',
|
||||
'Finanzen & Versicherungen' => 'currency-dollar',
|
||||
'Handel & E-Commerce' => 'shopping-cart',
|
||||
'Bauen & Immobilien' => 'building-office',
|
||||
'Mobilität & Logistik' => 'truck',
|
||||
'Energie & Umwelt' => 'bolt',
|
||||
'Medizin & Gesundheit' => 'heart',
|
||||
'Personal & HR' => 'user-group',
|
||||
'Marketing, PR & Medien' => 'megaphone',
|
||||
'Recht & Steuern' => 'scale',
|
||||
'Politik, Verbände & NGOs' => 'flag',
|
||||
'Wissenschaft & Forschung' => 'beaker',
|
||||
'Lifestyle' => 'sparkles',
|
||||
'Tourismus & Kultur' => 'globe-alt',
|
||||
];
|
||||
|
||||
$iconName = $industryIcons[$highlight['industry']] ?? 'building-office';
|
||||
$iconPath = "/heroicons/optimized/24/outline/{$iconName}.svg";
|
||||
@endphp
|
||||
|
||||
<a href="#" class="highlight-card-link">
|
||||
<article class="highlight-card">
|
||||
<div class="grid md:grid-cols-2">
|
||||
<!-- Image -->
|
||||
<div class="highlight-card-image">
|
||||
<img src="{{ $highlight['image'] }}" alt="{{ $highlight['title'] }}"
|
||||
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105">
|
||||
<div class="absolute top-4 left-4">
|
||||
<span class="highlight-badge badge badge-{{ $highlight['badgeType'] }}">
|
||||
{{ $highlight['badge'] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="highlight-card-content">
|
||||
<div>
|
||||
{{-- Meta Info --}}
|
||||
<div class="highlight-meta">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="industry-icon-badge">
|
||||
<img src="{{ $iconPath }}" alt="{{ $highlight['industry'] }}" class="h-4 w-4" />
|
||||
</div>
|
||||
<span
|
||||
class="font-medium text-zinc-700 dark:text-zinc-300">{{ $highlight['industry'] }}</span>
|
||||
</div>
|
||||
<time datetime="{{ $highlight['date'] }}"
|
||||
class="text-zinc-600 dark:text-zinc-400">{{ $highlight['date'] }}</time>
|
||||
</div>
|
||||
|
||||
{{-- Title --}}
|
||||
<h3 class="highlight-title">
|
||||
{{ $highlight['title'] }}
|
||||
</h3>
|
||||
|
||||
{{-- Teaser --}}
|
||||
<p class="highlight-text">
|
||||
{{ $highlight['text'] }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{-- Footer --}}
|
||||
<div class="highlight-footer">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4">
|
||||
</path>
|
||||
</svg>
|
||||
<span
|
||||
class="text-sm font-medium text-zinc-700 dark:text-zinc-300">{{ $highlight['author'] }}</span>
|
||||
</div>
|
||||
<svg class="h-5 w-5 transition-transform group-hover:translate-x-1" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</a>
|
||||
|
|
@ -16,10 +16,11 @@
|
|||
$industries = array_slice($industries, 0, 8);
|
||||
@endphp
|
||||
|
||||
@if (! empty($industries))
|
||||
<section class="max-w-layout mx-auto px-8 mt-16">
|
||||
<header class="flex items-baseline justify-between mb-4 flex-wrap gap-3">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">Branchen-Index</h2>
|
||||
<span class="eyebrow muted">Letzte 7 Tage</span>
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">{{ __('web.industry_index.title') }}</h2>
|
||||
<span class="eyebrow muted">{{ __('web.industry_index.window') }}</span>
|
||||
</header>
|
||||
<hr class="rule-strong">
|
||||
|
||||
|
|
@ -49,7 +50,7 @@
|
|||
</div>
|
||||
<div class="flex items-baseline gap-2 mb-2">
|
||||
<span class="font-serif text-[22px] font-semibold tracking-[-0.3px] text-ink">{{ $industry['count'] }}</span>
|
||||
<span class="text-[11px] text-ink-3">Meldungen</span>
|
||||
<span class="text-[11px] text-ink-3">{{ __('web.industry_index.releases') }}</span>
|
||||
</div>
|
||||
<div class="h-[3px] bg-bg-rule">
|
||||
<div class="h-full {{ $barClass }}" style="width: {{ $percent }}%;"></div>
|
||||
|
|
@ -58,3 +59,4 @@
|
|||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -2,118 +2,73 @@
|
|||
'industry' => null,
|
||||
'stats' => null,
|
||||
'releases' => [],
|
||||
'study' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$industry ??= 'Energie & Klima';
|
||||
$stats ??= [
|
||||
['label' => 'Meldungen heute', 'value' => '47', 'sub' => '+18 ggü. Wochenschnitt'],
|
||||
['label' => 'Investitionsvolumen', 'value' => '€8,4 Mrd.', 'sub' => 'Q4 2025 angekündigt'],
|
||||
['label' => 'Aktive Unternehmen', 'value' => '23', 'sub' => 'in dieser Branche heute'],
|
||||
];
|
||||
|
||||
$releases = collect($releases);
|
||||
if ($releases->isEmpty()) {
|
||||
$releases = collect([
|
||||
['time' => '14:12', 'date' => '12. Mai', 'category' => 'Energie', 'title' => 'RWE startet 1,8 GW Offshore-Windpark vor Helgoland — größtes Projekt in der Nordsee 2025', 'company' => 'RWE AG', 'city' => 'Essen', 'minutes' => 4, 'href' => '#'],
|
||||
['time' => '12:28', 'date' => '12. Mai', 'category' => 'Energie', 'title' => 'EnBW unterzeichnet Wasserstoff-Lieferabkommen mit Air Liquide', 'company' => 'EnBW Energie', 'city' => 'Karlsruhe', 'minutes' => 3, 'href' => '#'],
|
||||
['time' => '10:55', 'date' => '12. Mai', 'category' => 'Klima', 'title' => 'Stadtwerke München erreichen 100 % Ökostrom-Ziel für Privatkunden', 'company' => 'SWM', 'city' => 'München', 'minutes' => 2, 'href' => '#'],
|
||||
]);
|
||||
}
|
||||
|
||||
$study ??= [
|
||||
'kicker' => 'STUDIE · ENERGIE',
|
||||
'title' => 'Energiewende-Monitor 2025: Investitionen, Engpässe, Potenziale im DACH-Raum',
|
||||
'source' => 'Fraunhofer-Institut für Solare Energiesysteme',
|
||||
'meta' => 'PDF · 84 Seiten · Veröffentlicht 11. Nov 2025',
|
||||
'href' => '#',
|
||||
];
|
||||
$stats = collect($stats ?? []);
|
||||
@endphp
|
||||
|
||||
@if ($industry && $releases->isNotEmpty())
|
||||
<section class="max-w-layout mx-auto px-8 mt-16">
|
||||
<header class="flex items-baseline justify-between mb-4 flex-wrap gap-3">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">
|
||||
Heute im Fokus <span class="text-brand">· {{ $industry }}</span>
|
||||
{{ __('web.spotlight.title') }} <span class="text-brand">· {{ $industry }}</span>
|
||||
</h2>
|
||||
<div class="eyebrow muted">Aktualisiert {{ now()->format('H:i') }} Uhr · {{ $stats[0]['value'] ?? '47' }} Meldungen</div>
|
||||
</header>
|
||||
<hr class="rule-strong">
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 border-b border-bg-rule">
|
||||
@foreach ($stats as $idx => $stat)
|
||||
<div class="px-6 py-5 {{ $idx < count($stats) - 1 ? 'md:border-r border-bg-rule' : '' }}">
|
||||
<div class="eyebrow muted mb-2">{{ $stat['label'] }}</div>
|
||||
<div class="font-serif text-[32px] font-semibold leading-none tracking-[-0.5px] text-ink">{{ $stat['value'] }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-1.5">{{ $stat['sub'] }}</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="grid gap-9 mt-6 grid-cols-1 lg:grid-cols-[1.7fr_1fr]">
|
||||
<div>
|
||||
@foreach ($releases as $release)
|
||||
<a href="{{ $release['href'] }}" @class([
|
||||
'grid items-start border-b border-bg-rule py-4',
|
||||
'cursor-pointer transition-colors group hover:bg-bg-elev',
|
||||
'pl-1.5 pr-2 sm:pl-2',
|
||||
'max-md:grid-cols-[auto_minmax(0,1fr)] max-md:gap-x-3',
|
||||
'md:grid-cols-[3.75rem_6.25rem_minmax(0,1fr)] md:gap-x-4',
|
||||
])>
|
||||
<div class="flex min-w-0 shrink-0 flex-col gap-0.5 tabular-nums pt-0.5">
|
||||
<span class="font-mono text-[11px] leading-none text-ink-3 sm:text-[12px]">{{ $release['time'] }}</span>
|
||||
@if (($release['date'] ?? '') !== '')
|
||||
<span class="text-[9px] leading-tight text-ink-4 sm:text-[10px]">{{ $release['date'] }}</span>
|
||||
@endif
|
||||
<span class="bp-cat self-start whitespace-nowrap leading-snug max-md:mt-1 max-md:text-[9px] max-md:tracking-[0.1em] md:hidden">{{ $release['category'] }}</span>
|
||||
</div>
|
||||
<span class="bp-cat hidden self-start whitespace-nowrap leading-snug md:inline">{{ $release['category'] }}</span>
|
||||
<div class="min-w-0">
|
||||
<div class="font-serif text-[16px] font-medium leading-[1.3] tracking-[-0.1px] text-ink transition-colors group-hover:text-brand">
|
||||
{{ $release['title'] }}
|
||||
</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-1 flex items-center gap-1.5 flex-wrap">
|
||||
<svg width="10" height="10" viewBox="0 0 11 11" class="text-ok" aria-hidden="true">
|
||||
<circle cx="5.5" cy="5.5" r="5" fill="currentColor" />
|
||||
<path d="M3 5.5l1.8 1.8L8 4" stroke="white" stroke-width="1.4" fill="none" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span>{{ $release['company'] }}</span>
|
||||
@if ($release['city'] ?? null)
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>{{ $release['city'] }}</span>
|
||||
@endif
|
||||
@if ($release['minutes'] ?? null)
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>{{ $release['minutes'] }} Min.</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@if ($stats->isNotEmpty())
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 border-b border-bg-rule">
|
||||
@foreach ($stats as $idx => $stat)
|
||||
<div class="px-6 py-5 {{ $idx < count($stats) - 1 ? 'md:border-r border-bg-rule' : '' }}">
|
||||
<div class="eyebrow muted mb-2">{{ $stat['label'] }}</div>
|
||||
<div class="font-serif text-[32px] font-semibold leading-none tracking-[-0.5px] text-ink">{{ $stat['value'] }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-1.5">{{ $stat['sub'] }}</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<aside>
|
||||
<div class="bg-bg-card-warm p-6 border-t-[3px] border-brand">
|
||||
<div class="eyebrow mb-2.5">{{ $study['kicker'] }}</div>
|
||||
<h3 class="font-serif text-[20px] font-semibold m-0 mb-3 tracking-[-0.2px] leading-[1.25] text-ink">
|
||||
{{ $study['title'] }}
|
||||
</h3>
|
||||
<div class="text-[12px] text-ink-3 mb-3.5">
|
||||
{{ $study['source'] }}<br>
|
||||
<span class="font-mono text-[11px]">{{ $study['meta'] }}</span>
|
||||
<div class="mt-6">
|
||||
@foreach ($releases as $release)
|
||||
<a href="{{ $release['href'] }}" @class([
|
||||
'grid items-start border-b border-bg-rule py-4',
|
||||
'cursor-pointer transition-colors group hover:bg-bg-elev',
|
||||
'pl-1.5 pr-2 sm:pl-2',
|
||||
'max-md:grid-cols-[auto_minmax(0,1fr)] max-md:gap-x-3',
|
||||
'md:grid-cols-[3.75rem_6.25rem_minmax(0,1fr)] md:gap-x-4',
|
||||
])>
|
||||
<div class="flex min-w-0 shrink-0 flex-col gap-0.5 tabular-nums pt-0.5">
|
||||
<span class="font-mono text-[11px] leading-none text-ink-3 sm:text-[12px]">{{ $release['time'] }}</span>
|
||||
@if (($release['date'] ?? '') !== '')
|
||||
<span class="text-[9px] leading-tight text-ink-4 sm:text-[10px]">{{ $release['date'] }}</span>
|
||||
@endif
|
||||
<span class="bp-cat self-start whitespace-nowrap leading-snug max-md:mt-1 max-md:text-[9px] max-md:tracking-[0.1em] md:hidden">{{ $release['category'] }}</span>
|
||||
</div>
|
||||
<a href="{{ $study['href'] }}"
|
||||
class="w-full inline-flex items-center justify-center gap-2 px-[18px] py-2.5 text-[13px] font-semibold bg-transparent text-ink border border-ink rounded-[2px] cursor-pointer hover:bg-ink hover:text-bg transition-colors">
|
||||
↓ Studie herunterladen
|
||||
</a>
|
||||
<div class="mt-2.5 text-[10.5px] leading-[1.45] flex items-center gap-1.5 text-ink-3">
|
||||
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" class="flex-shrink-0 text-ink-4" aria-hidden="true">
|
||||
<rect x="1.5" y="3" width="9" height="6.5" stroke="currentColor" stroke-width="1" />
|
||||
<path d="M1.5 3l4.5 3.5L10.5 3" stroke="currentColor" stroke-width="1" fill="none" />
|
||||
</svg>
|
||||
<span>Kostenlos · Anmeldung per E-Mail erforderlich</span>
|
||||
<span class="bp-cat hidden self-start whitespace-nowrap leading-snug md:inline">{{ $release['category'] }}</span>
|
||||
<div class="min-w-0">
|
||||
<div class="font-serif text-[16px] font-medium leading-[1.3] tracking-[-0.1px] text-ink transition-colors group-hover:text-brand">
|
||||
{{ $release['title'] }}
|
||||
</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-1 flex items-center gap-1.5 flex-wrap">
|
||||
<svg width="10" height="10" viewBox="0 0 11 11" class="text-ok" aria-hidden="true">
|
||||
<circle cx="5.5" cy="5.5" r="5" fill="currentColor" />
|
||||
<path d="M3 5.5l1.8 1.8L8 4" stroke="white" stroke-width="1.4" fill="none" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span>{{ $release['company'] }}</span>
|
||||
@if ($release['city'] ?? null)
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>{{ $release['city'] }}</span>
|
||||
@endif
|
||||
@if ($release['minutes'] ?? null)
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>{{ __('web.shared.minutes_short', ['minutes' => $release['minutes']]) }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -1,112 +1,93 @@
|
|||
@props([
|
||||
'items' => [],
|
||||
'marketIndex' => null,
|
||||
'asideSlides' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$items = collect($items)->all();
|
||||
if (empty($items)) {
|
||||
$items = [
|
||||
['time' => '14:32', 'text' => 'Siemens Energy hebt Jahresprognose an'],
|
||||
['time' => '14:18', 'text' => 'BASF: Restrukturierung in Ludwigshafen abgeschlossen'],
|
||||
['time' => '13:55', 'text' => 'Deutsche Bahn meldet Rekord bei Fernverkehr'],
|
||||
];
|
||||
}
|
||||
// Selbstversorgend, damit der Ticker auf jeder Unterseite gefüllt ist
|
||||
// (nicht nur auf der Startseite): jüngste PMs des Portals, kurz gecacht.
|
||||
$items = app(\App\Services\Web\TickerData::class)
|
||||
->items(\App\Support\WebPortalContext::portalFromHost(), app()->getLocale());
|
||||
|
||||
$marketIndex ??= [
|
||||
'label' => 'DAX',
|
||||
'value' => '18.247',
|
||||
'delta' => '+0,8%',
|
||||
'positive' => true,
|
||||
];
|
||||
|
||||
$asideSlides = collect($asideSlides ?? [
|
||||
$marketIndex,
|
||||
[
|
||||
'label' => 'EUR/USD',
|
||||
'value' => '1,08',
|
||||
'delta' => '+0,2%',
|
||||
'positive' => true,
|
||||
],
|
||||
[
|
||||
'label' => 'Brent Öl',
|
||||
'value' => '78,40 $',
|
||||
'delta' => '-0,9%',
|
||||
'positive' => false,
|
||||
],
|
||||
])->values()->all();
|
||||
// Echte Kursdaten aus dem Cache (market:refresh); leer = Slot ausblenden.
|
||||
$marketSlides = app(\App\Services\Web\MarketDataService::class)->slides();
|
||||
@endphp
|
||||
|
||||
<div class="bg-topbar-grad text-ink-on-dark" aria-label="Ad-Hoc-Ticker">
|
||||
@if (! empty($items) || ! empty($marketSlides))
|
||||
<div class="bg-topbar-grad text-ink-on-dark" aria-label="{{ __('web.ticker.aria_label') }}">
|
||||
<div class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 py-2.5 flex items-center gap-3 sm:gap-5 text-[12.5px]">
|
||||
<div class="flex items-center gap-3 sm:gap-4 overflow-hidden min-w-0 flex-1">
|
||||
<span class="flex items-center gap-1.5 text-accent-warm font-bold text-[10.5px] sm:text-[11px] tracking-[0.15em] flex-shrink-0">
|
||||
<span class="w-[7px] h-[7px] rounded-full bg-accent-warm pulse-dot"></span>
|
||||
AD-HOC
|
||||
</span>
|
||||
@if (! empty($items))
|
||||
<div class="flex items-center gap-3 sm:gap-4 overflow-hidden min-w-0 flex-1">
|
||||
<span class="flex items-center gap-1.5 text-accent-warm font-bold text-[10.5px] sm:text-[11px] tracking-[0.15em] flex-shrink-0">
|
||||
<span class="w-[7px] h-[7px] rounded-full bg-accent-warm pulse-dot"></span>
|
||||
AD-HOC
|
||||
</span>
|
||||
|
||||
<span class="sr-only">
|
||||
Ad-hoc-Meldungen:
|
||||
@foreach ($items as $item)
|
||||
{{ $item['time'] }} {{ $item['text'] }}.
|
||||
@endforeach
|
||||
</span>
|
||||
|
||||
<div class="overflow-hidden min-w-0 flex-1" aria-hidden="true">
|
||||
<div class="ticker-marquee-track text-[12px] sm:text-[12.5px] whitespace-nowrap">
|
||||
@foreach (array_merge($items, $items) as $item)
|
||||
<span class="inline-flex shrink-0 gap-2 items-baseline">
|
||||
<span class="font-mono text-[11px] text-ink-on-dark-muted">{{ $item['time'] }}</span>
|
||||
<span class="text-ink-on-dark">{{ $item['text'] }}</span>
|
||||
</span>
|
||||
<span class="sr-only">
|
||||
{{ __('web.ticker.sr_intro') }}
|
||||
@foreach ($items as $item)
|
||||
{{ $item['time'] }} {{ $item['text'] }}.
|
||||
@endforeach
|
||||
</span>
|
||||
|
||||
<div class="overflow-hidden min-w-0 flex-1" aria-hidden="true">
|
||||
<div class="ticker-marquee-track text-[12px] sm:text-[12.5px] whitespace-nowrap">
|
||||
@foreach (array_merge($items, $items) as $item)
|
||||
<span class="inline-flex shrink-0 gap-2 items-baseline">
|
||||
<span class="font-mono text-[11px] text-ink-on-dark-muted">{{ $item['time'] }}</span>
|
||||
<span class="text-ink-on-dark">{{ $item['text'] }}</span>
|
||||
</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<span class="flex-1"></span>
|
||||
@endif
|
||||
|
||||
<span class="hidden sm:inline-block w-px h-[18px] bg-ink-on-dark-rule flex-shrink-0"></span>
|
||||
@if (! empty($marketSlides))
|
||||
<span class="hidden sm:inline-block w-px h-[18px] bg-ink-on-dark-rule flex-shrink-0"></span>
|
||||
|
||||
{{-- Kein position:absolute: sonst null Breite im Fluss, bis Layout kollabiert --}}
|
||||
<div class="shrink-0 flex justify-end items-center min-w-0 max-w-[min(92vw,280px)] sm:max-w-[min(42vw,280px)]"
|
||||
x-data="{
|
||||
slides: @js($asideSlides),
|
||||
i: 0,
|
||||
init() {
|
||||
if (this.slides.length < 2) {
|
||||
return;
|
||||
}
|
||||
window.setInterval(() => {
|
||||
this.i = (this.i + 1) % this.slides.length;
|
||||
}, 5200);
|
||||
},
|
||||
}"
|
||||
aria-live="polite">
|
||||
<div class="grid min-h-[22px] justify-items-end overflow-hidden [grid-template-columns:minmax(0,max-content)]">
|
||||
<template x-for="(slide, idx) in slides" :key="idx">
|
||||
<div x-show="i === idx"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 -translate-y-full"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-280"
|
||||
x-transition:leave-start="opacity-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 translate-y-full"
|
||||
class="col-start-1 row-start-1 flex items-center gap-2 whitespace-nowrap">
|
||||
<span class="text-ink-on-dark-muted font-semibold text-[10.5px] tracking-wider" x-text="slide.label"></span>
|
||||
<span class="text-ink-on-dark font-mono text-[12.5px]" x-text="slide.value"></span>
|
||||
<span class="font-semibold text-[11.5px] font-mono"
|
||||
:class="(slide.positive ?? true) ? 'text-gain' : 'text-loss'"
|
||||
x-text="slide.delta"></span>
|
||||
<svg width="26" height="10" viewBox="0 0 26 10"
|
||||
class="hidden sm:inline-block shrink-0"
|
||||
:class="(slide.positive ?? true) ? 'text-gain' : 'text-loss'"
|
||||
aria-hidden="true">
|
||||
<path d="M0,8 L4,7 L8,5 L12,6 L16,4 L20,3 L24,2"
|
||||
fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
{{-- Kein position:absolute: sonst null Breite im Fluss, bis Layout kollabiert --}}
|
||||
<div class="shrink-0 flex justify-end items-center min-w-0 max-w-[min(92vw,280px)] sm:max-w-[min(42vw,280px)]"
|
||||
x-data="{
|
||||
slides: @js($marketSlides),
|
||||
i: 0,
|
||||
init() {
|
||||
if (this.slides.length < 2) {
|
||||
return;
|
||||
}
|
||||
window.setInterval(() => {
|
||||
this.i = (this.i + 1) % this.slides.length;
|
||||
}, 5200);
|
||||
},
|
||||
}"
|
||||
aria-live="polite">
|
||||
<div class="grid min-h-[22px] justify-items-end overflow-hidden [grid-template-columns:minmax(0,max-content)]">
|
||||
<template x-for="(slide, idx) in slides" :key="idx">
|
||||
<div x-show="i === idx"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 -translate-y-full"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-280"
|
||||
x-transition:leave-start="opacity-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 translate-y-full"
|
||||
class="col-start-1 row-start-1 flex items-center gap-2 whitespace-nowrap">
|
||||
<span class="text-ink-on-dark-muted font-semibold text-[10.5px] tracking-wider" x-text="slide.label"></span>
|
||||
<span class="text-ink-on-dark font-mono text-[12.5px]" x-text="slide.value"></span>
|
||||
<span class="font-semibold text-[11.5px] font-mono"
|
||||
:class="(slide.positive ?? true) ? 'text-gain' : 'text-loss'"
|
||||
x-text="slide.delta"></span>
|
||||
{{-- Dekorativer Richtungspfeil (auf/ab je Vorzeichen, keine echte Kurve) --}}
|
||||
<svg width="26" height="10" viewBox="0 0 26 10"
|
||||
class="hidden sm:inline-block shrink-0"
|
||||
:class="(slide.positive ?? true) ? 'text-gain' : 'text-loss'"
|
||||
aria-hidden="true">
|
||||
<path x-show="slide.positive ?? true" d="M0,8 L4,7 L8,5 L12,6 L16,4 L20,3 L24,2"
|
||||
fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||
<path x-show="!(slide.positive ?? true)" d="M0,2 L4,3 L8,5 L12,4 L16,6 L20,7 L24,8"
|
||||
fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -3,29 +3,19 @@
|
|||
])
|
||||
|
||||
@php
|
||||
$releases = collect($releases)->take(4)->values();
|
||||
|
||||
$fallback = collect([
|
||||
['title' => 'FinTech-Startup PaymentFlow sichert sich 45 Mio. Euro', 'hits' => 12428],
|
||||
['title' => 'Energiewende: Neue Rekorde bei Erneuerbaren', 'hits' => 9831],
|
||||
['title' => 'Immobilienmarkt 2025: Trendwende bei Kaufpreisen', 'hits' => 7104],
|
||||
['title' => 'Telemedizin-Boom: 3 Mio. Online-Sprechstunden', 'hits' => 5298],
|
||||
$items = collect($releases)->take(4)->values()->map(fn ($release) => [
|
||||
'title' => \Illuminate\Support\Str::limit($release->title, 70),
|
||||
'hits' => (int) ($release->hits ?? 0),
|
||||
'href' => route('release.detail', ['slug' => $release->slug]),
|
||||
]);
|
||||
|
||||
$items = $releases->isNotEmpty()
|
||||
? $releases->map(fn ($release) => [
|
||||
'title' => \Illuminate\Support\Str::limit($release->title, 70),
|
||||
'hits' => (int) ($release->hits ?? 0),
|
||||
'href' => route('release.detail', ['slug' => $release->slug]),
|
||||
])
|
||||
: $fallback->map(fn ($mock) => array_merge($mock, ['href' => '#']));
|
||||
|
||||
$maxHits = max(1, $items->max('hits'));
|
||||
@endphp
|
||||
|
||||
@if ($items->isNotEmpty())
|
||||
<section>
|
||||
<header class="flex items-baseline justify-between mb-4 min-h-[34px]">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] leading-[1.2] text-ink">Meistgelesen</h2>
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] leading-[1.2] text-ink">{{ __('web.most_read.title') }}</h2>
|
||||
</header>
|
||||
<hr class="rule-strong mb-1">
|
||||
|
||||
|
|
@ -46,3 +36,4 @@
|
|||
</a>
|
||||
@endforeach
|
||||
</section>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
@props([
|
||||
'topics' => null,
|
||||
'subscribers' => 84000,
|
||||
])
|
||||
|
||||
@php
|
||||
$themeKey = config('app.theme');
|
||||
$topics ??= config('domains.domains.'.$themeKey.'.brand.newsletter_topics', [
|
||||
['name' => 'Tageszusammenfassung', 'meta' => 'Mo–Fr · 07:00 Uhr', 'active' => true],
|
||||
['name' => 'Wochenrückblick', 'meta' => 'Sonntags · 18:00 Uhr', 'active' => false],
|
||||
['name' => 'Energie & Klima', 'meta' => 'ab Content-Score 80 · max. 2× pro Woche', 'active' => true],
|
||||
['name' => 'IPO & M&A', 'meta' => 'ausgewählt von der Redaktion · ca. 3–5 Mails/Monat', 'active' => false],
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<section id="newsletter" class="max-w-layout mx-auto px-8 mt-16">
|
||||
<div class="relative overflow-hidden bg-topbar-grad text-ink-on-dark p-12 px-14">
|
||||
<svg width="280" height="280" viewBox="0 0 280 280" class="absolute -right-10 -top-10 opacity-[0.06]" aria-hidden="true">
|
||||
<circle cx="140" cy="140" r="120" stroke="white" stroke-width="1" fill="none" />
|
||||
<circle cx="140" cy="140" r="80" stroke="white" stroke-width="1" fill="none" />
|
||||
<circle cx="140" cy="140" r="40" stroke="white" stroke-width="1" fill="none" />
|
||||
</svg>
|
||||
|
||||
<div class="relative grid gap-12 grid-cols-1 lg:grid-cols-[1.1fr_1fr]">
|
||||
<div>
|
||||
<div class="eyebrow on-dark mb-3">Bleiben Sie informiert</div>
|
||||
<h2 class="font-serif text-[30px] font-semibold m-0 leading-[1.18] tracking-[-0.4px] mb-3.5 max-w-[460px] text-white">
|
||||
Pressemeldungen, kuratiert in Ihren Posteingang
|
||||
</h2>
|
||||
<p class="text-[14px] leading-[1.55] text-white/70 max-w-[440px] m-0 mb-6">
|
||||
Die wichtigsten Meldungen aus DACH — täglich, wöchentlich oder branchenspezifisch. Keine Werbung, jederzeit kündbar.
|
||||
</p>
|
||||
|
||||
<form action="#newsletter-subscribe" method="post" class="flex mb-3.5 max-w-[460px]">
|
||||
@csrf
|
||||
<label class="sr-only" for="newsletter-email">E-Mail-Adresse</label>
|
||||
<input id="newsletter-email" type="email" name="email" required
|
||||
placeholder="Ihre E-Mail-Adresse"
|
||||
class="flex-1 px-4 py-3.5 text-[14px] bg-white/[0.08] border border-white/[0.18] text-white outline-none rounded-none placeholder-white/40 focus:border-brand focus:ring-0">
|
||||
<button type="submit"
|
||||
class="px-[22px] py-3.5 text-[13px] font-semibold bg-brand text-white whitespace-nowrap rounded-none cursor-pointer hover:bg-brand-deep transition-colors">
|
||||
Kostenlos abonnieren →
|
||||
</button>
|
||||
</form>
|
||||
<div class="text-[11px] text-white/50">
|
||||
Über {{ number_format($subscribers, 0, ',', '.') }} Abonnenten · Datenschutz nach DSGVO
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2.5 self-stretch">
|
||||
@foreach ($topics as $topic)
|
||||
<label class="flex items-start gap-3.5 px-4 py-3.5 cursor-pointer transition-colors
|
||||
{{ ($topic['active'] ?? false) ? 'bg-white/[0.06] border border-brand/40 hover:bg-white/[0.1]' : 'border border-white/10 hover:border-white/30 hover:bg-white/[0.04]' }}">
|
||||
@if ($topic['active'] ?? false)
|
||||
<span class="w-[18px] h-[18px] flex-shrink-0 mt-0.5 bg-brand border border-brand flex items-center justify-center">
|
||||
<svg width="10" height="10" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<path d="M2 6l3 3 5-6" stroke="white" stroke-width="2" />
|
||||
</svg>
|
||||
</span>
|
||||
@else
|
||||
<span class="w-[18px] h-[18px] flex-shrink-0 mt-0.5 border border-white/30"></span>
|
||||
@endif
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="text-[13.5px] font-semibold text-white">{{ $topic['name'] }}</div>
|
||||
<div class="text-[11.5px] text-white/55 mt-0.5">{{ $topic['meta'] }}</div>
|
||||
</div>
|
||||
|
||||
<input type="checkbox" name="topics[]" value="{{ \Illuminate\Support\Str::slug($topic['name']) }}"
|
||||
{{ ($topic['active'] ?? false) ? 'checked' : '' }} class="sr-only">
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
$start = max(1, $paginator->currentPage() - 2);
|
||||
$end = min($paginator->lastPage(), $paginator->currentPage() + 2);
|
||||
@endphp
|
||||
<nav class="mt-8 flex items-center justify-center gap-1.5" aria-label="Seitennummerierung">
|
||||
<nav class="mt-8 flex items-center justify-center gap-1.5" aria-label="{{ __('web.search.pagination') }}">
|
||||
@if ($paginator->onFirstPage())
|
||||
<span class="inline-flex items-center justify-center w-9 h-9 border border-bg-rule text-ink-4 opacity-40 cursor-not-allowed" aria-hidden="true">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"><path d="M15 19l-7-7 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
@else
|
||||
<a href="{{ $paginator->previousPageUrl() }}" rel="prev"
|
||||
class="inline-flex items-center justify-center w-9 h-9 border border-bg-rule-strong text-ink hover:bg-bg-elev transition-colors"
|
||||
aria-label="Vorherige Seite">
|
||||
aria-label="{{ __('web.search.previous_page') }}">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"><path d="M15 19l-7-7 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</a>
|
||||
@endif
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
@if ($paginator->hasMorePages())
|
||||
<a href="{{ $paginator->nextPageUrl() }}" rel="next"
|
||||
class="inline-flex items-center justify-center w-9 h-9 border border-bg-rule-strong text-ink hover:bg-bg-elev transition-colors"
|
||||
aria-label="Nächste Seite">
|
||||
aria-label="{{ __('web.search.next_page') }}">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"><path d="M9 5l7 7-7 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</a>
|
||||
@else
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
@props([
|
||||
'title',
|
||||
'teaser',
|
||||
'company',
|
||||
'industry',
|
||||
'region',
|
||||
'date',
|
||||
'contentType' => 'FACHMELDUNG', // ANALYSE, INTERVIEW, FACHMELDUNG
|
||||
'slug',
|
||||
'imageUrl' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
// Map industry to Heroicon
|
||||
$industryIcons = [
|
||||
'IT & Digitalisierung' => 'cpu-chip',
|
||||
'Industrie & Technik' => 'cog-6-tooth',
|
||||
'Finanzen & Versicherungen' => 'currency-dollar',
|
||||
'Handel & E-Commerce' => 'shopping-cart',
|
||||
'Bauen & Immobilien' => 'building-office',
|
||||
'Mobilität & Logistik' => 'truck',
|
||||
'Energie & Umwelt' => 'bolt',
|
||||
'Medizin & Gesundheit' => 'heart',
|
||||
'Personal & HR' => 'user-group',
|
||||
'Marketing, PR & Medien' => 'megaphone',
|
||||
'Recht & Steuern' => 'scale',
|
||||
'Politik, Verbände & NGOs' => 'flag',
|
||||
'Wissenschaft & Forschung' => 'beaker',
|
||||
'Lifestyle' => 'sparkles',
|
||||
'Tourismus & Kultur' => 'globe-alt',
|
||||
];
|
||||
|
||||
$iconName = $industryIcons[$industry] ?? 'building-office';
|
||||
$iconPath = "/heroicons/optimized/24/outline/{$iconName}.svg";
|
||||
|
||||
// Content Type Badge Classes
|
||||
$contentTypeClasses = match ($contentType) {
|
||||
'ANALYSE' => 'bg-[var(--color-primary)] text-white',
|
||||
'INTERVIEW' => 'bg-[var(--color-secondary)] text-white',
|
||||
default => 'bg-white/80 dark:bg-zinc-900/90 border border-[var(--color-primary)]/30',
|
||||
};
|
||||
|
||||
$badgeExtraClasses = $imageUrl ? 'shadow-md backdrop-blur-sm' : 'shadow-sm';
|
||||
@endphp
|
||||
|
||||
<a href="/release/{{ $slug }}" class="group block">
|
||||
<article
|
||||
class="bg-gradient-to-br from-[var(--color-white)] to-[var(--color-primary)]/3 dark:from-[var(--color-zinc-800)] dark:to-[var(--color-primary)]/3 rounded-xl border border-[var(--color-primary)]/20 dark:border-[var(--color-primary)]/20 overflow-hidden transition-all duration-300 hover:shadow-xl hover:border-[var(--color-primary)]/30 hover:translate-y-[-2px] shadow-sm h-full flex flex-col">
|
||||
|
||||
@if ($imageUrl)
|
||||
{{-- Card mit Bild --}}
|
||||
<div
|
||||
class="relative h-46 bg-gradient-to-br from-zinc-100 to-zinc-50 dark:from-zinc-800 dark:to-zinc-900 overflow-hidden">
|
||||
<img src="{{ $imageUrl }}" alt="{{ $title }}"
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
loading="lazy" />
|
||||
|
||||
{{-- Content Type Label --}}
|
||||
<div class="absolute top-4 left-4">
|
||||
<span
|
||||
class="inline-flex bg-primary/10 items-center gap-2 text-[11px] font-semibold px-3 py-1.5 {{ $contentTypeClasses }} {{ $badgeExtraClasses }} rounded-xl shadow-sm">
|
||||
<div class="industry-icon-badge">
|
||||
<img src="{{ $iconPath }}" alt="{{ $industry }}" class="h-4 w-4" />
|
||||
</div>
|
||||
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $industry }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Content --}}
|
||||
<div class="p-7 flex flex-col flex-grow">
|
||||
{{-- Meta Info --}}
|
||||
<div class="flex justify-end left gap-2 mb-4 text-xs text-zinc-500 dark:text-zinc-400">
|
||||
|
||||
<time datetime="{{ $date }}"
|
||||
class="text-zinc-600 dark:text-zinc-400">{{ $date }}</time>
|
||||
</div>
|
||||
|
||||
{{-- Title --}}
|
||||
<h3
|
||||
class="text-xl font-bold text-zinc-900 dark:text-zinc-100 line-clamp-2 mb-4 group-hover:text-[var(--color-primary)] transition-colors leading-tight">
|
||||
{{ $title }}
|
||||
</h3>
|
||||
|
||||
{{-- Teaser --}}
|
||||
<p class="text-[15px] leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-3 mb-5 flex-grow">
|
||||
{{ $teaser }}
|
||||
</p>
|
||||
|
||||
{{-- Company Footer --}}
|
||||
<div
|
||||
class="flex items-center justify-between pt-5 border-t border-[var(--color-primary)]/10 group-hover:border-[var(--color-primary)]/20 transition-all duration-300">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4">
|
||||
</path>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-zinc-700 dark:text-zinc-300">{{ $company }}</span>
|
||||
</div>
|
||||
<svg class="h-5 w-5 transition-transform group-hover:translate-x-2" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
{{-- Card ohne Bild - Textbasiert mit farbigem Hintergrund --}}
|
||||
<div class="relative p-8 flex flex-col h-full">
|
||||
|
||||
{{-- Content Type Label --}}
|
||||
<div class="mb-0 mt-0">
|
||||
<span
|
||||
class="inline-flex items-center gap-2 text-[11px] font-bold px-3 py-2 {{ $contentTypeClasses }} {{ $badgeExtraClasses }} rounded-xl">
|
||||
<div class="industry-icon-badge">
|
||||
<img src="{{ $iconPath }}" alt="{{ $industry }}" class="h-4 w-4" />
|
||||
</div>
|
||||
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $industry }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{-- Meta Info --}}
|
||||
<div class="flex justify-end left gap-2 mb-10 text-xs text-zinc-500 dark:text-zinc-400">
|
||||
<time datetime="{{ $date }}"
|
||||
class="text-zinc-600 dark:text-zinc-400">{{ $date }}</time>
|
||||
</div>
|
||||
|
||||
{{-- Title - Prominenter ohne Bild --}}
|
||||
<h3
|
||||
class="text-2xl font-bold text-zinc-900 dark:text-zinc-100 mb-5 group-hover:text-[var(--color-primary)] transition-colors leading-tight">
|
||||
{{ $title }}
|
||||
</h3>
|
||||
|
||||
{{-- Teaser as Quote/Highlight --}}
|
||||
<blockquote
|
||||
class="text-base leading-relaxed text-zinc-700 dark:text-zinc-300 line-clamp-5 mb-6 flex-grow italic border-l-4 border-[var(--color-primary)] pl-4">
|
||||
{{ $teaser }}
|
||||
</blockquote>
|
||||
|
||||
{{-- Company Footer --}}
|
||||
<div
|
||||
class="flex items-center justify-between pt-5 border-t border-[var(--color-primary)]/10 group-hover:border-[var(--color-primary)]/20 transition-all duration-300">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4">
|
||||
</path>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-zinc-700 dark:text-zinc-300">{{ $company }}</span>
|
||||
</div>
|
||||
<svg class="h-5 w-5 transition-transform group-hover:translate-x-2" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</article>
|
||||
</a>
|
||||
|
|
@ -18,9 +18,9 @@
|
|||
@if ($plans->isNotEmpty())
|
||||
<section class="max-w-layout mx-auto px-8 mt-16">
|
||||
<header class="flex items-baseline justify-between mb-4 flex-wrap gap-3">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">Tarife & Pakete</h2>
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">{{ __('web.pricing_teaser.title') }}</h2>
|
||||
<a href="{{ $pricingHref }}" class="eyebrow muted hover:text-ink transition-colors inline-flex items-center gap-1.5 cursor-pointer">
|
||||
Alle Details
|
||||
{{ __('web.pricing_teaser.details') }}
|
||||
<svg width="10" height="10" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<path d="M2 6h8M6 2l4 4-4 4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
<div class="relative flex flex-col p-6 bg-bg {{ $isPopular ? 'ring-1 ring-inset ring-brand' : '' }}">
|
||||
@if ($isPopular)
|
||||
<span class="absolute top-0 right-0 inline-flex items-center px-2.5 py-1 text-[9.5px] font-bold uppercase tracking-[0.14em] text-white bg-brand">
|
||||
Beliebt
|
||||
{{ __('web.pricing_teaser.popular') }}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
|
|
@ -46,14 +46,14 @@
|
|||
<div class="flex items-baseline gap-1.5 mb-1">
|
||||
<span class="font-serif text-[34px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmtPrice((int) $plan->monthly_price_cents) }} €</span>
|
||||
@if ($hasMonthly)
|
||||
<span class="text-[12px] text-ink-3">/ Monat</span>
|
||||
<span class="text-[12px] text-ink-3">/ {{ __('web.pricing_teaser.month') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($hasMonthly && (int) $plan->yearly_price_cents > 0)
|
||||
<div class="text-[11.5px] text-ink-3 mb-4">
|
||||
{{ $fmtPrice((int) $plan->yearly_price_cents) }} € / Jahr
|
||||
<span class="text-gain font-medium">· 2 Monate gratis</span>
|
||||
{{ $fmtPrice((int) $plan->yearly_price_cents) }} € / {{ __('web.pricing_teaser.year') }}
|
||||
<span class="text-gain font-medium">· {{ __('web.pricing_teaser.two_months_free') }}</span>
|
||||
</div>
|
||||
@else
|
||||
<div class="mb-4"></div>
|
||||
|
|
@ -62,17 +62,17 @@
|
|||
<ul class="space-y-2 mb-6 flex-1 list-none p-0 m-0 text-[12.5px] text-ink-2">
|
||||
<li class="flex items-baseline gap-2">
|
||||
<span class="font-mono text-[12px] font-semibold text-ink">{{ $plan->press_release_quota }}</span>
|
||||
<span>Pressemitteilungen / Monat</span>
|
||||
<span>{{ __('web.pricing_teaser.quota') }}</span>
|
||||
</li>
|
||||
@if ($plan->daily_limit !== null)
|
||||
<li class="flex items-baseline gap-2">
|
||||
<span class="font-mono text-[12px] font-semibold text-ink">{{ $plan->daily_limit }}</span>
|
||||
<span>max. pro Tag</span>
|
||||
<span>{{ __('web.pricing_teaser.daily_limit') }}</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="flex items-baseline gap-2 text-ink-3">
|
||||
<span class="font-mono text-[12px]">∞</span>
|
||||
<span>kein Tageslimit</span>
|
||||
<span>{{ __('web.pricing_teaser.no_daily_limit') }}</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
<a href="{{ $pricingHref }}"
|
||||
class="inline-flex items-center justify-center gap-1.5 w-full px-4 py-2.5 text-[12.5px] font-semibold rounded-[2px] transition-colors cursor-pointer
|
||||
{{ $isPopular ? 'bg-brand text-white hover:bg-brand-deep' : 'border border-bg-rule-strong text-ink hover:bg-bg-elev' }}">
|
||||
Auswählen
|
||||
{{ __('web.pricing_teaser.select') }}
|
||||
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<path d="M2 6h8M6 2l4 4-4 4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
|
@ -91,10 +91,10 @@
|
|||
|
||||
<div class="flex items-center justify-between gap-4 flex-wrap mt-5">
|
||||
<p class="text-[12px] text-ink-3 m-0">
|
||||
Alle Preise zzgl. MwSt. · Jederzeit kündbar · Auch ohne Abo als Einzelveröffentlichung möglich.
|
||||
{{ __('web.pricing_teaser.footnote') }}
|
||||
</p>
|
||||
<a href="{{ $submitHref }}" class="inline-flex items-center gap-1.5 text-[12.5px] font-semibold text-brand hover:text-brand-deep transition-colors cursor-pointer whitespace-nowrap">
|
||||
Direkt veröffentlichen
|
||||
{{ __('web.pricing_teaser.publish_direct') }}
|
||||
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<path d="M2 6h8M6 2l4 4-4 4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
|
|
|||
55
resources/views/components/web/publish-band.blade.php
Normal file
55
resources/views/components/web/publish-band.blade.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
{{--
|
||||
Abschluss-CTA der Startseite (ehemaliger Newsletter-Platz vor dem Footer):
|
||||
zahlt auf Anmelden / Einreichen / Veröffentlichen ein.
|
||||
--}}
|
||||
<section id="veroeffentlichen-cta" class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 mt-16">
|
||||
<div class="relative overflow-hidden bg-topbar-grad text-ink-on-dark p-8 sm:p-12 lg:px-14">
|
||||
<svg width="280" height="280" viewBox="0 0 280 280" class="absolute -right-10 -top-10 opacity-[0.06]" aria-hidden="true">
|
||||
<circle cx="140" cy="140" r="120" stroke="white" stroke-width="1" fill="none" />
|
||||
<circle cx="140" cy="140" r="80" stroke="white" stroke-width="1" fill="none" />
|
||||
<circle cx="140" cy="140" r="40" stroke="white" stroke-width="1" fill="none" />
|
||||
</svg>
|
||||
|
||||
<div class="relative grid gap-10 lg:gap-12 grid-cols-1 lg:grid-cols-[1.1fr_1fr]">
|
||||
<div>
|
||||
<div class="eyebrow on-dark mb-3">{{ __('web.publish_band.eyebrow') }}</div>
|
||||
<h2 class="font-serif text-[26px] sm:text-[30px] font-semibold m-0 leading-[1.18] tracking-[-0.4px] mb-3.5 max-w-[460px] text-white">
|
||||
{{ __('web.publish_band.title') }}
|
||||
</h2>
|
||||
<p class="text-[14px] leading-[1.55] text-white/70 max-w-[440px] m-0 mb-7">
|
||||
{{ __('web.publish_band.subline') }}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-3 max-w-[460px]">
|
||||
<a href="{{ route('veroeffentlichen') }}"
|
||||
class="inline-flex items-center justify-center px-[22px] py-3.5 text-[13.5px] font-semibold bg-brand text-white whitespace-nowrap cursor-pointer hover:bg-brand-deep transition-colors">
|
||||
{{ __('web.publish_band.cta_submit') }} →
|
||||
</a>
|
||||
<a href="{{ route('preise') }}"
|
||||
class="inline-flex items-center justify-center px-[22px] py-3.5 text-[13.5px] font-semibold text-white border border-white/30 whitespace-nowrap cursor-pointer hover:bg-white/10 transition-colors">
|
||||
{{ __('web.publish_band.cta_pricing') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-[11px] text-white/50 mt-4">
|
||||
{{ __('web.publish_band.footnote') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ol class="flex flex-col gap-2.5 self-center m-0 p-0 list-none">
|
||||
@foreach ([
|
||||
['n' => '01', 't' => __('web.publish_band.step1_title'), 'd' => __('web.publish_band.step1_text')],
|
||||
['n' => '02', 't' => __('web.publish_band.step2_title'), 'd' => __('web.publish_band.step2_text')],
|
||||
['n' => '03', 't' => __('web.publish_band.step3_title'), 'd' => __('web.publish_band.step3_text')],
|
||||
] as $step)
|
||||
<li class="flex items-start gap-3.5 px-4 py-3.5 border border-white/10 bg-white/[0.04]">
|
||||
<span class="font-mono text-[13px] text-brand-soft font-semibold flex-shrink-0 mt-0.5">{{ $step['n'] }}</span>
|
||||
<div class="flex-1">
|
||||
<div class="text-[13.5px] font-semibold text-white">{{ $step['t'] }}</div>
|
||||
<div class="text-[11.5px] text-white/55 mt-0.5 leading-[1.5]">{{ $step['d'] }}</div>
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -9,22 +9,22 @@
|
|||
@endphp
|
||||
|
||||
<section class="bg-topbar-grad text-ink-on-dark p-6">
|
||||
<div class="eyebrow on-dark mb-2">Für Unternehmen</div>
|
||||
<div class="eyebrow on-dark mb-2">{{ __('web.publisher_cta.eyebrow') }}</div>
|
||||
<h3 class="font-serif text-[20px] font-semibold m-0 mb-2 tracking-[-0.2px] leading-[1.2] text-white">
|
||||
Veröffentlichen Sie Ihre Pressemitteilung
|
||||
{{ __('web.publisher_cta.title') }}
|
||||
</h3>
|
||||
<p class="text-[12.5px] leading-[1.5] m-0 mb-4 text-white/85">
|
||||
Reichweite in DACH · Redaktionelle Prüfung · Strukturierte Distribution
|
||||
{{ __('web.publisher_cta.subline') }}
|
||||
</p>
|
||||
|
||||
<a href="{{ $submitHref }}"
|
||||
class="w-full inline-flex items-center justify-center gap-2 px-[18px] py-2.5 text-[13px] font-semibold bg-brand text-white rounded-[2px] cursor-pointer hover:bg-brand-deep transition-colors">
|
||||
Jetzt einreichen →
|
||||
{{ __('web.publisher_cta.submit') }} →
|
||||
</a>
|
||||
|
||||
<a href="{{ $pricingHref }}"
|
||||
class="flex items-center justify-center gap-1.5 w-full mt-3 py-2 text-[12px] font-medium text-white/80 cursor-pointer hover:text-white underline underline-offset-[3px] decoration-white/30 hover:decoration-white/70 transition-colors">
|
||||
Tarife & Pakete ansehen
|
||||
{{ __('web.publisher_cta.pricing') }}
|
||||
<svg width="10" height="10" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<path d="M2 6h8M6 2l4 4-4 4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@
|
|||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="eyebrow mb-1">Redaktioneller Qualitätsstandard</div>
|
||||
<div class="eyebrow mb-1">{{ __('web.quality.eyebrow') }}</div>
|
||||
<div class="text-[13.5px] text-ink leading-[1.5] font-medium">
|
||||
Alle Pressemitteilungen werden redaktionell geprüft.
|
||||
{{ __('web.quality.claim') }}
|
||||
<span class="text-ink-2 font-normal">
|
||||
Mindestqualität gewährleistet durch unseren Content-Score — eine Bewertung von Quellenqualität, Verifizierungsstatus und redaktioneller Relevanz.
|
||||
{{ __('web.quality.detail') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ route('faq') }}" class="text-[12px] font-semibold text-brand whitespace-nowrap underline underline-offset-[3px] decoration-brand/40 cursor-pointer hover:text-brand-deep hover:decoration-brand transition-colors">
|
||||
Redaktionsrichtlinien →
|
||||
<a href="{{ route('richtlinien') }}" class="text-[12px] font-semibold text-brand whitespace-nowrap underline underline-offset-[3px] decoration-brand/40 cursor-pointer hover:text-brand-deep hover:decoration-brand transition-colors">
|
||||
{{ __('web.quality.guidelines') }} →
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
'name' => 'businessportal',
|
||||
'accent' => '24',
|
||||
'tagline_short' => 'Pressemitteilungen · DACH',
|
||||
'tagline_long' => 'Veröffentlichungs-Portal für redaktionell geprüfte Pressemitteilungen aus Deutschland, Österreich und der Schweiz.',
|
||||
'tagline_long' => 'Veröffentlichungs-Portal für qualitätsgeprüfte Pressemitteilungen aus Deutschland, Österreich und der Schweiz.',
|
||||
'footer_legal' => '© :year businessportal24 · Alle Rechte vorbehalten',
|
||||
'about_label' => 'Über businessportal24',
|
||||
]);
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
<footer class="mt-16 bg-topbar-grad text-ink-on-dark">
|
||||
<div class="max-w-layout mx-auto px-8 py-12 grid gap-10 grid-cols-1 md:grid-cols-2 lg:grid-cols-[1.4fr_1fr_1fr_1fr]">
|
||||
<div>
|
||||
<a href="{{ route('web.home') }}" class="block cursor-pointer group" aria-label="{{ $brandName }}{{ $brandAccent }} Startseite">
|
||||
<a href="{{ route('web.home') }}" class="block cursor-pointer group" aria-label="{{ $brandName }}{{ $brandAccent }} {{ __('web.header.homepage') }}">
|
||||
<div class="text-[24px] font-semibold leading-none tracking-[-0.5px]">
|
||||
<x-web.brand-mark :brand="$themeKey" variant="on-dark" />
|
||||
</div>
|
||||
|
|
@ -40,33 +40,33 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class="eyebrow mb-3.5">Rubriken</div>
|
||||
<div class="eyebrow mb-3.5">{{ __('web.footer.sections') }}</div>
|
||||
<ul class="space-y-2 text-[13px] text-white/75 list-none p-0 m-0">
|
||||
@foreach (\App\Support\CategoryNavigation::items(5) as $footerCategory)
|
||||
<li><a href="{{ $footerCategory['href'] }}" class="cursor-pointer hover:text-white transition-colors">{{ $footerCategory['label'] }}</a></li>
|
||||
@endforeach
|
||||
<li><a href="{{ route('newsrooms') }}" class="cursor-pointer hover:text-white transition-colors">Newsrooms</a></li>
|
||||
<li><a href="{{ route('newsrooms') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.newsrooms') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="eyebrow mb-3.5">Für Unternehmen</div>
|
||||
<div class="eyebrow mb-3.5">{{ __('web.footer.for_companies') }}</div>
|
||||
<ul class="space-y-2 text-[13px] text-white/75 list-none p-0 m-0">
|
||||
<li><a href="{{ route('veroeffentlichen') }}" class="cursor-pointer hover:text-white transition-colors">Veröffentlichen</a></li>
|
||||
<li><a href="{{ route('preise') }}" class="cursor-pointer hover:text-white transition-colors">Tarife & Pakete</a></li>
|
||||
<li><a href="{{ route('api') }}" class="cursor-pointer hover:text-white transition-colors">API & RSS</a></li>
|
||||
<li><a href="{{ route('veroeffentlichen') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.publish') }}</a></li>
|
||||
<li><a href="{{ route('preise') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.pricing') }}</a></li>
|
||||
<li><a href="{{ route('api') }}" class="cursor-pointer hover:text-white transition-colors">API</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="eyebrow mb-3.5">Unternehmen</div>
|
||||
<div class="eyebrow mb-3.5">{{ __('web.footer.company') }}</div>
|
||||
<ul class="space-y-2 text-[13px] text-white/75 list-none p-0 m-0">
|
||||
<li><a href="mailto:{{ $contactEmail }}" class="cursor-pointer hover:text-white transition-colors">Kontakt</a></li>
|
||||
<li><a href="mailto:{{ $contactEmail }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.contact') }}</a></li>
|
||||
<li><a href="{{ route('faq') }}" class="cursor-pointer hover:text-white transition-colors">FAQ</a></li>
|
||||
<li><a href="{{ route('hilfe') }}" class="cursor-pointer hover:text-white transition-colors">Hilfe-Center</a></li>
|
||||
<li><a href="{{ route('kuendigung') }}" class="cursor-pointer hover:text-white transition-colors">Verträge hier kündigen</a></li>
|
||||
<li><a href="{{ route('impressum') }}" class="cursor-pointer hover:text-white transition-colors">Impressum</a></li>
|
||||
<li><a href="{{ route('datenschutz') }}" class="cursor-pointer hover:text-white transition-colors">Datenschutz</a></li>
|
||||
<li><a href="{{ route('hilfe') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.help') }}</a></li>
|
||||
<li><a href="{{ route('kuendigung') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.cancellation') }}</a></li>
|
||||
<li><a href="{{ route('impressum') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.imprint') }}</a></li>
|
||||
<li><a href="{{ route('datenschutz') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.privacy') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -75,10 +75,9 @@
|
|||
<div class="max-w-layout mx-auto px-8 py-5 flex items-center justify-between gap-4 text-[11.5px] text-white/50 flex-wrap">
|
||||
<span>{{ $legal }}</span>
|
||||
<span class="flex items-center gap-4 flex-wrap">
|
||||
<a href="{{ route('agb') }}" class="cursor-pointer hover:text-white transition-colors">AGB</a>
|
||||
<a href="{{ route('richtlinien') }}" class="cursor-pointer hover:text-white transition-colors">Richtlinien</a>
|
||||
<a href="{{ route('melden') }}" class="cursor-pointer hover:text-white transition-colors">Inhalte melden</a>
|
||||
<a href="{{ route('datenschutz') }}" class="cursor-pointer hover:text-white transition-colors">Datenschutz</a>
|
||||
<a href="{{ route('agb') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.terms') }}</a>
|
||||
<a href="{{ route('richtlinien') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.guidelines') }}</a>
|
||||
<a href="{{ route('melden') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.report') }}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
@props([
|
||||
'navigation' => null,
|
||||
'ticker' => [],
|
||||
'marketIndex' => null,
|
||||
'asideSlides' => null,
|
||||
'brand' => null,
|
||||
])
|
||||
|
||||
|
|
@ -32,7 +29,7 @@
|
|||
// Navigation = echte Rubriken der aktiven Ausgabe. Es gibt keine separate
|
||||
// Übersichtsseite; jeder Eintrag führt direkt auf die Kategorieseite.
|
||||
$navigation ??= collect([
|
||||
['label' => 'Startseite', 'href' => route('web.home'), 'active' => request()->routeIs('web.home')],
|
||||
['label' => __('web.nav.home'), 'href' => route('web.home'), 'active' => request()->routeIs('web.home')],
|
||||
])->concat(
|
||||
\App\Support\CategoryNavigation::items()->map(fn (array $category): array => [
|
||||
'label' => $category['label'],
|
||||
|
|
@ -41,7 +38,7 @@
|
|||
])
|
||||
)->all();
|
||||
|
||||
$activeLabel = collect($navigation)->firstWhere('active', true)['label'] ?? 'Menü';
|
||||
$activeLabel = collect($navigation)->firstWhere('active', true)['label'] ?? __('web.nav.menu');
|
||||
@endphp
|
||||
|
||||
<header>
|
||||
|
|
@ -54,7 +51,7 @@
|
|||
</span>
|
||||
|
||||
<span class="hidden md:flex items-center px-3.5 text-[9.5px] uppercase font-semibold tracking-[0.16em] text-ink-3 whitespace-nowrap">
|
||||
Ausgabe
|
||||
{{ __('web.header.edition') }}
|
||||
</span>
|
||||
|
||||
@foreach ($editions as $edition)
|
||||
|
|
@ -62,7 +59,7 @@
|
|||
class="flex items-center gap-1 sm:gap-1.5 px-1.5 sm:px-2.5 py-2 text-[11px] sm:text-[11.5px] whitespace-nowrap -mb-px border-b-2 transition-colors
|
||||
@if ($edition['code'] === $activeEdition) font-semibold text-ink-on-dark bg-white/[0.06] border-brand
|
||||
@else font-medium text-ink-on-dark-2 border-transparent hover:text-ink-on-dark hover:bg-white/[0.04] @endif"
|
||||
aria-label="Ausgabe {{ $edition['label'] }}"
|
||||
aria-label="{{ __('web.header.edition') }} {{ $edition['label'] }}"
|
||||
@if ($edition['code'] === $activeEdition) aria-current="true" @endif>
|
||||
<span class="text-[11px] sm:text-[12px] leading-none">{{ $edition['flag'] }}</span>
|
||||
<span>{{ $edition['label'] }}</span>
|
||||
|
|
@ -71,7 +68,7 @@
|
|||
|
||||
<span class="flex-1"></span>
|
||||
|
||||
{{-- Sprache: mobil verkürzt; ab lg mit Newsletter/RSS/API --}}
|
||||
{{-- Sprache: mobil verkürzt; ab lg mit FAQ/API --}}
|
||||
<span class="flex items-center gap-3 sm:gap-4 py-2 whitespace-nowrap">
|
||||
<span class="inline-flex items-center gap-1.5">
|
||||
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" class="opacity-60" aria-hidden="true">
|
||||
|
|
@ -84,7 +81,7 @@
|
|||
</strong>
|
||||
</span>
|
||||
<span class="hidden lg:inline-block w-px h-[14px] bg-ink-on-dark-rule"></span>
|
||||
<a href="{{ route('preise') }}" class="cursor-pointer font-medium text-ink-on-dark hover:text-brand transition-colors">Preise</a>
|
||||
<a href="{{ route('preise') }}" class="cursor-pointer font-medium text-ink-on-dark hover:text-brand transition-colors">{{ __('web.header.pricing') }}</a>
|
||||
<span class="hidden lg:inline-block w-px h-[14px] bg-ink-on-dark-rule"></span>
|
||||
<a href="{{ route('faq') }}" class="hidden lg:inline cursor-pointer hover:text-ink-on-dark transition-colors">FAQ</a>
|
||||
<a href="{{ route('api') }}" class="hidden lg:inline cursor-pointer hover:text-ink-on-dark transition-colors">API</a>
|
||||
|
|
@ -95,9 +92,11 @@
|
|||
{{-- Header (Logo + Suche + Buttons + Burger) --}}
|
||||
<div id="header" class="header-normal bg-bg border-b border-bg-rule"
|
||||
x-data="{ searchOpen: false }"
|
||||
@keydown.escape.window="searchOpen = false">
|
||||
@keydown.escape.window="searchOpen = false"
|
||||
@keydown.window.prevent.cmd.k="$refs.desktopSearch ? $refs.desktopSearch.focus() : searchOpen = true"
|
||||
@keydown.window.prevent.ctrl.k="$refs.desktopSearch ? $refs.desktopSearch.focus() : searchOpen = true">
|
||||
<div class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 py-3 lg:py-[18px] flex items-center gap-3 sm:gap-4 lg:gap-6">
|
||||
<a href="{{ route('web.home') }}" class="block cursor-pointer group flex-shrink-0" aria-label="{{ $brandName }}{{ $brandAccent }} Startseite">
|
||||
<a href="{{ route('web.home') }}" class="block cursor-pointer group flex-shrink-0" aria-label="{{ $brandName }}{{ $brandAccent }} {{ __('web.header.homepage') }}">
|
||||
<div class="text-[22px] sm:text-[24px] lg:text-[28px] font-semibold leading-none tracking-[-0.5px]">
|
||||
<x-web.brand-mark :brand="$themeKey" />
|
||||
</div>
|
||||
|
|
@ -108,14 +107,14 @@
|
|||
|
||||
{{-- Suchfeld nur ab md sichtbar --}}
|
||||
<form action="{{ route('suche') }}" method="get" class="hidden md:block flex-1 max-w-[460px]">
|
||||
<label class="sr-only" for="site-search">Pressemitteilungen, Unternehmen, Branchen, ISIN durchsuchen</label>
|
||||
<label class="sr-only" for="site-search">{{ __('web.header.search_label') }}</label>
|
||||
<div class="flex items-center gap-2.5 px-3.5 py-2.5 border border-bg-rule bg-bg-elev text-[13px] text-ink-3 rounded-[2px] focus-within:border-brand transition-colors">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="1.5" />
|
||||
<path d="M11 11l3 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||
</svg>
|
||||
<input id="site-search" type="search" name="q"
|
||||
placeholder="Pressemitteilungen, Unternehmen, Branchen, ISIN…"
|
||||
<input id="site-search" type="search" name="q" x-ref="desktopSearch"
|
||||
placeholder="{{ __('web.header.search_placeholder') }}"
|
||||
class="flex-1 bg-transparent border-0 p-0 text-[13px] text-ink placeholder:text-ink-3 focus:outline-none focus:ring-0 min-w-0">
|
||||
<span class="hidden xl:inline-block text-[10.5px] text-ink-4 border border-bg-rule px-1.5 py-0.5 rounded-[2px] font-mono">⌘K</span>
|
||||
</div>
|
||||
|
|
@ -124,7 +123,7 @@
|
|||
<svg width="10" height="10" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<path d="M2 3h8M3.5 6h5M5 9h2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" />
|
||||
</svg>
|
||||
Erweiterte Suche
|
||||
{{ __('web.header.advanced_search') }}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -135,7 +134,7 @@
|
|||
<button type="button"
|
||||
@click="searchOpen = true"
|
||||
class="md:hidden inline-flex items-center justify-center w-10 h-10 text-ink rounded-[2px] cursor-pointer hover:bg-bg-elev transition-colors"
|
||||
aria-label="Suche öffnen">
|
||||
aria-label="{{ __('web.header.search_open') }}">
|
||||
<svg width="20" height="20" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="1.5" />
|
||||
<path d="M11 11l3 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||
|
|
@ -145,23 +144,23 @@
|
|||
{{-- Anmelden: führt in den Hub (Portal-Domain). --}}
|
||||
<a href="{{ rtrim(config('domains.domain_portal_url', config('app.url')), '/') }}/login"
|
||||
class="inline-flex items-center gap-2 px-2 sm:px-3 lg:px-4 py-2 lg:py-2.5 text-[13px] font-semibold text-ink rounded-[2px] cursor-pointer hover:text-brand transition-colors"
|
||||
aria-label="Anmelden">
|
||||
aria-label="{{ __('web.header.login') }}">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" class="sm:hidden" aria-hidden="true">
|
||||
<circle cx="12" cy="8" r="4" stroke="currentColor" stroke-width="1.8" />
|
||||
<path d="M4 21c0-4 4-7 8-7s8 3 8 7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span class="hidden sm:inline">Anmelden</span>
|
||||
<span class="hidden sm:inline">{{ __('web.header.login') }}</span>
|
||||
</a>
|
||||
|
||||
{{-- Veröffentlichen --}}
|
||||
<a href="{{ route('veroeffentlichen') }}"
|
||||
class="inline-flex items-center gap-1.5 sm:gap-2 px-3 sm:px-4 lg:px-[18px] py-2 lg:py-2.5 text-[12.5px] sm:text-[13px] font-semibold text-white bg-brand cursor-pointer hover:bg-brand-deep rounded-[2px] transition-colors whitespace-nowrap"
|
||||
aria-label="Pressemitteilung veröffentlichen">
|
||||
aria-label="{{ __('web.header.publish_aria') }}">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" class="sm:hidden" aria-hidden="true">
|
||||
<path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span class="sm:hidden">PM</span>
|
||||
<span class="hidden sm:inline">Veröffentlichen</span>
|
||||
<span class="hidden sm:inline">{{ __('web.header.publish') }}</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" class="hidden sm:inline" aria-hidden="true">
|
||||
<path d="M3 6h6M6 3l3 3-3 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
|
@ -180,13 +179,13 @@
|
|||
<div class="max-w-layout mx-auto px-4 sm:px-6 py-6">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<div class="eyebrow mb-1">Suche</div>
|
||||
<h2 class="font-serif text-[22px] font-semibold text-ink">Pressemitteilungen durchsuchen</h2>
|
||||
<div class="eyebrow mb-1">{{ __('web.header.search_eyebrow') }}</div>
|
||||
<h2 class="font-serif text-[22px] font-semibold text-ink">{{ __('web.header.search_title') }}</h2>
|
||||
</div>
|
||||
<button type="button"
|
||||
@click="searchOpen = false"
|
||||
class="inline-flex items-center justify-center w-10 h-10 text-ink rounded-[2px] cursor-pointer hover:bg-bg-elev transition-colors"
|
||||
aria-label="Suche schließen">
|
||||
aria-label="{{ __('web.header.search_close') }}">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M6 6l12 12M6 18L18 6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" />
|
||||
</svg>
|
||||
|
|
@ -194,20 +193,20 @@
|
|||
</div>
|
||||
|
||||
<form action="{{ route('suche') }}" method="get">
|
||||
<label class="sr-only" for="site-search-mobile">Pressemitteilungen durchsuchen</label>
|
||||
<label class="sr-only" for="site-search-mobile">{{ __('web.header.search_title') }}</label>
|
||||
<div class="flex items-center gap-2.5 px-4 py-3.5 border border-bg-rule-strong bg-bg-elev text-[15px] text-ink-3 rounded-[2px]">
|
||||
<svg width="20" height="20" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="1.5" />
|
||||
<path d="M11 11l3 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||
</svg>
|
||||
<input id="site-search-mobile" type="search" name="q"
|
||||
placeholder="Pressemitteilungen, Unternehmen, Branchen, ISIN…"
|
||||
placeholder="{{ __('web.header.search_placeholder') }}"
|
||||
x-ref="searchInput"
|
||||
x-init="$watch('searchOpen', value => value && setTimeout(() => $refs.searchInput.focus(), 50))"
|
||||
class="flex-1 bg-transparent border-0 p-0 text-[15px] text-ink placeholder:text-ink-3 focus:outline-none focus:ring-0 min-w-0">
|
||||
<button type="submit"
|
||||
class="px-3 py-1.5 text-[12px] font-semibold bg-brand text-white rounded-[2px] cursor-pointer hover:bg-brand-deep transition-colors">
|
||||
Suchen
|
||||
{{ __('web.header.search_submit') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-3 flex justify-between text-[11.5px] text-ink-3">
|
||||
|
|
@ -215,12 +214,12 @@
|
|||
<svg width="10" height="10" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<path d="M2 3h8M3.5 6h5M5 9h2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" />
|
||||
</svg>
|
||||
Erweiterte Suche
|
||||
{{ __('web.header.advanced_search') }}
|
||||
</a>
|
||||
<span class="hidden sm:inline-flex items-center gap-1.5">
|
||||
Drücken Sie
|
||||
{{ __('web.header.press') }}
|
||||
<kbd class="font-mono text-[10.5px] text-ink-4 border border-bg-rule px-1.5 py-0.5 rounded-[2px]">Esc</kbd>
|
||||
zum Schließen
|
||||
{{ __('web.header.to_close') }}
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -234,7 +233,7 @@
|
|||
x-data="{ menuOpen: false }"
|
||||
@keydown.escape.window="menuOpen = false"
|
||||
@click.outside="menuOpen = false"
|
||||
aria-label="Hauptnavigation">
|
||||
aria-label="{{ __('web.nav.aria_label') }}">
|
||||
|
||||
{{-- Desktop: horizontale Tabs --}}
|
||||
<div class="hidden lg:flex max-w-layout mx-auto px-8 items-stretch">
|
||||
|
|
@ -255,7 +254,7 @@
|
|||
class="flex items-center justify-between w-full py-3 cursor-pointer group"
|
||||
:aria-expanded="menuOpen.toString()"
|
||||
aria-controls="mobile-nav-menu"
|
||||
aria-label="Hauptmenü öffnen">
|
||||
aria-label="{{ __('web.nav.open_menu') }}">
|
||||
<span class="flex items-center gap-3">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" class="text-ink"
|
||||
:class="{ 'hidden': menuOpen }" aria-hidden="true">
|
||||
|
|
@ -265,7 +264,7 @@
|
|||
:class="{ 'hidden': !menuOpen, '!block': menuOpen }" aria-hidden="true">
|
||||
<path d="M6 6l12 12M6 18L18 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span class="text-[10px] font-bold tracking-[0.18em] uppercase text-ink-3 group-hover:text-ink transition-colors">Rubrik</span>
|
||||
<span class="text-[10px] font-bold tracking-[0.18em] uppercase text-ink-3 group-hover:text-ink transition-colors">{{ __('web.nav.section') }}</span>
|
||||
<span class="font-serif text-[15px] font-semibold text-ink leading-none">{{ $activeLabel }}</span>
|
||||
</span>
|
||||
<svg width="14" height="14" viewBox="0 0 12 12" fill="none" class="text-ink-3 transition-transform"
|
||||
|
|
@ -304,5 +303,6 @@
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<x-web.live-ticker :items="$ticker" :market-index="$marketIndex" :aside-slides="$asideSlides" />
|
||||
{{-- Befüllt sich selbst (TickerData) — auf allen Seiten identisch. --}}
|
||||
<x-web.live-ticker />
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ new #[Layout('components.layouts.app'), Title('Tarife & Pakete')] class extends
|
|||
</div>
|
||||
|
||||
<flux:textarea wire:model="benefits" :label="__('Vorteile (eine Zeile = ein Vorteil)')" rows="5"
|
||||
:placeholder="__('Redaktionelle Prüfung inklusive Höheres Monatskontingent Günstigere Extra-Pressemitteilungen')"
|
||||
:placeholder="__('Inhaltsprüfung inklusive Höheres Monatskontingent Günstigere Extra-Pressemitteilungen')"
|
||||
:description="__('Wird auf der Buchungs-Seite und der öffentlichen Preise-Seite als Vorteilsliste angezeigt.')" />
|
||||
|
||||
<flux:switch wire:model="isActive" :label="__('Tarif aktiv (buchbar und auf der Tarif-Seite sichtbar)')" />
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex
|
|||
@if ($selectedCompany->website)
|
||||
<p class="m-0 text-[12px] text-[color:var(--color-ink-3)] mt-3">
|
||||
<span
|
||||
class="font-semibold text-[color:var(--color-ink-2)]">{{ __('Web') }}:</span>
|
||||
class="font-semibold text-[color:var(--color-ink-2)]">Web:</span>
|
||||
{{ $selectedCompany->website }}
|
||||
</p>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -655,7 +655,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl
|
|||
@if ($selectedCompany->website)
|
||||
<p class="m-0 text-[12px] text-[color:var(--color-ink-3)] mt-3">
|
||||
<span
|
||||
class="font-semibold text-[color:var(--color-ink-2)]">{{ __('Web') }}:</span>
|
||||
class="font-semibold text-[color:var(--color-ink-2)]">Web:</span>
|
||||
{{ $selectedCompany->website }}
|
||||
</p>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -404,7 +404,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
</x-portal.stat-card>
|
||||
<x-portal.stat-card variant="warn" :label="__('In Prüfung')" :value="number_format($stats['review'])">
|
||||
<x-slot:meta>{{ __('queue') }}</x-slot:meta>
|
||||
<x-slot:trend>{{ __('redaktionelle Prüfung') }}</x-slot:trend>
|
||||
<x-slot:trend>{{ __('Inhaltsprüfung') }}</x-slot:trend>
|
||||
</x-portal.stat-card>
|
||||
<x-portal.stat-card variant="muted" :label="__('Entwürfe')" :value="number_format($stats['draft'])">
|
||||
<x-slot:meta>{{ __('privat') }}</x-slot:meta>
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
<flux:icon.clock class="size-[18px]" />
|
||||
</div>
|
||||
<div class="flex-1 min-w-[220px] text-[13px] text-[color:var(--color-ink-2)]">
|
||||
<p class="m-0">{{ __('Diese PM wartet auf eine redaktionelle Entscheidung.') }}</p>
|
||||
<p class="m-0">{{ __('Diese PM wartet auf eine Prüf-Entscheidung.') }}</p>
|
||||
@if ($latestClassification && $latestClassification->reason)
|
||||
<p class="m-0 mt-1 text-[12px] text-[color:var(--color-ink-3)]">
|
||||
<strong class="text-[color:var(--color-ink-2)]">{{ __('KI-Hinweis') }}:</strong>
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ new #[Layout('components.layouts.app'), Title('Mein Dashboard')] class extends C
|
|||
|
||||
<x-portal.stat-card variant="warn" :label="__('In Prüfung')" :value="$stats['review']">
|
||||
<x-slot:meta>{{ __('Ø 4 h') }}</x-slot:meta>
|
||||
<x-slot:trend>{{ __('redaktionelle Prüfung') }}</x-slot:trend>
|
||||
<x-slot:trend>{{ __('Inhaltsprüfung') }}</x-slot:trend>
|
||||
</x-portal.stat-card>
|
||||
|
||||
<x-portal.stat-card variant="muted" :label="__('Entwürfe')" :value="$stats['draft']">
|
||||
|
|
@ -365,7 +365,7 @@ new #[Layout('components.layouts.app'), Title('Mein Dashboard')] class extends C
|
|||
{{ __('Noch keine Pressemitteilungen') }}
|
||||
</div>
|
||||
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[460px] text-[color:var(--color-ink-3)]">
|
||||
{{ __('Starten Sie mit einer ersten Mitteilung für die aktive Firma oder Ihr Kundenkonto. Veröffentlichung erfolgt nach redaktioneller Prüfung auf beiden Portalen.') }}
|
||||
{{ __('Starten Sie mit einer ersten Mitteilung für die aktive Firma oder Ihr Kundenkonto. Veröffentlichung erfolgt nach bestandener Inhaltsprüfung auf beiden Portalen.') }}
|
||||
</p>
|
||||
<div class="mt-6 flex items-center gap-2.5">
|
||||
<flux:button variant="primary" icon="plus" href="{{ route('me.press-releases.create') }}" wire:navigate>
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ new #[Layout('components.layouts.app'), Title('Neue Firma anlegen')] class exten
|
|||
{{ __('Neue Firma anlegen') }}
|
||||
</h1>
|
||||
<p class="text-[12.5px] leading-[1.55] mt-2 m-0 max-w-[640px] text-[color:var(--color-ink-3)]">
|
||||
{{ __('Lege Stammdaten und Portal-Zuordnung an. Die Firma steht sofort zur Verfügung — die redaktionelle Prüfung erfolgt erst bei der ersten Pressemitteilung.') }}
|
||||
{{ __('Lege Stammdaten und Portal-Zuordnung an. Die Firma steht sofort zur Verfügung — die Inhaltsprüfung erfolgt erst bei der ersten Pressemitteilung.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -430,7 +430,7 @@ new #[Layout('components.layouts.app'), Title('Meine Firmen')] class extends Com
|
|||
</div>
|
||||
|
||||
<p class="mt-3 text-[12.5px] leading-[1.55] max-w-[640px] m-0 text-[color:var(--color-ink-3)]">
|
||||
{{ __('Eine Firma ist der Container für Pressemitteilungen: Stammdaten, Boilerplate, Pressekontakte. Anlage ohne separate Freigabe — die redaktionelle Prüfung erfolgt erst bei der Pressemitteilung.') }}
|
||||
{{ __('Eine Firma ist der Container für Pressemitteilungen: Stammdaten, Boilerplate, Pressekontakte. Anlage ohne separate Freigabe — die Inhaltsprüfung erfolgt erst bei der Pressemitteilung.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex
|
|||
|
||||
Flux::toast(
|
||||
heading: __('Eingereicht'),
|
||||
text: __('Die Redaktion prüft die Pressemitteilung typischerweise innerhalb von 24 h.'),
|
||||
text: __('Die Inhaltsprüfung ist typischerweise innerhalb von 24 h abgeschlossen.'),
|
||||
variant: 'success',
|
||||
);
|
||||
} else {
|
||||
|
|
@ -734,7 +734,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex
|
|||
<p class="m-0">{!! nl2br(e($selectedCompany->boilerplate)) !!}</p>
|
||||
@if ($selectedCompany->website)
|
||||
<p class="m-0 text-[12px] text-[color:var(--color-ink-3)] mt-3">
|
||||
<span class="font-semibold text-[color:var(--color-ink-2)]">{{ __('Web') }}:</span>
|
||||
<span class="font-semibold text-[color:var(--color-ink-2)]">Web:</span>
|
||||
{{ $selectedCompany->website }}
|
||||
</p>
|
||||
@endif
|
||||
|
|
@ -789,7 +789,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex
|
|||
</flux:button>
|
||||
</flux:modal.trigger>
|
||||
<p class="text-[11px] text-[color:var(--color-ink-3)] mt-2 leading-[1.45] m-0">
|
||||
{{ __('Warnungen blockieren nicht. Pflichtfelder blockieren. Die Redaktion prüft typ. innerhalb von 24h.') }}
|
||||
{{ __('Warnungen blockieren nicht. Pflichtfelder blockieren. Die Inhaltsprüfung dauert typ. bis zu 24h.') }}
|
||||
</p>
|
||||
|
||||
<hr class="border-0 border-t border-[color:var(--color-bg-rule)] my-3" />
|
||||
|
|
@ -979,7 +979,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex
|
|||
{{ __('Sofort nach Freigabe') }}
|
||||
</span>
|
||||
<span class="text-[11px] text-[color:var(--color-ink-3)] block mt-0.5 leading-tight">
|
||||
{{ __('geht live, sobald die Redaktion grünes Licht gibt') }}
|
||||
{{ __('geht live, sobald die Inhaltsprüfung grünes Licht gibt') }}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl
|
|||
|
||||
Flux::toast(
|
||||
heading: __('Eingereicht'),
|
||||
text: __('Die Redaktion prüft die Pressemitteilung typischerweise innerhalb von 24 h.'),
|
||||
text: __('Die Inhaltsprüfung ist typischerweise innerhalb von 24 h abgeschlossen.'),
|
||||
variant: 'success',
|
||||
);
|
||||
} else {
|
||||
|
|
@ -656,7 +656,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl
|
|||
<p class="m-0">{!! nl2br(e($selectedCompany->boilerplate)) !!}</p>
|
||||
@if ($selectedCompany->website)
|
||||
<p class="m-0 text-[12px] text-[color:var(--color-ink-3)] mt-3">
|
||||
<span class="font-semibold text-[color:var(--color-ink-2)]">{{ __('Web') }}:</span>
|
||||
<span class="font-semibold text-[color:var(--color-ink-2)]">Web:</span>
|
||||
{{ $selectedCompany->website }}
|
||||
</p>
|
||||
@endif
|
||||
|
|
@ -715,7 +715,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl
|
|||
</flux:button>
|
||||
</flux:modal.trigger>
|
||||
<p class="text-[11px] text-[color:var(--color-ink-3)] mt-2 leading-[1.45] m-0">
|
||||
{{ __('Warnungen blockieren nicht. Pflichtfelder blockieren. Die Redaktion prüft typ. innerhalb von 24h.') }}
|
||||
{{ __('Warnungen blockieren nicht. Pflichtfelder blockieren. Die Inhaltsprüfung dauert typ. bis zu 24h.') }}
|
||||
</p>
|
||||
|
||||
<hr class="border-0 border-t border-[color:var(--color-bg-rule)] my-3" />
|
||||
|
|
@ -941,7 +941,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl
|
|||
{{ __('Sofort nach Freigabe') }}
|
||||
</span>
|
||||
<span class="text-[11px] text-[color:var(--color-ink-3)] block mt-0.5 leading-tight">
|
||||
{{ __('geht live, sobald die Redaktion grünes Licht gibt') }}
|
||||
{{ __('geht live, sobald die Inhaltsprüfung grünes Licht gibt') }}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -459,7 +459,7 @@ new #[Layout('components.layouts.app'), Title('Meine Pressemitteilungen')] class
|
|||
Schnell-Submits, der die Bestätigungen umgehen würde. --}}
|
||||
<flux:modal.trigger :name="'confirm-submit-'.$pr->id">
|
||||
<button type="button" class="inline-action"
|
||||
title="{{ __('Zur redaktionellen Prüfung senden') }}">
|
||||
title="{{ __('Zur Inhaltsprüfung senden') }}">
|
||||
{{ __('Zur Prüfung →') }}
|
||||
</button>
|
||||
</flux:modal.trigger>
|
||||
|
|
@ -579,7 +579,7 @@ new #[Layout('components.layouts.app'), Title('Meine Pressemitteilungen')] class
|
|||
{{-- Empty: gar keine PMs überhaupt --}}
|
||||
<x-portal.empty-state
|
||||
:title="__('Noch keine Pressemitteilungen')"
|
||||
:subtitle="__('Starten Sie mit Ihrer ersten Mitteilung. Nach redaktioneller Prüfung erscheint sie i. d. R. binnen 4 Stunden werktags auf beiden Portalen.')"
|
||||
:subtitle="__('Starten Sie mit Ihrer ersten Mitteilung. Nach bestandener Inhaltsprüfung erscheint sie i. d. R. binnen 4 Stunden werktags auf beiden Portalen.')"
|
||||
>
|
||||
<x-slot:icon>
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none">
|
||||
|
|
@ -653,7 +653,7 @@ new #[Layout('components.layouts.app'), Title('Meine Pressemitteilungen')] class
|
|||
<ul
|
||||
class="text-[11.5px] text-[color:var(--color-ink-2)] leading-[1.7] mt-2 list-none p-0 m-0 space-y-0.5">
|
||||
<li>{{ __('Vorschau') }}</li>
|
||||
<li class="text-[color:var(--color-ink-3)] italic">{{ __('Warten auf Redaktion') }}</li>
|
||||
<li class="text-[color:var(--color-ink-3)] italic">{{ __('Warten auf Prüfung') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
|
||||
Flux::toast(
|
||||
heading: __('Eingereicht'),
|
||||
text: __('Die Redaktion prüft die Pressemitteilung typischerweise innerhalb von 24 h.'),
|
||||
text: __('Die Inhaltsprüfung ist typischerweise innerhalb von 24 h abgeschlossen.'),
|
||||
variant: 'success',
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new class extends Component {
|
||||
public function with(): array
|
||||
{
|
||||
// Mock-Daten für Featured Releases
|
||||
return [
|
||||
'releases' => [
|
||||
[
|
||||
'slug' => 'ki-revolution-deutsche-unternehmen',
|
||||
'title' => 'KI-Revolution: Deutsche Unternehmen investieren Milliarden in Automatisierung',
|
||||
'teaser' => 'Eine neue Studie zeigt: Unternehmen im DACH-Raum planen für 2025 Investitionen in Höhe von über 15 Milliarden Euro in KI-gestützte Automatisierungslösungen.',
|
||||
'company' => 'TechVision Analytics',
|
||||
'industry' => 'IT & Software',
|
||||
'region' => 'Deutschland',
|
||||
'date' => 'Heute, 14:30',
|
||||
'hasImage' => true,
|
||||
'hasPdf' => true,
|
||||
'imageUrl' => 'https://images.unsplash.com/photo-1677442136019-21780ecad995?w=800&h=600&fit=crop',
|
||||
'companyLogo' => 'logo',
|
||||
],
|
||||
[
|
||||
'slug' => 'energiewende-beschleunigt',
|
||||
'title' => 'Energiewende beschleunigt sich: Neue Rekorde bei erneuerbaren Energien',
|
||||
'teaser' => 'Im ersten Quartal 2025 erreicht der Anteil erneuerbarer Energien am Strommix einen historischen Höchststand von 58%. Experten sprechen von einem Wendepunkt.',
|
||||
'company' => 'GreenPower Deutschland',
|
||||
'industry' => 'Energie',
|
||||
'region' => 'Deutschland',
|
||||
'date' => 'Heute, 11:15',
|
||||
'hasImage' => true,
|
||||
'imageUrl' => 'https://images.unsplash.com/photo-1466611653911-95081537e5b7?w=800&h=600&fit=crop',
|
||||
],
|
||||
[
|
||||
'slug' => 'fintech-startup-series-b',
|
||||
'title' => 'FinTech-Startup sichert sich 45 Millionen Euro in Series-B-Runde',
|
||||
'teaser' => 'Das Berliner FinTech-Startup PaymentFlow konnte in einer Series-B-Finanzierungsrunde 45 Millionen Euro einsammeln.',
|
||||
'company' => 'PaymentFlow GmbH',
|
||||
'industry' => 'Finanzen',
|
||||
'region' => 'Berlin',
|
||||
'date' => 'Gestern, 16:45',
|
||||
'hasPdf' => true,
|
||||
'imageUrl' => 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800&h=600&fit=crop',
|
||||
],
|
||||
]
|
||||
];
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<!-- Large Featured Card - Takes 2 columns -->
|
||||
<div class="lg:col-span-2">
|
||||
<x-web.press-release-card
|
||||
:title="$releases[0]['title']"
|
||||
:teaser="$releases[0]['teaser']"
|
||||
:company="$releases[0]['company']"
|
||||
:industry="$releases[0]['industry']"
|
||||
:region="$releases[0]['region']"
|
||||
:date="$releases[0]['date']"
|
||||
:hasImage="$releases[0]['hasImage']"
|
||||
:hasPdf="$releases[0]['hasPdf']"
|
||||
:slug="$releases[0]['slug']"
|
||||
:imageUrl="$releases[0]['imageUrl']"
|
||||
:companyLogo="$releases[0]['companyLogo']"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Two Smaller Featured Cards - Stack in right column -->
|
||||
<div class="flex flex-col gap-6">
|
||||
<x-web.press-release-card
|
||||
:title="$releases[1]['title']"
|
||||
:teaser="$releases[1]['teaser']"
|
||||
:company="$releases[1]['company']"
|
||||
:industry="$releases[1]['industry']"
|
||||
:region="$releases[1]['region']"
|
||||
:date="$releases[1]['date']"
|
||||
:hasImage="$releases[1]['hasImage']"
|
||||
:hasPdf="$releases[1]['hasPdf'] ?? false"
|
||||
:slug="$releases[1]['slug']"
|
||||
:imageUrl="$releases[1]['imageUrl']"
|
||||
:companyLogo="null"
|
||||
/>
|
||||
|
||||
<x-web.press-release-card
|
||||
:title="$releases[2]['title']"
|
||||
:teaser="$releases[2]['teaser']"
|
||||
:company="$releases[2]['company']"
|
||||
:industry="$releases[2]['industry']"
|
||||
:region="$releases[2]['region']"
|
||||
:date="$releases[2]['date']"
|
||||
:hasImage="$releases[2]['hasImage'] ?? false"
|
||||
:hasPdf="$releases[2]['hasPdf']"
|
||||
:slug="$releases[2]['slug']"
|
||||
:imageUrl="$releases[2]['imageUrl']"
|
||||
:companyLogo="null"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -94,20 +94,20 @@ new class extends Component
|
|||
<div wire:loading.class="opacity-60">
|
||||
<header class="flex items-baseline justify-between mb-4 min-h-[34px] flex-wrap gap-3">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] leading-[1.2] text-ink">
|
||||
Aktuelle Meldungen
|
||||
{{ __('web.feed.title') }}
|
||||
</h2>
|
||||
<div class="flex gap-[18px] text-[12.5px] text-ink-3">
|
||||
<button type="button" wire:click="setTimeframe('all')"
|
||||
class="cursor-pointer transition-colors @if ($timeframe === 'all') text-ink border-b-[1.5px] border-brand pb-0.5 @else hover:text-ink @endif">
|
||||
Alle
|
||||
{{ __('web.feed.filter_all') }}
|
||||
</button>
|
||||
<button type="button" wire:click="setTimeframe('today')"
|
||||
class="cursor-pointer transition-colors @if ($timeframe === 'today') text-ink border-b-[1.5px] border-brand pb-0.5 @else hover:text-ink @endif">
|
||||
Heute
|
||||
{{ __('web.feed.filter_today') }}
|
||||
</button>
|
||||
<button type="button" wire:click="setTimeframe('week')"
|
||||
class="cursor-pointer transition-colors @if ($timeframe === 'week') text-ink border-b-[1.5px] border-brand pb-0.5 @else hover:text-ink @endif">
|
||||
Diese Woche
|
||||
{{ __('web.feed.filter_week') }}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -117,55 +117,23 @@ new class extends Component
|
|||
$items = $releases->items();
|
||||
$top = $items[0] ?? null;
|
||||
$rest = array_slice($items, 1);
|
||||
|
||||
$mockFeedItems = [
|
||||
['time' => '13:42', 'date' => '12. Mai', 'category' => 'Tourismus', 'title' => 'Nachhaltiger Tourismus auf Erfolgskurs: Buchungen steigen um 45 %', 'company' => 'GreenTravel Consulting', 'city' => 'Berlin', 'recommended' => true],
|
||||
['time' => '12:55', 'date' => '12. Mai', 'category' => 'Bildung', 'title' => 'Digitalisierung im Bildungssektor: Schulen erhalten 2 Mrd. Euro Förderung', 'company' => 'EduTech Initiative', 'city' => 'Frankfurt'],
|
||||
['time' => '11:20', 'date' => '12. Mai', 'category' => 'Medien', 'title' => 'Medienbranche im Umbruch: Streaming-Dienste überholen klassisches TV', 'company' => 'MediaWatch Analytics', 'city' => 'Hamburg'],
|
||||
['time' => '10:48', 'date' => '12. Mai', 'category' => 'Handel', 'title' => 'Einzelhandel setzt auf KI: Personalisierte Shopping-Erlebnisse werden Standard', 'company' => 'RetailTech Innovations', 'city' => 'Köln', 'recommended' => true],
|
||||
['time' => '09:33', 'date' => '12. Mai', 'category' => 'Gesundheit', 'title' => 'Telemedizin-Boom: 3 Millionen Online-Sprechstunden im letzten Quartal', 'company' => 'HealthConnect Digital', 'city' => 'Stuttgart'],
|
||||
['time' => '08:15', 'date' => '12. Mai', 'category' => 'Mobilität', 'title' => 'E-Mobilität: Ladeinfrastruktur wächst um 38 % gegenüber Vorjahr', 'company' => 'eMobility Verband', 'city' => 'Düsseldorf'],
|
||||
];
|
||||
|
||||
$inFeedAd = [
|
||||
'time' => '12:14',
|
||||
'date' => now()->translatedFormat('j. M'),
|
||||
'category' => 'Cloud · Software',
|
||||
'title' => 'Microsoft Azure: Neue EU-Region Frankfurt mit DSGVO-zertifizierter KI-Infrastruktur',
|
||||
'company' => 'Microsoft Deutschland GmbH',
|
||||
];
|
||||
@endphp
|
||||
|
||||
@if ($top)
|
||||
<x-web.feed-top-item :release="$top" />
|
||||
@else
|
||||
<x-web.feed-top-item />
|
||||
<p class="py-8 text-[13.5px] text-ink-3 m-0">{{ __('web.feed.empty') }}</p>
|
||||
@endif
|
||||
|
||||
@if (! empty($rest))
|
||||
@foreach ($rest as $i => $release)
|
||||
<x-web.feed-item :release="$release" :recommended="in_array($i, [0, 3], true)" />
|
||||
@endforeach
|
||||
@else
|
||||
@foreach ($mockFeedItems as $mock)
|
||||
<x-web.feed-item :mock="$mock" />
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
<x-web.feed-ad :ad="$inFeedAd" />
|
||||
@foreach ($rest as $release)
|
||||
<x-web.feed-item :release="$release" :recommended="($release->content_score ?? 0) >= 80" />
|
||||
@endforeach
|
||||
|
||||
@if ($releases->hasMorePages())
|
||||
<div class="flex justify-center mt-7">
|
||||
<a href="{{ $releases->nextPageUrl() }}"
|
||||
class="inline-flex items-center gap-2 px-[18px] py-2.5 text-[13px] font-semibold text-ink bg-transparent border border-bg-rule-strong rounded-[2px] cursor-pointer hover:bg-ink hover:text-bg transition-colors">
|
||||
Weitere {{ number_format(max(0, $totalCount - $releases->currentPage() * $releases->perPage()), 0, ',', '.') }} Meldungen anzeigen →
|
||||
</a>
|
||||
</div>
|
||||
@elseif (! $top)
|
||||
<div class="flex justify-center mt-7">
|
||||
<a href="{{ route('web.home') }}"
|
||||
class="inline-flex items-center gap-2 px-[18px] py-2.5 text-[13px] font-semibold text-ink bg-transparent border border-bg-rule-strong rounded-[2px] cursor-pointer hover:bg-ink hover:text-bg transition-colors">
|
||||
Zur Startseite →
|
||||
{{ __('web.feed.more', ['count' => number_format(max(0, $totalCount - $releases->currentPage() * $releases->perPage()), 0, ',', '.')]) }} →
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -1,187 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new class extends Component {
|
||||
public function with(): array
|
||||
{
|
||||
// Mock-Daten für Press Releases Grid
|
||||
return [
|
||||
'releases' => [
|
||||
[
|
||||
'slug' => 'ki-revolution-deutsche-unternehmen',
|
||||
'title' => 'KI-Revolution: Deutsche Unternehmen investieren Milliarden in Automatisierung',
|
||||
'teaser' => 'Eine neue Studie zeigt: Unternehmen im DACH-Raum planen für 2025 Investitionen in Höhe von über 15 Milliarden Euro in KI-gestützte Automatisierungslösungen.',
|
||||
'company' => 'TechVision Analytics',
|
||||
'industry' => 'IT & Digitalisierung',
|
||||
'region' => 'Deutschland',
|
||||
'date' => '17. Okt 2024',
|
||||
'contentType' => 'ANALYSE',
|
||||
'hasImage' => true,
|
||||
'hasPdf' => true,
|
||||
'imageUrl' => 'https://images.unsplash.com/photo-1677442136019-21780ecad995?w=800&h=600&fit=crop',
|
||||
'companyLogo' => 'logo',
|
||||
],
|
||||
[
|
||||
'slug' => 'energiewende-beschleunigt',
|
||||
'title' => 'Energiewende beschleunigt sich: Neue Rekorde bei erneuerbaren Energien',
|
||||
'teaser' => 'Im ersten Quartal 2025 erreicht der Anteil erneuerbarer Energien am Strommix einen historischen Höchststand von 58%.',
|
||||
'company' => 'GreenPower Deutschland',
|
||||
'industry' => 'Energie & Umwelt',
|
||||
'region' => 'Deutschland',
|
||||
'date' => '16. Okt 2024',
|
||||
'contentType' => 'FACHMELDUNG',
|
||||
'hasImage' => true,
|
||||
'imageUrl' => 'https://images.unsplash.com/photo-1466611653911-95081537e5b7?w=800&h=600&fit=crop',
|
||||
],
|
||||
[
|
||||
'slug' => 'fintech-startup-series-b',
|
||||
'title' => 'FinTech-Startup sichert sich 45 Millionen Euro in Series-B-Runde',
|
||||
'teaser' => 'Das Berliner FinTech-Startup PaymentFlow konnte in einer Series-B-Finanzierungsrunde 45 Millionen Euro einsammeln.',
|
||||
'company' => 'PaymentFlow GmbH',
|
||||
'industry' => 'Finanzen & Versicherungen',
|
||||
'region' => 'Berlin',
|
||||
'date' => '15. Okt 2024',
|
||||
'contentType' => 'FACHMELDUNG',
|
||||
'hasPdf' => true,
|
||||
'imageUrl' => null,
|
||||
],
|
||||
[
|
||||
'slug' => 'gesundheitsbranche-digital',
|
||||
'title' => 'Gesundheitsbranche setzt verstärkt auf digitale Lösungen',
|
||||
'teaser' => 'Telemedizin und KI-gestützte Diagnostik werden zum Standard: 78% der Krankenhäuser in Deutschland planen Investitionen.',
|
||||
'company' => 'MediTech Solutions',
|
||||
'industry' => 'Medizin & Gesundheit',
|
||||
'region' => 'München',
|
||||
'date' => '14. Okt 2024',
|
||||
'contentType' => 'INTERVIEW',
|
||||
'hasImage' => true,
|
||||
'imageUrl' => 'https://images.unsplash.com/photo-1576091160399-112ba8d25d1d?w=800&h=600&fit=crop',
|
||||
],
|
||||
[
|
||||
'slug' => 'automobilindustrie-transformation',
|
||||
'title' => 'Automobilindustrie: Transformation zur E-Mobilität nimmt Fahrt auf',
|
||||
'teaser' => 'Führende Automobilhersteller kündigen massive Investitionen in E-Mobilität an. Bis 2030 sollen 80% der Neufahrzeuge elektrisch sein.',
|
||||
'company' => 'Auto Industry Report',
|
||||
'industry' => 'Mobilität & Logistik',
|
||||
'region' => 'Stuttgart',
|
||||
'date' => '13. Okt 2024',
|
||||
'contentType' => 'ANALYSE',
|
||||
'hasImage' => true,
|
||||
'hasPdf' => true,
|
||||
'imageUrl' => 'https://images.unsplash.com/photo-1593941707882-a5bba14938c7?w=800&h=600&fit=crop',
|
||||
],
|
||||
[
|
||||
'slug' => 'cybersecurity-massnahmen',
|
||||
'title' => 'Cybersecurity: Unternehmen verstärken Schutzmaßnahmen gegen Hackerangriffe',
|
||||
'teaser' => 'Nach einer Serie von Cyberattacken erhöhen deutsche Unternehmen ihre Investitionen in IT-Sicherheit um durchschnittlich 35%.',
|
||||
'company' => 'CyberSafe Europe',
|
||||
'industry' => 'IT & Digitalisierung',
|
||||
'region' => 'Frankfurt',
|
||||
'date' => '12. Okt 2024',
|
||||
'contentType' => 'FACHMELDUNG',
|
||||
'hasImage' => true,
|
||||
'imageUrl' => 'https://images.unsplash.com/photo-1550751827-4bd374c3f58b?w=800&h=600&fit=crop',
|
||||
],
|
||||
// Neue Artikel ohne Bild für verschiedene Branchen
|
||||
[
|
||||
'slug' => 'immobilienmarkt-trendwende',
|
||||
'title' => 'Immobilienmarkt 2025: Experten prognostizieren Trendwende bei Kaufpreisen',
|
||||
'teaser' => 'Nach Jahren steigender Preise zeigen aktuelle Analysen eine Stabilisierung des Immobilienmarktes. Besonders Metropolregionen verzeichnen erstmals rückläufige Tendenzen.',
|
||||
'company' => 'ImmoConsult Deutschland',
|
||||
'industry' => 'Bauen & Immobilien',
|
||||
'region' => 'Hamburg',
|
||||
'date' => '11. Okt 2024',
|
||||
'contentType' => 'ANALYSE',
|
||||
'hasImage' => false,
|
||||
'hasPdf' => true,
|
||||
'imageUrl' => null,
|
||||
],
|
||||
[
|
||||
'slug' => 'tourismus-nachhaltigkeit',
|
||||
'title' => 'Nachhaltiger Tourismus auf Erfolgskurs: Buchungen steigen um 45%',
|
||||
'teaser' => 'Öko-Hotels und klimaneutrales Reisen liegen im Trend. Die Reisebranche verzeichnet einen deutlichen Wandel im Buchungsverhalten deutscher Urlauber.',
|
||||
'company' => 'GreenTravel Consulting',
|
||||
'industry' => 'Tourismus & Kultur',
|
||||
'region' => 'Österreich',
|
||||
'date' => '10. Okt 2024',
|
||||
'contentType' => 'FACHMELDUNG',
|
||||
'hasImage' => false,
|
||||
'imageUrl' => null,
|
||||
],
|
||||
[
|
||||
'slug' => 'bildungssektor-digitalisierung',
|
||||
'title' => 'Digitalisierung im Bildungssektor: Schulen erhalten 2 Milliarden Euro Förderung',
|
||||
'teaser' => 'Bundesweites Digitalpakt-Programm wird ausgeweitet. Schwerpunkt liegt auf KI-gestützten Lernplattformen und moderner IT-Infrastruktur für Schulen.',
|
||||
'company' => 'EduTech Initiative',
|
||||
'industry' => 'Wissenschaft & Forschung',
|
||||
'region' => 'Berlin',
|
||||
'date' => '09. Okt 2024',
|
||||
'contentType' => 'FACHMELDUNG',
|
||||
'hasImage' => false,
|
||||
'hasPdf' => true,
|
||||
'imageUrl' => null,
|
||||
],
|
||||
[
|
||||
'slug' => 'medienbranche-streaming',
|
||||
'title' => 'Medienbranche im Umbruch: Streaming-Dienste überholen klassisches TV',
|
||||
'teaser' => 'Erstmals nutzen mehr Deutsche regelmäßig Streaming-Plattformen als lineares Fernsehen. Die Werbebudgets verschieben sich entsprechend.',
|
||||
'company' => 'MediaWatch Analytics',
|
||||
'industry' => 'Marketing, PR & Medien',
|
||||
'region' => 'Köln',
|
||||
'date' => '08. Okt 2024',
|
||||
'contentType' => 'ANALYSE',
|
||||
'hasImage' => false,
|
||||
'imageUrl' => null,
|
||||
],
|
||||
[
|
||||
'slug' => 'einzelhandel-ki-revolution',
|
||||
'title' => 'Einzelhandel setzt auf KI: Personalisierte Shopping-Erlebnisse werden Standard',
|
||||
'teaser' => 'Künstliche Intelligenz revolutioniert den Handel. Von virtuellen Anproben bis zu KI-gestützter Beratung – der Einzelhandel wird digital.',
|
||||
'company' => 'RetailTech Innovations',
|
||||
'industry' => 'Handel & E-Commerce',
|
||||
'region' => 'Düsseldorf',
|
||||
'date' => '07. Okt 2024',
|
||||
'contentType' => 'INTERVIEW',
|
||||
'hasImage' => false,
|
||||
'hasPdf' => true,
|
||||
'imageUrl' => null,
|
||||
],
|
||||
[
|
||||
'slug' => 'gesundheit-telemedizin-boom',
|
||||
'title' => 'Telemedizin-Boom: 3 Millionen Online-Sprechstunden im letzten Quartal',
|
||||
'teaser' => 'Die Akzeptanz von Videosprechstunden erreicht neue Höchststände. Besonders ländliche Regionen profitieren von der digitalen Gesundheitsversorgung.',
|
||||
'company' => 'HealthConnect Digital',
|
||||
'industry' => 'Medizin & Gesundheit',
|
||||
'region' => 'Schweiz',
|
||||
'date' => '06. Okt 2024',
|
||||
'contentType' => 'FACHMELDUNG',
|
||||
'hasImage' => false,
|
||||
'imageUrl' => null,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div>
|
||||
@php
|
||||
$theme = config('app.theme', 'businessportal24');
|
||||
$isPresseecho = $theme === 'presseecho';
|
||||
@endphp
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach ($releases as $release)
|
||||
@if ($isPresseecho)
|
||||
{{-- Presseecho: Hochwertige Magazine-Card --}}
|
||||
<x-web.press-release-card :title="$release['title']" :teaser="$release['teaser']" :company="$release['company']" :industry="$release['industry']"
|
||||
:region="$release['region']" :date="$release['date']" :contentType="$release['contentType'] ?? 'FACHMELDUNG'" :slug="$release['slug']" :imageUrl="$release['imageUrl'] ?? null" />
|
||||
@else
|
||||
{{-- Businessportal24: Standard-Card --}}
|
||||
<x-web.press-release-card :title="$release['title']" :teaser="$release['teaser']" :company="$release['company']" :industry="$release['industry']"
|
||||
:region="$release['region']" :date="$release['date']" :hasImage="$release['hasImage'] ?? false" :hasPdf="$release['hasPdf'] ?? false" :slug="$release['slug']"
|
||||
:imageUrl="$release['imageUrl'] ?? null" :companyLogo="$release['companyLogo'] ?? null" />
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -165,7 +165,7 @@ new class extends Component
|
|||
<div wire:loading.class="opacity-60" class="transition-opacity">
|
||||
{{-- Suchfeld --}}
|
||||
<form wire:submit.prevent class="mb-6">
|
||||
<label class="sr-only" for="search-input">Pressemitteilungen durchsuchen</label>
|
||||
<label class="sr-only" for="search-input">{{ __('web.header.search_title') }}</label>
|
||||
<div class="flex items-center gap-3 px-4 py-3.5 border border-bg-rule-strong bg-bg-elev focus-within:border-brand transition-colors">
|
||||
<svg width="20" height="20" viewBox="0 0 16 16" fill="none" class="flex-shrink-0 text-ink-3" aria-hidden="true">
|
||||
<circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="1.5" />
|
||||
|
|
@ -173,14 +173,14 @@ new class extends Component
|
|||
</svg>
|
||||
<input id="search-input" type="search"
|
||||
wire:model.live.debounce.400ms="q"
|
||||
placeholder="Suchbegriff, Unternehmen oder Branche…"
|
||||
placeholder="{{ __('web.search.placeholder') }}"
|
||||
autocomplete="off"
|
||||
class="flex-1 bg-transparent border-0 p-0 text-[15px] text-ink placeholder:text-ink-3 focus:outline-none focus:ring-0 min-w-0">
|
||||
<span wire:loading wire:target="q" class="font-mono text-[11px] text-ink-3 whitespace-nowrap">sucht…</span>
|
||||
<span wire:loading wire:target="q" class="font-mono text-[11px] text-ink-3 whitespace-nowrap">{{ __('web.search.searching') }}</span>
|
||||
@if ($q !== '' || $category !== '')
|
||||
<button type="button" wire:click="clearFilters"
|
||||
class="flex-shrink-0 inline-flex items-center justify-center w-7 h-7 text-ink-3 hover:text-ink transition-colors"
|
||||
aria-label="Suche zurücksetzen">
|
||||
aria-label="{{ __('web.search.reset') }}">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M6 6l12 12M6 18L18 6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" />
|
||||
</svg>
|
||||
|
|
@ -197,7 +197,7 @@ new class extends Component
|
|||
'border-brand bg-brand text-white' => $category === '',
|
||||
'border-bg-rule-strong text-ink hover:bg-bg-elev' => $category !== '',
|
||||
])>
|
||||
Alle Rubriken
|
||||
{{ __('web.search.all_categories') }}
|
||||
</button>
|
||||
@foreach ($categories as $cat)
|
||||
<button type="button" wire:click="selectCategory('{{ $cat['slug'] }}')" @class([
|
||||
|
|
@ -216,18 +216,18 @@ new class extends Component
|
|||
<div class="flex items-baseline justify-between gap-3 flex-wrap mb-1">
|
||||
<h2 class="font-serif text-[22px] font-semibold m-0 tracking-[-0.3px] text-ink">
|
||||
{{ number_format($results->total(), 0, ',', '.') }}
|
||||
{{ $results->total() === 1 ? 'Ergebnis' : 'Ergebnisse' }}
|
||||
{{ trans_choice('web.search.results', $results->total()) }}
|
||||
@if ($term !== '')
|
||||
<span class="text-ink-3 font-normal">für „{{ $term }}"</span>
|
||||
<span class="text-ink-3 font-normal">{{ __('web.search.for_term', ['term' => $term]) }}</span>
|
||||
@endif
|
||||
</h2>
|
||||
<label class="flex items-center gap-2 text-[12.5px] text-ink-3">
|
||||
<span class="whitespace-nowrap">Sortierung</span>
|
||||
<span class="whitespace-nowrap">{{ __('web.search.sort') }}</span>
|
||||
<select wire:model.live="sort"
|
||||
class="border border-bg-rule-strong bg-bg-elev text-ink text-[12.5px] py-1.5 pl-2.5 pr-7 focus:outline-none focus:border-brand">
|
||||
<option value="newest">Neueste zuerst</option>
|
||||
<option value="oldest">Älteste zuerst</option>
|
||||
<option value="most-read">Meistgelesen</option>
|
||||
<option value="newest">{{ __('web.search.sort_newest') }}</option>
|
||||
<option value="oldest">{{ __('web.search.sort_oldest') }}</option>
|
||||
<option value="most-read">{{ __('web.most_read.title') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -237,22 +237,22 @@ new class extends Component
|
|||
<x-web.feed-item :release="$release" />
|
||||
@empty
|
||||
<div class="border border-bg-rule bg-bg-elev px-6 py-12 text-center mt-4">
|
||||
<h3 class="font-serif text-[20px] font-semibold text-ink m-0 mb-2">Keine Treffer</h3>
|
||||
<h3 class="font-serif text-[20px] font-semibold text-ink m-0 mb-2">{{ __('web.search.no_results') }}</h3>
|
||||
<p class="text-[13px] text-ink-3 m-0 mb-5 max-w-md mx-auto">
|
||||
Für Ihre Suche gibt es derzeit keine Pressemitteilungen. Versuchen Sie einen anderen Begriff oder entfernen Sie Filter.
|
||||
{{ __('web.search.no_results_text') }}
|
||||
</p>
|
||||
<button type="button" wire:click="clearFilters"
|
||||
class="inline-flex items-center gap-2 px-4 py-2.5 text-[13px] font-semibold border border-bg-rule-strong bg-white text-ink hover:bg-bg-elev transition-colors">
|
||||
Suche zurücksetzen
|
||||
{{ __('web.search.reset') }}
|
||||
</button>
|
||||
</div>
|
||||
@endforelse
|
||||
|
||||
@if ($results->hasPages())
|
||||
<nav class="mt-7 flex items-center justify-center gap-1.5" aria-label="Seitennummerierung">
|
||||
<nav class="mt-7 flex items-center justify-center gap-1.5" aria-label="{{ __('web.search.pagination') }}">
|
||||
<button type="button" wire:click="previousPage" @disabled($results->onFirstPage())
|
||||
class="inline-flex items-center justify-center w-9 h-9 border border-bg-rule-strong text-ink disabled:opacity-40 disabled:cursor-not-allowed hover:bg-bg-elev transition-colors"
|
||||
aria-label="Vorherige Seite">
|
||||
aria-label="{{ __('web.search.previous_page') }}">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M15 19l-7-7 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
@foreach ($results->getUrlRange(max(1, $results->currentPage() - 2), min($results->lastPage(), $results->currentPage() + 2)) as $page => $url)
|
||||
|
|
@ -264,7 +264,7 @@ new class extends Component
|
|||
@endforeach
|
||||
<button type="button" wire:click="nextPage" @disabled(! $results->hasMorePages())
|
||||
class="inline-flex items-center justify-center w-9 h-9 border border-bg-rule-strong text-ink disabled:opacity-40 disabled:cursor-not-allowed hover:bg-bg-elev transition-colors"
|
||||
aria-label="Nächste Seite">
|
||||
aria-label="{{ __('web.search.next_page') }}">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M9 5l7 7-7 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
|
@ -278,15 +278,14 @@ new class extends Component
|
|||
<path d="M11 11l3 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="font-serif text-[22px] font-semibold text-ink m-0 mb-2">Durchsuchen Sie das Archiv</h2>
|
||||
<h2 class="font-serif text-[22px] font-semibold text-ink m-0 mb-2">{{ __('web.search.discover_title') }}</h2>
|
||||
<p class="text-[13.5px] text-ink-3 m-0 max-w-lg mx-auto">
|
||||
{{ number_format($archiveTotal, 0, ',', '.') }} geprüfte Pressemitteilungen — durchsuchbar nach Stichwort,
|
||||
Unternehmen und Branche. Geben Sie oben einen Suchbegriff ein oder wählen Sie eine Rubrik.
|
||||
{{ __('web.search.discover_text', ['count' => number_format($archiveTotal, 0, ',', '.')]) }}
|
||||
</p>
|
||||
|
||||
@if ($categories->isNotEmpty())
|
||||
<div class="mt-7">
|
||||
<div class="eyebrow muted text-[10px] mb-3">Beliebte Rubriken</div>
|
||||
<div class="eyebrow muted text-[10px] mb-3">{{ __('web.search.popular_categories') }}</div>
|
||||
<div class="flex flex-wrap gap-2 justify-center">
|
||||
@foreach ($categories->take(8) as $cat)
|
||||
<button type="button" wire:click="selectCategory('{{ $cat['slug'] }}')"
|
||||
|
|
|
|||
|
|
@ -4,8 +4,13 @@
|
|||
$brand = config('domains.domains.businessportal24.brand', []);
|
||||
@endphp
|
||||
|
||||
@section('title', $brand['meta_title'] ?? 'Aktuelle Pressemitteilungen aus der deutschen Wirtschaft – businessportal24')
|
||||
@section('meta_description', $brand['meta_description'] ?? 'Pressemitteilungen aus Deutschland, Österreich und der Schweiz. Redaktionell geprüft. Strukturiert distribuiert.')
|
||||
{{-- DE-Meta kommt brand-spezifisch aus config/domains.php, EN aus den Übersetzungsdateien. --}}
|
||||
@section('title', app()->getLocale() === 'de'
|
||||
? ($brand['meta_title'] ?? 'Aktuelle Pressemitteilungen aus der deutschen Wirtschaft – businessportal24')
|
||||
: __('web.meta.home_title', ['brand' => ($brand['name'] ?? 'businessportal').($brand['accent'] ?? '')]))
|
||||
@section('meta_description', app()->getLocale() === 'de'
|
||||
? ($brand['meta_description'] ?? 'Pressemitteilungen aus Deutschland, Österreich und der Schweiz. Qualitätsgeprüft. Strukturiert distribuiert.')
|
||||
: __('web.meta.home_description'))
|
||||
|
||||
@section('hreflang')
|
||||
<link rel="alternate" hreflang="de" href="{{ url('/de') }}">
|
||||
|
|
@ -14,7 +19,7 @@
|
|||
@endsection
|
||||
|
||||
@section('content')
|
||||
<x-web.site-header :ticker="$tickerItems ?? []" />
|
||||
<x-web.site-header />
|
||||
|
||||
<main class="bg-bg text-ink">
|
||||
<x-web.focus-hero
|
||||
|
|
@ -42,7 +47,9 @@
|
|||
|
||||
<x-web.pricing-teaser />
|
||||
|
||||
<x-web.newsletter-strip />
|
||||
{{-- Newsletter-Sektion bewusst entfernt (Double-Opt-in nachgelagert,
|
||||
siehe dev/release/entwicklungsplan-release.md) — stattdessen Veröffentlichen-CTA. --}}
|
||||
<x-web.publish-band />
|
||||
|
||||
<x-web.quality-summary :stats="$homeStats ?? []" />
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
[
|
||||
'title' => 'Pressemitteilungen & Inhalte',
|
||||
'items' => [
|
||||
['q' => 'Welche Dateiformate werden unterstützt?', 'a' => 'Bilder: JPG, PNG, WebP (max. 16 MB). Anhänge: PDF, Word, Excel, PowerPoint, ZIP und TXT (max. 25 MB je Datei).'],
|
||||
['q' => 'Welche Dateiformate werden unterstützt?', 'a' => 'Bilder: JPG, PNG, WebP (max. 16 MB im Editor; über die API max. 8 MB). Anhänge: PDF, Word, Excel, PowerPoint, ZIP und TXT (max. 25 MB je Datei).'],
|
||||
['q' => 'Kann ich eine Pressemitteilung nach Veröffentlichung korrigieren?', 'a' => 'Ja — wir korrigieren statt zu löschen: Die URL Ihrer Mitteilung bleibt erhalten, Verweise und Verlinkungen bestehen weiter. Wenden Sie sich mit Korrekturwünschen an unseren Support.'],
|
||||
['q' => 'Wie optimiere ich meine Pressemitteilung für SEO?', 'a' => 'Nutzen Sie aussagekräftige Headlines, integrieren Sie relevante Keywords natürlich, hinterlegen Sie Bildtitel und strukturieren Sie den Text mit Zwischenüberschriften.'],
|
||||
['q' => 'Was passiert, wenn eine Mitteilung abgelehnt wird?', 'a' => 'Sie erhalten die Ablehnung mit Begründung und können die Mitteilung überarbeiten und erneut einreichen. Ein abgelehnter Beitrag verbraucht kein Kontingent.'],
|
||||
|
|
|
|||
|
|
@ -3,16 +3,14 @@
|
|||
@php
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
\Carbon\Carbon::setLocale('de');
|
||||
|
||||
$catTranslation = $category->translations->firstWhere('locale', 'de') ?? $category->translations->first();
|
||||
$catName = $catTranslation?->name ?? 'Branche';
|
||||
$catTranslation = $category->translations->firstWhere('locale', app()->getLocale()) ?? $category->translations->first();
|
||||
$catName = $catTranslation?->name ?? __('web.category.fallback_name');
|
||||
$catSlug = $catTranslation?->slug;
|
||||
$catDescription = filled($catTranslation?->description)
|
||||
? $catTranslation->description
|
||||
: 'Aktuelle Pressemitteilungen aus dem Bereich '.$catName.' – redaktionell geprüft und strukturiert distribuiert im DACH-Raum.';
|
||||
: __('web.category.default_description', ['category' => $catName]);
|
||||
|
||||
$parentTranslation = $category->parent?->translations->firstWhere('locale', 'de') ?? $category->parent?->translations->first();
|
||||
$parentTranslation = $category->parent?->translations->firstWhere('locale', app()->getLocale()) ?? $category->parent?->translations->first();
|
||||
$parentName = $parentTranslation?->name;
|
||||
$parentHref = $parentTranslation?->slug ? route('kategorie', ['slug' => $parentTranslation->slug]) : null;
|
||||
|
||||
|
|
@ -25,7 +23,7 @@
|
|||
|
||||
$leadImage = $leadRelease?->images->first();
|
||||
$leadImageUrl = $leadImage?->variantUrl('detail') ?? $leadImage?->variantUrl('card') ?? $leadImage?->url();
|
||||
$leadCat = $leadRelease?->category?->translations->firstWhere('locale', 'de') ?? $leadRelease?->category?->translations->first();
|
||||
$leadCat = $leadRelease?->category?->translations->firstWhere('locale', app()->getLocale()) ?? $leadRelease?->category?->translations->first();
|
||||
$leadCompany = $leadRelease?->company?->name;
|
||||
$leadLead = $leadRelease
|
||||
? (filled($leadRelease->subtitle)
|
||||
|
|
@ -36,7 +34,7 @@
|
|||
$leadReadTime = max(1, (int) round($leadWords / 200));
|
||||
@endphp
|
||||
|
||||
@section('title', 'Pressemitteilungen ' . $catName . ' – ' . config('app.name'))
|
||||
@section('title', __('web.category.meta_title', ['category' => $catName, 'brand' => config('app.name')]))
|
||||
@section('meta_description', Str::limit(strip_tags($catDescription), 155))
|
||||
|
||||
@section('content')
|
||||
|
|
@ -46,7 +44,7 @@
|
|||
{{-- Breadcrumb --}}
|
||||
<div class="border-b border-bg-rule">
|
||||
<div class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 py-2.5 flex items-center gap-2 text-[11.5px] text-ink-3">
|
||||
<a href="{{ route('web.home') }}" class="hover:text-ink transition-colors">Startseite</a>
|
||||
<a href="{{ route('web.home') }}" class="hover:text-ink transition-colors">{{ __('web.nav.home') }}</a>
|
||||
@if ($parentName)
|
||||
<span class="text-ink-4" aria-hidden="true">/</span>
|
||||
<a href="{{ $parentHref }}" class="hover:text-ink transition-colors">{{ $parentName }}</a>
|
||||
|
|
@ -59,10 +57,10 @@
|
|||
{{-- Masthead --}}
|
||||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 pt-8 lg:pt-9">
|
||||
<div class="flex items-center justify-between gap-4 mb-4">
|
||||
<div class="eyebrow">Branche</div>
|
||||
<a href="{{ route('web.home') }}" class="inline-flex items-center gap-1.5 text-[12px] font-semibold text-ink-3 hover:text-ink transition-colors whitespace-nowrap" aria-label="Zurück zu allen Meldungen">
|
||||
<div class="eyebrow">{{ __('web.category.eyebrow') }}</div>
|
||||
<a href="{{ route('web.home') }}" class="inline-flex items-center gap-1.5 text-[12px] font-semibold text-ink-3 hover:text-ink transition-colors whitespace-nowrap" aria-label="{{ __('web.category.back_aria') }}">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M15 19l-7-7 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
Alle Meldungen
|
||||
{{ __('web.category.all_releases') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -72,12 +70,12 @@
|
|||
<p class="font-serif text-[16px] leading-[1.55] m-0 mt-4 max-w-[680px] text-ink-2">{{ $catDescription }}</p>
|
||||
<div class="mt-4 text-[11.5px] text-ink-3 flex items-center gap-2.5 flex-wrap">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-brand pulse-dot"></span>
|
||||
{{ $fmt($categoryStats['month'] ?? 0) }} Meldungen in den letzten 30 Tagen · {{ $fmt($categoryStats['today'] ?? 0) }} am {{ $referenceNow->translatedFormat('j. M Y') }}
|
||||
{{ __('web.category.month_stat', ['count' => $fmt($categoryStats['month'] ?? 0), 'today' => $fmt($categoryStats['today'] ?? 0), 'date' => $referenceNow->translatedFormat('j. M Y')]) }}
|
||||
</div>
|
||||
|
||||
@if ($subCategories->isNotEmpty())
|
||||
<div class="flex flex-wrap items-center gap-2 mt-5 pt-5 border-t border-bg-rule">
|
||||
<span class="eyebrow muted text-[10px] mr-1.5">Sub-Themen</span>
|
||||
<span class="eyebrow muted text-[10px] mr-1.5">{{ __('web.category.sub_topics') }}</span>
|
||||
@foreach ($subCategories as $sub)
|
||||
<a href="{{ $sub['href'] }}" class="inline-flex items-baseline gap-[7px] px-3 py-1.5 text-[12px] font-medium border border-bg-rule-strong bg-bg text-ink hover:bg-white hover:border-brand transition-colors">
|
||||
{{ $sub['name'] }}
|
||||
|
|
@ -93,26 +91,26 @@
|
|||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 mt-6">
|
||||
<div class="grid grid-cols-2 lg:grid-cols-4 border-y border-bg-rule">
|
||||
<div class="px-6 py-5 border-r border-bg-rule">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Meldungen aktueller Tag</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.category.stat_today') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($categoryStats['today'] ?? 0) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">Stand {{ $referenceNow->translatedFormat('j. M Y') }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.spotlight.stat_window_sub', ['date' => $referenceNow->translatedFormat('j. M Y')]) }}</div>
|
||||
</div>
|
||||
<div class="px-6 py-5 lg:border-r border-bg-rule">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Letzte 7 Tage</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.industry_index.window') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($categoryStats['week'] ?? 0) }}</div>
|
||||
<div class="text-[11.5px] mt-2 {{ $weekDelta >= 0 ? 'text-gain' : 'text-loss' }} font-semibold">
|
||||
{{ $weekDelta >= 0 ? '+' : '' }}{{ $fmt(abs($weekDelta)) }}@if ($weekDeltaPct !== null) ({{ $weekDelta >= 0 ? '+' : '-' }}{{ abs($weekDeltaPct) }} %)@endif <span class="text-ink-3 font-normal">ggü. Vorwoche</span>
|
||||
{{ $weekDelta >= 0 ? '+' : '' }}{{ $fmt(abs($weekDelta)) }}@if ($weekDeltaPct !== null) ({{ $weekDelta >= 0 ? '+' : '-' }}{{ abs($weekDeltaPct) }} %)@endif <span class="text-ink-3 font-normal">{{ __('web.category.vs_previous_week') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 py-5 border-r border-bg-rule border-t lg:border-t-0">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Aktive Unternehmen</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.spotlight.stat_companies') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($categoryStats['activeCompanies'] ?? 0) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">in den letzten 7 Tagen</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.category.last_7_days') }}</div>
|
||||
</div>
|
||||
<div class="px-6 py-5 border-t lg:border-t-0">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Im Archiv</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.spotlight.stat_archive') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($categoryStats['total'] ?? 0) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">Meldungen gesamt</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.spotlight.stat_archive_sub') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -123,12 +121,15 @@
|
|||
<div class="grid gap-9 lg:grid-cols-[1.7fr_1fr]">
|
||||
<article>
|
||||
<a href="{{ route('release.detail', ['slug' => $leadRelease->slug]) }}" class="block group">
|
||||
<div class="relative overflow-hidden bg-hero-grad hatch-dark" style="aspect-ratio: 16 / 10;">
|
||||
{{-- Kein hatch-dark: dessen background-image würde den bg-hero-grad-Verlauf überschreiben. --}}
|
||||
<div class="relative overflow-hidden bg-hero-grad" style="aspect-ratio: 16 / 10;">
|
||||
@if ($leadImageUrl)
|
||||
<img src="{{ $leadImageUrl }}" alt="{{ $leadRelease->title }}" class="absolute inset-0 w-full h-full object-cover" loading="lazy">
|
||||
@else
|
||||
<x-web.hero-fallback />
|
||||
@endif
|
||||
<div class="absolute top-5 left-5 flex gap-2">
|
||||
<span class="bp-tag">Top-Meldung</span>
|
||||
<span class="bp-tag">{{ __('web.hero.top_story') }}</span>
|
||||
</div>
|
||||
<div class="absolute left-0 right-0 bottom-0 px-6 sm:px-9 pb-8 pt-[70px] text-white" style="background:linear-gradient(180deg,transparent,rgba(0,0,0,0.92));">
|
||||
@if ($leadCat)
|
||||
|
|
@ -152,7 +153,7 @@
|
|||
<span class="font-mono">{{ $leadRelease->published_at->translatedFormat('d.m.Y · H:i') }}</span>
|
||||
<span aria-hidden="true">·</span>
|
||||
@endif
|
||||
<span>{{ $leadReadTime }} Min. Lesezeit</span>
|
||||
<span>{{ __('web.shared.read_time', ['minutes' => $leadReadTime]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -160,10 +161,10 @@
|
|||
</article>
|
||||
|
||||
<aside>
|
||||
<div class="eyebrow muted mb-3.5">Weitere Top-Meldungen</div>
|
||||
<div class="eyebrow muted mb-3.5">{{ __('web.category.more_top') }}</div>
|
||||
@forelse ($topReleases as $top)
|
||||
@php
|
||||
$topCat = $top->category?->translations->firstWhere('locale', 'de') ?? $top->category?->translations->first();
|
||||
$topCat = $top->category?->translations->firstWhere('locale', app()->getLocale()) ?? $top->category?->translations->first();
|
||||
@endphp
|
||||
<article class="py-[18px] {{ $loop->first ? 'border-t border-bg-rule-strong' : '' }} border-b border-bg-rule">
|
||||
<a href="{{ route('release.detail', ['slug' => $top->slug]) }}" class="block group">
|
||||
|
|
@ -181,7 +182,7 @@
|
|||
</a>
|
||||
</article>
|
||||
@empty
|
||||
<p class="text-[12.5px] text-ink-3 py-4 border-t border-bg-rule">Aktuell keine weiteren Top-Meldungen.</p>
|
||||
<p class="text-[12.5px] text-ink-3 py-4 border-t border-bg-rule">{{ __('web.category.no_more_top') }}</p>
|
||||
@endforelse
|
||||
|
||||
<div class="mt-[22px]">
|
||||
|
|
@ -196,7 +197,7 @@
|
|||
<div>
|
||||
<header class="flex items-baseline justify-between mb-4 min-h-[34px] flex-wrap gap-3">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">
|
||||
Aktuelle Meldungen <span class="text-[14px] text-ink-3 font-normal ml-1.5">· {{ $catName }}</span>
|
||||
{{ __('web.feed.title') }} <span class="text-[14px] text-ink-3 font-normal ml-1.5">· {{ $catName }}</span>
|
||||
</h2>
|
||||
</header>
|
||||
<hr class="rule-strong">
|
||||
|
|
@ -204,12 +205,12 @@
|
|||
@forelse ($feedReleases as $release)
|
||||
<x-web.feed-item :release="$release" :recommended="$release->content_score !== null && $release->content_score >= 80" />
|
||||
@empty
|
||||
<p class="text-[13px] text-ink-3 py-6">Aktuell keine weiteren Meldungen in dieser Branche.</p>
|
||||
<p class="text-[13px] text-ink-3 py-6">{{ __('web.category.no_more_releases') }}</p>
|
||||
@endforelse
|
||||
|
||||
<div class="flex justify-between items-center mt-7 flex-wrap gap-3">
|
||||
<span class="text-[11.5px] text-ink-3">{{ $fmt($categoryStats['week'] ?? 0) }} Meldungen in den letzten 7 Tagen</span>
|
||||
<a href="{{ route('suche') }}" class="px-3.5 py-2.5 text-[13px] font-semibold border border-bg-rule-strong bg-white text-ink hover:bg-bg-elev transition-colors">Im Archiv suchen →</a>
|
||||
<span class="text-[11.5px] text-ink-3">{{ __('web.category.week_stat', ['count' => $fmt($categoryStats['week'] ?? 0)]) }}</span>
|
||||
<a href="{{ route('suche') }}" class="px-3.5 py-2.5 text-[13px] font-semibold border border-bg-rule-strong bg-white text-ink hover:bg-bg-elev transition-colors">{{ __('web.category.search_archive') }} →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -221,8 +222,8 @@
|
|||
@else
|
||||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 mt-12 mb-4">
|
||||
<div class="border border-bg-rule bg-bg-elev px-6 py-10 text-center">
|
||||
<h2 class="font-serif text-[22px] font-semibold text-ink m-0 mb-2">Noch keine Meldungen in {{ $catName }}</h2>
|
||||
<p class="text-[13px] text-ink-3 m-0">Sobald Pressemitteilungen in dieser Branche veröffentlicht werden, erscheinen sie hier.</p>
|
||||
<h2 class="font-serif text-[22px] font-semibold text-ink m-0 mb-2">{{ __('web.category.empty_title', ['category' => $catName]) }}</h2>
|
||||
<p class="text-[13px] text-ink-3 m-0">{{ __('web.category.empty_text') }}</p>
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
|
@ -230,8 +231,8 @@
|
|||
@if ($subCategories->isNotEmpty())
|
||||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 mt-16">
|
||||
<header class="flex items-baseline justify-between mb-4 flex-wrap gap-3">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">Sub-Branchen <span class="text-[14px] text-ink-3 font-normal ml-1.5">· Volumen letzte 30 Tage</span></h2>
|
||||
<span class="eyebrow muted">7-Tage-Trend</span>
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">{{ __('web.category.sub_industries') }} <span class="text-[14px] text-ink-3 font-normal ml-1.5">· {{ __('web.category.volume_30_days') }}</span></h2>
|
||||
<span class="eyebrow muted">{{ __('web.category.trend_7_days') }}</span>
|
||||
</header>
|
||||
<hr class="rule-strong">
|
||||
@php $subMax = max(1, (int) $subCategories->max('total')); @endphp
|
||||
|
|
@ -244,7 +245,7 @@
|
|||
</div>
|
||||
<div class="flex items-baseline gap-2 mb-2.5">
|
||||
<span class="font-serif text-[24px] font-semibold tracking-[-0.3px] text-ink">{{ $fmt($sub['total']) }}</span>
|
||||
<span class="text-[11px] text-ink-3">Meldungen</span>
|
||||
<span class="text-[11px] text-ink-3">{{ __('web.industry_index.releases') }}</span>
|
||||
</div>
|
||||
<div class="h-[3px] bg-bg-rule"><div class="h-full {{ $sub['delta'] >= 0 ? 'bg-gain' : 'bg-brand' }}" style="width: {{ max(8, (int) round($sub['total'] / $subMax * 100)) }}%;"></div></div>
|
||||
</a>
|
||||
|
|
@ -255,7 +256,7 @@
|
|||
|
||||
@if ($relatedCategories->isNotEmpty())
|
||||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 mt-16">
|
||||
<header class="mb-4"><h2 class="font-serif text-[22px] font-semibold m-0 tracking-[-0.2px] text-ink">Verwandte Branchen</h2></header>
|
||||
<header class="mb-4"><h2 class="font-serif text-[22px] font-semibold m-0 tracking-[-0.2px] text-ink">{{ __('web.category.related') }}</h2></header>
|
||||
<hr class="rule mb-5">
|
||||
<div class="flex flex-wrap gap-2.5">
|
||||
@foreach ($relatedCategories as $related)
|
||||
|
|
@ -273,13 +274,13 @@
|
|||
<div class="bg-topbar-grad text-ink-on-dark p-6 sm:p-9">
|
||||
<div class="grid gap-9 lg:grid-cols-[1.4fr_1fr] lg:items-center">
|
||||
<div>
|
||||
<div class="eyebrow on-dark mb-2.5">Für Unternehmen aus {{ $catName }}</div>
|
||||
<h3 class="font-serif text-[24px] sm:text-[28px] font-semibold m-0 mb-2.5 leading-[1.15] text-white tracking-[-0.3px]">Veröffentlichen Sie Ihre Pressemitteilung gezielt in {{ $catName }}.</h3>
|
||||
<p class="text-[14px] leading-[1.55] m-0 max-w-[540px] text-white/80">Reichweite über aktive Newsrooms · Distribution an Fach- und Wirtschaftsredaktionen · Redaktionell geprüft.</p>
|
||||
<div class="eyebrow on-dark mb-2.5">{{ __('web.category.cta_eyebrow', ['category' => $catName]) }}</div>
|
||||
<h3 class="font-serif text-[24px] sm:text-[28px] font-semibold m-0 mb-2.5 leading-[1.15] text-white tracking-[-0.3px]">{{ __('web.category.cta_title', ['category' => $catName]) }}</h3>
|
||||
<p class="text-[14px] leading-[1.55] m-0 max-w-[540px] text-white/80">{{ __('web.category.cta_text') }}</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2.5">
|
||||
<a href="{{ route('veroeffentlichen') }}" class="w-full inline-flex items-center justify-center px-[18px] py-3.5 text-[14px] font-semibold bg-brand text-white hover:bg-brand-deep transition-colors">Pressemitteilung einreichen →</a>
|
||||
<a href="{{ route('preise') }}" class="w-full inline-flex items-center justify-center px-[18px] py-3 text-[14px] font-semibold text-white border border-white/30 hover:bg-white/10 transition-colors">Tarife & Branchen-Pakete ansehen</a>
|
||||
<a href="{{ route('veroeffentlichen') }}" class="w-full inline-flex items-center justify-center px-[18px] py-3.5 text-[14px] font-semibold bg-brand text-white hover:bg-brand-deep transition-colors">{{ __('web.category.cta_submit') }} →</a>
|
||||
<a href="{{ route('preise') }}" class="w-full inline-flex items-center justify-center px-[18px] py-3 text-[14px] font-semibold text-white border border-white/30 hover:bg-white/10 transition-colors">{{ __('web.category.cta_pricing') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
|
||||
@php
|
||||
$siteName = $domainName ?? config('app.name', 'Laravel');
|
||||
$pageTitle = trim($__env->yieldContent('title')) ?: $siteName;
|
||||
$pageDescription = trim($__env->yieldContent('meta_description')) ?: $siteName;
|
||||
// Inline-@section('title', …)-Werte kommen von Blade bereits escaped an —
|
||||
// einmal dekodieren, damit {{ }} unten nicht doppelt escapt („&amp;").
|
||||
$pageTitle = html_entity_decode(trim($__env->yieldContent('title')), ENT_QUOTES, 'UTF-8') ?: $siteName;
|
||||
$pageDescription = html_entity_decode(trim($__env->yieldContent('meta_description')), ENT_QUOTES, 'UTF-8') ?: $siteName;
|
||||
// Self-Canonical als Default aus der Portal-URL + Request-Pfad (ohne Query):
|
||||
// pro Portal/Domain getrennt und unabhängig von URL::forceRootUrl – verhindert Cross-Portal-Duplicate.
|
||||
$portalBase = rtrim($domainUrl ?? config('app.url'), '/');
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
@endphp
|
||||
|
||||
@section('title', $company->name.' – Newsroom · '.$brandName.$brandAccent)
|
||||
@section('meta_description', Str::limit($boilerplate ?? ('Newsroom von '.$company->name.': alle redaktionell geprüften Pressemitteilungen gebündelt an einem Ort.'), 155))
|
||||
@section('meta_description', Str::limit($boilerplate ?? __('web.newsroom_page.meta_description', ['company' => $company->name]), 155))
|
||||
@section('og_type', 'profile')
|
||||
|
||||
@section('hreflang')
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
{{-- Breadcrumb --}}
|
||||
<div class="border-b border-bg-rule">
|
||||
<div class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 py-2.5 flex items-center gap-2 text-[11.5px] text-ink-3">
|
||||
<a href="{{ route('web.home') }}" class="hover:text-ink transition-colors">Startseite</a>
|
||||
<a href="{{ route('web.home') }}" class="hover:text-ink transition-colors">{{ __('web.nav.home') }}</a>
|
||||
<span class="text-ink-4" aria-hidden="true">/</span>
|
||||
<a href="{{ route('newsrooms') }}" class="hover:text-ink transition-colors">Newsrooms</a>
|
||||
<span class="text-ink-4" aria-hidden="true">/</span>
|
||||
|
|
@ -63,9 +63,9 @@
|
|||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 pt-8 lg:pt-9">
|
||||
<div class="flex items-center justify-between gap-4 mb-4">
|
||||
<div class="eyebrow">Newsroom</div>
|
||||
<a href="{{ route('newsrooms') }}" class="inline-flex items-center gap-1.5 text-[12px] font-semibold text-ink-3 hover:text-ink transition-colors whitespace-nowrap" aria-label="Zurück zu allen Newsrooms">
|
||||
<a href="{{ route('newsrooms') }}" class="inline-flex items-center gap-1.5 text-[12px] font-semibold text-ink-3 hover:text-ink transition-colors whitespace-nowrap" aria-label="{{ __('web.newsroom_page.back_aria') }}">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M15 19l-7-7 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
Alle Newsrooms
|
||||
{{ __('web.newsroom_page.all_newsrooms') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -91,11 +91,11 @@
|
|||
@endif
|
||||
<span class="inline-flex items-center gap-1.5">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-brand pulse-dot"></span>
|
||||
{{ $fmt($newsroomStats['recent'] ?? 0) }} Meldungen in den letzten 30 Tagen
|
||||
{{ __('web.newsroom_page.recent_stat', ['count' => $fmt($newsroomStats['recent'] ?? 0)]) }}
|
||||
</span>
|
||||
@if (! empty($newsroomStats['sinceYear']))
|
||||
<span class="text-ink-4" aria-hidden="true">·</span>
|
||||
<span>Newsroom seit {{ $newsroomStats['sinceYear'] }}</span>
|
||||
<span>{{ __('web.newsroom_page.since', ['year' => $newsroomStats['sinceYear']]) }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
|
||||
@if ($categories->isNotEmpty())
|
||||
<div class="flex flex-wrap items-center gap-2 mt-5 pt-5 border-t border-bg-rule">
|
||||
<span class="eyebrow muted text-[10px] mr-1.5">Branchen</span>
|
||||
<span class="eyebrow muted text-[10px] mr-1.5">{{ __('web.newsroom_page.industries') }}</span>
|
||||
@foreach ($categories as $cat)
|
||||
<a href="{{ $cat['href'] }}" class="inline-flex items-baseline gap-[7px] px-3 py-1.5 text-[12px] font-medium border border-bg-rule-strong bg-bg text-ink hover:bg-white hover:border-brand transition-colors">
|
||||
{{ $cat['name'] }}
|
||||
|
|
@ -123,24 +123,24 @@
|
|||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 mt-6">
|
||||
<div class="grid grid-cols-2 lg:grid-cols-4 border-y border-bg-rule">
|
||||
<div class="px-6 py-5 border-r border-bg-rule">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Meldungen aktueller Tag</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.category.stat_today') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($newsroomStats['today'] ?? 0) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">Stand {{ $referenceNow->translatedFormat('j. M Y') }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.spotlight.stat_window_sub', ['date' => $referenceNow->translatedFormat('j. M Y')]) }}</div>
|
||||
</div>
|
||||
<div class="px-6 py-5 lg:border-r border-bg-rule">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Letzte 30 Tage</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.newsroom_page.last_30_days') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($newsroomStats['recent'] ?? 0) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">veröffentlichte Meldungen</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.newsroom_page.published_releases') }}</div>
|
||||
</div>
|
||||
<div class="px-6 py-5 border-r border-bg-rule border-t lg:border-t-0">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Im Archiv</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.spotlight.stat_archive') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($newsroomStats['total'] ?? 0) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">Meldungen gesamt</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.spotlight.stat_archive_sub') }}</div>
|
||||
</div>
|
||||
<div class="px-6 py-5 border-t lg:border-t-0">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Aufrufe gesamt</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.newsroom_page.total_hits') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($newsroomStats['totalHits'] ?? 0) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">über alle Meldungen</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.newsroom_page.total_hits_sub') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -150,12 +150,15 @@
|
|||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 mt-10">
|
||||
<article>
|
||||
<a href="{{ route('release.detail', ['slug' => $leadRelease->slug]) }}" class="block group">
|
||||
<div class="relative overflow-hidden bg-hero-grad hatch-dark" style="aspect-ratio: 16 / 9;">
|
||||
{{-- Kein hatch-dark: dessen background-image würde den bg-hero-grad-Verlauf überschreiben. --}}
|
||||
<div class="relative overflow-hidden bg-hero-grad" style="aspect-ratio: 16 / 9;">
|
||||
@if ($leadImageUrl)
|
||||
<img src="{{ $leadImageUrl }}" alt="{{ $leadRelease->title }}" class="absolute inset-0 w-full h-full object-cover" loading="lazy">
|
||||
@else
|
||||
<x-web.hero-fallback />
|
||||
@endif
|
||||
<div class="absolute top-5 left-5 flex gap-2">
|
||||
<span class="bp-tag">Aktuellste Meldung</span>
|
||||
<span class="bp-tag">{{ __('web.newsroom_page.latest') }}</span>
|
||||
</div>
|
||||
<div class="absolute left-0 right-0 bottom-0 px-6 sm:px-9 pb-8 pt-[70px] text-white" style="background:linear-gradient(180deg,transparent,rgba(0,0,0,0.92));">
|
||||
@if ($leadCat)
|
||||
|
|
@ -172,7 +175,7 @@
|
|||
<span class="font-mono">{{ $leadRelease->published_at->translatedFormat('d.m.Y · H:i') }}</span>
|
||||
<span aria-hidden="true">·</span>
|
||||
@endif
|
||||
<span>{{ $leadReadTime }} Min. Lesezeit</span>
|
||||
<span>{{ __('web.shared.read_time', ['minutes' => $leadReadTime]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -186,7 +189,7 @@
|
|||
<div>
|
||||
<header class="flex items-baseline justify-between mb-4 min-h-[34px] flex-wrap gap-3">
|
||||
<h2 class="font-serif text-[28px] font-semibold m-0 tracking-[-0.3px] text-ink">
|
||||
Alle Meldungen <span class="text-[14px] text-ink-3 font-normal ml-1.5">· {{ $fmt($newsroomStats['total'] ?? 0) }}</span>
|
||||
{{ __('web.category.all_releases') }} <span class="text-[14px] text-ink-3 font-normal ml-1.5">· {{ $fmt($newsroomStats['total'] ?? 0) }}</span>
|
||||
</h2>
|
||||
</header>
|
||||
<hr class="rule-strong">
|
||||
|
|
@ -194,7 +197,7 @@
|
|||
@forelse ($releases as $release)
|
||||
<x-web.feed-item :release="$release" />
|
||||
@empty
|
||||
<p class="text-[13px] text-ink-3 py-6">Aktuell keine weiteren Meldungen in diesem Newsroom.</p>
|
||||
<p class="text-[13px] text-ink-3 py-6">{{ __('web.newsroom_page.empty') }}</p>
|
||||
@endforelse
|
||||
|
||||
<x-web.pagination :paginator="$releases" />
|
||||
|
|
@ -205,16 +208,16 @@
|
|||
|
||||
{{-- Eigenen Newsroom starten --}}
|
||||
<section class="border border-bg-rule-strong bg-bg-elev p-6">
|
||||
<div class="eyebrow mb-2">Für Unternehmen</div>
|
||||
<div class="eyebrow mb-2">{{ __('web.publisher_cta.eyebrow') }}</div>
|
||||
<h3 class="font-serif text-[20px] font-semibold m-0 mb-2 tracking-[-0.2px] leading-[1.2] text-ink">
|
||||
Eigenen Newsroom starten
|
||||
{{ __('web.newsroom_page.own_title') }}
|
||||
</h3>
|
||||
<p class="text-[12.5px] leading-[1.5] m-0 mb-4 text-ink-2">
|
||||
Bündeln Sie alle Ihre Pressemitteilungen unter einer eigenen Marken-URL – mit Logo, Branchen-Zuordnung und SEO-Vorteil.
|
||||
{{ __('web.newsroom_page.own_text') }}
|
||||
</p>
|
||||
<a href="{{ route('veroeffentlichen') }}"
|
||||
class="w-full inline-flex items-center justify-center gap-2 px-[18px] py-2.5 text-[13px] font-semibold bg-white border border-bg-rule-strong text-ink rounded-[2px] cursor-pointer hover:border-brand hover:text-brand transition-colors">
|
||||
Newsroom anlegen →
|
||||
{{ __('web.newsroom_page.own_cta') }} →
|
||||
</a>
|
||||
</section>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
$fmt = fn ($value) => number_format((int) $value, 0, ',', '.');
|
||||
@endphp
|
||||
|
||||
@section('title', 'Newsrooms – '.$brandName.$brandAccent)
|
||||
@section('meta_description', 'Alle aktiven Unternehmens-Newsrooms im Überblick – redaktionell geprüfte Pressemitteilungen je Unternehmen, gebündelt und dauerhaft auffindbar im DACH-Raum.')
|
||||
@section('title', __('web.newsrooms_page.meta_title', ['brand' => $brandName.$brandAccent]))
|
||||
@section('meta_description', __('web.newsrooms_page.meta_description'))
|
||||
|
||||
@section('hreflang')
|
||||
<link rel="alternate" hreflang="de" href="{{ url('/de/newsrooms') }}">
|
||||
|
|
@ -38,31 +38,31 @@
|
|||
{{-- Masthead --}}
|
||||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 pt-8 lg:pt-10">
|
||||
<div class="max-w-[680px]">
|
||||
<div class="eyebrow mb-3">Unternehmens-Newsrooms</div>
|
||||
<div class="eyebrow mb-3">{{ __('web.newsrooms_page.eyebrow') }}</div>
|
||||
<h1 class="font-serif text-[36px] sm:text-[46px] lg:text-[50px] font-semibold m-0 mb-4 leading-[1.05] tracking-[-1px] text-ink text-balance">
|
||||
Newsrooms
|
||||
</h1>
|
||||
<p class="font-serif text-[16px] leading-[1.55] m-0 text-ink-2">
|
||||
Alle aktiven Unternehmen mit ihren redaktionell geprüften Pressemitteilungen – gebündelt an einem Ort.
|
||||
{{ __('web.newsrooms_page.intro') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{-- Stat strip --}}
|
||||
<div class="grid grid-cols-2 lg:grid-cols-3 border-y border-bg-rule mt-8">
|
||||
<div class="px-6 py-5 border-r border-bg-rule">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Aktive Newsrooms</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.newsrooms.title') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($totalNewsrooms) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">mit veröffentlichten Meldungen</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.newsrooms_page.stat_total_sub') }}</div>
|
||||
</div>
|
||||
<div class="px-6 py-5 lg:border-r border-bg-rule">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Aktiv (30 Tage)</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.newsrooms_page.stat_active') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($activeNewsroomsCount) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">Stand {{ $referenceNow->translatedFormat('j. M Y') }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.spotlight.stat_window_sub', ['date' => $referenceNow->translatedFormat('j. M Y')]) }}</div>
|
||||
</div>
|
||||
<div class="px-6 py-5 border-t lg:border-t-0">
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">Meldungen im Archiv</div>
|
||||
<div class="eyebrow muted text-[10px] mb-2.5">{{ __('web.newsrooms_page.stat_archive') }}</div>
|
||||
<div class="font-serif text-[32px] sm:text-[36px] font-semibold leading-none tracking-[-0.6px] text-ink">{{ $fmt($archiveTotal) }}</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">redaktionell geprüft</div>
|
||||
<div class="text-[11.5px] text-ink-3 mt-2">{{ __('web.newsrooms_page.stat_archive_sub') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -70,18 +70,18 @@
|
|||
{{-- Suche + Verzeichnis --}}
|
||||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-16">
|
||||
<form action="{{ route('newsrooms') }}" method="get" class="mb-7 max-w-[520px]">
|
||||
<label class="sr-only" for="newsroom-search">Newsroom suchen</label>
|
||||
<label class="sr-only" for="newsroom-search">{{ __('web.newsrooms_page.search_label') }}</label>
|
||||
<div class="flex items-center gap-3 px-4 py-3 border border-bg-rule-strong bg-bg-elev focus-within:border-brand transition-colors">
|
||||
<svg width="18" height="18" viewBox="0 0 16 16" fill="none" class="flex-shrink-0 text-ink-3" aria-hidden="true">
|
||||
<circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="1.5" />
|
||||
<path d="M11 11l3 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||
</svg>
|
||||
<input id="newsroom-search" type="search" name="q" value="{{ $term }}"
|
||||
placeholder="Unternehmen suchen…"
|
||||
placeholder="{{ __('web.newsrooms_page.search_placeholder') }}"
|
||||
autocomplete="off"
|
||||
class="flex-1 bg-transparent border-0 p-0 text-[14px] text-ink placeholder:text-ink-3 focus:outline-none focus:ring-0 min-w-0">
|
||||
<button type="submit" class="flex-shrink-0 px-3.5 py-1.5 text-[12.5px] font-semibold bg-brand text-white rounded-[2px] hover:bg-brand-deep transition-colors">
|
||||
Suchen
|
||||
{{ __('web.header.search_submit') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -91,11 +91,11 @@
|
|||
{{ $fmt($newsrooms->total()) }}
|
||||
{{ $newsrooms->total() === 1 ? 'Newsroom' : 'Newsrooms' }}
|
||||
@if ($term !== '')
|
||||
<span class="text-ink-3 font-normal">für „{{ $term }}"</span>
|
||||
<span class="text-ink-3 font-normal">{{ __('web.search.for_term', ['term' => $term]) }}</span>
|
||||
@endif
|
||||
</h2>
|
||||
@if ($term !== '')
|
||||
<a href="{{ route('newsrooms') }}" class="text-[12.5px] text-brand font-semibold hover:text-brand-deep transition-colors">Filter zurücksetzen</a>
|
||||
<a href="{{ route('newsrooms') }}" class="text-[12.5px] text-brand font-semibold hover:text-brand-deep transition-colors">{{ __('web.newsrooms_page.reset_filter') }}</a>
|
||||
@endif
|
||||
</header>
|
||||
<hr class="rule-strong mb-6">
|
||||
|
|
@ -123,18 +123,18 @@
|
|||
<div class="min-w-0 flex-1">
|
||||
<h3 class="font-serif text-[16px] font-semibold leading-[1.25] text-ink group-hover:text-brand transition-colors truncate">{{ $company->name }}</h3>
|
||||
@if ($latest)
|
||||
<div class="text-[11px] text-ink-3 mt-1">Letzte Meldung {{ $latest->translatedFormat('j. M Y') }}</div>
|
||||
<div class="text-[11px] text-ink-3 mt-1">{{ __('web.newsrooms_page.latest_release', ['date' => $latest->translatedFormat('j. M Y')]) }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-auto flex items-center justify-between pt-3 border-t border-bg-rule">
|
||||
<span class="text-[12px] text-ink-2">
|
||||
<span class="font-mono font-semibold text-ink">{{ $fmt($company->releases_count ?? 0) }}</span> Meldungen
|
||||
<span class="font-mono font-semibold text-ink">{{ $fmt($company->releases_count ?? 0) }}</span> {{ __('web.industry_index.releases') }}
|
||||
</span>
|
||||
@if ($isRecentlyActive)
|
||||
<span class="inline-flex items-center gap-1.5 text-[10.5px] font-semibold text-ink-2 px-2 py-0.5 bg-brand/[0.06] border border-brand/20 rounded-[2px]">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-brand"></span>
|
||||
{{ $fmt($company->recent_count) }} / 30 Tage
|
||||
{{ $fmt($company->recent_count) }} / {{ __('web.newsrooms_page.days_30') }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -145,17 +145,17 @@
|
|||
<x-web.pagination :paginator="$newsrooms" />
|
||||
@else
|
||||
<div class="border border-bg-rule bg-bg-elev px-6 py-12 text-center">
|
||||
<h3 class="font-serif text-[20px] font-semibold text-ink m-0 mb-2">Keine Newsrooms gefunden</h3>
|
||||
<h3 class="font-serif text-[20px] font-semibold text-ink m-0 mb-2">{{ __('web.newsrooms_page.none_found') }}</h3>
|
||||
<p class="text-[13px] text-ink-3 m-0 mb-5 max-w-md mx-auto">
|
||||
@if ($term !== '')
|
||||
Für „{{ $term }}" gibt es derzeit keinen passenden Newsroom. Versuchen Sie einen anderen Suchbegriff.
|
||||
{{ __('web.newsrooms_page.none_found_term', ['term' => $term]) }}
|
||||
@else
|
||||
Aktuell sind keine aktiven Newsrooms verfügbar.
|
||||
{{ __('web.newsrooms_page.none_available') }}
|
||||
@endif
|
||||
</p>
|
||||
@if ($term !== '')
|
||||
<a href="{{ route('newsrooms') }}" class="inline-flex items-center gap-2 px-4 py-2.5 text-[13px] font-semibold border border-bg-rule-strong bg-white text-ink hover:bg-bg-elev transition-colors">
|
||||
Alle Newsrooms anzeigen
|
||||
{{ __('web.newsrooms.all') }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -167,13 +167,13 @@
|
|||
<div class="bg-topbar-grad text-ink-on-dark p-6 sm:p-9">
|
||||
<div class="grid gap-9 lg:grid-cols-[1.4fr_1fr] lg:items-center">
|
||||
<div>
|
||||
<div class="eyebrow on-dark mb-2.5">Eigener Newsroom</div>
|
||||
<h3 class="font-serif text-[24px] sm:text-[28px] font-semibold m-0 mb-2.5 leading-[1.15] text-white tracking-[-0.3px]">Bündeln Sie Ihre Pressemitteilungen im eigenen Newsroom.</h3>
|
||||
<p class="text-[14px] leading-[1.55] m-0 max-w-[540px] text-white/80">Eigene Marken-URL · alle Meldungen zentral · SEO-Vorteil für Ihr Unternehmen · redaktionell geprüft.</p>
|
||||
<div class="eyebrow on-dark mb-2.5">{{ __('web.newsrooms_page.cta_eyebrow') }}</div>
|
||||
<h3 class="font-serif text-[24px] sm:text-[28px] font-semibold m-0 mb-2.5 leading-[1.15] text-white tracking-[-0.3px]">{{ __('web.newsrooms_page.cta_title') }}</h3>
|
||||
<p class="text-[14px] leading-[1.55] m-0 max-w-[540px] text-white/80">{{ __('web.newsrooms_page.cta_text') }}</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2.5">
|
||||
<a href="{{ route('veroeffentlichen') }}" class="w-full inline-flex items-center justify-center px-[18px] py-3.5 text-[14px] font-semibold bg-brand text-white hover:bg-brand-deep transition-colors">Pressemitteilung einreichen →</a>
|
||||
<a href="{{ route('preise') }}" class="w-full inline-flex items-center justify-center px-[18px] py-3 text-[14px] font-semibold text-white border border-white/30 hover:bg-white/10 transition-colors">Tarife & Pakete ansehen</a>
|
||||
<a href="{{ route('veroeffentlichen') }}" class="w-full inline-flex items-center justify-center px-[18px] py-3.5 text-[14px] font-semibold bg-brand text-white hover:bg-brand-deep transition-colors">{{ __('web.category.cta_submit') }} →</a>
|
||||
<a href="{{ route('preise') }}" class="w-full inline-flex items-center justify-center px-[18px] py-3 text-[14px] font-semibold text-white border border-white/30 hover:bg-white/10 transition-colors">{{ __('web.publisher_cta.pricing') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -285,8 +285,8 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{{-- Abschluss mit vorhandener Editorial-Komponente. --}}
|
||||
<x-web.newsletter-strip />
|
||||
{{-- Newsletter-Sektion bewusst entfernt: kommt nachgelagert mit echtem
|
||||
Double-Opt-in (siehe dev/release/entwicklungsplan-release.md). --}}
|
||||
</main>
|
||||
|
||||
<x-web.site-footer />
|
||||
|
|
|
|||
|
|
@ -4,8 +4,13 @@
|
|||
$brand = config('domains.domains.presseecho.brand', []);
|
||||
@endphp
|
||||
|
||||
@section('title', $brand['meta_title'] ?? 'Aktuelle Pressemitteilungen aus Wirtschaft & Industrie – presseecho')
|
||||
@section('meta_description', $brand['meta_description'] ?? 'Redaktionell kuratierte Pressemitteilungen aus Wirtschaft und Industrie im DACH-Raum.')
|
||||
{{-- DE-Meta kommt brand-spezifisch aus config/domains.php, EN aus den Übersetzungsdateien. --}}
|
||||
@section('title', app()->getLocale() === 'de'
|
||||
? ($brand['meta_title'] ?? 'Aktuelle Pressemitteilungen aus Wirtschaft & Industrie – presseecho')
|
||||
: __('web.meta.home_title', ['brand' => ($brand['name'] ?? 'presseecho').($brand['accent'] ?? '')]))
|
||||
@section('meta_description', app()->getLocale() === 'de'
|
||||
? ($brand['meta_description'] ?? 'Qualitätsgeprüfte Pressemitteilungen aus Wirtschaft und Industrie im DACH-Raum.')
|
||||
: __('web.meta.home_description'))
|
||||
|
||||
@section('hreflang')
|
||||
<link rel="alternate" hreflang="de" href="{{ url('/de') }}">
|
||||
|
|
@ -14,7 +19,7 @@
|
|||
@endsection
|
||||
|
||||
@section('content')
|
||||
<x-web.site-header :ticker="$tickerItems ?? []" />
|
||||
<x-web.site-header />
|
||||
|
||||
<main class="bg-bg text-ink">
|
||||
<x-web.focus-hero
|
||||
|
|
@ -42,7 +47,9 @@
|
|||
|
||||
<x-web.pricing-teaser />
|
||||
|
||||
<x-web.newsletter-strip />
|
||||
{{-- Newsletter-Sektion bewusst entfernt (Double-Opt-in nachgelagert,
|
||||
siehe dev/release/entwicklungsplan-release.md) — stattdessen Veröffentlichen-CTA. --}}
|
||||
<x-web.publish-band />
|
||||
|
||||
<x-web.quality-summary :stats="$homeStats ?? []" />
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
<p class="text-[16px] lg:text-[17px] leading-[1.55] text-ink-2 mt-7 m-0 max-w-[560px]">
|
||||
<x-web.brand-mark brand="pressekonto" :serif="false" /> ist der gemeinsame Publisher-Bereich für
|
||||
unsere beiden Pressefachportale. Pressemitteilungen schreiben, redaktionell prüfen lassen, auf
|
||||
unsere beiden Pressefachportale. Pressemitteilungen schreiben, automatisiert prüfen lassen, auf
|
||||
beiden Reichweiten veröffentlichen — und Mitteilungen, Newsrooms und Abrechnung an einem Ort
|
||||
verwalten.
|
||||
</p>
|
||||
|
|
@ -234,7 +234,7 @@
|
|||
Pressemitteilungen auf beiden Portalen
|
||||
</h3>
|
||||
<p class="text-[13.5px] leading-[1.6] text-ink-2 mt-3 m-0">
|
||||
Eine Mitteilung schreiben, redaktionell prüfen lassen, auf <x-web.brand-mark brand="presseecho"
|
||||
Eine Mitteilung schreiben, Inhaltsprüfung durchlaufen lassen, auf <x-web.brand-mark brand="presseecho"
|
||||
:serif="false" /> und <x-web.brand-mark brand="businessportal24" :serif="false" />
|
||||
gleichzeitig veröffentlichen. Vorschau und Qualitäts-Checkliste inklusive.
|
||||
</p>
|
||||
|
|
@ -291,7 +291,7 @@
|
|||
Aufrufe, Status und Abrechnung zentral
|
||||
</h3>
|
||||
<p class="text-[13.5px] leading-[1.6] text-ink-2 mt-3 m-0">
|
||||
Aufrufe je Mitteilung über beide Portale. Prüf-Status und redaktionelles Feedback direkt an der
|
||||
Aufrufe je Mitteilung über beide Portale. Prüf-Status und Feedback direkt an der
|
||||
Mitteilung, Buchungen und Rechnungen zentral im Konto.
|
||||
</p>
|
||||
<ul class="mt-5 space-y-1.5 text-[12.5px] text-ink-3 list-none p-0">
|
||||
|
|
@ -326,8 +326,9 @@
|
|||
</div>
|
||||
<div class="text-[16.5px] font-semibold text-ink tracking-[-0.2px] mb-2">Registrieren</div>
|
||||
<p class="text-[12.5px] text-ink-2 leading-[1.55] m-0">
|
||||
Konto in unter zwei Minuten anlegen — mit E-Mail und Passwort oder Google-Konto. Nach
|
||||
Bestätigung Ihrer E-Mail-Adresse ist das Konto für beide Portale freigeschaltet.
|
||||
Konto in unter zwei Minuten anlegen — mit E-Mail und Passwort, Anmeldung wahlweise
|
||||
auch passwortlos per Magic-Link. Nach Bestätigung Ihrer E-Mail-Adresse ist das Konto
|
||||
für beide Portale freigeschaltet.
|
||||
</p>
|
||||
</li>
|
||||
<li class="relative px-7 py-7 border-r border-b lg:border-b-0 border-bg-rule">
|
||||
|
|
@ -539,7 +540,7 @@
|
|||
</h2>
|
||||
<p class="text-[14.5px] text-ink-2 leading-[1.55] m-0 mt-4 max-w-[760px]">
|
||||
<x-web.brand-mark brand="pressekonto" :serif="false" /> ist nicht „irgendein Tool" — es ist die
|
||||
zentrale Plattform für unsere beiden redaktionell geführten Pressefachportale. Jedes Portal hat einen
|
||||
zentrale Plattform für unsere beiden Pressefachportale. Jedes Portal hat einen
|
||||
eigenen Charakter, eigene Leserschaft und eigene Themen-Schwerpunkte.
|
||||
</p>
|
||||
</header>
|
||||
|
|
@ -564,7 +565,7 @@
|
|||
<div class="px-7 py-7">
|
||||
<p class="text-[13.5px] leading-[1.6] text-ink-2 m-0">
|
||||
Themen-Sammlung und Langzeit-Archiv für Pressemitteilungen aus DACH. Editorial-Charakter,
|
||||
Themen-Tiefe, redaktionell erschlossen — der Ort, an dem eine Mitteilung über Jahre auffindbar
|
||||
Themen-Tiefe, nach Branchen erschlossen — der Ort, an dem eine Mitteilung über Jahre auffindbar
|
||||
bleibt.
|
||||
</p>
|
||||
<hr class="rule my-5" />
|
||||
|
|
@ -577,7 +578,7 @@
|
|||
<div>
|
||||
<div class="font-mono text-[18px] font-semibold text-ink leading-none tabular-nums">
|
||||
{{ $fmtCount($hubStats['categories']) }} Branchen</div>
|
||||
<div class="text-[11px] text-ink-3 mt-1">redaktionell erschlossen</div>
|
||||
<div class="text-[11px] text-ink-3 mt-1">nach Branchen erschlossen</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ config('domains.domain_presseecho_url') }}"
|
||||
|
|
@ -797,7 +798,7 @@
|
|||
</svg>
|
||||
</summary>
|
||||
<div class="pb-5 text-[13.5px] text-ink-2 leading-[1.65] max-w-[640px]">
|
||||
Nach der redaktionellen Freigabe geht Ihre Mitteilung sofort live — werktags in der Regel
|
||||
Nach bestandener Inhaltsprüfung geht Ihre Mitteilung sofort live — werktags in der Regel
|
||||
noch am selben Tag. Alternativ legen Sie einen Wunschtermin fest: Dann erscheint die
|
||||
Mitteilung automatisch zum geplanten Zeitpunkt, geprüft und freigegeben ist sie bereits davor.
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,17 +3,15 @@
|
|||
@php
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
\Carbon\Carbon::setLocale('de');
|
||||
|
||||
$company = $release->company;
|
||||
|
||||
$categoryTranslation = $release->category?->translations?->firstWhere('locale', 'de')
|
||||
$categoryTranslation = $release->category?->translations?->firstWhere('locale', app()->getLocale())
|
||||
?? $release->category?->translations?->first();
|
||||
$categoryName = $categoryTranslation?->name;
|
||||
$categorySlug = $categoryTranslation?->slug;
|
||||
$categoryHref = $categorySlug ? route('kategorie', ['slug' => $categorySlug]) : route('web.home');
|
||||
|
||||
$parentTranslation = $release->category?->parent?->translations?->firstWhere('locale', 'de')
|
||||
$parentTranslation = $release->category?->parent?->translations?->firstWhere('locale', app()->getLocale())
|
||||
?? $release->category?->parent?->translations?->first();
|
||||
$parentName = $parentTranslation?->name;
|
||||
$parentSlug = $parentTranslation?->slug;
|
||||
|
|
@ -38,9 +36,9 @@
|
|||
|
||||
$companyName = $company?->name;
|
||||
$companyInitial = $companyName ? mb_strtoupper(mb_substr($companyName, 0, 1)) : '·';
|
||||
$companyHref = $company?->slug ? route('newsrooms').'#'.$company->slug : route('newsrooms');
|
||||
$companyHref = $company?->slug ? route('newsroom', ['slug' => $company->slug]) : route('newsrooms');
|
||||
|
||||
$countryNames = ['DE' => 'Deutschland', 'AT' => 'Österreich', 'CH' => 'Schweiz'];
|
||||
$countryNames = ['DE' => __('web.countries.de'), 'AT' => __('web.countries.at'), 'CH' => __('web.countries.ch')];
|
||||
$companyCountry = $company?->country_code ? ($countryNames[strtoupper($company->country_code)] ?? null) : null;
|
||||
|
||||
$boilerplate = filled($release->boilerplate_override) ? $release->boilerplate_override : $company?->boilerplate;
|
||||
|
|
@ -84,8 +82,8 @@
|
|||
<article>
|
||||
{{-- Breadcrumbs --}}
|
||||
<div class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 pt-7">
|
||||
<nav class="flex flex-wrap gap-2 text-[11.5px] text-ink-3 mb-2 font-mono tracking-wide" aria-label="Brotkrumen">
|
||||
<a href="{{ route('web.home') }}" class="cursor-pointer hover:text-ink transition-colors">Startseite</a>
|
||||
<nav class="flex flex-wrap gap-2 text-[11.5px] text-ink-3 mb-2 font-mono tracking-wide" aria-label="{{ __('web.detail.breadcrumbs') }}">
|
||||
<a href="{{ route('web.home') }}" class="cursor-pointer hover:text-ink transition-colors">{{ __('web.nav.home') }}</a>
|
||||
<span aria-hidden="true">/</span>
|
||||
@if ($parentName)
|
||||
<a href="{{ $parentHref }}" class="cursor-pointer hover:text-ink transition-colors">{{ $parentName }}</a>
|
||||
|
|
@ -95,7 +93,7 @@
|
|||
<a href="{{ $categoryHref }}" class="cursor-pointer text-brand hover:text-brand-deep transition-colors">{{ $categoryName }}</a>
|
||||
<span aria-hidden="true">/</span>
|
||||
@endif
|
||||
<span class="text-ink-2">Pressemitteilung</span>
|
||||
<span class="text-ink-2">{{ __('web.detail.press_release') }}</span>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
|
@ -105,7 +103,7 @@
|
|||
@if ($embargoAt)
|
||||
<div class="flex items-center gap-2.5 mb-3.5 flex-wrap">
|
||||
<span class="text-[9.5px] font-bold tracking-[0.18em] uppercase text-brand px-2.5 py-[3px] border border-brand font-mono">
|
||||
● Embargo aufgehoben
|
||||
● {{ __('web.detail.embargo_lifted') }}
|
||||
</span>
|
||||
<span class="text-[11px] text-ink-3 font-mono">{{ $embargoAt->translatedFormat('j. F Y, H:i') }}</span>
|
||||
</div>
|
||||
|
|
@ -116,7 +114,7 @@
|
|||
<a href="{{ $categoryHref }}" class="bp-cat cursor-pointer hover:text-brand-deep transition-colors">{{ $categoryName }}</a>
|
||||
<span class="w-1 h-1 rounded-full bg-ink-4" aria-hidden="true"></span>
|
||||
@endif
|
||||
<span class="text-[11px] text-ink-3 italic font-serif">Für Wirtschaftsjournalisten</span>
|
||||
<span class="text-[11px] text-ink-3 italic font-serif">{{ __('web.detail.audience') }}</span>
|
||||
</div>
|
||||
|
||||
<h1 class="font-serif text-[32px] sm:text-[40px] lg:text-[48px] font-semibold m-0 mb-[22px] tracking-[-0.8px] leading-[1.1] max-w-[980px]" style="text-wrap:balance;">
|
||||
|
|
@ -139,10 +137,10 @@
|
|||
@endif
|
||||
@if ($publishedAt)
|
||||
<time datetime="{{ $publishedAt->toIso8601String() }}" class="font-mono text-[11.5px] text-ink-3">
|
||||
{{ $publishedAt->translatedFormat('j. F Y') }} · {{ $publishedAt->format('H:i') }} Uhr
|
||||
{{ $publishedAt->translatedFormat('j. F Y') }} · {{ __('web.detail.time_at', ['time' => $publishedAt->format('H:i')]) }}
|
||||
</time>
|
||||
@endif
|
||||
<span class="font-mono text-[11.5px] text-ink-3">{{ $readTime }} Min. Lesezeit · {{ number_format($wordCount, 0, ',', '.') }} Wörter</span>
|
||||
<span class="font-mono text-[11.5px] text-ink-3">{{ __('web.shared.read_time', ['minutes' => $readTime]) }} · {{ __('web.detail.words', ['count' => number_format($wordCount, 0, ',', '.')]) }}</span>
|
||||
@if ($companyCountry)
|
||||
<span class="inline-flex items-center gap-1.5 font-mono text-[11.5px] text-ink-3">
|
||||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M7 1a5 5 0 015 5c0 4-5 7-5 7s-5-3-5-7a5 5 0 015-5z" stroke="currentColor" stroke-width="1.3" fill="none" /><circle cx="7" cy="6" r="1.5" fill="currentColor" /></svg>
|
||||
|
|
@ -152,12 +150,12 @@
|
|||
|
||||
@if ($isRecommended)
|
||||
<span class="inline-flex items-center gap-1.5 px-2 py-[3px] text-[9.5px] font-bold tracking-[0.14em] uppercase text-brand bg-brand/[0.04] border border-brand/30 font-mono cursor-help"
|
||||
title="Diese Pressemitteilung wurde von der Redaktion empfohlen — basierend auf Quellenqualität, Verifizierungsstatus und redaktioneller Relevanz.">
|
||||
title="{{ __('web.detail.recommended_hint') }}">
|
||||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M12 2l9 4v6c0 5-3.5 9-9 10-5.5-1-9-5-9-10V6z" stroke="currentColor" stroke-width="2" fill="none" />
|
||||
<path d="M8 12l3 3 5-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
Redaktionsempfehlung
|
||||
{{ __('web.detail.recommended') }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -172,13 +170,13 @@
|
|||
class="absolute inset-0 h-full w-full object-cover" loading="eager">
|
||||
@else
|
||||
<span class="relative text-[10px] font-mono tracking-wider uppercase text-white/50">
|
||||
{{ $companyName ? 'Bild · Pressefoto '.$companyName : 'Bild · Pressefoto' }} (16:9)
|
||||
{{ $companyName ? __('web.detail.image_placeholder_company', ['company' => $companyName]) : __('web.detail.image_placeholder') }} (16:9)
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
@if ($imageCaption || $imageCredit)
|
||||
<div class="text-[11.5px] text-ink-3 mt-2.5 italic max-w-[720px]">
|
||||
{{ $imageCaption }}@if ($imageCaption && $imageCredit) · @endif@if ($imageCredit)<span class="not-italic">Foto: {{ $imageCredit }}</span>@endif
|
||||
{{ $imageCaption }}@if ($imageCaption && $imageCredit) · @endif@if ($imageCredit)<span class="not-italic">{{ __('web.detail.photo') }}: {{ $imageCredit }}</span>@endif
|
||||
</div>
|
||||
@endif
|
||||
</section>
|
||||
|
|
@ -193,45 +191,20 @@
|
|||
|
||||
@if (filled($boilerplate))
|
||||
<div class="mt-9 pt-5 border-t border-bg-rule-strong max-w-[680px]">
|
||||
<div class="eyebrow muted text-[9.5px] mb-2.5">Über das Unternehmen</div>
|
||||
<div class="eyebrow muted text-[9.5px] mb-2.5">{{ __('web.detail.about_company') }}</div>
|
||||
<p class="text-[13px] leading-[1.6] text-ink-3 m-0 max-w-[620px]">{{ $boilerplate }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Inline Ad --}}
|
||||
<div class="my-8 max-w-[680px]">
|
||||
<div class="flex items-center gap-2.5 mb-2.5">
|
||||
<span class="h-px bg-bg-rule-strong flex-1"></span>
|
||||
<span class="text-[9px] font-bold tracking-[0.22em] text-ink-on-dark-muted uppercase font-mono">Anzeige</span>
|
||||
<span class="h-px bg-bg-rule-strong flex-1"></span>
|
||||
</div>
|
||||
<a href="#" rel="sponsored nofollow" class="grid px-[22px] py-5 bg-bg-card-warm items-center gap-[22px] cursor-pointer hover:bg-bg-card-warm-hover transition-colors" style="grid-template-columns:120px 1fr;">
|
||||
<div class="aspect-square bg-white hatch-light flex items-center justify-center text-[9px] text-ink-on-dark-muted font-mono">LOGO</div>
|
||||
<div>
|
||||
<div class="text-[9.5px] font-bold tracking-[0.16em] uppercase mb-1.5 text-card-warm-cat">Partner · Branchen-Whitepaper</div>
|
||||
<h3 class="font-serif text-[18px] leading-[1.3] m-0 mb-2 font-semibold tracking-[-0.2px] text-card-warm-title">
|
||||
CO₂-Reporting nach CSRD: Was Industriebetriebe jetzt automatisieren müssen
|
||||
</h3>
|
||||
<div class="text-[12px] leading-[1.5] mb-2.5 text-card-warm-cat">Whitepaper · 32 Seiten · Praxisleitfaden für Mittelstand und Konzerne</div>
|
||||
<span class="text-[12px] font-semibold underline text-card-warm-title">Studie kostenfrei anfordern →</span>
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex items-center gap-2.5 mt-2.5">
|
||||
<span class="h-px bg-bg-rule-strong flex-1"></span>
|
||||
<span class="text-[9px] font-bold tracking-[0.22em] text-ink-on-dark-muted uppercase font-mono">Ende Anzeige</span>
|
||||
<span class="h-px bg-bg-rule-strong flex-1"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Pressekontakt --}}
|
||||
@if ($contact)
|
||||
<div class="mt-8 px-5 sm:px-7 py-6 bg-white border border-bg-rule-strong max-w-[680px]">
|
||||
<div class="eyebrow mb-3 text-[10.5px]">Pressekontakt</div>
|
||||
<div class="eyebrow mb-3 text-[10.5px]">{{ __('web.detail.press_contact') }}</div>
|
||||
|
||||
<div class="flex gap-1.5 items-center mb-1 flex-wrap">
|
||||
<strong class="font-serif text-[19px] font-semibold tracking-[-0.2px]">{{ $contactName ?: $companyName }}</strong>
|
||||
@if ($publisherVerified)
|
||||
<span title="Verifizierter Publisher — Identität und Domain bestätigt" class="inline-flex items-center text-verify-border flex-shrink-0">
|
||||
<span title="{{ __('web.detail.verified_hint') }}" class="inline-flex items-center text-verify-border flex-shrink-0">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M12 1l2.5 3 3.8-.4-.5 3.8L21 9l-2.6 2.5L19 15l-3.8.4L12 19l-2.5-3.6L5.7 15 5.6 11.4 3 9l2.5-1.6L4.7 4 8.5 4z" fill="currentColor" />
|
||||
<path d="M7.5 12l3 3 6-6" stroke="white" stroke-width="2.2" fill="none" stroke-linecap="round" stroke-linejoin="round" />
|
||||
|
|
@ -277,16 +250,16 @@
|
|||
|
||||
{{-- Inhalt melden (T13): sichtbar an jeder Mitteilung, nicht nur in der Sidebar --}}
|
||||
<div class="mt-8 pt-4 border-t border-bg-rule flex flex-wrap items-center gap-x-4 gap-y-1.5 text-[11.5px] text-ink-3">
|
||||
<span>Sie haben ein rechtliches Anliegen zu dieser Mitteilung?</span>
|
||||
<span>{{ __('web.detail.legal_question') }}</span>
|
||||
<a href="{{ route('legal-request.create', ['slug' => $release->slug, 'type' => 'report']) }}" class="inline-flex items-center gap-1.5 text-ink-2 underline underline-offset-2 hover:text-ink transition-colors">
|
||||
<svg width="12" height="12" viewBox="0 0 14 14" class="flex-shrink-0" aria-hidden="true"><circle cx="7" cy="7" r="5.5" stroke="currentColor" stroke-width="1.3" fill="none" /><path d="M7 4v3.5M7 9.5v.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" /></svg>
|
||||
Inhalt melden
|
||||
{{ __('web.detail.report_content') }}
|
||||
</a>
|
||||
<a href="{{ route('legal-request.create', ['slug' => $release->slug, 'type' => 'dsgvo']) }}" class="inline-flex items-center gap-1.5 text-ink-2 underline underline-offset-2 hover:text-ink transition-colors">
|
||||
<svg width="12" height="12" viewBox="0 0 14 14" class="flex-shrink-0" aria-hidden="true"><path d="M7 1l5 2v4c0 3-2.2 5.2-5 6-2.8-.8-5-3-5-6V3z" stroke="currentColor" stroke-width="1.3" fill="none" stroke-linejoin="round" /></svg>
|
||||
Eigene Daten entfernen
|
||||
{{ __('web.detail.remove_data') }}
|
||||
</a>
|
||||
<span class="text-ink-4">Meldungen sind kostenlos.</span>
|
||||
<span class="text-ink-4">{{ __('web.detail.reports_free') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -295,7 +268,7 @@
|
|||
{{-- Publisher --}}
|
||||
@if ($companyName)
|
||||
<div class="p-4 bg-white border border-bg-rule-strong">
|
||||
<div class="eyebrow muted text-[9.5px] mb-3">Veröffentlicht von</div>
|
||||
<div class="eyebrow muted text-[9.5px] mb-3">{{ __('web.detail.published_by') }}</div>
|
||||
<div class="flex gap-3 items-start mb-3">
|
||||
<span class="w-11 h-11 bg-black text-white inline-flex items-center justify-center text-[20px] font-bold font-serif flex-shrink-0">{{ $companyInitial }}</span>
|
||||
<div class="min-w-0">
|
||||
|
|
@ -308,18 +281,17 @@
|
|||
|
||||
@if ($publisherVerified)
|
||||
<div class="flex items-center gap-2 px-2.5 py-2 mb-3 bg-verify-bg border border-verify-border text-verify-ink cursor-help"
|
||||
title="Identität und Domain durch {{ $brandLabel }} bestätigt">
|
||||
title="{{ __('web.detail.verified_by', ['brand' => $brandLabel]) }}">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" class="text-verify-border flex-shrink-0" aria-hidden="true">
|
||||
<path d="M12 1l2.5 3 3.8-.4-.5 3.8L21 9l-2.6 2.5L19 15l-3.8.4L12 19l-2.5-3.6L5.7 15 5.6 11.4 3 9l2.5-1.6L4.7 4 8.5 4z" fill="currentColor" />
|
||||
<path d="M7.5 12l3 3 6-6" stroke="white" stroke-width="2.2" fill="none" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
<span class="text-[11.5px] font-semibold">Verifizierter Publisher</span>
|
||||
<span class="text-[11.5px] font-semibold">{{ __('web.detail.verified_publisher') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="pt-3 border-t border-bg-rule flex flex-col gap-2">
|
||||
<button type="button" class="px-2.5 py-2 text-[11.5px] font-semibold bg-ink text-white hover:bg-black transition-colors cursor-pointer">+ Newsroom folgen</button>
|
||||
<a href="{{ $companyHref }}" class="text-[11.5px] text-ink-2 text-center py-1.5 underline underline-offset-2 cursor-pointer hover:text-ink transition-colors">Alle Mitteilungen →</a>
|
||||
<a href="{{ $companyHref }}" class="px-2.5 py-2 text-center text-[11.5px] font-semibold bg-ink text-white hover:bg-black transition-colors cursor-pointer">{{ __('web.detail.to_newsroom') }} →</a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
|
@ -331,7 +303,7 @@
|
|||
copied: false,
|
||||
copy() { navigator.clipboard?.writeText(this.url).then(() => { this.copied = true; setTimeout(() => this.copied = false, 2000); }); }
|
||||
}">
|
||||
<div class="eyebrow muted text-[9.5px] mb-2.5">Teilen</div>
|
||||
<div class="eyebrow muted text-[9.5px] mb-2.5">{{ __('web.detail.share') }}</div>
|
||||
<div class="grid grid-cols-2 gap-1.5">
|
||||
<a :href="`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`" target="_blank" rel="noopener" class="inline-flex items-center gap-[7px] px-2.5 py-2 text-[11.5px] font-medium bg-white border border-bg-rule-strong text-ink hover:bg-bg-elev transition-colors cursor-pointer">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 6h2.5v8H3V6zm1.25-3a1.25 1.25 0 110 2.5 1.25 1.25 0 010-2.5zM7 6h2.4v1.1c.4-.7 1.3-1.3 2.6-1.3 2.7 0 3 1.7 3 3.9V14h-2.5v-3.7c0-.9 0-2-1.3-2s-1.5 1-1.5 2V14H7V6z" fill="currentColor" /></svg>
|
||||
|
|
@ -343,11 +315,11 @@
|
|||
</a>
|
||||
<a :href="`mailto:?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(url)}`" class="inline-flex items-center gap-[7px] px-2.5 py-2 text-[11.5px] font-medium bg-white border border-bg-rule-strong text-ink hover:bg-bg-elev transition-colors cursor-pointer">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" aria-hidden="true"><rect x="2" y="3" width="12" height="10" stroke="currentColor" stroke-width="1.3" fill="none" /><path d="M2 4l6 4 6-4" stroke="currentColor" stroke-width="1.3" fill="none" /></svg>
|
||||
E-Mail
|
||||
{{ __('web.detail.email') }}
|
||||
</a>
|
||||
<button type="button" @click="copy()" class="inline-flex items-center gap-[7px] px-2.5 py-2 text-[11.5px] font-medium bg-white border border-bg-rule-strong text-ink hover:bg-bg-elev transition-colors cursor-pointer">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" aria-hidden="true"><path d="M6 9l4-4M5 4h2a3 3 0 010 6h-1M11 12H9a3 3 0 010-6h1" stroke="currentColor" stroke-width="1.3" fill="none" stroke-linecap="round" /></svg>
|
||||
<span x-text="copied ? 'Kopiert!' : 'Link kopieren'">Link kopieren</span>
|
||||
<span x-text="copied ? @js(__('web.detail.copied')) : @js(__('web.detail.copy_link'))">{{ __('web.detail.copy_link') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -355,7 +327,7 @@
|
|||
{{-- Schlagwörter --}}
|
||||
@if ($keywords->isNotEmpty())
|
||||
<div>
|
||||
<div class="eyebrow muted text-[9.5px] mb-2.5">Schlagwörter</div>
|
||||
<div class="eyebrow muted text-[9.5px] mb-2.5">{{ __('web.detail.keywords') }}</div>
|
||||
<div class="flex flex-wrap gap-[5px]">
|
||||
@foreach ($keywords as $keyword)
|
||||
<a href="{{ route('suche', ['q' => $keyword]) }}" class="px-2.5 py-1 text-[11px] font-medium bg-white border border-bg-rule-strong text-ink-2 hover:text-ink transition-colors cursor-pointer">{{ $keyword }}</a>
|
||||
|
|
@ -364,40 +336,26 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Newsletter --}}
|
||||
<div class="p-4 bg-ink text-white">
|
||||
<div class="eyebrow on-dark text-[9.5px] mb-2">Newsletter</div>
|
||||
<h4 class="font-serif text-[17px] font-semibold m-0 mb-2 leading-[1.25]">
|
||||
{{ $categoryName ?: 'Wirtschaft' }} · ab Score 80
|
||||
</h4>
|
||||
<p class="text-[11.5px] text-white/70 leading-[1.5] m-0 mb-3">
|
||||
Höchstens 2 Mails pro Woche. Nur Meldungen ab Content-Score 80.
|
||||
</p>
|
||||
<a href="#newsletter" class="block text-center w-full py-2.5 text-[12px] font-semibold bg-brand hover:bg-brand-deep text-white transition-colors cursor-pointer">Abonnieren →</a>
|
||||
</div>
|
||||
{{-- Newsletter-Box bewusst entfernt: kommt nachgelagert mit echtem
|
||||
Double-Opt-in (siehe dev/release/entwicklungsplan-release.md). --}}
|
||||
|
||||
{{-- Weiterführend --}}
|
||||
<div>
|
||||
<div class="eyebrow muted text-[9.5px] mb-2.5">Weiterführend</div>
|
||||
<div class="eyebrow muted text-[9.5px] mb-2.5">{{ __('web.detail.further') }}</div>
|
||||
<div class="flex flex-col border border-bg-rule-strong bg-white">
|
||||
<a href="#" class="flex items-center gap-2.5 px-3 py-2.5 text-[12px] text-ink-2 hover:bg-bg-elev transition-colors cursor-pointer">
|
||||
<svg width="13" height="13" viewBox="0 0 14 14" class="text-ink-3 flex-shrink-0" aria-hidden="true"><path d="M2 1h7l3 3v9H2z" stroke="currentColor" stroke-width="1.3" fill="none" /><path d="M9 1v3h3" stroke="currentColor" stroke-width="1.3" fill="none" /></svg>
|
||||
<span class="flex-1">Permalink & Zitiervorschlag</span>
|
||||
<svg width="9" height="9" viewBox="0 0 12 12" fill="none" class="text-ink-4" aria-hidden="true"><path d="M4 2l4 4-4 4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
</a>
|
||||
<a href="#" class="flex items-center gap-2.5 px-3 py-2.5 text-[12px] text-ink-2 border-t border-bg-rule hover:bg-bg-elev transition-colors cursor-pointer">
|
||||
<button type="button" onclick="window.print()" class="flex items-center gap-2.5 px-3 py-2.5 text-[12px] text-ink-2 hover:bg-bg-elev transition-colors cursor-pointer text-left">
|
||||
<svg width="13" height="13" viewBox="0 0 14 14" class="text-ink-3 flex-shrink-0" aria-hidden="true"><path d="M2 4h10v8H2zM2 4V2h10v2" stroke="currentColor" stroke-width="1.3" fill="none" /></svg>
|
||||
<span class="flex-1">Drucken & PDF</span>
|
||||
<span class="flex-1">{{ __('web.detail.print') }}</span>
|
||||
<svg width="9" height="9" viewBox="0 0 12 12" fill="none" class="text-ink-4" aria-hidden="true"><path d="M4 2l4 4-4 4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
</a>
|
||||
</button>
|
||||
<a href="{{ route('legal-request.create', ['slug' => $release->slug, 'type' => 'report']) }}" class="flex items-center gap-2.5 px-3 py-2.5 text-[12px] text-ink-2 border-t border-bg-rule hover:bg-bg-elev transition-colors cursor-pointer">
|
||||
<svg width="13" height="13" viewBox="0 0 14 14" class="text-ink-3 flex-shrink-0" aria-hidden="true"><circle cx="7" cy="7" r="5.5" stroke="currentColor" stroke-width="1.3" fill="none" /><path d="M7 4v3.5M7 9.5v.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" /></svg>
|
||||
<span class="flex-1">Pressemitteilung melden</span>
|
||||
<span class="flex-1">{{ __('web.detail.report_release') }}</span>
|
||||
<svg width="9" height="9" viewBox="0 0 12 12" fill="none" class="text-ink-4" aria-hidden="true"><path d="M4 2l4 4-4 4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
</a>
|
||||
<a href="{{ route('legal-request.create', ['slug' => $release->slug, 'type' => 'dsgvo']) }}" class="flex items-center gap-2.5 px-3 py-2.5 text-[12px] text-ink-2 border-t border-bg-rule hover:bg-bg-elev transition-colors cursor-pointer">
|
||||
<svg width="13" height="13" viewBox="0 0 14 14" class="text-ink-3 flex-shrink-0" aria-hidden="true"><path d="M7 1l5 2v4c0 3-2.2 5.2-5 6-2.8-.8-5-3-5-6V3z" stroke="currentColor" stroke-width="1.3" fill="none" stroke-linejoin="round" /></svg>
|
||||
<span class="flex-1">Datenschutz & Persönlichkeitsrecht</span>
|
||||
<span class="flex-1">{{ __('web.detail.privacy_rights') }}</span>
|
||||
<svg width="9" height="9" viewBox="0 0 12 12" fill="none" class="text-ink-4" aria-hidden="true"><path d="M4 2l4 4-4 4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -411,26 +369,26 @@
|
|||
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
||||
@if ($companyReleases->isNotEmpty())
|
||||
<div>
|
||||
<h3 class="font-serif text-[20px] font-semibold m-0 mb-1 tracking-[-0.2px]">Mehr von {{ $companyName }}</h3>
|
||||
<div class="text-[11.5px] text-ink-3 mb-3.5">Aktuelle Mitteilungen aus diesem Newsroom</div>
|
||||
<h3 class="font-serif text-[20px] font-semibold m-0 mb-1 tracking-[-0.2px]">{{ __('web.detail.more_from', ['company' => $companyName]) }}</h3>
|
||||
<div class="text-[11.5px] text-ink-3 mb-3.5">{{ __('web.detail.more_from_sub') }}</div>
|
||||
<hr class="rule-strong mb-1" />
|
||||
@foreach ($companyReleases as $companyRelease)
|
||||
<x-web.feed-item :release="$companyRelease" />
|
||||
@endforeach
|
||||
<a href="{{ $companyHref }}" class="block mt-3.5 text-[12.5px] font-semibold text-brand hover:text-brand-deep transition-colors cursor-pointer">Alle Mitteilungen von {{ $companyName }} →</a>
|
||||
<a href="{{ $companyHref }}" class="block mt-3.5 text-[12.5px] font-semibold text-brand hover:text-brand-deep transition-colors cursor-pointer">{{ __('web.detail.all_from', ['company' => $companyName]) }} →</a>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($relatedReleases->isNotEmpty())
|
||||
<div>
|
||||
<h3 class="font-serif text-[20px] font-semibold m-0 mb-1 tracking-[-0.2px]">Verwandte Meldungen</h3>
|
||||
<div class="text-[11.5px] text-ink-3 mb-3.5">Reaktionen, ähnliche Themen, Branchenkontext</div>
|
||||
<h3 class="font-serif text-[20px] font-semibold m-0 mb-1 tracking-[-0.2px]">{{ __('web.detail.related') }}</h3>
|
||||
<div class="text-[11.5px] text-ink-3 mb-3.5">{{ __('web.detail.related_sub') }}</div>
|
||||
<hr class="rule-strong mb-1" />
|
||||
@foreach ($relatedReleases as $relatedRelease)
|
||||
<x-web.feed-item :release="$relatedRelease" />
|
||||
@endforeach
|
||||
@if ($categoryName)
|
||||
<a href="{{ $categoryHref }}" class="block mt-3.5 text-[12.5px] font-semibold text-brand hover:text-brand-deep transition-colors cursor-pointer">Alle Meldungen aus {{ $categoryName }} →</a>
|
||||
<a href="{{ $categoryHref }}" class="block mt-3.5 text-[12.5px] font-semibold text-brand hover:text-brand-deep transition-colors cursor-pointer">{{ __('web.detail.all_from_category', ['category' => $categoryName]) }} →</a>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
$brandAccent = $brandConfig['accent'] ?? '24';
|
||||
@endphp
|
||||
|
||||
@section('title', 'Pressemitteilungen durchsuchen – '.$brandName.$brandAccent)
|
||||
@section('meta_description', 'Durchsuchen Sie das geprüfte Pressemitteilungs-Archiv nach Stichwort, Unternehmen und Branche – redaktionell geprüft und dauerhaft auffindbar im DACH-Raum.')
|
||||
@section('title', __('web.search.meta_title', ['brand' => $brandName.$brandAccent]))
|
||||
@section('meta_description', __('web.search.meta_description'))
|
||||
|
||||
@section('hreflang')
|
||||
<link rel="alternate" hreflang="de" href="{{ url('/de/suche') }}">
|
||||
|
|
@ -24,19 +24,19 @@
|
|||
<div class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 py-2.5 flex items-center gap-2 text-[11.5px] text-ink-3">
|
||||
<a href="{{ route('web.home') }}" class="hover:text-ink transition-colors">{{ $brandName }}{{ $brandAccent }}</a>
|
||||
<span class="text-ink-4" aria-hidden="true">›</span>
|
||||
<span class="text-ink font-semibold">Suche</span>
|
||||
<span class="text-ink font-semibold">{{ __('web.header.search_eyebrow') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Masthead --}}
|
||||
<section class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 pt-8 lg:pt-10">
|
||||
<div class="max-w-[680px]">
|
||||
<div class="eyebrow mb-3">Archiv-Suche</div>
|
||||
<div class="eyebrow mb-3">{{ __('web.search.eyebrow') }}</div>
|
||||
<h1 class="font-serif text-[36px] sm:text-[46px] lg:text-[50px] font-semibold m-0 mb-4 leading-[1.05] tracking-[-1px] text-ink text-balance">
|
||||
Pressemitteilungen durchsuchen
|
||||
{{ __('web.header.search_title') }}
|
||||
</h1>
|
||||
<p class="font-serif text-[16px] leading-[1.55] m-0 text-ink-2">
|
||||
Stichwort, Unternehmen oder Branche – durchsuchen Sie das vollständige, redaktionell geprüfte Archiv.
|
||||
{{ __('web.search.intro') }}
|
||||
</p>
|
||||
</div>
|
||||
<hr class="rule-strong mt-8">
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@
|
|||
@section('title', 'Pressemitteilung veröffentlichen – '.$brandName.$brandAccent)
|
||||
@section('meta_description', 'Pressemitteilungen geprüft veröffentlichen und dauerhaft auffindbar im Archiv. Faire Preise, keine Vertragsbindung – Einreichung über den zentralen Publisher-Bereich.')
|
||||
|
||||
{{-- Kein hreflang="en": Der Seiteninhalt liegt bisher nur auf Deutsch vor.
|
||||
EN-Übersetzung der Langform-Seiten ist nachgelagert (siehe Release-Plan). --}}
|
||||
@section('hreflang')
|
||||
<link rel="alternate" hreflang="de" href="{{ url('/de/veroeffentlichen') }}">
|
||||
<link rel="alternate" hreflang="en" href="{{ url('/en/veroeffentlichen') }}">
|
||||
<link rel="alternate" hreflang="x-default" href="{{ url('/de/veroeffentlichen') }}">
|
||||
@endsection
|
||||
|
||||
|
|
@ -48,7 +49,7 @@
|
|||
<div class="hidden md:flex items-center gap-[18px]">
|
||||
<span class="inline-flex items-center gap-1.5">
|
||||
<svg width="11" height="11" viewBox="0 0 11 11" aria-hidden="true"><path d="M2 5.5l2.2 2.2L9 3" stroke="currentColor" class="text-ok" stroke-width="1.6" fill="none" stroke-linecap="round"/></svg>
|
||||
<span>Redaktionell geprüft seit {{ $sinceYear }}</span>
|
||||
<span>Qualitätsgeprüft seit {{ $sinceYear }}</span>
|
||||
</span>
|
||||
<span class="text-ink-4">·</span>
|
||||
<a href="{{ route('hilfe') }}" class="text-ink-2 hover:text-ink transition-colors">Hilfe & Support</a>
|
||||
|
|
@ -229,8 +230,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-px bg-bg-rule border-y border-bg-rule-strong">
|
||||
@foreach ([
|
||||
['n' => '01', 'meta' => '≈ 2 Minuten', 't' => 'Konto anlegen', 'd' => 'Mit E-Mail und Passwort oder Google-Konto. Konto und Abrechnung liegen im zentralen Publisher-Bereich — ein Konto für beide Portale.'],
|
||||
['n' => '02', 'meta' => 'werktags · innerhalb 24 h', 't' => 'Mitteilung einreichen', 'd' => 'Text, Bild, Ansprechpartner. Eine automatische Qualitätsprüfung läuft parallel zu einer kurzen redaktionellen Sichtung.'],
|
||||
['n' => '01', 'meta' => '≈ 2 Minuten', 't' => 'Konto anlegen', 'd' => 'Mit E-Mail-Adresse und Passwort — Anmeldung wahlweise auch passwortlos per Magic-Link. Konto und Abrechnung liegen im zentralen Publisher-Bereich — ein Konto für beide Portale.'],
|
||||
['n' => '02', 'meta' => 'werktags · innerhalb 24 h', 't' => 'Mitteilung einreichen', 'd' => 'Text, Bild, Ansprechpartner. Die automatisierte Inhaltsprüfung läuft direkt beim Einreichen mit.'],
|
||||
['n' => '03', 'meta' => 'permanent · indexiert', 't' => 'Veröffentlichung', 'd' => 'Nach Freigabe online und im Archiv. Bei Bedarf jederzeit korrigierbar — die URL bleibt stabil, Verweise bestehen weiter.'],
|
||||
] as $step)
|
||||
<div class="bg-bg-elev px-7 pt-8 pb-9">
|
||||
|
|
@ -291,7 +292,7 @@
|
|||
Nicht geeignet sind reine Werbeanzeigen, SEO-Linkbuilding-Texte oder Inhalte ohne nachvollziehbaren Pressewert.
|
||||
</h3>
|
||||
<p class="mt-[18px] mb-0 text-[13.5px] leading-[1.6] text-ink-2">
|
||||
Die redaktionelle Sichtung sichert den Charakter der Plattform. Mitteilungen, die diese Schwelle nicht
|
||||
Die Inhaltsprüfung sichert den Charakter der Plattform. Mitteilungen, die diese Schwelle nicht
|
||||
erreichen, werden mit einer kurzen Begründung zurückgegeben — meist mit einem konkreten Korrekturvorschlag.
|
||||
</p>
|
||||
<hr class="rule my-5" />
|
||||
|
|
@ -382,7 +383,7 @@
|
|||
<div class="grid items-start gap-10 lg:gap-16 grid-cols-1 lg:grid-cols-[1fr_1.6fr]">
|
||||
<div class="lg:sticky lg:top-6 pt-2">
|
||||
<p class="font-serif m-0 text-[16px] leading-[1.55] text-ink-2">Sie finden hier keine Antwort?</p>
|
||||
<a href="mailto:{{ config('domains.domains.'.config('app.theme').'.brand.contact_email') ?? 'info@businessportal24.com' }}" class="mt-3.5 inline-flex items-center gap-1.5 text-[13.5px] font-medium text-brand border-b border-brand pb-0.5">Redaktion direkt anschreiben</a>
|
||||
<a href="mailto:{{ config('domains.domains.'.config('app.theme').'.brand.contact_email') ?? 'info@businessportal24.com' }}" class="mt-3.5 inline-flex items-center gap-1.5 text-[13.5px] font-medium text-brand border-b border-brand pb-0.5">Direkt Kontakt aufnehmen</a>
|
||||
<div class="mt-3.5 text-[12px] text-ink-3 leading-[1.55]">Antwort werktags per E-Mail</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -419,7 +420,7 @@
|
|||
<div class="eyebrow mb-3.5 text-[10.5px] text-brand-soft">Mitteilung einreichen · Schritt eins</div>
|
||||
<h2 class="font-serif m-0 text-[34px] lg:text-[48px] font-semibold leading-[1.08] text-ink-on-dark max-w-[700px] tracking-[-0.9px] text-balance">Bereit, Ihre Mitteilung einzureichen?</h2>
|
||||
<p class="font-serif mt-5 mb-0 text-[16px] lg:text-[17px] leading-[1.55] text-ink-on-dark-2 max-w-[580px]">
|
||||
Konto in zwei Minuten anlegen, Text und Bild einreichen, nach redaktioneller Sichtung veröffentlicht.
|
||||
Konto in zwei Minuten anlegen, Text und Bild einreichen, nach bestandener Inhaltsprüfung veröffentlicht.
|
||||
Einmaliges Konto — beide Portale (businessportal24 & presseecho.de) nutzbar.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use App\Console\Commands\GenerateManualInvoices;
|
|||
use App\Console\Commands\PublishScheduledPressReleases;
|
||||
use App\Console\Commands\PurgeExpiredPressReleaseDrafts;
|
||||
use App\Console\Commands\PurgeMagicLinks;
|
||||
use App\Console\Commands\RefreshMarketData;
|
||||
use App\Console\Commands\ResetMonthlyPressReleaseQuota;
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
|
@ -63,3 +64,14 @@ Schedule::command(ResetMonthlyPressReleaseQuota::class)
|
|||
->monthlyOn(1, '00:05')
|
||||
->withoutOverlapping()
|
||||
->runInBackground();
|
||||
|
||||
// ========================================
|
||||
// Börsendaten für den Web-Ticker
|
||||
// ========================================
|
||||
|
||||
// Kurse (DAX, EUR/USD, Brent) alle 15 Minuten aktualisieren; die Views lesen
|
||||
// nur aus dem Cache — ein API-Ausfall leert die Anzeige nicht sofort.
|
||||
Schedule::command(RefreshMarketData::class)
|
||||
->everyFifteenMinutes()
|
||||
->withoutOverlapping()
|
||||
->runInBackground();
|
||||
|
|
|
|||
|
|
@ -7,8 +7,32 @@ use App\Models\Company;
|
|||
use App\Models\PressRelease;
|
||||
use Tests\TestCase;
|
||||
|
||||
test('businessportal24 homepage renders the editorial shell', function () {
|
||||
test('businessportal24 homepage renders the editorial shell with real data', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = Category::factory()->create([
|
||||
'portal' => Portal::Businessportal24,
|
||||
]);
|
||||
$category->translations()->create([
|
||||
'locale' => 'de',
|
||||
'name' => 'Energie & Klima',
|
||||
'slug' => 'energie-klima',
|
||||
]);
|
||||
|
||||
$company = Company::factory()->businessportal24()->create([
|
||||
'name' => 'Muster Energie GmbH',
|
||||
]);
|
||||
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Shell-Test Meldung',
|
||||
'slug' => 'shell-test-meldung',
|
||||
'published_at' => now()->subHour(),
|
||||
]);
|
||||
|
||||
$this->get('https://businessportal24.test/de')
|
||||
->assertSuccessful()
|
||||
->assertSee('businessportal', false)
|
||||
|
|
@ -16,19 +40,69 @@ test('businessportal24 homepage renders the editorial shell', function () {
|
|||
->assertSeeText('Aktuelle Meldungen')
|
||||
->assertSeeText('Im Fokus')
|
||||
->assertSeeText('Aktive Newsrooms')
|
||||
->assertSeeText('Bleiben Sie informiert')
|
||||
->assertSeeText('Veröffentlichen Sie Ihre Pressemitteilung')
|
||||
->assertSeeText('Redaktioneller Qualitätsstandard')
|
||||
->assertSeeText('Ihre Pressemitteilung, qualitätsgeprüft veröffentlicht')
|
||||
->assertSeeText('Konto anlegen')
|
||||
->assertSeeText('Unser Qualitätsstandard')
|
||||
->assertDontSeeText('redaktionell geprüft')
|
||||
->assertSeeText('AD-HOC')
|
||||
->assertSeeText('Branchen-Index')
|
||||
->assertSeeText('Heute im Fokus')
|
||||
->assertSee('businessportal', false)
|
||||
->assertSee('>24<', false)
|
||||
->assertDontSeeText('führende Plattform')
|
||||
->assertDontSeeText('maximale Reichweite')
|
||||
->assertDontSeeText('Exklusiv-Interview');
|
||||
});
|
||||
|
||||
test('the ad-hoc ticker also renders on subpages', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = Category::factory()->create([
|
||||
'portal' => Portal::Businessportal24,
|
||||
]);
|
||||
$category->translations()->create([
|
||||
'locale' => 'de',
|
||||
'name' => 'Energie & Klima',
|
||||
'slug' => 'energie-klima',
|
||||
]);
|
||||
|
||||
$company = Company::factory()->businessportal24()->create([
|
||||
'name' => 'Muster Energie GmbH',
|
||||
]);
|
||||
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Ticker-Meldung auf Unterseite',
|
||||
'slug' => 'ticker-meldung-auf-unterseite',
|
||||
'published_at' => now()->subHour(),
|
||||
]);
|
||||
|
||||
$this->get('https://businessportal24.test/de/newsrooms')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('AD-HOC')
|
||||
->assertSeeText('Ticker-Meldung auf Unterseite');
|
||||
|
||||
$this->get('https://businessportal24.test/de/preise')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('AD-HOC')
|
||||
->assertSeeText('Ticker-Meldung auf Unterseite');
|
||||
});
|
||||
|
||||
test('businessportal24 homepage hides data sections and shows the empty feed state without content', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://businessportal24.test/de')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Aktuelle Meldungen')
|
||||
->assertSeeText('Aktuell liegen keine Meldungen vor')
|
||||
->assertDontSeeText('Im Fokus')
|
||||
->assertDontSeeText('Aktive Newsrooms')
|
||||
->assertDontSeeText('Branchen-Index')
|
||||
->assertDontSeeText('Heute im Fokus');
|
||||
});
|
||||
|
||||
test('businessportal24 homepage feed only shows published businessportal content', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = Category::factory()->create([
|
||||
|
|
|
|||
|
|
@ -23,6 +23,22 @@ test('both editions render the homepage', function () {
|
|||
$this->get('https://businessportal24.test/en')->assertSuccessful();
|
||||
});
|
||||
|
||||
test('the english edition renders a localized ui', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://businessportal24.test/en')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Latest releases')
|
||||
->assertSeeText('Publish your press release')
|
||||
->assertSeeText('Our quality standard')
|
||||
->assertDontSeeText('Aktuelle Meldungen')
|
||||
->assertDontSeeText('Unser Qualitätsstandard');
|
||||
|
||||
$this->get('https://businessportal24.test/de')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Aktuelle Meldungen')
|
||||
->assertDontSeeText('Latest releases');
|
||||
});
|
||||
|
||||
test('an unsupported edition prefix returns 404', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://businessportal24.test/fr')->assertNotFound();
|
||||
|
|
|
|||
98
tests/Feature/Web/MarketDataTest.php
Normal file
98
tests/Feature/Web/MarketDataTest.php
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
use App\Services\Web\MarketDataService;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
beforeEach(function () {
|
||||
Cache::forget((string) config('market.cache_key'));
|
||||
|
||||
config()->set('market.enabled', true);
|
||||
config()->set('market.symbols', [
|
||||
['label' => 'DAX', 'symbol' => '^GDAXI', 'precision' => 0, 'suffix' => ''],
|
||||
['label' => 'EUR/USD', 'symbol' => 'EURUSD=X', 'precision' => 2, 'suffix' => ''],
|
||||
]);
|
||||
});
|
||||
|
||||
function fakeYahooChart(float $price, float $previousClose): array
|
||||
{
|
||||
return [
|
||||
'chart' => [
|
||||
'result' => [
|
||||
['meta' => ['regularMarketPrice' => $price, 'chartPreviousClose' => $previousClose]],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
it('holt Kurse, berechnet Deltas und cacht die Slides', function () {
|
||||
Http::fake([
|
||||
'*GDAXI*' => Http::response(fakeYahooChart(24150.0, 24000.0)),
|
||||
'*EURUSD*' => Http::response(fakeYahooChart(1.0815, 1.0902)),
|
||||
]);
|
||||
|
||||
$slides = app(MarketDataService::class)->refresh();
|
||||
|
||||
expect($slides)->toHaveCount(2)
|
||||
->and($slides[0]['label'])->toBe('DAX')
|
||||
->and($slides[0]['value'])->toBe('24.150')
|
||||
->and($slides[0]['delta'])->toBe('+0,6%')
|
||||
->and($slides[0]['positive'])->toBeTrue()
|
||||
->and($slides[1]['delta'])->toBe('-0,8%')
|
||||
->and($slides[1]['positive'])->toBeFalse();
|
||||
|
||||
Http::fake();
|
||||
|
||||
expect(app(MarketDataService::class)->slides())->toBe($slides);
|
||||
});
|
||||
|
||||
it('behält den Cache-Stand, wenn der Abruf komplett fehlschlägt', function () {
|
||||
Cache::put((string) config('market.cache_key'), [
|
||||
['label' => 'DAX', 'value' => '24.150', 'delta' => '+0,6%', 'positive' => true],
|
||||
], 3600);
|
||||
|
||||
Http::fake(['*' => Http::response(null, 500)]);
|
||||
|
||||
$slides = app(MarketDataService::class)->refresh();
|
||||
|
||||
expect($slides)->toHaveCount(1)
|
||||
->and($slides[0]['value'])->toBe('24.150');
|
||||
});
|
||||
|
||||
it('liefert keine Slides, wenn Marktdaten deaktiviert sind', function () {
|
||||
config()->set('market.enabled', false);
|
||||
|
||||
Cache::put((string) config('market.cache_key'), [
|
||||
['label' => 'DAX', 'value' => '24.150', 'delta' => '+0,6%', 'positive' => true],
|
||||
], 3600);
|
||||
|
||||
expect(app(MarketDataService::class)->slides())->toBe([]);
|
||||
});
|
||||
|
||||
it('überspringt Symbole mit unbrauchbarer API-Antwort', function () {
|
||||
Http::fake([
|
||||
'*GDAXI*' => Http::response(['chart' => ['result' => [['meta' => []]]]]),
|
||||
'*EURUSD*' => Http::response(fakeYahooChart(1.0815, 1.0902)),
|
||||
]);
|
||||
|
||||
$slides = app(MarketDataService::class)->refresh();
|
||||
|
||||
expect($slides)->toHaveCount(1)
|
||||
->and($slides[0]['label'])->toBe('EUR/USD');
|
||||
});
|
||||
|
||||
it('aktualisiert Kurse über den Artisan-Command', function () {
|
||||
Http::fake(['*' => Http::response(fakeYahooChart(24150.0, 24000.0))]);
|
||||
|
||||
$this->artisan('market:refresh')
|
||||
->expectsOutputToContain('DAX')
|
||||
->assertSuccessful();
|
||||
|
||||
expect(app(MarketDataService::class)->slides())->not->toBeEmpty();
|
||||
});
|
||||
|
||||
it('meldet einen Fehlschlag des Commands ohne Kursdaten', function () {
|
||||
Http::fake(['*' => Http::response(null, 500)]);
|
||||
|
||||
$this->artisan('market:refresh')->assertFailed();
|
||||
});
|
||||
|
|
@ -7,8 +7,32 @@ use App\Models\Company;
|
|||
use App\Models\PressRelease;
|
||||
use Tests\TestCase;
|
||||
|
||||
test('presseecho homepage renders the editorial shell', function () {
|
||||
test('presseecho homepage renders the editorial shell with real data', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = Category::factory()->create([
|
||||
'portal' => Portal::Presseecho,
|
||||
]);
|
||||
$category->translations()->create([
|
||||
'locale' => 'de',
|
||||
'name' => 'Energie & Klima',
|
||||
'slug' => 'energie-klima',
|
||||
]);
|
||||
|
||||
$company = Company::factory()->presseecho()->create([
|
||||
'name' => 'Muster Energie GmbH',
|
||||
]);
|
||||
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Presseecho)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Shell-Test Meldung',
|
||||
'slug' => 'shell-test-meldung',
|
||||
'published_at' => now()->subHour(),
|
||||
]);
|
||||
|
||||
$this->get('https://presseecho.test/de')
|
||||
->assertSuccessful()
|
||||
->assertSee('presseecho', false)
|
||||
|
|
@ -16,14 +40,28 @@ test('presseecho homepage renders the editorial shell', function () {
|
|||
->assertSeeText('Aktuelle Meldungen')
|
||||
->assertSeeText('Im Fokus')
|
||||
->assertSeeText('Aktive Newsrooms')
|
||||
->assertSeeText('Bleiben Sie informiert')
|
||||
->assertSeeText('Veröffentlichen Sie Ihre Pressemitteilung')
|
||||
->assertSeeText('Redaktioneller Qualitätsstandard')
|
||||
->assertSeeText('Ihre Pressemitteilung, qualitätsgeprüft veröffentlicht')
|
||||
->assertSeeText('Konto anlegen')
|
||||
->assertSeeText('Unser Qualitätsstandard')
|
||||
->assertDontSeeText('redaktionell geprüft')
|
||||
->assertSeeText('AD-HOC')
|
||||
->assertSeeText('Branchen-Index')
|
||||
->assertSeeText('Heute im Fokus');
|
||||
});
|
||||
|
||||
test('presseecho homepage hides data sections and shows the empty feed state without content', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://presseecho.test/de')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Aktuelle Meldungen')
|
||||
->assertSeeText('Aktuell liegen keine Meldungen vor')
|
||||
->assertDontSeeText('Im Fokus')
|
||||
->assertDontSeeText('Aktive Newsrooms')
|
||||
->assertDontSeeText('Branchen-Index')
|
||||
->assertDontSeeText('Heute im Fokus');
|
||||
});
|
||||
|
||||
test('presseecho homepage feed only shows published presseecho content', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = Category::factory()->create([
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue