diff --git a/docs/Echte öffentliche Unterseiten.md b/docs/Echte öffentliche Unterseiten.md index 33f4c9c4..c36495de 100644 --- a/docs/Echte öffentliche Unterseiten.md +++ b/docs/Echte öffentliche Unterseiten.md @@ -2,6 +2,23 @@ > der oeffentlichen Strecke. Welcher Punkt bereits umgesetzt ist, ist > jeweils mit einer kurzen IST-Notiz markiert. +> **Update 17.06.2026 — Bestands-URL-Mapping (Launch-Vorbereitung).** Die +> komplette URL-Struktur der Altseite (siehe `docs/frontend/Struktur Frontend.md`) +> ist jetzt per 301 auf die neue Struktur abgebildet: englische Alt-Slugs +> (`login`, `contact`, `imprint`, `terms`, `privacypolicy`, `pricelist`, +> `service`) → neue deutsche Pfade bzw. Hub, `/presse/*`-Ratgeber → `/faq`, +> `presskit/{id}/{slug}` → `/newsroom/{slug}` (Feinmapping über +> `companies.legacy_id`, Fallback `/newsrooms`), entfallene Sprachvarianten +> `de-ch`/`de-at` → `/de`. Region-Übersichten (`/region/{slug}`) sind bewusst +> ein späterer Schritt. Implementierung in `routes/web.php`; Test: +> `tests/Feature/Web/LegacyRedirectTest.php`. + +> **Update 17.06.2026 — Newsrooms.** Newsroom-Markenseiten pro Unternehmen +> (`/{edition}/newsroom/{slug}`) und das Newsrooms-Verzeichnis +> (`/{edition}/newsrooms`) sind im Editorial-Design mit echten Daten +> umgesetzt (siehe Punkt 4). Neue server-seitige Pagination-Komponente +> `x-web.pagination`. Test: `tests/Feature/Web/NewsroomPageTest.php`. + > **Update 16.06.2026 — Editorial-Relaunch & Ausgaben-Routing.** > Die oeffentliche Strecke wurde auf ein gemeinsames Editorial-Design > (Komponenten `x-web.site-header` / `x-web.site-footer`, Design-Tokens in @@ -45,6 +62,7 @@ _IST 21.05.2026_: noch nicht umgesetzt. **4. Newsroom-Seite eines Unternehmens** – `/newsroom/[slug]` Markenseite eines Premium-Publishers mit eigener URL, Logo, allen PMs des Unternehmens. Ist gleichzeitig Verkaufsargument für Pro-/Agency-Tarif und SEO-Vorteil für die Unternehmen. _IST 21.05.2026_: Layout vorhanden (`web/newsrooms.blade.php`), Daten-Anbindung pro Firma noch offen. +_IST 17.06.2026_: umgesetzt im Editorial-Design mit echten Daten. **Markenseite** `web/newsroom.blade.php` (Route `newsroom`, URL `/{edition}/newsroom/{slug}`): Logo/Initiale, Boilerplate, Website, Branchen der Firma, Kennzahlen (gesamt/30 Tage/heute/Aufrufe/seit Jahr), Top-Meldung und paginierte Meldungsliste (`x-web.pagination`), Sidebar (Meistgelesen, Publisher-CTA). 404 bei unbekanntem Slug oder Firma ohne veröffentlichte Meldung. Zusätzlich **Verzeichnis** `web/newsrooms.blade.php` (Route `newsrooms`, URL `/{edition}/newsrooms`): Karten-Grid aller aktiven Newsrooms mit Meldungs-/Aktivitätszahlen, Namens-Suche (`?q=`) und Pagination. `x-web.active-newsrooms` verlinkt jetzt über `route('newsroom', …)`. Legacy-`.html`-URLs (`/newsrooms.html`, `/newsroom/{slug}.html`) per 301. Test: `tests/Feature/Web/NewsroomPageTest.php`. **5. Such-Ergebnisseite** – `/suche?q=...` Volltextsuche mit Filtern (Erweiterte Suche schreibt in URL-Parameter, dadurch teilbar/bookmarkbar). _IST 21.05.2026_: Layout vorhanden (`web/suche.blade.php`), Volltextsuche noch nicht aktiv. @@ -61,6 +79,7 @@ _IST 16.06.2026_: Landing-Seite im Editorial-Design neu aufgebaut, echte Kennzah **8. Tarife & Preise** – `/preise` _(oder als Modal aus mehreren Stellen aufrufbar)_ Da Tarife auch im Modal aus dem CTA aufgerufen werden, ist die Frage: brauchen wir die Seite? Antwort ja, weil SEO ("Pressemitteilung veröffentlichen Preise" ist eine wichtige Suche) und weil sie verlinkbar sein muss aus AGB, Footer, Mediadaten. _IST 21.05.2026_: Layout vorhanden (`web/preise.blade.php`), echte Tarife noch nicht hinterlegt (Tarif-Modul siehe `Presseportal – Konzept für Relaunch.md` Abschnitt 8). +_IST 17.06.2026_: Tarife sind jetzt **datengetrieben** aus dem System (`Plan::active()`) auf der **Startseite** als Tarif-Teaser sichtbar (neue Komponente `x-web.pricing-teaser`, Preis/Quota/Tageslimit/Jahrespreis je Tarif, „Beliebt"-Hervorhebung). Der **Preise-Link** ist auf jeder Portalseite präsent: in der Header-Utility-Bar (neu) und im Footer („Tarife & Pakete", bestand bereits). Test: `tests/Feature/Web/PricingTeaserTest.php`. **Offen**: Die eigentliche `/preise`-Seite (`web/preise.blade.php`) ist noch im alten Theme mit Platzhalter-Tarifen — Editorial-Rebuild mit `x-web.pricing-teaser`/echten Plänen ist der nächste Schritt. **9. Mediadaten / Werbung** – `/mediadaten` oder `/werben` Für Mediaplaner und potentielle Werbekunden: alle buchbaren Slot-Typen (Top-Slot, Highlights, Newsletter, Branchen-Sponsoring), Reichweiten-Daten, Preise, Booking-Kontakt. Pflicht-Seite für jede Plattform mit Anzeigeninventar. _IST 21.05.2026_: nicht umgesetzt. diff --git a/docs/frontend/Struktur Frontend.md b/docs/frontend/Struktur Frontend.md new file mode 100644 index 00000000..d7e87f2d --- /dev/null +++ b/docs/frontend/Struktur Frontend.md @@ -0,0 +1,150 @@ + + +## 1. Hauptnavigation (rechts oben) + +| Seite | Link | Dahinter | Relaunch | +| ------------- | ----------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------- | +| Home | `/` | Startseite | **Portal** | +| Registrierung | `/de/registration.html` | Account-Anlage | → **Hub** (pressekonto) | +| Login | `/de/login.html` | Anmeldung | → **Hub** (Sanctum, kein eigener Login am Portal) | +| Preisliste | `/de/pricelist.html` | Tarife | → **Hub** (neue Tarifstruktur Einzel/Starter/…/Agency) | +| Service | `/de/service.html` | Leistungsübersicht / Verkaufsseite | wird zu veroeffentlichen | +| Kontakt | `/de/contact.html` | Kontakt | **Portal** + Hub (auch hier muss deutlich werden, dass der Hub das ganze Backend System ist) | + +## 2. Region/Sprache + +| Variante | Link | Relaunch | +| ------------ | -------- | ---------- | +| Deutsch | `/` | **Portal** | +| Deutsch (CH) | `/de-ch` | raus | +| Deutsch (AT) | `/de-at` | raus | +| English | `/en` | Portal | + + +## 3. „Presse"-Dropdown — Ratgeber/SEO-Content (12 Seiten) + +Das ist editorial wertvollster Teil: statische Ratgeber-Seiten, die wahrscheinlich organischen Long-Tail-Traffic ziehen. + +|Seite|Link| +|---|---| +|Pressemitteilung schreiben|`/de/presse/pressemitteilung-schreiben.html`| +|Aufbau Pressemitteilung|`/de/presse/pressemitteilung-aufbau.html`| +|Pressemitteilung Nutzen & Einsatz|`/de/presse/pressemitteilung-nutzen.html`| +|Pressemappe|`/de/presse/pressemappe.html`| +|Redakteure & Journalisten|`/de/presse/redakteure-journalisten.html`| +|Pressemitteilung Beispiel|`/de/presse/pressemitteilung-beispiel.html`| +|Pressekonferenz|`/de/presse/pressekonferenz.html`| +|Deutsche Presse Agentur|`/de/presse/deutsche-presseagentur.html`| +|Redaktion|`/de/presse/redaktion.html`| +|Pressestelle|`/de/presse/pressestelle.html`| +|Pressesprecher|`/de/presse/pressesprecher.html`| +|Presseausweis|`/de/presse/presseausweis.html`| + +**Relaunch:** Diese URLs unbedingt 1:1 erhalten oder sauber 301-redirecten — hier steckt der SEO-Wert. Editorial passen sie zu beiden Portalen, ggf. sogar besser zu presseecho (Kontexttiefe). Entscheidung offen, wo sie inhaltlich angesiedelt werden. +Hier würde ich eine FAQ Seite machen, die alle Themen beinhaltet und sauber 301-redirectet + +## 4. Kategorien (14 Stück) + +`/de/category/{slug}.html` — Listenseiten je Themenrubrik: + +Handel · IT · Finanzen · Reisen · Kultur · Politik · Medizin · Sport · Motor/Verkehr · Haus · Mode · Sonstiges · Wissenschaft/Forschung · Recht + +**Relaunch:** Diese Kategoriestruktur ist die Backbone für Migration + Boost-Logik (Kategorieseiten sind Teil deiner Boost-Platzierung). Beim Relaunch zu prüfen: Übernimmst du alle 14 unverändert, oder konsolidierst du? „Sonstiges" als Auffangbecken ist für deine Content-Score-/Trust-Logik suboptimal. + +## 5. Content-/Detailseiten (die eigentlichen Templates) + +|Typ|URL-Muster|Funktion|Relaunch| +|---|---|---|---| +|PM-Detailseite|`/de/{slug}.html`|Einzelne Pressemitteilung|**Portal** — Kern-Template| +|Kategorie-Liste|`/de/category/{slug}.html`|PMs je Rubrik, paginiert|**Portal**| +|Pressemappe (Presskit)|`/de/presskit/{id}/{slug}.html`|Firmenprofil + alle PMs der Firma|**Portal** = deine „Firmen-Detailseite"| +|Stream + Paginierung|`/de?page=N`|732 Seiten Archiv|**Portal** — aber Paginierungs-/Indexierungsstrategie überdenken| + +Die Presskit-Seite entspricht funktional exakt deiner geplanten **Firmen-Detailseite** (Header, Stammdaten, Boilerplate, Kontakt, gefilterte PM-Liste). Gutes Mapping — du baust das also nicht neu, sondern modernisierst ein bestehendes Konstrukt. + +## 6. Footer / Legal + +| Seite | Link | Relaunch | +| ----------- | ------------------------ | -------------------------------------------------- | +| Impressum | `/de/imprint.html` | **Portal** + Hub (pflicht, pro Domain) | +| AGB | `/de/terms.html` | **Hub** (Vertragsbeziehung läuft über pressekonto) | +| Datenschutz | `/de/privacypolicy.html` | **Portal** + Hub | + + + +--- + +## Einordnung für den Relaunch + +Wenn ich das gegen deine Hub-and-Spoke-Architektur lege, fällt die Seitenmenge in drei klare Eimer: + +**Bleibt am Portal (businessportal24.com & presseecho.de = öffentlicher Spoke):** Home/Stream, PM-Detail, Kategorie-Listen, Pressemappe/Firmen-Detail, Ratgeber-Seiten, Impressum, Datenschutz, Kontakt. + +**Wandert in den Hub (pressekonto):** Registrierung, Login, Preisliste/Tarife, AGB, und alles dahinter (PM erstellen, Pressemappe anlegen, Account). Das deckt sich mit deinem Prinzip „Portale haben keine eigenen Login-Systeme". + +--- + +## Relaunch-Mapping der Bestands-URLs (IST 17.06.2026) + +Alle Alt-URLs werden per **301** auf die neue Struktur geleitet (Implementierung +in `routes/web.php`, Closure `$htmlRedirect` + Regions-Routen; Test: +`tests/Feature/Web/LegacyRedirectTest.php`). Die neue Struktur liegt unter dem +Ausgaben-Präfix `/{de|en}/…`. + +| Alt-URL (Altsystem) | Ziel (301) | Status | +| ------------------------------------ | ---------------------------------- | ------ | +| `/` | `/de` (Default-Ausgabe) | ✅ | +| `/de/{slug}.html` (PM) | `/de/press-release/{slug}` | ✅ | +| `/de/category/{slug}.html` | `/de/category/{slug}` | ✅ | +| `/de/presskit/{id}/{slug}.html` | `/de/newsroom/{slug}` *(Lookup über `companies.legacy_id`; Fallback `/newsrooms`)* | ✅ | +| `/de/presse/*.html` (12 Ratgeber) | `/de/faq` (konsolidiert) | ✅ | +| `/de/pricelist.html` | `/de/preise` | ✅ | +| `/de/service.html` | `/de/veroeffentlichen` | ✅ | +| `/de/contact.html` | `/de/kontakt` | ✅ | +| `/de/imprint.html` | `/de/impressum` | ✅ | +| `/de/terms.html` | `/de/agb` | ✅ | +| `/de/privacypolicy.html` | `/de/datenschutz` | ✅ | +| `/de/registration.html` | Hub `pressekonto/register` | ✅ | +| `/de/login.html` | Hub `pressekonto/login` | ✅ | +| `/de/kategorien.html` (Übersicht) | `/de` (Übersicht entfällt) | ✅ | +| `/de-ch`, `/de-at` (+ Subpfade) | `/de` | ✅ | + +**Bewusst auf später verschoben (eigene Slice):** + +- **Regionen-Übersichten** `/region/{slug}` (Punkt 3 der öffentlichen Strecke) – + aktuell leiten nur die entfallenen Sprachvarianten `de-ch`/`de-at` auf `/de`. +- **Tarife/AGB im Hub**: `pricelist` zeigt vorerst auf die öffentliche + SEO-Seite `/preise`; die buchbare Tarifstruktur (Checkout) lebt im Hub. + +**Erledigtes Feinmapping:** + +- **Presskit→Newsroom**: `presskit/{id}/{slug}` löst über den Unique-Index + `companies.(legacy_portal, legacy_id)` den heutigen Newsroom auf und leitet + 301 direkt auf `/newsroom/{slug}` (nur bei aktiver Firma mit ≥1 veröffentlichter + Meldung; sonst Fallback `/newsrooms`). +- **Preise datengetrieben & überall sichtbar**: Tarif-Teaser auf der Startseite + aus `Plan::active()` (`x-web.pricing-teaser`); Preise-Link in Header-Utility-Bar + und Footer. Test: `tests/Feature/Web/PricingTeaserTest.php`. +- **Statische Seiten im Editorial-Design** (Rebuild abgeschlossen): Pflicht- + seiten `impressum`, `datenschutz`, `agb`, `cookies` (Prosa über `.pm-body`) + sowie `faq` (natives `
`-Akkordeon), `hilfe` (Support-Landing), + `kontakt` (Kontaktwege; Formular wartet auf POST-Handler — als TODO markiert), + `ueber-uns` (Kennzahlen/Mission/Werte/Team/Timeline) und `api` + (Features/Code-Beispiel/Integrationen). Tote Doku-/Anker-Links durch echte + Ziele bzw. Hinweise ersetzt. Gemeinsames Gerüst: `x-web.static-page`. +- **Header/Footer-Linkpflege**: `site-header`/`site-footer` ohne tote `#`-Links — + FAQ/Hilfe/Cookies verlinkt, „Anmelden" führt in den Hub + (`{domain_portal_url}/login`), nicht verlinkte Platzhalter entfernt. + Test: `tests/Feature/Web/NavigationLinksTest.php`. + Gemeinsames Gerüst: wiederverwendbare Komponente `x-web.static-page` + (site-header, Breadcrumb, Masthead, site-footer). Altes Theme + (`livewire:web.header`, `gradient-hero`) entfernt. Rechtstexte sind weiterhin + Platzhalter (vor Go-Live ersetzen). Test: `tests/Feature/Web/StaticPagesTest.php`. +- **`/preise`-Seite im Editorial-Design** (Rebuild abgeschlossen): Reihenfolge + Abo-Tarife → Einzel-PM → Add-ons. Datengetrieben über `Plan::active()`, + `config('billing.single_pm_price_cents')` und `config('credits.*')`; Route + `$preisePage`-Closure. Texte ausgelagert in `lang/{de,en}/pricing.php` + (Schlüssel `pricing.*`), damit Portal/Hub einheitlich ziehen können. Add-on- + Lineup (Magic-Link-Pfade) ist mit TODO-Kommentaren als „noch im Rework" + markiert. Test: `tests/Feature/Web/PreisePageTest.php`. + diff --git a/lang/de/pricing.php b/lang/de/pricing.php new file mode 100644 index 00000000..e69fe375 --- /dev/null +++ b/lang/de/pricing.php @@ -0,0 +1,103 @@ + 'Preise & Tarife – Pressemitteilungen veröffentlichen', + 'meta_description' => 'Transparente Tarife für Pressemitteilungen: Abo mit Monatskontingent, Einzelveröffentlichung und flexible Add-ons. Alle Preise netto zzgl. USt.', + + 'eyebrow' => 'Tarife & Preise', + 'headline' => 'Transparente Preise für jede Reichweite', + 'intro' => 'Veröffentlichen Sie regelmäßig im Abo mit monatlichem Kontingent oder einzeln ohne Bindung. Add-ons buchen Sie flexibel dazu. Alle Preise sind Nettopreise zzgl. gesetzlicher USt.', + + // 1. Abo-Tarife (Daten: Plan::active()) + 'plans' => [ + 'eyebrow' => 'Abo-Tarife', + 'title' => 'Abo-Tarife mit Monatskontingent', + 'subtitle' => 'Monatlich kündbar. Im Jahrestarif sind zwei Monate gratis enthalten.', + 'per_month' => '/ Monat', + 'per_year' => '/ Jahr', + 'net' => 'netto zzgl. USt.', + 'yearly_hint' => ':price € / Jahr · 2 Monate gratis', + 'quota' => ':count Pressemitteilungen / Monat', + 'daily_limit' => 'max. :count Veröffentlichungen / Tag', + 'no_daily_limit' => 'Ohne Tageslimit', + 'included' => 'Redaktionelle Prüfung & Veröffentlichung inklusive', + 'cta' => 'Tarif wählen', + 'popular' => 'Beliebt', + 'empty' => 'Tarife werden in Kürze veröffentlicht.', + // TODO: Enterprise-Kontaktadresse zentralisieren, sobald final. + 'enterprise' => 'Mehr Veröffentlichungen, mehrere Marken oder Sonderkonditionen? Enterprise-Konditionen erhalten Sie auf Anfrage über den Support.', + ], + + // 2. Einzel-Pressemitteilung (Daten: config('billing.single_pm_price_cents')) + 'single' => [ + 'eyebrow' => 'Ohne Abo', + 'title' => 'Einzel-Pressemitteilung', + 'description' => 'Genau eine Veröffentlichung inklusive redaktioneller Prüfung. Bezahlt wird erst bei Veröffentlichung – abgelehnte Mitteilungen kosten nichts.', + 'price_suffix' => 'pro Veröffentlichung', + 'net' => 'netto zzgl. USt.', + 'cta' => 'Einzeln veröffentlichen', + ], + + // 3. Add-ons & Einzelkosten (Daten: config('credits')) + 'addons' => [ + 'eyebrow' => 'Add-ons', + 'title' => 'Add-ons & Einzelkosten', + 'subtitle' => '1 Credit = 1 €. Add-ons buchen Sie flexibel aus Ihrem Guthaben – größere Credit-Pakete enthalten Bonus-Guthaben.', + 'credits_unit' => 'Credits', + 'from' => 'ab', + + 'extra_pm' => [ + 'title' => 'Extra-Pressemitteilung', + 'description' => 'Eine weitere Veröffentlichung, wenn das Monatskontingent ausgeschöpft ist – ohne Tarifwechsel. Der Preis sinkt mit höherem Tarif.', + 'tier_label' => 'Tarif', + 'price_label' => 'Preis je PM', + ], + + 'boost' => [ + 'title' => 'Boost-Platzierung', + 'description' => 'Prominente Platzierung Ihrer Meldung auf der Startseite und der passenden Branchenseite.', + 'days' => ':count Tage', + ], + + 'proof_pdf' => [ + 'title' => 'Veröffentlichungsnachweis (PDF)', + 'description' => 'Offizieller Nachweis Ihrer Veröffentlichung als PDF-Dokument – ideal für Reporting und Archiv.', + 'unit' => 'pro Nachweis', + ], + + 'packs' => [ + 'title' => 'Credit-Pakete', + 'description' => 'Guthaben aufladen und flexibel für Add-ons einsetzen. Größere Pakete enthalten Bonus-Credits.', + 'credits' => ':count Credits', + 'bonus' => '+:count Bonus', + 'net' => 'netto', + ], + + // TODO: Diese Leistungen hängen am Magic-Link-/Bookings-Rework — Lineup + // und Preise final abstimmen, sobald die In-App-Seite steht. + 'more' => [ + 'title' => 'Weitere Leistungen', + 'correction' => 'Inhaltliche Korrektur', + 'update' => 'Update / Ergänzung', + 'depublish' => 'Depublizieren', + ], + ], + + 'vat_note' => 'Alle Preise sind Nettopreise und verstehen sich zzgl. der gesetzlichen Umsatzsteuer. Die Abrechnung erfolgt sicher über unseren Zahlungsdienstleister.', +]; diff --git a/lang/en/pricing.php b/lang/en/pricing.php new file mode 100644 index 00000000..928109aa --- /dev/null +++ b/lang/en/pricing.php @@ -0,0 +1,97 @@ + 'Pricing & Plans – Publish press releases', + 'meta_description' => 'Transparent pricing for press releases: subscription with a monthly quota, single publication and flexible add-ons. All prices net, plus VAT.', + + 'eyebrow' => 'Plans & Pricing', + 'headline' => 'Transparent pricing for every reach', + 'intro' => 'Publish regularly on a subscription with a monthly quota, or one-off without commitment. Add-ons are booked flexibly on top. All prices are net, plus statutory VAT.', + + // 1. Subscription plans (data: Plan::active()) + 'plans' => [ + 'eyebrow' => 'Subscriptions', + 'title' => 'Subscriptions with a monthly quota', + 'subtitle' => 'Cancel monthly. The annual plan includes two months free.', + 'per_month' => '/ month', + 'per_year' => '/ year', + 'net' => 'net, plus VAT.', + 'yearly_hint' => ':price € / year · 2 months free', + 'quota' => ':count press releases / month', + 'daily_limit' => 'max. :count publications / day', + 'no_daily_limit' => 'No daily limit', + 'included' => 'Editorial review & publication included', + 'cta' => 'Choose plan', + 'popular' => 'Popular', + 'empty' => 'Plans will be published shortly.', + 'enterprise' => 'More publications, multiple brands or special terms? Enterprise conditions are available on request via support.', + ], + + // 2. Single press release (data: config('billing.single_pm_price_cents')) + 'single' => [ + 'eyebrow' => 'No subscription', + 'title' => 'Single press release', + 'description' => 'Exactly one publication including editorial review. You only pay on publication – rejected releases are free.', + 'price_suffix' => 'per publication', + 'net' => 'net, plus VAT.', + 'cta' => 'Publish a single release', + ], + + // 3. Add-ons & individual costs (data: config('credits')) + 'addons' => [ + 'eyebrow' => 'Add-ons', + 'title' => 'Add-ons & individual costs', + 'subtitle' => '1 credit = 1 €. Add-ons are booked flexibly from your balance – larger credit packs include bonus credits.', + 'credits_unit' => 'credits', + 'from' => 'from', + + 'extra_pm' => [ + 'title' => 'Extra press release', + 'description' => 'One more publication when your monthly quota is used up – without changing plans. The price drops with a higher plan.', + 'tier_label' => 'Plan', + 'price_label' => 'Price per release', + ], + + 'boost' => [ + 'title' => 'Boost placement', + 'description' => 'Prominent placement of your release on the homepage and the matching industry page.', + 'days' => ':count days', + ], + + 'proof_pdf' => [ + 'title' => 'Proof of publication (PDF)', + 'description' => 'Official proof of your publication as a PDF document – ideal for reporting and archiving.', + 'unit' => 'per proof', + ], + + 'packs' => [ + 'title' => 'Credit packs', + 'description' => 'Top up your balance and use it flexibly for add-ons. Larger packs include bonus credits.', + 'credits' => ':count credits', + 'bonus' => '+:count bonus', + 'net' => 'net', + ], + + 'more' => [ + 'title' => 'Further services', + 'correction' => 'Content correction', + 'update' => 'Update / addition', + 'depublish' => 'Unpublish', + ], + ], + + 'vat_note' => 'All prices are net and exclude statutory VAT. Billing is handled securely via our payment provider.', +]; diff --git a/resources/views/components/web/active-newsrooms.blade.php b/resources/views/components/web/active-newsrooms.blade.php index af86a304..1dbd8c6c 100644 --- a/resources/views/components/web/active-newsrooms.blade.php +++ b/resources/views/components/web/active-newsrooms.blade.php @@ -33,7 +33,7 @@ $count = (int) ($company['count'] ?? 0); $color = $company['color'] ?? $palette[$index % count($palette)]; $slug = $company['slug'] ?? null; - $href = $slug ? url('/newsroom/'.$slug) : route('newsrooms'); + $href = $slug ? route('newsroom', ['slug' => $slug]) : route('newsrooms'); $isLast = $loop->last; @endphp format('H:i'); if ($published->isToday()) { - $dateLine = $published->translatedFormat('j. MMM'); + $dateLine = $published->translatedFormat('j. M'); } elseif ($published->isYesterday()) { $dateLine = 'Gestern'; } else { - $dateLine = $published->translatedFormat('j. MMM'); + $dateLine = $published->translatedFormat('j. M'); } $datetimeAttr = $published->toIso8601String(); } else { diff --git a/resources/views/components/web/pagination.blade.php b/resources/views/components/web/pagination.blade.php index b6b299bc..7ef38d40 100644 --- a/resources/views/components/web/pagination.blade.php +++ b/resources/views/components/web/pagination.blade.php @@ -1,82 +1,44 @@ @props([ - 'currentPage' => 1, - 'totalPages' => 10, - 'showFirstLast' => true, - 'maxVisible' => 5, + 'paginator' => null, ]) -@php - $hasPrevious = $currentPage > 1; - $hasNext = $currentPage < $totalPages; - - // Berechne sichtbare Seitenzahlen - $visiblePages = []; - - if ($totalPages <= $maxVisible) { - // Zeige alle Seiten wenn weniger als maxVisible - $visiblePages = range(1, $totalPages); - } else { - // Berechne dynamischen Bereich - $start = max(1, $currentPage - floor($maxVisible / 2)); - $end = min($totalPages, $start + $maxVisible - 1); - - // Korrigiere Start wenn am Ende - if ($end - $start < $maxVisible - 1) { - $start = max(1, $end - $maxVisible + 1); - } - - $visiblePages = range($start, $end); - } - - $showStartEllipsis = $visiblePages[0] > 1; - $showEndEllipsis = end($visiblePages) < $totalPages; -@endphp - - + +@endif diff --git a/resources/views/components/web/pricing-teaser.blade.php b/resources/views/components/web/pricing-teaser.blade.php new file mode 100644 index 00000000..56da1032 --- /dev/null +++ b/resources/views/components/web/pricing-teaser.blade.php @@ -0,0 +1,104 @@ +@props([ + 'plans' => null, + 'popular' => 'business', + 'pricingHref' => null, + 'submitHref' => null, +]) + +@php + // Datengetrieben aus dem Tarif-Katalog (plans). Fällt die Tabelle leer aus, + // wird die Sektion komplett ausgeblendet. + $plans = $plans ?? \App\Models\Plan::active()->get(); + $pricingHref ??= route('preise'); + $submitHref ??= route('veroeffentlichen'); + + $fmtPrice = static fn (int $cents): string => number_format($cents / 100, $cents % 100 === 0 ? 0 : 2, ',', '.'); +@endphp + +@if ($plans->isNotEmpty()) +
+
+

Tarife & Pakete

+ + Alle Details + + +
+
+ +
+ @foreach ($plans as $plan) + @php + $isPopular = $plan->slug === $popular; + $hasMonthly = (int) $plan->monthly_price_cents > 0; + @endphp +
+ @if ($isPopular) + + Beliebt + + @endif + +
{{ $plan->name }}
+ +
+ {{ $fmtPrice((int) $plan->monthly_price_cents) }} € + @if ($hasMonthly) + / Monat + @endif +
+ + @if ($hasMonthly && (int) $plan->yearly_price_cents > 0) +
+ {{ $fmtPrice((int) $plan->yearly_price_cents) }} € / Jahr + · 2 Monate gratis +
+ @else +
+ @endif + +
    +
  • + {{ $plan->press_release_quota }} + Pressemitteilungen / Monat +
  • + @if ($plan->daily_limit !== null) +
  • + {{ $plan->daily_limit }} + max. pro Tag +
  • + @else +
  • + ∞ + kein Tageslimit +
  • + @endif +
+ + + Auswählen + + +
+ @endforeach +
+ +
+

+ Alle Preise zzgl. MwSt. · Jederzeit kündbar · Auch ohne Abo als Einzelveröffentlichung möglich. +

+ + Direkt veröffentlichen + + +
+
+@endif diff --git a/resources/views/components/web/site-footer.blade.php b/resources/views/components/web/site-footer.blade.php index 558ac62e..abe42144 100644 --- a/resources/views/components/web/site-footer.blade.php +++ b/resources/views/components/web/site-footer.blade.php @@ -52,9 +52,7 @@ @@ -62,8 +60,9 @@
Unternehmen
@@ -75,8 +74,8 @@ {{ $legal }} AGB - Cookie-Einstellungen - DSGVO + Cookies + Datenschutz diff --git a/resources/views/components/web/site-header.blade.php b/resources/views/components/web/site-header.blade.php index 7634e098..55949e45 100644 --- a/resources/views/components/web/site-header.blade.php +++ b/resources/views/components/web/site-header.blade.php @@ -84,8 +84,9 @@ - - + Preise + + @@ -141,8 +142,8 @@ - {{-- Anmelden --}} -
+
+ {{ $brandName }}{{ $brandAccent }} + @if ($breadcrumb) + + {{ $breadcrumb }} + @endif +
+
+ + {{-- Masthead --}} +
+ @if ($eyebrow) +
{{ $eyebrow }}
+ @endif +

{{ $title }}

+ @if ($lead) +

{{ $lead }}

+ @endif + @if ($updated) +

{{ $updated }}

+ @endif +
+
+ + {{-- Inhalt --}} +
+ {{ $slot }} +
+ + + diff --git a/resources/views/livewire/web/press-release-feed.blade.php b/resources/views/livewire/web/press-release-feed.blade.php index 478c1555..adca8272 100644 --- a/resources/views/livewire/web/press-release-feed.blade.php +++ b/resources/views/livewire/web/press-release-feed.blade.php @@ -129,7 +129,7 @@ new class extends Component $inFeedAd = [ 'time' => '12:14', - 'date' => now()->translatedFormat('j. MMM'), + 'date' => now()->translatedFormat('j. M'), 'category' => 'Cloud · Software', 'title' => 'Microsoft Azure: Neue EU-Region Frankfurt mit DSGVO-zertifizierter KI-Infrastruktur', 'company' => 'Microsoft Deutschland GmbH', diff --git a/resources/views/web/agb.blade.php b/resources/views/web/agb.blade.php index def91dbb..b24eed44 100644 --- a/resources/views/web/agb.blade.php +++ b/resources/views/web/agb.blade.php @@ -1,158 +1,105 @@ @extends('web.layouts.web-master') -@section('title', 'AGB - Business Portal 24') +@section('title', 'Allgemeine Geschäftsbedingungen') +@section('meta_description', 'Allgemeine Geschäftsbedingungen für die Nutzung der Plattform.') @section('content') + + {{-- Platzhalter-Text — vor Go-Live durch die finalen AGB ersetzen. --}} +
+

§ 1 Geltungsbereich

+

+ Diese Allgemeinen Geschäftsbedingungen (AGB) gelten für alle Verträge zwischen der Business Portal + GmbH (im Folgenden „Anbieter") und ihren Kunden (im Folgenden „Kunde" oder „Nutzer") über die + Nutzung der Plattform Business Portal. +

+

+ Abweichende, entgegenstehende oder ergänzende AGB des Kunden werden nicht Vertragsbestandteil, es + sei denn, der Anbieter stimmt ihrer Geltung ausdrücklich schriftlich zu. +

-
- - +

§ 2 Vertragsgegenstand

+

+ Der Anbieter stellt dem Kunden eine Online-Plattform zur Veröffentlichung von Pressemitteilungen + zur Verfügung. Der Funktionsumfang richtet sich nach dem gewählten Tarif. +

+

Der Anbieter ist berechtigt, Subunternehmer zur Vertragserfüllung einzusetzen.

- -
-
-

- Allgemeine Geschäftsbedingungen -

-
-
+

§ 3 Vertragsschluss und Registrierung

+

+ Der Vertrag kommt durch die Registrierung des Kunden und die Bestätigung durch den Anbieter + zustande. Die Registrierung ist nur volljährigen, unbeschränkt geschäftsfähigen Personen gestattet. +

+

+ Der Kunde verpflichtet sich, bei der Registrierung wahre und vollständige Angaben zu machen und + diese aktuell zu halten. +

- -
-
-
-
+

§ 4 Leistungsumfang

+

+ Der konkrete Leistungsumfang richtet sich nach dem gewählten Tarif. Eine Beschreibung der Tarife + findet sich auf der Preisseite der Website. +

+

+ Der Anbieter ist berechtigt, die Dienste weiterzuentwickeln und Funktionen zu ändern, sofern dies + die Vertragserfüllung nicht wesentlich beeinträchtigt. +

-
-

§ 1 Geltungsbereich

-

- Diese Allgemeinen Geschäftsbedingungen (AGB) gelten für alle Verträge zwischen der Business Portal GmbH (im Folgenden „Anbieter") - und ihren Kunden (im Folgenden „Kunde" oder „Nutzer") über die Nutzung der Plattform Business Portal. -

-

- Abweichende, entgegenstehende oder ergänzende AGB des Kunden werden nicht Vertragsbestandteil, es sei denn, der Anbieter - stimmt ihrer Geltung ausdrücklich schriftlich zu. -

-
+

§ 5 Verfügbarkeit

+

+ Der Anbieter stellt die Plattform mit einer Verfügbarkeit von mindestens 98 % im Jahresmittel zur + Verfügung. Ausgenommen hiervon sind Wartungsarbeiten sowie Ausfälle, die nicht im Einflussbereich + des Anbieters liegen. +

-
-

§ 2 Vertragsgegenstand

-

- Der Anbieter stellt dem Kunden eine Online-Plattform zur Veröffentlichung von Pressemitteilungen zur Verfügung. - Der Funktionsumfang richtet sich nach dem gewählten Tarif. -

-

- Der Anbieter ist berechtigt, Subunternehmer zur Vertragserfüllung einzusetzen. -

-
+

§ 6 Pflichten des Kunden

+

Der Kunde verpflichtet sich:

+
    +
  • Nur rechtmäßige Inhalte zu veröffentlichen
  • +
  • Keine Rechte Dritter zu verletzen
  • +
  • Die Zugangsdaten vertraulich zu behandeln
  • +
  • Den Account nicht an Dritte weiterzugeben
  • +
  • Die Plattform nicht missbräuchlich zu nutzen
  • +
-
-

§ 3 Vertragsschluss und Registrierung

-

- Der Vertrag kommt durch die Registrierung des Kunden und die Bestätigung durch den Anbieter zustande. - Die Registrierung ist nur volljährigen, unbeschränkt geschäftsfähigen Personen gestattet. -

-

- Der Kunde verpflichtet sich, bei der Registrierung wahre und vollständige Angaben zu machen und diese aktuell zu halten. -

-
+

§ 7 Vergütung und Zahlung

+

+ Die Vergütung richtet sich nach dem gewählten Tarif. Alle Preise verstehen sich zzgl. der + gesetzlichen Mehrwertsteuer. +

+

+ Die Zahlung erfolgt im Voraus für den gewählten Abrechnungszeitraum (monatlich oder jährlich) per + Kreditkarte, SEPA-Lastschrift oder anderen vom Anbieter akzeptierten Zahlungsmethoden. +

-
-

§ 4 Leistungsumfang

-

- Der konkrete Leistungsumfang richtet sich nach dem gewählten Tarif. Eine Beschreibung der Tarife findet sich auf der - Preisseite der Website. -

-

- Der Anbieter ist berechtigt, die Dienste weiterzuentwickeln und Funktionen zu ändern, sofern dies die Vertragserfüllung - nicht wesentlich beeinträchtigt. -

-
+

§ 8 Laufzeit und Kündigung

+

+ Der Vertrag wird für die gewählte Mindestlaufzeit geschlossen und verlängert sich automatisch um den + gewählten Abrechnungszeitraum, sofern nicht gekündigt wird. +

+

+ Die Kündigung muss schriftlich oder in Textform (z. B. E-Mail) unter Einhaltung einer Frist von 30 + Tagen zum Ende des Abrechnungszeitraums erfolgen. +

-
-

§ 5 Verfügbarkeit

-

- Der Anbieter stellt die Plattform mit einer Verfügbarkeit von mindestens 98% im Jahresmittel zur Verfügung. - Ausgenommen hiervon sind Wartungsarbeiten sowie Ausfälle, die nicht im Einflussbereich des Anbieters liegen. -

-
- -
-

§ 6 Pflichten des Kunden

-

Der Kunde verpflichtet sich:

-
    -
  • Nur rechtmäßige Inhalte zu veröffentlichen
  • -
  • Keine Rechte Dritter zu verletzen
  • -
  • Die Zugangsdaten vertraulich zu behandeln
  • -
  • Den Account nicht an Dritte weiterzugeben
  • -
  • Die Plattform nicht missbräuchlich zu nutzen
  • -
-
- -
-

§ 7 Vergütung und Zahlung

-

- Die Vergütung richtet sich nach dem gewählten Tarif. Alle Preise verstehen sich zzgl. der gesetzlichen Mehrwertsteuer. -

-

- Die Zahlung erfolgt im Voraus für den gewählten Abrechnungszeitraum (monatlich oder jährlich) per Kreditkarte, - SEPA-Lastschrift oder anderen vom Anbieter akzeptierten Zahlungsmethoden. -

-
- -
-

§ 8 Laufzeit und Kündigung

-

- Der Vertrag wird für die gewählte Mindestlaufzeit geschlossen und verlängert sich automatisch um den gewählten - Abrechnungszeitraum, sofern nicht gekündigt wird. -

-

- Die Kündigung muss schriftlich oder in Textform (z.B. E-Mail) unter Einhaltung einer Frist von 30 Tagen zum Ende - des Abrechnungszeitraums erfolgen. -

-
- -
-

§ 9 Haftung

-

- Der Anbieter haftet nur für Schäden, die auf einer vorsätzlichen oder grob fahrlässigen Pflichtverletzung beruhen. - Dies gilt nicht für Schäden aus der Verletzung des Lebens, des Körpers oder der Gesundheit. -

-
- -
-

§ 10 Schlussbestimmungen

-

- Es gilt das Recht der Bundesrepublik Deutschland unter Ausschluss des UN-Kaufrechts. -

-

- Sollten einzelne Bestimmungen dieser AGB unwirksam sein oder werden, berührt dies die Wirksamkeit der übrigen Bestimmungen nicht. -

-
- -
-

- Stand: Januar 2025 -

-
- -
-
-
-
- - -
+

§ 9 Haftung

+

+ Der Anbieter haftet nur für Schäden, die auf einer vorsätzlichen oder grob fahrlässigen + Pflichtverletzung beruhen. Dies gilt nicht für Schäden aus der Verletzung des Lebens, des Körpers + oder der Gesundheit. +

+

§ 10 Schlussbestimmungen

+

Es gilt das Recht der Bundesrepublik Deutschland unter Ausschluss des UN-Kaufrechts.

+

+ Sollten einzelne Bestimmungen dieser AGB unwirksam sein oder werden, berührt dies die Wirksamkeit + der übrigen Bestimmungen nicht. +

+
+
@endsection - -@push('styles') - -@endpush diff --git a/resources/views/web/api.blade.php b/resources/views/web/api.blade.php index 8a2f8eec..95d81319 100644 --- a/resources/views/web/api.blade.php +++ b/resources/views/web/api.blade.php @@ -1,280 +1,97 @@ @extends('web.layouts.web-master') -@section('title', 'API & Integrationen - Business Portal 24') +@section('title', 'API & Integrationen') +@section('meta_description', 'Integrieren Sie unsere Dienste per REST-API: Pressemitteilungen programmatisch veröffentlichen, verwalten und abrufen.') + +@php + $features = [ + ['title' => 'RESTful API', 'text' => 'Moderne REST-Architektur mit JSON-Responses für einfache Integration in jede Programmiersprache.'], + ['title' => 'OAuth 2.0', 'text' => 'Sichere Authentifizierung mit OAuth 2.0 und API-Tokens für maximale Sicherheit.'], + ['title' => 'Webhooks', 'text' => 'Echtzeit-Benachrichtigungen über Events wie Veröffentlichungen oder Statusänderungen.'], + ['title' => 'Dokumentation', 'text' => 'Umfassende API-Dokumentation mit Code-Beispielen in verschiedenen Sprachen.'], + ['title' => 'Rate Limiting', 'text' => 'Faire Rate Limits mit klaren Headern und automatischer Skalierung bei Bedarf.'], + ['title' => 'SDKs', 'text' => 'Offizielle SDKs für PHP, JavaScript, Python und weitere Sprachen.'], + ]; + + $integrations = ['WordPress', 'Zapier', 'Slack', 'HubSpot']; +@endphp @section('content') + + {{-- Überblick --}} +
+

Leistungsstarke REST-API

+

+ Unsere RESTful API ermöglicht es Ihnen, Pressemitteilungen programmatisch zu veröffentlichen, zu + verwalten und abzurufen – ideal für Unternehmen mit hohem Veröffentlichungsvolumen oder bestehenden + Content-Management-Systemen. +

+

+ Die API ist vollständig dokumentiert und unterstützt moderne Authentifizierungsmethoden sowie + Webhooks für Echtzeit-Benachrichtigungen. +

+
-
- - - - - - - -
-
-
-

- API & Integrationen -

-

- Integrieren Sie Business Portal nahtlos in Ihre bestehenden Systeme und Workflows -

+ {{-- Features --}} +

API-Features

+
+ @foreach ($features as $feature) +
+

{{ $feature['title'] }}

+

{{ $feature['text'] }}

-
- -
- - - - - - - - -
-
+ @endforeach + - -
-
-
-
-

- Leistungsstarke REST API -

-

- Automatisieren Sie Ihre Presseveröffentlichungen und integrieren Sie unsere Dienste -

-
- -
-

- Unsere RESTful API ermöglicht es Ihnen, Pressemitteilungen programmatisch zu veröffentlichen, zu verwalten und abzurufen. Perfekt für Unternehmen mit hohem Veröffentlichungsvolumen oder bestehenden Content-Management-Systemen. -

-

- Die API ist vollständig dokumentiert und unterstützt moderne Authentifizierungsmethoden sowie Webhooks für Echtzeit-Benachrichtigungen. -

-
-
-
-
- - -
-
-
-

- API Features -

-

- Alles, was Sie für eine nahtlose Integration benötigen -

-
- -
- -
-
- - - -
-

- RESTful API -

-

- Moderne REST-Architektur mit JSON-Responses für einfache Integration in jede Programmiersprache. -

-
- - -
-
- - - -
-

- OAuth 2.0 -

-

- Sichere Authentifizierung mit OAuth 2.0 und API-Tokens für maximale Sicherheit. -

-
- - -
-
- - - -
-

- Webhooks -

-

- Erhalten Sie Echtzeit-Benachrichtigungen über Events wie Veröffentlichungen oder Statusänderungen. -

-
- - -
-
- - - -
-

- Dokumentation -

-

- Umfassende API-Dokumentation mit Code-Beispielen in verschiedenen Sprachen. -

-
- - -
-
- - - -
-

- Rate Limiting -

-

- Faire Rate Limits mit klaren Headern und automatischer Skalierung bei Bedarf. -

-
- - -
-
- - - -
-

- SDKs -

-

- Offizielle SDKs für PHP, JavaScript, Python und weitere Sprachen verfügbar. -

-
-
-
-
- - -
-
-
-
-

- Schneller Einstieg -

-

- Veröffentlichen Sie Ihre erste Pressemitteilung in wenigen Zeilen Code -

-
- -
-
// Example: Pressemitteilung veröffentlichen
-
curl -X POST https://api.businessportal.de/v1/releases \
+        {{-- Code-Beispiel --}}
+        

Schneller Einstieg

+

Veröffentlichen Sie Ihre erste Pressemitteilung in wenigen Zeilen Code.

+
+
// Beispiel: Pressemitteilung veröffentlichen
+
curl -X POST https://api.example.com/v1/releases \
   -H "Authorization: Bearer YOUR_API_TOKEN" \
   -H "Content-Type: application/json" \
   -d '{
     "title": "Neue Produktankündigung",
-    "content": "Unser neues Produkt...",
+    "content": "Unser neues Produkt …",
     "category": "wirtschaft",
-    "publish_at": "2025-01-15T10:00:00Z"
+    "publish_at": "2026-01-15T10:00:00Z"
   }'
-
+
+ {{-- TODO: Link auf die vollständige API-Dokumentation, sobald sie online ist. --}} +

Die vollständige Dokumentation folgt. Bei Fragen zum API-Zugang hilft unser Support.

- + {{-- Integrationen --}} +

Native Integrationen

+
+ @foreach ($integrations as $integration) +
+ {{ $integration }}
+ @endforeach +
+ + {{-- CTA --}} +
+
+

Starten Sie mit der API

+

API-Zugang ist ab dem passenden Tarif verfügbar – Details finden Sie auf der Preisseite.

-
- - -
-
-
-

- Native Integrationen -

-

- Verbinden Sie Business Portal mit Ihren Lieblings-Tools -

-
- -
- -
- WordPress -
-
- Zapier -
-
- Slack -
-
- HubSpot -
-
+ -
- - -
-
-
- - - -
-

- Starten Sie mit der API -

-

- API-Zugang ist ab dem Professional-Tarif verfügbar -

- -
-
- - - -
- + +
@endsection - -@push('styles') - -@endpush diff --git a/resources/views/web/businessportal24.blade.php b/resources/views/web/businessportal24.blade.php index 99ba25d0..8cf2888b 100644 --- a/resources/views/web/businessportal24.blade.php +++ b/resources/views/web/businessportal24.blade.php @@ -36,6 +36,8 @@ + + diff --git a/resources/views/web/cookies.blade.php b/resources/views/web/cookies.blade.php index 09cc397c..2d3786a4 100644 --- a/resources/views/web/cookies.blade.php +++ b/resources/views/web/cookies.blade.php @@ -1,201 +1,120 @@ @extends('web.layouts.web-master') -@section('title', 'Cookie-Richtlinien - Business Portal 24') +@section('title', 'Cookie-Richtlinien') +@section('meta_description', 'Informationen zu den auf dieser Website eingesetzten Cookies und deren Verwaltung.') @section('content') + + {{-- Platzhalter-Text — vor Go-Live an das echte Cookie-/Consent-Setup anpassen. --}} +
+

Was sind Cookies?

+

+ Cookies sind kleine Textdateien, die auf Ihrem Computer oder mobilen Gerät gespeichert werden, wenn + Sie eine Website besuchen. Sie ermöglichen es der Website, Ihr Gerät zu erkennen und bestimmte + Informationen über Ihre Nutzung zu speichern. +

+

+ Wir verwenden Cookies, um Ihnen ein optimales Nutzungserlebnis zu bieten und unsere Website + kontinuierlich zu verbessern. +

-
- - +

Welche Cookies verwenden wir?

- -
-
-

- Cookie-Richtlinien -

+

1. Notwendige Cookies

+

+ Diese Cookies sind für den Betrieb der Website unbedingt erforderlich und können nicht deaktiviert + werden. Beispiele: +

+
    +
  • Session-Cookies (Anmeldung)
  • +
  • Sicherheits-Cookies (CSRF-Schutz)
  • +
  • Cookie-Einwilligungs-Cookies
  • +
+ +

2. Funktionale Cookies

+

Diese Cookies ermöglichen erweiterte Funktionalitäten und Personalisierung. Beispiele:

+
    +
  • Spracheinstellungen
  • +
  • Dark-Mode-Präferenzen
  • +
  • Gespeicherte Filtereinstellungen
  • +
+ +

3. Analytische Cookies

+

Diese Cookies helfen uns zu verstehen, wie Besucher mit unserer Website interagieren. Dienste:

+
    +
  • Google Analytics (anonymisiert)
  • +
  • Interne Analysetools
  • +
+ +

4. Marketing-Cookies

+

Diese Cookies werden verwendet, um Ihnen relevante Werbung anzuzeigen. Beispiele:

+
    +
  • Remarketing-Cookies
  • +
  • Social Media Tracking
  • +
+
+ + {{-- Cookie-Laufzeiten als editoriale Tabelle (außerhalb .pm-body für klare Rahmung). --}} +
+

Cookie-Laufzeiten

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Cookie-TypLaufzeit
Session-CookiesBis zum Schließen des Browsers
Funktionale Cookies1 Jahr
Analytische Cookies2 Jahre
Marketing-Cookies1 Jahr
-
+
- -
-
-
-
- -
-

Was sind Cookies?

-

- Cookies sind kleine Textdateien, die auf Ihrem Computer oder mobilen Gerät gespeichert - werden, wenn Sie eine Website besuchen. - Sie ermöglichen es der Website, Ihr Gerät zu erkennen und bestimmte Informationen über Ihre - Nutzung zu speichern. -

-

- Wir verwenden Cookies, um Ihnen ein optimales Nutzungserlebnis zu bieten und unsere Website - kontinuierlich zu verbessern. -

-
- -
-

Welche Cookies verwenden - wir?

- -
-

1. Notwendige - Cookies

-

- Diese Cookies sind für den Betrieb der Website unbedingt erforderlich und können nicht - deaktiviert werden. -

-
-

Beispiele:

-
    -
  • Session-Cookies (Anmeldung)
  • -
  • Sicherheits-Cookies (CSRF-Schutz)
  • -
  • Cookie-Einwilligungs-Cookies
  • -
-
-
- -
-

2. Funktionale - Cookies

-

- Diese Cookies ermöglichen erweiterte Funktionalitäten und Personalisierung. -

-
-

Beispiele:

-
    -
  • Spracheinstellungen
  • -
  • Dark-Mode-Präferenzen
  • -
  • Gespeicherte Filtereinstellungen
  • -
-
-
- -
-

3. Analytische - Cookies

-

- Diese Cookies helfen uns zu verstehen, wie Besucher mit unserer Website interagieren. -

-
-

Dienste:

-
    -
  • Google Analytics (anonymisiert)
  • -
  • Interne Analysetools
  • -
-
-
- -
-

4. Marketing-Cookies -

-

- Diese Cookies werden verwendet, um Ihnen relevante Werbung anzuzeigen. -

-
-

Beispiele:

-
    -
  • Remarketing-Cookies
  • -
  • Social Media Tracking
  • -
-
-
-
- -
-

Cookie-Laufzeiten

-
- - - - - - - - - - - - - - - - - - - - - - - - - -
Cookie-TypLaufzeit
Session-CookiesBis zum Schließen des - Browsers
Funktionale Cookies1 Jahr
Analytische Cookies2 Jahre
Marketing-Cookies1 Jahr
-
-
- -
-

Wie können Sie Cookies - verwalten?

-

- Sie können Ihre Cookie-Einstellungen jederzeit ändern. Die meisten Browser erlauben es - Ihnen: -

-
    -
  • Cookies anzuzeigen und zu löschen
  • -
  • Cookies von Drittanbietern zu blockieren
  • -
  • Alle Cookies zu blockieren
  • -
  • Cookies beim Schließen des Browsers zu löschen
  • -
-

- Bitte beachten Sie, dass das Deaktivieren von Cookies die Funktionalität unserer Website - beeinträchtigen kann. -

-
- -
-

Weitere Informationen

-

- Weitere Informationen zum Datenschutz finden Sie in unserer Datenschutzerklärung. -

-

- Bei Fragen zu unseren Cookie-Richtlinien kontaktieren Sie uns bitte unter: - datenschutz@businessportal.de -

-
- -
-

- Stand: Januar 2025 -

-
- -
-
-
-
- - - +
+

Wie können Sie Cookies verwalten?

+

Sie können Ihre Cookie-Einstellungen jederzeit ändern. Die meisten Browser erlauben es Ihnen:

+
    +
  • Cookies anzuzeigen und zu löschen
  • +
  • Cookies von Drittanbietern zu blockieren
  • +
  • Alle Cookies zu blockieren
  • +
  • Cookies beim Schließen des Browsers zu löschen
  • +
+

+ Bitte beachten Sie, dass das Deaktivieren von Cookies die Funktionalität unserer Website + beeinträchtigen kann. +

+

Weitere Informationen

+

+ Weitere Informationen zum Datenschutz finden Sie in unserer + Datenschutzerklärung. +

+

+ Bei Fragen zu unseren Cookie-Richtlinien kontaktieren Sie uns bitte unter: + datenschutz@businessportal.de +

+
+
@endsection - -@push('styles') - -@endpush diff --git a/resources/views/web/datenschutz.blade.php b/resources/views/web/datenschutz.blade.php index 968f285a..4685b58e 100644 --- a/resources/views/web/datenschutz.blade.php +++ b/resources/views/web/datenschutz.blade.php @@ -1,152 +1,98 @@ @extends('web.layouts.web-master') -@section('title', 'Datenschutzerklärung - Business Portal 24') +@section('title', 'Datenschutzerklärung') +@section('meta_description', 'Informationen zur Verarbeitung personenbezogener Daten auf dieser Website.') @section('content') + + {{-- Platzhalter-Text — vor Go-Live durch die finale Datenschutzerklärung ersetzen. --}} +
+

1. Datenschutz auf einen Blick

+

Allgemeine Hinweise

+

+ Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen + Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen + Sie persönlich identifiziert werden können. +

-
- - +

Datenerfassung auf dieser Website

+

+ Wer ist verantwortlich für die Datenerfassung auf dieser Website?
+ Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten + können Sie dem Abschnitt „Hinweis zur Verantwortlichen Stelle" in dieser Datenschutzerklärung + entnehmen. +

- -
-
-

- Datenschutzerklärung -

-
-
+

2. Hosting

+

Wir hosten die Inhalte unserer Website bei folgenden Anbietern:

+

Externes Hosting

+

+ Diese Website wird extern gehostet. Die personenbezogenen Daten, die auf dieser Website erfasst + werden, werden auf den Servern des Hosters / der Hoster gespeichert. Hierbei kann es sich v. a. um + IP-Adressen, Kontaktanfragen, Meta- und Kommunikationsdaten, Vertragsdaten, Kontaktdaten, Namen, + Websitezugriffe und sonstige Daten, die über eine Website generiert werden, handeln. +

- -
-
-
-
+

3. Allgemeine Hinweise und Pflichtinformationen

+

Datenschutz

+

+ Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln + Ihre personenbezogenen Daten vertraulich und entsprechend den gesetzlichen Datenschutzvorschriften + sowie dieser Datenschutzerklärung. +

-
-

1. Datenschutz auf einen Blick

-

Allgemeine Hinweise

-

- Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, - wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen Sie persönlich identifiziert werden können. -

-
+

4. Datenerfassung auf dieser Website

+

Cookies

+

+ Unsere Internetseiten verwenden so genannte „Cookies". Cookies sind kleine Datenpakete und richten + auf Ihrem Endgerät keinen Schaden an. Sie werden entweder vorübergehend für die Dauer einer Sitzung + (Session-Cookies) oder dauerhaft (permanente Cookies) auf Ihrem Endgerät gespeichert. +

+

Server-Log-Dateien

+

+ Der Provider der Seiten erhebt und speichert automatisch Informationen in so genannten + Server-Log-Dateien, die Ihr Browser automatisch an uns übermittelt. Dies sind: +

+
    +
  • Browsertyp und Browserversion
  • +
  • verwendetes Betriebssystem
  • +
  • Referrer URL
  • +
  • Hostname des zugreifenden Rechners
  • +
  • Uhrzeit der Serveranfrage
  • +
  • IP-Adresse
  • +
-
-

Datenerfassung auf dieser Website

-

Wer ist verantwortlich für die Datenerfassung auf dieser Website?

-

- Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten können Sie dem Abschnitt - „Hinweis zur Verantwortlichen Stelle" in dieser Datenschutzerklärung entnehmen. -

-
+

5. Analyse-Tools und Werbung

+

Google Analytics

+

+ Diese Website nutzt Funktionen des Webanalysedienstes Google Analytics. Anbieter ist die Google + Ireland Limited („Google"), Gordon House, Barrow Street, Dublin 4, Irland. +

-
-

2. Hosting

-

- Wir hosten die Inhalte unserer Website bei folgenden Anbietern: -

-

Externes Hosting

-

- Diese Website wird extern gehostet. Die personenbezogenen Daten, die auf dieser Website erfasst werden, werden auf den Servern - des Hosters / der Hoster gespeichert. Hierbei kann es sich v. a. um IP-Adressen, Kontaktanfragen, Meta- und Kommunikationsdaten, - Vertragsdaten, Kontaktdaten, Namen, Websitezugriffe und sonstige Daten, die über eine Website generiert werden, handeln. -

-
- -
-

3. Allgemeine Hinweise und Pflichtinformationen

-

Datenschutz

-

- Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten - vertraulich und entsprechend den gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung. -

-
- -
-

4. Datenerfassung auf dieser Website

-

Cookies

-

- Unsere Internetseiten verwenden so genannte „Cookies". Cookies sind kleine Datenpakete und richten auf Ihrem Endgerät keinen Schaden an. - Sie werden entweder vorübergehend für die Dauer einer Sitzung (Session-Cookies) oder dauerhaft (permanente Cookies) auf Ihrem Endgerät - gespeichert. -

-
- -
-

Server-Log-Dateien

-

- Der Provider der Seiten erhebt und speichert automatisch Informationen in so genannten Server-Log-Dateien, die Ihr Browser - automatisch an uns übermittelt. Dies sind: -

-
    -
  • Browsertyp und Browserversion
  • -
  • verwendetes Betriebssystem
  • -
  • Referrer URL
  • -
  • Hostname des zugreifenden Rechners
  • -
  • Uhrzeit der Serveranfrage
  • -
  • IP-Adresse
  • -
-
- -
-

5. Analyse-Tools und Werbung

-

Google Analytics

-

- Diese Website nutzt Funktionen des Webanalysedienstes Google Analytics. Anbieter ist die Google Ireland Limited („Google"), - Gordon House, Barrow Street, Dublin 4, Irland. -

-
- -
-

6. Newsletter

-

Newsletterdaten

-

- Wenn Sie den auf der Website angebotenen Newsletter beziehen möchten, benötigen wir von Ihnen eine E-Mail-Adresse sowie - Informationen, welche uns die Überprüfung gestatten, dass Sie der Inhaber der angegebenen E-Mail-Adresse sind und mit dem - Empfang des Newsletters einverstanden sind. -

-
- -
-

7. Ihre Rechte

-

- Sie haben folgende Rechte: -

-
    -
  • Recht auf Auskunft über Ihre gespeicherten personenbezogenen Daten
  • -
  • Recht auf Berichtigung unrichtiger Daten
  • -
  • Recht auf Löschung Ihrer Daten
  • -
  • Recht auf Einschränkung der Datenverarbeitung
  • -
  • Recht auf Datenübertragbarkeit
  • -
  • Widerspruchsrecht gegen die Datenverarbeitung
  • -
  • Beschwerderecht bei einer Aufsichtsbehörde
  • -
-
- -
-

- Stand: Januar 2025 -

-
- -
-
-
-
- - -
+

6. Newsletter

+

Newsletterdaten

+

+ Wenn Sie den auf der Website angebotenen Newsletter beziehen möchten, benötigen wir von Ihnen eine + E-Mail-Adresse sowie Informationen, welche uns die Überprüfung gestatten, dass Sie der Inhaber der + angegebenen E-Mail-Adresse sind und mit dem Empfang des Newsletters einverstanden sind. +

+

7. Ihre Rechte

+

Sie haben folgende Rechte:

+
    +
  • Recht auf Auskunft über Ihre gespeicherten personenbezogenen Daten
  • +
  • Recht auf Berichtigung unrichtiger Daten
  • +
  • Recht auf Löschung Ihrer Daten
  • +
  • Recht auf Einschränkung der Datenverarbeitung
  • +
  • Recht auf Datenübertragbarkeit
  • +
  • Widerspruchsrecht gegen die Datenverarbeitung
  • +
  • Beschwerderecht bei einer Aufsichtsbehörde
  • +
+
+
@endsection - -@push('styles') - -@endpush diff --git a/resources/views/web/faq.blade.php b/resources/views/web/faq.blade.php index 8a54b1dc..43e0599f 100644 --- a/resources/views/web/faq.blade.php +++ b/resources/views/web/faq.blade.php @@ -1,419 +1,93 @@ @extends('web.layouts.web-master') -@section('title', 'FAQ - Häufig gestellte Fragen - Business Portal 24') +@section('title', 'Häufig gestellte Fragen (FAQ)') +@section('meta_description', 'Antworten auf die wichtigsten Fragen rund um das Veröffentlichen von Pressemitteilungen, Account, Abrechnung und Newsroom.') + +@php + // FAQ-Inhalte als Datenstruktur (DRY). Vor Go-Live ggf. mit dem Support + // abstimmen; bei Bedarf später in lang/*/faq.php auslagern. + $faqCategories = [ + [ + 'title' => 'Erste Schritte', + 'items' => [ + ['q' => 'Wie veröffentliche ich meine erste Pressemitteilung?', 'a' => 'Nach der Registrierung klicken Sie auf „Veröffentlichen" in der Hauptnavigation. Folgen Sie dem 5-Schritte-Wizard: Metadaten eingeben, Content erstellen, Medien hochladen, Kontaktdaten hinterlegen und Vorschau prüfen. Bei Fragen steht unser Support-Team zur Verfügung.'], + ['q' => 'Muss ich mich registrieren, um Pressemitteilungen zu lesen?', 'a' => 'Nein, alle veröffentlichten Pressemitteilungen sind frei zugänglich. Eine Registrierung ist nur erforderlich, wenn Sie selbst Inhalte veröffentlichen oder einen Newsroom erstellen möchten.'], + ['q' => 'Wie lange dauert die Freischaltung meiner Pressemitteilung?', 'a' => 'In der Regel werden Pressemitteilungen innerhalb weniger Stunden redaktionell geprüft und freigeschaltet. Höhere Tarife profitieren von einer beschleunigten Prüfung.'], + ], + ], + [ + 'title' => 'Account & Abrechnung', + 'items' => [ + ['q' => 'Wie ändere ich meinen Tarif?', 'a' => 'In Ihrem Bereich unter „Buchungen & Add-ons" können Sie jederzeit upgraden oder downgraden. Upgrades gelten sofort, Downgrades zum nächsten Abrechnungszeitraum.'], + ['q' => 'Welche Zahlungsmethoden werden akzeptiert?', 'a' => 'Wir akzeptieren Kreditkarten (Visa, Mastercard, American Express) sowie SEPA-Lastschrift. Die Abrechnung erfolgt sicher über unseren Zahlungsdienstleister; Bestandskunden können weiter per Rechnung zahlen.'], + ['q' => 'Kann ich mein Konto löschen?', 'a' => 'Ja, Sie können Ihr Konto jederzeit in den Einstellungen löschen. Beachten Sie, dass bereits veröffentlichte Pressemitteilungen aus SEO-Gründen online bleiben, aber nicht mehr Ihrem Account zugeordnet sind.'], + ], + ], + [ + 'title' => 'Pressemitteilungen & Content', + 'items' => [ + ['q' => 'Welche Dateiformate werden für Medien unterstützt?', 'a' => 'Bilder: JPG, PNG, WebP (max. 10 MB). Dokumente: PDF (max. 25 MB). Videos können über YouTube-/Vimeo-Einbettung hinzugefügt werden.'], + ['q' => 'Kann ich eine Pressemitteilung nach Veröffentlichung bearbeiten?', 'a' => 'Inhaltliche Korrekturen und Updates sind über den jeweiligen Magic-Link möglich. Umfang und Konditionen richten sich nach dem gewählten Tarif.'], + ['q' => 'Wie optimiere ich meine Pressemitteilung für SEO?', 'a' => 'Nutzen Sie aussagekräftige Headlines, integrieren Sie relevante Keywords natürlich, hinterlegen Sie Alt-Texte für Bilder und strukturieren Sie den Text mit Zwischenüberschriften.'], + ], + ], + [ + 'title' => 'Newsroom & Features', + 'items' => [ + ['q' => 'Was ist ein Newsroom und wie erstelle ich einen?', 'a' => 'Ein Newsroom ist Ihre dedizierte Unternehmensseite, auf der alle Ihre Pressemitteilungen gebündelt werden. Je nach Tarif lässt er sich mit Logo, Farben und zusätzlichen Informationen individualisieren.'], + ['q' => 'Wie funktionieren die Analytics?', 'a' => 'Sie erhalten Statistiken zu Aufrufen, Klicks und Reichweite Ihrer Meldungen. Die Daten werden laufend aktualisiert und lassen sich exportieren.'], + ], + ], + ]; +@endphp @section('content') - -
- - - - - - - -
-
-
- - - + +
+ @foreach ($faqCategories as $category) +
+

{{ $category['title'] }}

+
+ @foreach ($category['items'] as $item) +
+ + {{ $item['q'] }} + + +
+ {{ $item['a'] }} +
+
+ @endforeach +
-

- Häufig gestellte Fragen -

-

- Finden Sie schnell Antworten auf die wichtigsten Fragen rund um Business Portal -

+ @endforeach +
+ + {{-- Abschluss-CTA: Frage nicht gefunden? --}} +
+

Frage nicht gefunden?

+

+ Unser Support-Team hilft Ihnen gerne weiter – per E-Mail oder über das Kontaktformular. +

+ - -
- - - - - - - - -
-
- - -
-
-
- - - - -
-
-
- - -
-
- -
-
-
- - - -
-

- Erste Schritte -

-
- -
- -
- -
-
- Nach der Registrierung klicken Sie auf 'Veröffentlichen' in der Hauptnavigation. Folgen Sie dem intuitiven 5-Schritte-Wizard: Metadaten eingeben, Content erstellen, Medien hochladen, Kontaktdaten hinterlegen und Vorschau prüfen. Bei Fragen steht unser Support-Team jederzeit zur Verfügung. -
-
-
- - -
- -
-
- Nein, alle veröffentlichten Pressemitteilungen sind frei zugänglich. Eine Registrierung ist nur erforderlich, wenn Sie selbst Inhalte veröffentlichen oder einen Newsroom erstellen möchten. -
-
-
- - -
- -
-
- In der Regel werden Pressemitteilungen innerhalb von 2-4 Stunden geprüft und freigeschaltet. Professional- und Enterprise-Kunden profitieren von einer beschleunigten Prüfung innerhalb von maximal 1 Stunde. -
-
-
-
-
- - -
-
-
- - - -
-

- Account & Abrechnung -

-
- -
- -
- -
-
- In Ihren Kontoeinstellungen unter 'Abonnement & Billing' können Sie jederzeit Ihren Tarif upgraden oder downgraden. Upgrades gelten sofort, Downgrades zum nächsten Abrechnungszeitraum. -
-
-
- - -
- -
-
- Wir akzeptieren Kreditkarten (Visa, Mastercard, American Express), SEPA-Lastschrift und PayPal. Enterprise-Kunden können auch per Rechnung zahlen. -
-
-
- - -
- -
-
- Ja, Sie können Ihr Konto jederzeit in den Einstellungen unter 'Account verwalten' löschen. Beachten Sie, dass bereits veröffentlichte Pressemitteilungen aus SEO-Gründen online bleiben, aber nicht mehr Ihrem Account zugeordnet sind. -
-
-
-
-
- - -
-
-
- - - -
-

- Pressemitteilungen & Content -

-
- -
- -
- -
-
- Bilder: JPG, PNG, WebP (max. 10 MB). Dokumente: PDF (max. 25 MB). Videos können über YouTube/Vimeo-Einbettung hinzugefügt werden. -
-
-
- - -
- -
-
- Ja, Professional- und Enterprise-Kunden können Pressemitteilungen bis zu 7 Tage nach Veröffentlichung bearbeiten. Bei Free-Accounts ist eine nachträgliche Bearbeitung nicht möglich. -
-
-
- - -
- -
-
- Nutzen Sie aussagekräftige Headlines (max. 70 Zeichen), integrieren Sie relevante Keywords natürlich, fügen Sie Alt-Texte für Bilder hinzu und strukturieren Sie den Text mit Zwischenüberschriften. Unser Editor zeigt Ihnen einen SEO-Score in Echtzeit. -
-
-
-
-
- - -
-
-
- - - -
-

- Newsroom & Features -

-
- -
- -
- -
-
- Ein Newsroom ist Ihre dedizierte Unternehmensseite auf Business Portal, wo alle Ihre Pressemitteilungen gebündelt werden. Professional- und Enterprise-Kunden können ihren Newsroom mit Logo, Farben und zusätzlichen Informationen individualisieren. -
-
-
- - -
- -
-
- Professional-Kunden erhalten detaillierte Statistiken zu Views, Klicks, geografischer Verteilung und Referrern. Die Daten werden in Echtzeit aktualisiert und können als Report exportiert werden. -
-
-
-
-
-
-
- - -
-
-
-

- Frage nicht gefunden? -

-

- Unser Support-Team hilft Ihnen gerne weiter. Kontaktieren Sie uns per E-Mail oder Chat. -

- -
-
-
- - -
- -
- - - -
- + + @endsection - -@push('styles') - -@endpush diff --git a/resources/views/web/hilfe.blade.php b/resources/views/web/hilfe.blade.php index 471d512a..836701e2 100644 --- a/resources/views/web/hilfe.blade.php +++ b/resources/views/web/hilfe.blade.php @@ -1,256 +1,115 @@ @extends('web.layouts.web-master') -@section('title', 'Hilfe-Center - Business Portal 24') +@section('title', 'Hilfe-Center') +@section('meta_description', 'Hilfe-Center: Antworten, Anleitungen und direkter Kontakt zum Support-Team.') + +@php + // Hilfe-Themen mit Links auf vorhandene Ziele. „#" markiert Inhalte, die + // noch entstehen (Anleitungen/Tutorials) — vor Go-Live verlinken. + $helpTopics = [ + [ + 'title' => 'Erste Schritte', + 'links' => [ + ['label' => 'Account erstellen', 'href' => '#'], + ['label' => 'Erste Pressemitteilung veröffentlichen', 'href' => route('veroeffentlichen')], + ['label' => 'Newsroom einrichten', 'href' => route('newsrooms')], + ['label' => 'Profil vervollständigen', 'href' => '#'], + ], + ], + [ + 'title' => 'Pressemitteilungen', + 'links' => [ + ['label' => 'PM erstellen und formatieren', 'href' => '#'], + ['label' => 'Medien hochladen', 'href' => '#'], + ['label' => 'SEO-Optimierung', 'href' => '#'], + ['label' => 'PM bearbeiten / löschen', 'href' => '#'], + ], + ], + [ + 'title' => 'Account & Abrechnung', + 'links' => [ + ['label' => 'Tarif wechseln', 'href' => route('preise')], + ['label' => 'Zahlungsmethoden', 'href' => route('preise')], + ['label' => 'Rechnungen herunterladen', 'href' => '#'], + ['label' => 'Account kündigen', 'href' => '#'], + ], + ], + [ + 'title' => 'Erweiterte Features', + 'links' => [ + ['label' => 'API-Integration', 'href' => route('api')], + ['label' => 'Analytics nutzen', 'href' => '#'], + ['label' => 'Newsroom anpassen', 'href' => route('newsrooms')], + ['label' => 'Team-Management', 'href' => '#'], + ], + ], + ]; + + $quickLinks = [ + ['label' => 'FAQ', 'desc' => 'Häufig gestellte Fragen', 'href' => route('faq')], + ['label' => 'Veröffentlichen', 'desc' => 'In wenigen Schritten zur Meldung', 'href' => route('veroeffentlichen')], + ['label' => 'Kontakt', 'desc' => 'Direkter Draht zum Support', 'href' => route('kontakt')], + ]; +@endphp @section('content') - -
- - - - -
-
-
- - - -
-

- Hilfe-Center -

-

- Wir helfen Ihnen gerne weiter – Finden Sie Antworten oder kontaktieren Sie unser Support-Team -

-
-
- - -
-
- - -
- -
- - -
-
-
- + + {{-- Support-CTA --}} +
+
+

Benötigen Sie weitere Hilfe?

+

Unser Support-Team ist Mo–Fr von 9–18 Uhr für Sie da.

-
- - -
- + + + @endsection - -@push('styles') - -@endpush diff --git a/resources/views/web/impressum.blade.php b/resources/views/web/impressum.blade.php index dfe7b09c..bee7066d 100644 --- a/resources/views/web/impressum.blade.php +++ b/resources/views/web/impressum.blade.php @@ -1,138 +1,85 @@ @extends('web.layouts.web-master') -@section('title', 'Impressum - Business Portal 24') +@section('title', 'Impressum') +@section('meta_description', 'Impressum und Anbieterkennzeichnung gemäß § 5 TMG.') @section('content') + + {{-- Platzhalter-Angaben — vor Go-Live durch die echten Anbieterdaten ersetzen. --}} +
+

Angaben gemäß § 5 TMG

+

+ Business Portal GmbH
+ Musterstraße 123
+ 10115 Berlin
+ Deutschland +

-
- - +

Kontakt

+

+ Telefon: +49 (0) 30 1234567-0
+ E-Mail: info@businessportal.de +

- -
-
-

- Impressum -

-
-
+

Vertreten durch

+

Geschäftsführer: Dr. Michael Weber

- -
-
-
-
+

Registereintrag

+

+ Eintragung im Handelsregister
+ Registergericht: Amtsgericht Berlin-Charlottenburg
+ Registernummer: HRB 123456 B +

-

Angaben gemäß § 5 TMG

+

Umsatzsteuer-ID

+

+ Umsatzsteuer-Identifikationsnummer gemäß § 27a UStG:
+ DE123456789 +

-
-

Business Portal GmbH

-

Musterstraße 123

-

10115 Berlin

-

Deutschland

-
+

Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV

+

+ Lisa Müller
+ Musterstraße 123
+ 10115 Berlin +

-
-

Kontakt

-

Telefon: +49 (0) 30 1234567-0

-

E-Mail: info@businessportal.de

-
+

Streitschlichtung

+

+ Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung (OS) bereit: + https://ec.europa.eu/consumers/odr +

+

+ Wir sind nicht bereit oder verpflichtet, an Streitbeilegungsverfahren vor einer + Verbraucherschlichtungsstelle teilzunehmen. +

-
-

Vertreten durch

-

Geschäftsführer: Dr. Michael Weber

-
+

Haftung für Inhalte

+

+ Als Diensteanbieter sind wir gemäß § 7 Abs. 1 TMG für eigene Inhalte auf diesen Seiten nach + den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter + jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen + oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. +

-
-

Registereintrag

-

Eintragung im Handelsregister

-

Registergericht: Amtsgericht Berlin-Charlottenburg -

-

Registernummer: HRB 123456 B

-
- -
-

Umsatzsteuer-ID

-

Umsatzsteuer-Identifikationsnummer gemäß § 27a UStG: -

-

DE123456789

-
- -
-

Verantwortlich für den - Inhalt nach § 55 Abs. 2 RStV

-

Lisa Müller

-

Musterstraße 123

-

10115 Berlin

-
- -
-

Streitschlichtung

-

- Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung (OS) bereit: - https://ec.europa.eu/consumers/odr -

-

- Wir sind nicht bereit oder verpflichtet, an Streitbeilegungsverfahren vor einer - Verbraucherschlichtungsstelle teilzunehmen. -

-
- -
-

Haftung für Inhalte

-

- Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach - den allgemeinen Gesetzen verantwortlich. - Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte - oder gespeicherte fremde Informationen zu - überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. -

-
- -
-

Haftung für Links

-

- Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen - Einfluss haben. Deshalb können wir für - diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten - ist stets der jeweilige Anbieter oder - Betreiber der Seiten verantwortlich. -

-
- -
-

Urheberrecht

-

- Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem - deutschen Urheberrecht. - Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der - Grenzen des Urheberrechtes bedürfen - der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. -

-
- -
-
-
-
- - -
+

Haftung für Links

+

+ Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen Einfluss + haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte + der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. +

+

Urheberrecht

+

+ Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem + deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung + außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen + Autors bzw. Erstellers. +

+
+
@endsection - -@push('styles') - -@endpush diff --git a/resources/views/web/kategorie.blade.php b/resources/views/web/kategorie.blade.php index 3de04273..d5050a86 100644 --- a/resources/views/web/kategorie.blade.php +++ b/resources/views/web/kategorie.blade.php @@ -58,47 +58,36 @@ {{-- Masthead --}}
-
-
-
Branche
-

{{ $catName }}

-

{{ $catDescription }}

-
- - {{ $fmt($categoryStats['month'] ?? 0) }} Meldungen in den letzten 30 Tagen · {{ $fmt($categoryStats['today'] ?? 0) }} am {{ $referenceNow->translatedFormat('j. M Y') }} -
-
- + -
-
- @if ($subCategories->isNotEmpty()) -
- Sub-Themen - @foreach ($subCategories as $sub) - - {{ $sub['name'] }} - {{ $fmt($sub['total']) }} - - @endforeach -
- @endif + {{-- Branchen-Profil --}} +
+

{{ $catName }}

+

{{ $catDescription }}

+
+ + {{ $fmt($categoryStats['month'] ?? 0) }} Meldungen in den letzten 30 Tagen · {{ $fmt($categoryStats['today'] ?? 0) }} am {{ $referenceNow->translatedFormat('j. M Y') }} +
+ + @if ($subCategories->isNotEmpty()) +
+ Sub-Themen + @foreach ($subCategories as $sub) + + {{ $sub['name'] }} + {{ $fmt($sub['total']) }} + + @endforeach +
+ @endif +
+ {{-- Stat strip --}}
diff --git a/resources/views/web/kontakt.blade.php b/resources/views/web/kontakt.blade.php index 1e58de9d..c48a9c93 100644 --- a/resources/views/web/kontakt.blade.php +++ b/resources/views/web/kontakt.blade.php @@ -1,326 +1,88 @@ @extends('web.layouts.web-master') -@section('title', 'Kontakt - Business Portal 24') +@section('title', 'Kontakt') +@section('meta_description', 'Kontaktieren Sie uns per E-Mail, Telefon oder vor Ort – wir helfen Ihnen gerne weiter.') + +@php + $brand = config('domains.domains.'.config('app.theme').'.brand', []); + $brandLabel = ($brand['name'] ?? 'businessportal').($brand['accent'] ?? ''); + $contactEmail = $brand['contact_email'] ?? 'info@businessportal.de'; + + $contactMethods = [ + ['label' => 'E-Mail', 'value' => $contactEmail, 'href' => 'mailto:'.$contactEmail, 'note' => 'Antwort i. d. R. innerhalb von 24 Stunden'], + ['label' => 'Telefon', 'value' => '+49 89 1234 5678', 'href' => 'tel:+498912345678', 'note' => 'Mo–Fr 9:00–18:00 Uhr'], + ['label' => 'Adresse', 'value' => 'Maximilianstraße 35', 'href' => null, 'note' => '80539 München, Deutschland'], + ['label' => 'Geschäftszeiten', 'value' => 'Mo–Fr 9:00–18:00', 'href' => null, 'note' => 'Sa–So geschlossen'], + ]; +@endphp @section('content') + + {{-- Kontaktwege --}} +
+ @foreach ($contactMethods as $method) +
+
{{ $method['label'] }}
+ @if ($method['href']) + {{ $method['value'] }} + @else +
{{ $method['value'] }}
+ @endif +

{{ $method['note'] }}

+
+ @endforeach +
-
- - + {{-- Besuch / Anfahrt --}} +
+
+

Schreiben Sie uns

+

+ Haben Sie Fragen oder benötigen Sie Unterstützung? Schreiben Sie uns direkt an + {{ $contactEmail }} – unser Team meldet sich + schnellstmöglich bei Ihnen zurück. Für häufige Fragen lohnt sich vorab ein Blick in unsere + FAQ oder das Hilfe-Center. +

- - + {{-- TODO: Kontaktformular reaktivieren, sobald ein POST-Handler + (Controller/Volt + Validierung + Mailversand) bereitsteht. + Bis dahin führt der direkte E-Mail-Weg ans Ziel. --}} - -
-
-

- Kontakt -

-

- Wir sind für Sie da – per E-Mail, Telefon oder vor Ort in München +

Anfahrt

+

+ Öffentliche Verkehrsmittel: U-Bahn U4/U5 – Haltestelle Lehel; Tram 19 – + Haltestelle Maxmonument.
+ Parken: Parkhaus Maximilianstraße (5 Min. Fußweg).

- -
- - - - - - - - -
-
- -
-
-
- -
-
- - - -
-

- E-Mail -

- - info@businessportal.de - -

- Antwort innerhalb von 24 Stunden -

-
- - -
-
- - - -
-

- Telefon -

- - +49 89 1234 5678 - -

- Mo-Fr 9:00-18:00 Uhr -

-
- - -
-
- - - - -
-

- Adresse -

-

- Maximilianstraße 35 -

-

- 80539 München, Deutschland -

-
- - -
-
- - - -
-

- Geschäftszeiten -

-

- Mo-Fr 9:00-18:00 -

-

- Sa-So geschlossen -

-
+
-
- - - -
- + + +
@endsection - -@push('styles') - -@endpush diff --git a/resources/views/web/newsroom.blade.php b/resources/views/web/newsroom.blade.php new file mode 100644 index 00000000..4d2bb954 --- /dev/null +++ b/resources/views/web/newsroom.blade.php @@ -0,0 +1,227 @@ +@extends('web.layouts.web-master') + +@php + use Illuminate\Support\Str; + + \Carbon\Carbon::setLocale(app()->getLocale()); + + $brandConfig = config('domains.domains.'.config('app.theme').'.brand', []); + $brandName = $brandConfig['name'] ?? 'businessportal'; + $brandAccent = $brandConfig['accent'] ?? '24'; + + $fmt = fn ($value) => number_format((int) $value, 0, ',', '.'); + + $palette = ['#0098A6', '#21A038', '#0018A8', '#1E1E1E', '#E20074', '#003781', '#C84A1E', '#5A6E2F']; + $logoUrl = $company->logoUrl(); + $initial = mb_strtoupper(mb_substr((string) $company->name, 0, 1)); + $color = $palette[crc32((string) $company->id) % count($palette)]; + + $boilerplate = filled($company->boilerplate) + ? trim((string) Str::of(strip_tags((string) $company->boilerplate))->squish()) + : null; + + $websiteHost = filled($company->website) ? preg_replace('#^https?://(www\.)?#', '', rtrim((string) $company->website, '/')) : null; + + $leadImage = $leadRelease?->images->first(); + $leadImageUrl = $leadImage?->variantUrl('detail') ?? $leadImage?->variantUrl('card') ?? $leadImage?->url(); + $leadCat = $leadRelease?->category?->translations->firstWhere('locale', app()->getLocale()) ?? $leadRelease?->category?->translations->first(); + $leadLead = $leadRelease + ? (filled($leadRelease->subtitle) + ? $leadRelease->subtitle + : (string) Str::of(strip_tags((string) $leadRelease->text))->squish()->limit(220)) + : null; + $leadWords = $leadRelease ? str_word_count(strip_tags((string) $leadRelease->text)) : 0; + $leadReadTime = max(1, (int) round($leadWords / 200)); +@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('og_type', 'profile') + +@section('hreflang') + + + +@endsection + +@section('content') + + +
+ {{-- Breadcrumb --}} +
+
+ Startseite + + Newsrooms + + {{ $company->name }} +
+
+ + {{-- Masthead --}} +
+
+
Newsroom
+ + + Alle Newsrooms + +
+ + {{-- Firmenprofil --}} +
+
+ @if ($logoUrl) + {{ $company->name }} + @else +
+ {{ $initial }} +
+ @endif +
+

{{ $company->name }}

+
+ @if ($websiteHost) + + + {{ $websiteHost }} + + + @endif + + + {{ $fmt($newsroomStats['recent'] ?? 0) }} Meldungen in den letzten 30 Tagen + + @if (! empty($newsroomStats['sinceYear'])) + + Newsroom seit {{ $newsroomStats['sinceYear'] }} + @endif +
+
+
+ + @if ($boilerplate) +

{{ Str::limit($boilerplate, 360) }}

+ @endif + + @if ($categories->isNotEmpty()) +
+ Branchen + @foreach ($categories as $cat) + + {{ $cat['name'] }} + {{ $fmt($cat['count']) }} + + @endforeach +
+ @endif +
+
+ + {{-- Stat strip --}} +
+
+
+
Meldungen aktueller Tag
+
{{ $fmt($newsroomStats['today'] ?? 0) }}
+
Stand {{ $referenceNow->translatedFormat('j. M Y') }}
+
+
+
Letzte 30 Tage
+
{{ $fmt($newsroomStats['recent'] ?? 0) }}
+
veröffentlichte Meldungen
+
+
+
Im Archiv
+
{{ $fmt($newsroomStats['total'] ?? 0) }}
+
Meldungen gesamt
+
+
+
Aufrufe gesamt
+
{{ $fmt($newsroomStats['totalHits'] ?? 0) }}
+
über alle Meldungen
+
+
+
+ + @if ($leadRelease) + {{-- Top-Meldung --}} +
+ +
+ @endif + + {{-- Meldungen + Sidebar --}} +
+
+
+

+ Alle Meldungen · {{ $fmt($newsroomStats['total'] ?? 0) }} +

+
+
+ + @forelse ($releases as $release) + + @empty +

Aktuell keine weiteren Meldungen in diesem Newsroom.

+ @endforelse + + +
+ + +
+
+ + +@endsection diff --git a/resources/views/web/newsrooms.blade.php b/resources/views/web/newsrooms.blade.php index ae09b84a..7a7dea50 100644 --- a/resources/views/web/newsrooms.blade.php +++ b/resources/views/web/newsrooms.blade.php @@ -1,331 +1,184 @@ @extends('web.layouts.web-master') -@section('title', 'Newsrooms - Business Portal 24') +@php + use Illuminate\Support\Carbon; -@section('content') + \Carbon\Carbon::setLocale(app()->getLocale()); -
- - + $brandConfig = config('domains.domains.'.config('app.theme').'.brand', []); + $brandName = $brandConfig['name'] ?? 'businessportal'; + $brandAccent = $brandConfig['accent'] ?? '24'; - - + $palette = ['#0098A6', '#21A038', '#0018A8', '#1E1E1E', '#E20074', '#003781', '#C84A1E', '#5A6E2F']; + $fmt = fn ($value) => number_format((int) $value, 0, ',', '.'); +@endphp - -
-
-
-

- Newsrooms -

-

- Ihr professioneller Unternehmens-Newsroom – Alle Pressemitteilungen zentral an einem Ort -

-
-
- -
- - - - - - - - -
-
- - -
-
-
-
-

- Was ist ein Newsroom? -

-

- Ihre digitale Pressezentrale für professionelle Unternehmenskommunikation -

-
- -
-

- Ein Newsroom ist Ihre dedizierte Unternehmensseite auf Business Portal, wo alle Ihre Pressemitteilungen gebündelt und professionell präsentiert werden. Journalisten, Partner und Interessenten finden hier alle relevanten Informationen zu Ihrem Unternehmen auf einen Blick. -

-

- Mit Ihrem individuellen Newsroom bauen Sie Vertrauen auf und schaffen eine zentrale Anlaufstelle für alle, die sich über Ihr Unternehmen informieren möchten. -

-
-
-
-
- - -
-
-
-

- Newsroom Features -

-

- Professionelle Features für Ihre Unternehmenskommunikation -

-
- -
- -
-
- - - -
-

- Individuelles Branding -

-

- Gestalten Sie Ihren Newsroom mit Ihrem Logo, Unternehmensfarben und individuellen Inhalten. -

-
- - -
-
- - - -
-

- Zentrale Verwaltung -

-

- Alle Pressemitteilungen chronologisch sortiert und nach Kategorien filterbar an einem Ort. -

-
- - -
-
- - - -
-

- SEO-Optimiert -

-

- Ihr Newsroom wird von Suchmaschinen indexiert und verbessert Ihre Online-Sichtbarkeit. -

-
- - -
-
- - - -
-

- Analytics & Insights -

-

- Detaillierte Statistiken zu Besuchern, Views und Engagement Ihrer Pressemitteilungen. -

-
- - -
-
- - - -
-

- Medien-Kontakte -

-

- Hinterlegen Sie Pressekontakte und ermöglichen Sie direkte Anfragen von Journalisten. -

-
- - -
-
- - - -
-

- RSS-Feeds -

-

- Automatische RSS-Feeds ermöglichen einfaches Abonnieren Ihrer Neuigkeiten. -

-
-
-
-
- - -
-
-
-

- Newsroom Pakete -

-

- Wählen Sie das passende Paket für Ihre Anforderungen -

-
- -
- -
-

- Basic Newsroom -

-

- Inklusive im Free-Tarif -

-
    -
  • - - - - Standard-Design -
  • -
  • - - - - Alle Pressemitteilungen -
  • -
  • - - - - RSS-Feed -
  • -
-
- - -
-
- Beliebt -
-

- Professional Newsroom -

-

- Professional-Tarif -

-
    -
  • - - - - Individuelles Branding -
  • -
  • - - - - Custom Domain -
  • -
  • - - - - Erweiterte Analytics -
  • -
  • - - - - Medien-Bibliothek -
  • -
-
- - -
-

- Enterprise Newsroom -

-

- Enterprise-Tarif -

-
    -
  • - - - - White Label Lösung -
  • -
  • - - - - API-Zugang -
  • -
  • - - - - Prioritäts-Support -
  • -
  • - - - - Individuelle Features -
  • -
-
-
-
-
- - -
-
-
- - - -
-

- Starten Sie Ihren eigenen Newsroom -

-

- Erstellen Sie in wenigen Minuten Ihren professionellen Unternehmens-Newsroom -

- - Jetzt kostenlos starten - -
-
- - - -
+@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('hreflang') + + + @endsection -@push('styles') - -@endpush +
+ {{-- Breadcrumb --}} +
+
+ {{ $brandName }}{{ $brandAccent }} + + Newsrooms +
+
+ + {{-- Masthead --}} +
+
+
Unternehmens-Newsrooms
+

+ Newsrooms +

+

+ Alle aktiven Unternehmen mit ihren redaktionell geprüften Pressemitteilungen – gebündelt an einem Ort. +

+
+ + {{-- Stat strip --}} +
+
+
Aktive Newsrooms
+
{{ $fmt($totalNewsrooms) }}
+
mit veröffentlichten Meldungen
+
+
+
Aktiv (30 Tage)
+
{{ $fmt($activeNewsroomsCount) }}
+
Stand {{ $referenceNow->translatedFormat('j. M Y') }}
+
+
+
Meldungen im Archiv
+
{{ $fmt($archiveTotal) }}
+
redaktionell geprüft
+
+
+
+ + {{-- Suche + Verzeichnis --}} +
+
+ +
+ + + +
+
+ +
+

+ {{ $fmt($newsrooms->total()) }} + {{ $newsrooms->total() === 1 ? 'Newsroom' : 'Newsrooms' }} + @if ($term !== '') + für „{{ $term }}" + @endif +

+ @if ($term !== '') + Filter zurücksetzen + @endif +
+
+ + @if ($newsrooms->isNotEmpty()) +
+ @foreach ($newsrooms as $company) + @php + $logoUrl = $company->logoUrl(); + $initial = mb_strtoupper(mb_substr((string) $company->name, 0, 1)); + $color = $palette[$loop->index % count($palette)]; + $latest = $company->latest_published_at ? Carbon::parse($company->latest_published_at) : null; + $isRecentlyActive = (int) ($company->recent_count ?? 0) > 0; + @endphp + +
+ @if ($logoUrl) + {{ $company->name }} + @else +
+ {{ $initial }} +
+ @endif +
+

{{ $company->name }}

+ @if ($latest) +
Letzte Meldung {{ $latest->translatedFormat('j. M Y') }}
+ @endif +
+
+
+ + {{ $fmt($company->releases_count ?? 0) }} Meldungen + + @if ($isRecentlyActive) + + + {{ $fmt($company->recent_count) }} / 30 Tage + + @endif +
+
+ @endforeach +
+ + + @else +
+

Keine Newsrooms gefunden

+

+ @if ($term !== '') + Für „{{ $term }}" gibt es derzeit keinen passenden Newsroom. Versuchen Sie einen anderen Suchbegriff. + @else + Aktuell sind keine aktiven Newsrooms verfügbar. + @endif +

+ @if ($term !== '') + + Alle Newsrooms anzeigen + + @endif +
+ @endif +
+ + {{-- Publisher CTA (breit) --}} +
+
+
+
+
Eigener Newsroom
+

Bündeln Sie Ihre Pressemitteilungen im eigenen Newsroom.

+

Eigene Marken-URL · alle Meldungen zentral · SEO-Vorteil für Ihr Unternehmen · redaktionell geprüft.

+
+ +
+
+
+
+ + +@endsection diff --git a/resources/views/web/preise.blade.php b/resources/views/web/preise.blade.php index c58fb960..f5544563 100644 --- a/resources/views/web/preise.blade.php +++ b/resources/views/web/preise.blade.php @@ -1,420 +1,276 @@ @extends('web.layouts.web-master') -@section('title', 'Preise & Leistungen - Business Portal 24') +{{-- + Öffentliche Preise-/Tarifseite (Editorial-Design). + Reihenfolge laut Vorgabe: 1) Abo-Tarife 2) Einzel-Pressemitteilung 3) Add-ons. -@section('content') + Datenquellen (alles aus dem System, nichts hartkodiert): + - Abos: Plan::active() -> $plans + - Einzel-PM: config('billing.single_pm_price_cents') -> $singlePmPriceCents + - Add-ons: config('credits.*') -> $extraPm, $boost, $proofPdf, $creditPacks, $morePaths -
- - + Inhalte/Texte: lang/de/pricing.php + lang/en/pricing.php (Schlüssel pricing.*). - - + HINWEIS (bewusst teilweise mit Kommentaren): Die korrespondierende In-App-Buchungs- + seite (resources/views/livewire/customer/bookings.blade.php) wird gerade überarbeitet. + Buchungs-CTAs verlinken vorerst auf den Veröffentlichen-Funnel; die Deep-Links in den + Checkout/zur Registrierung mit Tarif-Vorauswahl folgen, sobald der Flow final ist. +--}} - -
-
-

- Preise & Leistungen -

-

- Transparente Preise für jede Unternehmensgröße – von Start-ups bis Konzerne -

-
- -
- - - - - - - - -
-
- - -
-
-
- -
-
-

- Free -

-
- - 0€ - - kostenlos -
-

- Ideal für kleinere Unternehmen und Start-ups -

-
- -
    -
  • - - - - 1 Pressemitteilung pro Monat -
  • -
  • - - - - Standard-Veröffentlichung -
  • -
  • - - - - Basis-Statistiken -
  • -
  • - - - - E-Mail-Support -
  • -
  • - - - - Community-Forum Zugang -
  • -
- - - Jetzt starten - -
- - -
- - Beliebteste Wahl - - -
-

- Professional -

-
- - 99€ - - pro Monat -
-

- Für wachsende Unternehmen mit regelmäßigem Kommunikationsbedarf -

-
- -
    -
  • - - - - 10 Pressemitteilungen pro Monat -
  • -
  • - - - - Premium-Platzierung -
  • -
  • - - - - Erweiterte Statistiken & Analytics -
  • -
  • - - - - Medienkit-Hosting -
  • -
  • - - - - Prioritäts-Support -
  • -
  • - - - - Social Media Distribution -
  • -
  • - - - - Eigener Newsroom -
  • -
- - - Jetzt upgraden - -
- - -
-
-

- Enterprise -

-
- - Individuell - -
-

- auf Anfrage -

-

- Maßgeschneiderte Lösungen für große Organisationen -

-
- -
    -
  • - - - - Unbegrenzte Pressemitteilungen -
  • -
  • - - - - Top-Platzierung garantiert -
  • -
  • - - - - White-Label Newsroom -
  • -
  • - - - - Dedizierter Account Manager -
  • -
  • - - - - API-Zugang -
  • -
  • - - - - Custom Integrationen -
  • -
  • - - - - Individuelle SLAs -
  • -
  • - - - - Schulungen & Workshops -
  • -
- - - Kontakt aufnehmen - -
-
-
-
- - -
-
-
-

- Zusätzliche Services -

-

- Maximieren Sie die Reichweite Ihrer Pressemitteilungen mit unseren Premium-Features -

-
- -
- -
-
- - - -
-

- Featured Story -

-
- 199€ -
-

- Ihre Meldung prominent auf der Startseite platziert für 24 Stunden -

-
- - -
-
- - - -
-

- Newsletter Feature -

-
- 149€ -
-

- Exklusive Platzierung im wöchentlichen Newsletter an 50.000+ Abonnenten -

-
- - -
-
- - - -
-

- Social Media Boost -

-
- 99€ -
-

- Verstärkte Verbreitung über LinkedIn, Twitter und weitere Kanäle -

-
- - -
-
- - - -
-

- Premium Analytics -

-
- 79€/Monat -
-

- Detaillierte Auswertungen mit Reichweite, Engagement und Conversion-Tracking -

-
-
-
-
- - -
-
-

- Häufig gestellte Fragen -

- -
-
-

- Kann ich meinen Plan jederzeit wechseln? -

-

- Ja, Sie können jederzeit upgraden oder downgraden. Bei einem Upgrade wird anteilig berechnet, bei einem Downgrade gilt die Änderung ab dem nächsten Abrechnungszeitraum. -

-
- -
-

- Gibt es eine Mindestvertragslaufzeit? -

-

- Nein, alle unsere Pläne sind monatlich kündbar. Es gibt keine versteckten Kosten oder Mindestlaufzeiten. -

-
- -
-

- Welche Zahlungsmethoden akzeptieren Sie? -

-

- Wir akzeptieren alle gängigen Kreditkarten (Visa, Mastercard, American Express), SEPA-Lastschrift und PayPal. Für Enterprise-Kunden bieten wir auch Rechnungszahlung an. -

-
- -
-

- Wie funktioniert die Reichweitenmessung? -

-

- Alle Professional- und Enterprise-Kunden erhalten Zugang zu detaillierten Analytics: Views, Klicks, geografische Verteilung, Referrer und mehr. Die Daten werden in Echtzeit aktualisiert. -

-
-
-
-
- - -
-
-

- Noch Fragen zu unseren Preisen? -

-

- Unser Team berät Sie gerne und findet die passende Lösung für Ihre Anforderungen -

- -
-
- - - -
+@section('title', __('pricing.meta_title')) +@section('meta_description', __('pricing.meta_description')) +@section('hreflang') + + + @endsection -@push('styles') - -@endpush +@section('content') + + +
+ {{-- ============================================================ + MASTHEAD + ============================================================ --}} +
+
{{ __('pricing.eyebrow') }}
+

+ {{ __('pricing.headline') }} +

+

+ {{ __('pricing.intro') }} +

+
+ + {{-- ============================================================ + 1) ABO-TARIFE (Daten: Plan::active()) + ============================================================ --}} +
+
+
+
{{ __('pricing.plans.eyebrow') }}
+

{{ __('pricing.plans.title') }}

+
+

{{ __('pricing.plans.subtitle') }}

+
+
+ + @if ($plans->isNotEmpty()) +
+ @foreach ($plans as $plan) + @php + $isPopular = $plan->slug === $popularPlan; + $hasMonthly = (int) $plan->monthly_price_cents > 0; + @endphp +
+ @if ($isPopular) + + {{ __('pricing.plans.popular') }} + + @endif + +
{{ $plan->name }}
+ +
+ {{ $euro((int) $plan->monthly_price_cents) }} € + @if ($hasMonthly) + {{ __('pricing.plans.per_month') }} + @endif +
+ + @if ($hasMonthly && (int) $plan->yearly_price_cents > 0) +
+ {{ __('pricing.plans.yearly_hint', ['price' => $euro((int) $plan->yearly_price_cents)]) }} +
+ @else +
+ @endif + +
    +
  • + {{ $plan->press_release_quota }} + {{ __('pricing.plans.quota', ['count' => $plan->press_release_quota]) }} +
  • +
  • + @if ($plan->daily_limit !== null) + {{ $plan->daily_limit }} + {{ __('pricing.plans.daily_limit', ['count' => $plan->daily_limit]) }} + @else + ∞ + {{ __('pricing.plans.no_daily_limit') }} + @endif +
  • +
  • + {{ __('pricing.plans.included') }} +
  • +
+ + {{-- TODO: Deep-Link zu Checkout/Registrierung mit Tarif-Vorauswahl, + sobald der Buchungs-Flow (bookings.blade.php) final ist. --}} + + {{ __('pricing.plans.cta') }} + + +
+ @endforeach +
+ +

{{ __('pricing.plans.enterprise') }}

+ @else +

{{ __('pricing.plans.empty') }}

+ @endif +
+ + {{-- ============================================================ + 2) EINZEL-PRESSEMITTEILUNG (Daten: config('billing')) + ============================================================ --}} +
+
+
{{ __('pricing.single.eyebrow') }}
+

{{ __('pricing.single.title') }}

+
+
+ +
+

+ {{ __('pricing.single.description') }} +

+
+
+
{{ $euro($singlePmPriceCents) }} €
+
{{ __('pricing.single.price_suffix') }} · {{ __('pricing.single.net') }}
+
+ + {{ __('pricing.single.cta') }} + + +
+
+
+ + {{-- ============================================================ + 3) ADD-ONS & EINZELKOSTEN (Daten: config('credits')) + Spiegelt die In-App-Buchungsseite (bookings.blade.php), die noch + überarbeitet wird — daher Lineup/Preise hier als „teilweise final". + ============================================================ --}} +
+
+
+
{{ __('pricing.addons.eyebrow') }}
+

{{ __('pricing.addons.title') }}

+
+

{{ __('pricing.addons.subtitle') }}

+
+
+ +
+ {{-- Extra-PM (tier-gestaffelt) --}} +
+

{{ __('pricing.addons.extra_pm.title') }}

+

{{ __('pricing.addons.extra_pm.description') }}

+ + + + + + + + + @foreach ($extraPm as $row) + + + + + @endforeach + +
{{ __('pricing.addons.extra_pm.tier_label') }}{{ __('pricing.addons.extra_pm.price_label') }}
{{ $row['tier'] }}{{ $row['credits'] }} {{ __('pricing.addons.credits_unit') }}
+
+ + {{-- Boost-Platzierung (nach Dauer) --}} +
+

{{ __('pricing.addons.boost.title') }}

+

{{ __('pricing.addons.boost.description') }}

+
    + @foreach ($boost as $row) +
  • + {{ __('pricing.addons.boost.days', ['count' => $row['days']]) }} + {{ $row['credits'] }} {{ __('pricing.addons.credits_unit') }} +
  • + @endforeach +
+
+ + {{-- Veröffentlichungsnachweis-PDF --}} +
+

{{ __('pricing.addons.proof_pdf.title') }}

+

{{ __('pricing.addons.proof_pdf.description') }}

+
+ {{ $proofPdf }} + {{ __('pricing.addons.credits_unit') }} · {{ __('pricing.addons.proof_pdf.unit') }} +
+
+ + {{-- Credit-Pakete --}} +
+

{{ __('pricing.addons.packs.title') }}

+

{{ __('pricing.addons.packs.description') }}

+
+ @foreach ($creditPacks as $pack) + @php $bonus = (int) $pack['credits'] - intdiv((int) $pack['price_cents'], 100); @endphp +
+
{{ __('pricing.addons.packs.credits', ['count' => $pack['credits']]) }}
+
{{ $euro((int) $pack['price_cents']) }} € {{ __('pricing.addons.packs.net') }}
+ @if ($bonus > 0) + {{ __('pricing.addons.packs.bonus', ['count' => $bonus]) }} + @endif +
+ @endforeach +
+
+
+ + {{-- Weitere Leistungen — TODO: hängt am Bookings-/Magic-Link-Rework, + Lineup und Preise final abstimmen, sobald die In-App-Seite steht. --}} +
+
{{ __('pricing.addons.more.title') }}
+
+ + {{ __('pricing.addons.more.correction') }} + {{ $morePaths['correction'] }} {{ __('pricing.addons.credits_unit') }} + + + {{ __('pricing.addons.more.update') }} + {{ $morePaths['update'] }} {{ __('pricing.addons.credits_unit') }} + + + {{ __('pricing.addons.more.depublish') }} + {{ $morePaths['depublish'] }} {{ __('pricing.addons.credits_unit') }} + +
+
+ +

{{ __('pricing.vat_note') }}

+
+ + {{-- Abschluss mit vorhandener Editorial-Komponente. --}} + +
+ + +@endsection diff --git a/resources/views/web/presseecho.blade.php b/resources/views/web/presseecho.blade.php index 67d1bad3..d7d7e96f 100644 --- a/resources/views/web/presseecho.blade.php +++ b/resources/views/web/presseecho.blade.php @@ -40,6 +40,8 @@ + + diff --git a/resources/views/web/ueber-uns.blade.php b/resources/views/web/ueber-uns.blade.php index d73f086f..882fba57 100644 --- a/resources/views/web/ueber-uns.blade.php +++ b/resources/views/web/ueber-uns.blade.php @@ -1,412 +1,131 @@ @extends('web.layouts.web-master') -@section('title', 'Über Uns - Business Portal 24') +@section('title', 'Über uns') +@section('meta_description', 'Wir verbinden Unternehmen mit Medien, Journalisten und Multiplikatoren im deutschsprachigen Raum.') + +@php + $brand = config('domains.domains.'.config('app.theme').'.brand', []); + $brandLabel = ($brand['name'] ?? 'businessportal').($brand['accent'] ?? ''); + + // Platzhalter-Kennzahlen — vor Go-Live mit echten Werten ersetzen. + $stats = [ + ['value' => '250.000+', 'label' => 'Veröffentlichte Meldungen'], + ['value' => '15.000+', 'label' => 'Registrierte Unternehmen'], + ['value' => '2,5 Mio.', 'label' => 'Monatliche Leser'], + ['value' => '40+', 'label' => 'Länder'], + ]; + + $values = [ + ['title' => 'Qualität vor Quantität', 'text' => 'Wir setzen auf redaktionelle Qualität und kuratieren Inhalte sorgfältig, um höchste Standards zu gewährleisten.'], + ['title' => 'DACH-Expertise', 'text' => 'Spezialisiert auf den deutschsprachigen Raum mit tiefem Verständnis für regionale Besonderheiten.'], + ['title' => 'Kundenfokus', 'text' => 'Ihre Zufriedenheit steht im Mittelpunkt. Wir bieten persönlichen Support und maßgeschneiderte Lösungen.'], + ['title' => 'Innovation & Technologie', 'text' => 'Kontinuierliche Weiterentwicklung unserer Plattform mit modernsten Technologien und Features.'], + ]; + + $team = [ + ['name' => 'Dr. Michael Weber', 'role' => 'Gründer & CEO', 'bio' => '15+ Jahre Erfahrung in PR und Unternehmenskommunikation'], + ['name' => 'Sarah Hoffmann', 'role' => 'Chief Technology Officer', 'bio' => 'Expertin für digitale Plattformen und Skalierung'], + ['name' => 'Thomas Schneider', 'role' => 'Head of Customer Success', 'bio' => 'Spezialist für Kundenzufriedenheit und Account Management'], + ['name' => 'Lisa Müller', 'role' => 'Head of Content & Quality', 'bio' => 'Journalistin mit Schwerpunkt digitale Medien'], + ]; + + $timeline = [ + ['year' => '10', 'title' => 'Gründung', 'text' => 'Start als kleines Start-up mit der Vision, PR zu demokratisieren.'], + ['year' => '13', 'title' => 'Expansion', 'text' => 'Erreichen von 1.000 registrierten Unternehmen.'], + ['year' => '16', 'title' => 'Wachstum', 'text' => 'Launch der Premium-Features und Enterprise-Lösungen.'], + ['year' => '19', 'title' => 'Innovation', 'text' => 'Einführung KI-gestützter Reichweiten-Analytics.'], + ['year' => '22', 'title' => 'Marktführer', 'text' => 'Über 10.000 aktive Unternehmenskunden im DACH-Raum.'], + ['year' => '25', 'title' => 'Zukunft', 'text' => 'Internationalisierung und neue KI-Features.'], + ]; +@endphp @section('content') - -
- - - - - - - -
-
-
-

- Über Business Portal -

-

- Seit 2010 verbinden wir Unternehmen mit Medien, Journalisten und Multiplikatoren im deutschsprachigen Raum. Wir sind die führende Plattform für Unternehmenskommunikation und Pressearbeit im DACH-Raum. -

+ + {{-- Kennzahlen --}} +
+ @foreach ($stats as $stat) +
+
{{ $stat['value'] }}
+
{{ $stat['label'] }}
+ @endforeach +
+ + {{-- Mission --}} +
+
+
Unsere Mission
+

Professionelle Kommunikation für alle zugänglich machen

- -
- - - - - - - - -
-
- - -
-
-
-
-
- 250.000+ -
-
Veröffentlichte Meldungen
-
-
-
- 15.000+ -
-
Registrierte Unternehmen
-
-
-
- 2,5 Mio. -
-
Monatliche Leser
-
-
-
- 40+ -
-
Länder
-
-
-
-
- - -
-
-
-
-

- Unsere Mission -

-

- Professionelle Unternehmenskommunikation für alle zugänglich machen -

-
- -
-

- Wir glauben daran, dass jedes Unternehmen – unabhängig von Größe oder Budget – die Möglichkeit haben sollte, seine Geschichte zu erzählen und mit relevanten Zielgruppen zu kommunizieren. -

-

- Business Portal demokratisiert professionelle PR-Arbeit durch innovative Technologie, faire Preismodelle und exzellenten Service. Wir schaffen Sichtbarkeit für wichtige Unternehmensnachrichten und verbinden Organisationen mit den Menschen, die ihre Botschaften lesen und teilen möchten. -

-
-
-
-
- - -
-
-
-

- Unsere Werte -

-

- Diese Prinzipien leiten uns in allem, was wir tun -

-
- -
- -
-
- - - -
-

- Qualität vor Quantität -

-

- Wir setzen auf redaktionelle Qualität und kuratieren Inhalte sorgfältig, um höchste Standards zu gewährleisten. -

-
- - -
-
- - - -
-

- DACH-Expertise -

-

- Spezialisiert auf den deutschsprachigen Raum mit tiefem Verständnis für regionale Besonderheiten. -

-
- - -
-
- - - -
-

- Kundenfokus -

-

- Ihre Zufriedenheit steht im Mittelpunkt. Wir bieten persönlichen Support und maßgeschneiderte Lösungen. -

-
- - -
-
- - - -
-

- Innovation & Technologie -

-

- Kontinuierliche Weiterentwicklung unserer Plattform mit modernsten Technologien und Features. -

-
-
-
-
- - -
-
-
-

- Unser Team -

-

- Erfahrene Experten aus PR, Technologie und Medien -

-
- -
- -
-
- - - -
-

- Dr. Michael Weber -

-

Gründer & CEO

-

- 15+ Jahre Erfahrung in PR und Unternehmenskommunikation -

-
- - -
-
- - - -
-

- Sarah Hoffmann -

-

Chief Technology Officer

-

- Expertin für digitale Plattformen und Skalierung -

-
- - -
-
- - - -
-

- Thomas Schneider -

-

Head of Customer Success

-

- Spezialist für Kundenzufriedenheit und Account Management -

-
- - -
-
- - - -
-

- Lisa Müller -

-

Head of Content & Quality

-

- Journalistin mit Schwerpunkt digitale Medien -

-
-
-
-
- - -
-
-
-

- Unsere Geschichte -

-
- -
- -
-
-
- 10 -
-
-
-
-

- Gründung -

-

- Start als kleines Start-up mit der Vision, PR zu demokratisieren -

-
-
- - -
-
-
- 13 -
-
-
-
-

- Expansion -

-

- Erreichen von 1.000 registrierten Unternehmen -

-
-
- - -
-
-
- 16 -
-
-
-
-

- Wachstum -

-

- Launch der Premium-Features und Enterprise-Lösungen -

-
-
- - -
-
-
- 19 -
-
-
-
-

- Innovation -

-

- Einführung KI-gestützter Reichweiten-Analytics -

-
-
- - -
-
-
- 22 -
-
-
-
-

- Marktführer -

-

- Über 10.000 aktive Unternehmenskunden im DACH-Raum -

-
-
- - -
-
-
- 25 -
-
-
-

- Zukunft -

-

- Internationalisierung und neue AI-Features -

-
-
-
-
-
- - -
-
-
- - - -
-

- Werden Sie Teil unserer Success Story -

-

- Über 15.000 Unternehmen vertrauen bereits auf Business Portal +

+
+ + {{-- Werte --}} +

Unsere Werte

+
+ @foreach ($values as $value) +
+

{{ $value['title'] }}

+

{{ $value['text'] }}

+
+ @endforeach +
+ + {{-- Team --}} +

Unser Team

+
+ @foreach ($team as $member) +
+

{{ $member['name'] }}

+
{{ $member['role'] }}
+

{{ $member['bio'] }}

+
+ @endforeach +
+ + {{-- Geschichte / Timeline --}} +

Unsere Geschichte

+
+ @foreach ($timeline as $milestone) +
+ {{ $milestone['year'] }} +

{{ $milestone['title'] }}

+

{{ $milestone['text'] }}

+
+ @endforeach +
+ + {{-- CTA --}} +
- - - -
- + + @endsection - -@push('styles') - -@endpush diff --git a/routes/web.php b/routes/web.php index 685fe942..bd65643f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,10 +2,12 @@ use App\Enums\Portal; use App\Enums\PressReleaseStatus; +use App\Enums\Tier; use App\Http\Controllers\PressReleasePreviewController; use App\Http\Middleware\SetEdition; use App\Models\Category; use App\Models\Company; +use App\Models\Plan; use App\Models\PressRelease; use App\Scopes\PortalScope; use App\Support\DomainAssetContext; @@ -643,6 +645,47 @@ $veroeffentlichenPage = static function (string $edition) use ($applyWebDomainCo ]); }; +// Preise-/Tarifseite je Ausgabe: Abos (Plan-Modell), Einzel-PM (config/billing) +// und Add-on-Einzelkosten (config/credits). Inhalte liegen in lang/*/pricing.php. +$preisePage = static function (string $edition) use ($applyWebDomainConfig) { + $domain = request()->getHost(); + + $domainKey = str_contains($domain, 'presseecho') ? 'presseecho' : 'businessportal24'; + $applyWebDomainConfig($domainKey); + + // Add-on-Einzelkosten aus der Credit-Ökonomie (1 Credit = 1 €). + $extraPm = collect(config('credits.extra_pm', [])) + ->map(static fn (int $credits, string $tier): array => [ + 'tier' => Tier::tryFrom($tier)?->label() ?? ucfirst($tier), + 'credits' => $credits, + ]) + ->values() + ->all(); + + $boost = collect(config('credits.boost', [])) + ->map(static fn (int $credits, int|string $days): array => [ + 'days' => (int) $days, + 'credits' => $credits, + ]) + ->values() + ->all(); + + return view('web.preise', [ + 'plans' => Plan::query()->active()->get(), + 'singlePmPriceCents' => (int) config('billing.single_pm_price_cents'), + 'extraPm' => $extraPm, + 'boost' => $boost, + 'proofPdf' => (int) config('credits.proof_pdf'), + 'creditPacks' => config('credits.packs', []), + // TODO: Lineup/Preise hängen am Bookings-/Magic-Link-Rework — final abstimmen. + 'morePaths' => [ + 'correction' => (int) config('credits.paths.correction'), + 'update' => (int) config('credits.paths.update'), + 'depublish' => (int) config('credits.depublish'), + ], + ]); +}; + // Archiv-Suchseite je Ausgabe (interaktive Suche läuft in der Volt-Komponente). $suchePage = static function (string $edition) use ($applyWebDomainConfig) { $domain = request()->getHost(); @@ -676,6 +719,195 @@ $suchePage = static function (string $edition) use ($applyWebDomainConfig) { ]); }; +// Newsrooms-Verzeichnis je Ausgabe: alle aktiven Unternehmen mit Meldungen. +$newsroomsIndex = static function (string $edition) use ($applyWebDomainConfig) { + $domain = request()->getHost(); + $language = app()->getLocale(); + + [$domainKey, $portal] = match (true) { + str_contains($domain, 'presseecho') => ['presseecho', Portal::Presseecho], + default => ['businessportal24', Portal::Businessportal24], + }; + + $applyWebDomainConfig($domainKey); + + $portalValues = [$portal->value, Portal::Both->value]; + $referenceNow = EditorialClock::reference($portalValues, $language); + $recentSince = $referenceNow->copy()->subDays(30); + + $term = trim((string) request('q', '')); + + $published = static function (Builder $query) use ($portalValues, $language): void { + $query + ->whereIn('portal', $portalValues) + ->where('status', PressReleaseStatus::Published) + ->where('language', $language) + ->whereNotNull('published_at') + ->where('published_at', '<=', now()); + }; + + $activeCompanies = static fn (): Builder => Company::query() + ->withoutGlobalScope(PortalScope::class) + ->whereIn('portal', $portalValues) + ->where('is_active', true) + ->whereHas('pressReleases', fn (Builder $query) => $published($query)); + + $newsrooms = $activeCompanies() + ->when($term !== '', fn (Builder $query) => $query->whereLike('name', '%'.$term.'%')) + ->withCount([ + 'pressReleases as releases_count' => fn (Builder $query) => $published($query), + 'pressReleases as recent_count' => function (Builder $query) use ($published, $recentSince): void { + $published($query); + $query->where('published_at', '>=', $recentSince); + }, + ]) + ->withMax(['pressReleases as latest_published_at' => fn (Builder $query) => $published($query)], 'published_at') + ->orderByDesc('recent_count') + ->orderByDesc('releases_count') + ->orderBy('name') + ->paginate(24) + ->withQueryString(); + + $archiveTotal = PressRelease::query() + ->withoutGlobalScope(PortalScope::class) + ->tap(fn (Builder $query) => $published($query)) + ->count(); + + return view('web.newsrooms', [ + 'newsrooms' => $newsrooms, + 'term' => $term, + 'referenceNow' => $referenceNow, + 'totalNewsrooms' => (clone $activeCompanies())->count(), + 'activeNewsroomsCount' => $activeCompanies() + ->whereHas('pressReleases', function (Builder $query) use ($published, $recentSince): void { + $published($query); + $query->where('published_at', '>=', $recentSince); + }) + ->count(), + 'archiveTotal' => $archiveTotal, + ]); +}; + +// Newsroom-Markenseite eines Unternehmens je Ausgabe: alle Meldungen der Firma. +$newsroomDetail = static function (string $edition, string $slug) use ($applyWebDomainConfig) { + $domain = request()->getHost(); + $language = app()->getLocale(); + + [$domainKey, $portal] = match (true) { + str_contains($domain, 'presseecho') => ['presseecho', Portal::Presseecho], + default => ['businessportal24', Portal::Businessportal24], + }; + + $applyWebDomainConfig($domainKey); + + $portalValues = [$portal->value, Portal::Both->value]; + + $company = Company::query() + ->withoutGlobalScope(PortalScope::class) + ->whereIn('portal', $portalValues) + ->where('is_active', true) + ->where('slug', $slug) + ->with(['contacts']) + ->first(); + + if (! $company) { + abort(404); + } + + $base = static fn (): Builder => PressRelease::query() + ->withoutGlobalScope(PortalScope::class) + ->where('company_id', $company->id) + ->whereIn('portal', $portalValues) + ->where('status', PressReleaseStatus::Published) + ->where('language', $language) + ->whereNotNull('published_at') + ->where('published_at', '<=', now()); + + // Keine öffentliche Newsroom-Seite ohne veröffentlichte Meldung. + if (! $base()->exists()) { + abort(404); + } + + $latest = $base()->max('published_at'); + $referenceNow = $latest ? Carbon::parse($latest) : now(); + if ($referenceNow->gt(now())) { + $referenceNow = now(); + } + $recentSince = $referenceNow->copy()->subDays(30); + $oldest = $base()->min('published_at'); + + $with = [ + 'company', + 'category.translations' => fn ($query) => $query->where('locale', $language), + 'images' => fn ($query) => $query->orderByDesc('is_preview')->orderBy('sort_order')->limit(1), + ]; + + $leadRelease = $base()->with($with)->orderByDesc('published_at')->first(); + + $releases = $base() + ->with($with) + ->when($leadRelease, fn (Builder $query) => $query->whereKeyNot($leadRelease->getKey())) + ->orderByDesc('published_at') + ->paginate(10) + ->withQueryString(); + + $mostReadReleases = $base() + ->orderByDesc('hits') + ->orderByDesc('published_at') + ->limit(5) + ->get(['id', 'slug', 'title', 'hits', 'portal', 'language']); + + $companyFilter = static function (Builder $query) use ($company, $portalValues, $language): void { + $query + ->withoutGlobalScope(PortalScope::class) + ->where('company_id', $company->id) + ->whereIn('portal', $portalValues) + ->where('status', PressReleaseStatus::Published) + ->where('language', $language) + ->whereNotNull('published_at'); + }; + + $categories = Category::query() + ->whereIn('portal', $portalValues) + ->whereHas('pressReleases', fn (Builder $query) => $companyFilter($query)) + ->with(['translations' => fn ($query) => $query->where('locale', $language)]) + ->withCount(['pressReleases as releases_count' => fn (Builder $query) => $companyFilter($query)]) + ->orderByDesc('releases_count') + ->limit(8) + ->get() + ->map(function (Category $category): ?array { + $translation = $category->translations->first(); + + if (! $translation) { + return null; + } + + return [ + 'name' => $translation->name, + 'href' => $translation->slug ? route('kategorie', ['slug' => $translation->slug]) : route('web.home'), + 'count' => (int) ($category->releases_count ?? 0), + ]; + }) + ->filter() + ->values(); + + return view('web.newsroom', [ + 'company' => $company, + 'referenceNow' => $referenceNow, + 'leadRelease' => $leadRelease, + 'releases' => $releases, + 'mostReadReleases' => $mostReadReleases, + 'categories' => $categories, + 'newsroomStats' => [ + 'total' => $base()->count(), + 'recent' => $base()->where('published_at', '>=', $recentSince)->count(), + 'today' => $base()->whereDate('published_at', $referenceNow->toDateString())->count(), + 'totalHits' => (int) $base()->sum('hits'), + 'sinceYear' => $oldest ? (int) Carbon::parse($oldest)->format('Y') : null, + ], + ]); +}; + // --------------------------------------------------------------------------- // Bare Root: Publisher-Hub (pressekonto) oder Redirect auf die Default-Ausgabe. // --------------------------------------------------------------------------- @@ -696,20 +928,91 @@ Route::get('/', function () use ($applyWebDomainConfig) { // `category/x.html` nicht als Slug `x.html` in die Kategorie-Route läuft). // --------------------------------------------------------------------------- $htmlRedirect = static function (string $edition, string $path) { + $base = request()->getSchemeAndHttpHost(); $clean = trim((string) preg_replace('/\.html$/', '', $path), '/'); - $staticPages = [ - 'preise', 'faq', 'kontakt', 'ueber-uns', 'veroeffentlichen', - 'suche', 'newsrooms', 'api', 'team', 'partner', 'karriere', 'presse', - 'hilfe', 'impressum', 'datenschutz', 'agb', 'cookies', + // Account/Anmeldung lebt im Publisher-Hub (pressekonto), nicht am Portal. + // Die öffentlichen Spokes haben kein eigenes Login-System (Hub-and-Spoke). + $hubAliases = [ + 'registration' => '/register', + 'login' => '/login', ]; + if (array_key_exists($clean, $hubAliases)) { + $hubUrl = rtrim((string) config('domains.domain_portal_url'), '/'); + + return redirect($hubUrl.$hubAliases[$clean], 301); + } + // Entfallene Rubriken-Übersicht -> Startseite der Ausgabe. if ($clean === 'kategorien') { - return redirect(request()->getSchemeAndHttpHost().'/'.$edition, 301); + return redirect($base.'/'.$edition, 301); } - if (str_starts_with($clean, 'category/') || str_starts_with($clean, 'press-release/')) { + // Ratgeber-Strecke der Altseite (/de/presse/*.html) -> konsolidierte FAQ. + // Hier steckt der SEO-Wert; alle Einzelseiten bündeln wir 301 auf /faq. + if ($clean === 'presse' || str_starts_with($clean, 'presse/')) { + return redirect($base.'/'.$edition.'/faq', 301); + } + + // Alte Firmen-Pressemappe (presskit/{id}/{slug}) entspricht dem Newsroom. + // Feinmapping über die Legacy-ID: companies.(legacy_portal, legacy_id) ist + // unique-indiziert, ein Lookup liefert den heutigen Newsroom-Slug. Bei + // gültigem öffentlichem Newsroom 301 direkt dorthin (ein Hop), sonst sauber + // aufs Verzeichnis statt in einen 404. + if (preg_match('#^presskit/(\d+)#', $clean, $presskitMatch) === 1) { + $legacyPortal = str_contains(request()->getHost(), 'presseecho') + ? Portal::Presseecho->value + : Portal::Businessportal24->value; + + $company = Company::query() + ->withoutGlobalScope(PortalScope::class) + ->where('legacy_portal', $legacyPortal) + ->where('legacy_id', (int) $presskitMatch[1]) + ->first(); + + $hasPublicNewsroom = $company + && $company->is_active + && $company->pressReleases() + ->withoutGlobalScope(PortalScope::class) + ->where('status', PressReleaseStatus::Published) + ->whereNotNull('published_at') + ->where('published_at', '<=', now()) + ->exists(); + + return redirect( + $hasPublicNewsroom + ? $base.'/'.$edition.'/newsroom/'.$company->slug + : $base.'/'.$edition.'/newsrooms', + 301 + ); + } + + // Pressemappen-Pfade ohne numerische ID -> Newsrooms-Verzeichnis. + if ($clean === 'presskit' || str_starts_with($clean, 'presskit/')) { + return redirect($base.'/'.$edition.'/newsrooms', 301); + } + + // Legacy-Slugs des Altsystems (meist englisch) -> neue deutsche Pfade. + $staticAliases = [ + 'pricelist' => 'preise', + 'service' => 'veroeffentlichen', + 'contact' => 'kontakt', + 'imprint' => 'impressum', + 'terms' => 'agb', + 'privacypolicy' => 'datenschutz', + ]; + + // Bereits saubere Zielpfade der neuen Struktur (Pfad bleibt erhalten). + $staticPages = [ + 'preise', 'faq', 'kontakt', 'ueber-uns', 'veroeffentlichen', + 'suche', 'newsrooms', 'api', 'team', 'partner', 'karriere', + 'hilfe', 'impressum', 'datenschutz', 'agb', 'cookies', + ]; + + if (isset($staticAliases[$clean])) { + $target = $staticAliases[$clean]; + } elseif (str_starts_with($clean, 'category/') || str_starts_with($clean, 'press-release/')) { $target = $clean; } elseif ($clean === '' || str_contains($clean, '/')) { $target = $clean; @@ -720,9 +1023,24 @@ $htmlRedirect = static function (string $edition, string $path) { $target = 'press-release/'.$clean; } - return redirect(request()->getSchemeAndHttpHost().rtrim('/'.$edition.'/'.$target, '/'), 301); + return redirect(rtrim($base.'/'.$edition.'/'.$target, '/'), 301); }; +// --------------------------------------------------------------------------- +// Entfallene Sprach-/Regionsvarianten (de-ch, de-at) -> deutsche Ausgabe. +// Die echten Regionen-Übersichten (/region/{slug}) sind für einen späteren +// Schritt geplant; bis dahin verhindern diese 301 tote Bestands-Links. +// --------------------------------------------------------------------------- +$legacyRegions = ['de-ch', 'de-at']; + +Route::get('{region}', function (string $region) { + return redirect(request()->getSchemeAndHttpHost().'/'.SetEdition::DEFAULT_EDITION, 301); +})->whereIn('region', $legacyRegions); + +Route::get('{region}/{path}', function (string $region, string $path) { + return redirect(request()->getSchemeAndHttpHost().'/'.SetEdition::DEFAULT_EDITION, 301); +})->whereIn('region', $legacyRegions)->where('path', '.*'); + Route::get('{edition}/{path}', function (string $edition, string $path) use ($htmlRedirect) { return $htmlRedirect($edition, $path); })->where('path', '.*\.html')->whereIn('edition', SetEdition::EDITIONS); @@ -735,7 +1053,7 @@ Route::get('{path}', function (string $path) use ($htmlRedirect) { // --------------------------------------------------------------------------- // Öffentliche Inhalte je Ausgabe (Sprache): /de/... und /en/... // --------------------------------------------------------------------------- -Route::prefix('{edition}')->whereIn('edition', SetEdition::EDITIONS)->group(function () use ($applyWebDomainConfig, $webHomeData, $categoryPage, $releaseDetailPage, $veroeffentlichenPage, $suchePage) { +Route::prefix('{edition}')->whereIn('edition', SetEdition::EDITIONS)->group(function () use ($applyWebDomainConfig, $webHomeData, $categoryPage, $releaseDetailPage, $veroeffentlichenPage, $suchePage, $newsroomsIndex, $newsroomDetail, $preisePage) { // Startseite je Ausgabe Route::get('/', function (string $edition) use ($applyWebDomainConfig, $webHomeData) { $domain = request()->getHost(); @@ -760,14 +1078,17 @@ Route::prefix('{edition}')->whereIn('edition', SetEdition::EDITIONS)->group(func // Detailseite einer Pressemitteilung (z.B. /de/press-release/ki-revolution) Route::get('press-release/{slug}', $releaseDetailPage)->name('release.detail'); + // Preise / Tarife (datengetrieben: Plan + config/billing + config/credits) + Route::get('preise', $preisePage)->name('preise'); + // Statische Seiten - Route::view('preise', 'web.preise')->name('preise'); Route::view('faq', 'web.faq')->name('faq'); Route::view('kontakt', 'web.kontakt')->name('kontakt'); Route::view('ueber-uns', 'web.ueber-uns')->name('ueber-uns'); Route::get('veroeffentlichen', $veroeffentlichenPage)->name('veroeffentlichen'); Route::get('suche', $suchePage)->name('suche'); - Route::view('newsrooms', 'web.newsrooms')->name('newsrooms'); + Route::get('newsrooms', $newsroomsIndex)->name('newsrooms'); + Route::get('newsroom/{slug}', $newsroomDetail)->name('newsroom'); Route::view('api', 'web.api')->name('api'); Route::view('team', 'web.team')->name('team'); Route::view('partner', 'web.partner')->name('partner'); diff --git a/tests/Feature/Web/LegacyRedirectTest.php b/tests/Feature/Web/LegacyRedirectTest.php new file mode 100644 index 00000000..e0f1ed6a --- /dev/null +++ b/tests/Feature/Web/LegacyRedirectTest.php @@ -0,0 +1,114 @@ +get('https://businessportal24.test/de/'.$legacy) + ->assertStatus(301) + ->assertRedirect('https://businessportal24.test/de/'.$target); +})->with([ + 'pricelist -> preise' => ['pricelist.html', 'preise'], + 'service -> veroeffentlichen' => ['service.html', 'veroeffentlichen'], + 'contact -> kontakt' => ['contact.html', 'kontakt'], + 'imprint -> impressum' => ['imprint.html', 'impressum'], + 'terms -> agb' => ['terms.html', 'agb'], + 'privacypolicy -> datenschutz' => ['privacypolicy.html', 'datenschutz'], +]); + +test('legacy account urls redirect to the publisher hub', function (string $legacy, string $hubPath) { + /** @var TestCase $this */ + $hubUrl = rtrim((string) config('domains.domain_portal_url'), '/'); + + $this->get('https://businessportal24.test/de/'.$legacy) + ->assertStatus(301) + ->assertRedirect($hubUrl.$hubPath); +})->with([ + 'registration -> register' => ['registration.html', '/register'], + 'login -> login' => ['login.html', '/login'], +]); + +test('legacy press guide pages collapse into the faq', function (string $legacy) { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/de/'.$legacy) + ->assertStatus(301) + ->assertRedirect('https://businessportal24.test/de/faq'); +})->with([ + 'overview' => ['presse.html'], + 'pressemitteilung-schreiben' => ['presse/pressemitteilung-schreiben.html'], + 'pressemitteilung-aufbau' => ['presse/pressemitteilung-aufbau.html'], + 'presseausweis' => ['presse/presseausweis.html'], +]); + +test('legacy presskit urls map to the new newsroom via the legacy id', function () { + /** @var TestCase $this */ + $company = Company::factory()->businessportal24()->create([ + 'name' => 'Acme GmbH', + 'slug' => 'acme-gmbh-neu', + 'is_active' => true, + 'legacy_portal' => Portal::Businessportal24->value, + 'legacy_id' => 512, + ]); + + PressRelease::factory() + ->published() + ->forPortal(Portal::Businessportal24) + ->for($company) + ->create(['published_at' => now()->subDay()]); + + // Alt-Slug ist egal – die Legacy-ID bestimmt das Ziel (heutiger Slug). + $this->get('https://businessportal24.test/de/presskit/512/irgendein-alt-slug.html') + ->assertStatus(301) + ->assertRedirect('https://businessportal24.test/de/newsroom/acme-gmbh-neu'); +}); + +test('legacy presskit urls fall back to the directory without a public newsroom', function () { + /** @var TestCase $this */ + // Unbekannte ID. + $this->get('https://businessportal24.test/de/presskit/99999/unbekannt.html') + ->assertStatus(301) + ->assertRedirect('https://businessportal24.test/de/newsrooms'); + + // Firma ohne veröffentlichte Meldung -> kein öffentlicher Newsroom. + Company::factory()->businessportal24()->create([ + 'is_active' => true, + 'legacy_portal' => Portal::Businessportal24->value, + 'legacy_id' => 777, + ]); + + $this->get('https://businessportal24.test/de/presskit/777/leer.html') + ->assertStatus(301) + ->assertRedirect('https://businessportal24.test/de/newsrooms'); +}); + +test('dropped language/region variants redirect to the default edition', function (string $path) { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/'.$path) + ->assertStatus(301) + ->assertRedirect('https://businessportal24.test/de'); +})->with([ + 'de-ch root' => ['de-ch'], + 'de-at root' => ['de-at'], + 'de-ch subpath' => ['de-ch/category/finanzen.html'], + 'de-at subpath' => ['de-at/login.html'], +]); + +test('legacy aliases also work without an edition prefix', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/contact.html') + ->assertStatus(301) + ->assertRedirect('https://businessportal24.test/de/kontakt'); + + $this->get('https://businessportal24.test/imprint.html') + ->assertStatus(301) + ->assertRedirect('https://businessportal24.test/de/impressum'); +}); diff --git a/tests/Feature/Web/NavigationLinksTest.php b/tests/Feature/Web/NavigationLinksTest.php new file mode 100644 index 00000000..2698e57b --- /dev/null +++ b/tests/Feature/Web/NavigationLinksTest.php @@ -0,0 +1,22 @@ +get('https://businessportal24.test/de'); + + $response->assertSuccessful() + // Neu verlinkt (FAQ im Header + Footer, Hilfe + Cookies im Footer). + ->assertSee('/de/faq', false) + ->assertSee('/de/hilfe', false) + ->assertSee('/de/cookies', false) + // „Anmelden" führt jetzt in den Hub statt auf einen toten Anker. + ->assertSee('/login', false) + ->assertDontSee('href="#newsletter"', false) + ->assertDontSee('href="#rss"', false) + ->assertDontSee('href="#anmelden"', false) + // Nicht verlinkte Platzhalter-Punkte wurden entfernt. + ->assertDontSee('Mediendaten') + ->assertDontSee('Verifizierter Newsroom'); +}); diff --git a/tests/Feature/Web/NewsroomPageTest.php b/tests/Feature/Web/NewsroomPageTest.php new file mode 100644 index 00000000..0fbf6b10 --- /dev/null +++ b/tests/Feature/Web/NewsroomPageTest.php @@ -0,0 +1,116 @@ +state(['portal' => $portal->value]) + ->create(array_merge([ + 'name' => $name, + 'slug' => Str::slug($name).'-'.fake()->unique()->numberBetween(1000, 9999), + 'is_active' => true, + ], $attributes)); +} + +function publishForCompany(Company $company, string $title, array $attributes = [], ?Category $category = null): PressRelease +{ + $category ??= Category::factory()->create(['portal' => Portal::Both]); + + return PressRelease::factory() + ->published() + ->forPortal(Portal::Businessportal24) + ->for($category) + ->for($company) + ->create(array_merge([ + 'title' => $title, + 'slug' => Str::slug($title).'-'.fake()->unique()->numberBetween(1000, 9999), + 'language' => 'de', + 'published_at' => now()->subDay(), + ], $attributes)); +} + +test('the newsrooms index renders the editorial shell and lists active newsrooms', function () { + /** @var TestCase $this */ + $company = makeNewsroom('Nordwind Energie AG'); + publishForCompany($company, 'Nordwind nimmt neuen Windpark in Betrieb'); + + $this->get('https://businessportal24.test/de/newsrooms') + ->assertSuccessful() + ->assertSee('businessportal', false) + ->assertSeeText('Newsrooms') + ->assertSeeText('Nordwind Energie AG'); +}); + +test('the newsrooms index filters by company name', function () { + /** @var TestCase $this */ + $match = makeNewsroom('Solartec Industries GmbH'); + publishForCompany($match, 'Solartec meldet Rekordquartal'); + + $other = makeNewsroom('Hafenlogistik Bremen AG'); + publishForCompany($other, 'Hafenlogistik baut Terminal aus'); + + $this->get('https://businessportal24.test/de/newsrooms?q=Solartec') + ->assertSuccessful() + ->assertSeeText('Solartec Industries GmbH') + ->assertDontSeeText('Hafenlogistik Bremen AG'); +}); + +test('the newsrooms index excludes companies without published releases', function () { + /** @var TestCase $this */ + makeNewsroom('Stilles Unternehmen AG'); + + $this->get('https://businessportal24.test/de/newsrooms') + ->assertSuccessful() + ->assertDontSeeText('Stilles Unternehmen AG'); +}); + +test('the newsroom detail page renders company data and its releases', function () { + /** @var TestCase $this */ + $company = makeNewsroom('Bavaria Maschinenbau AG', attributes: [ + 'boilerplate' => 'Bavaria Maschinenbau ist ein führender Anbieter von Industrielösungen.', + ]); + publishForCompany($company, 'Bavaria präsentiert neue Produktionslinie'); + + $this->get('https://businessportal24.test/de/newsroom/'.$company->slug) + ->assertSuccessful() + ->assertSeeText('Bavaria Maschinenbau AG') + ->assertSeeText('Bavaria präsentiert neue Produktionslinie') + ->assertSeeText('Alle Meldungen'); +}); + +test('the newsroom detail page returns 404 for an unknown slug', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/de/newsroom/gibt-es-nicht') + ->assertNotFound(); +}); + +test('the newsroom detail page returns 404 when the company has no published releases', function () { + /** @var TestCase $this */ + $company = makeNewsroom('Leeres Newsroom AG'); + + $this->get('https://businessportal24.test/de/newsroom/'.$company->slug) + ->assertNotFound(); +}); + +test('the newsrooms pages are available per edition and portal', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/en/newsrooms')->assertSuccessful(); + $this->get('https://presseecho.test/de/newsrooms') + ->assertSuccessful() + ->assertSee('presse', false); +}); + +test('legacy newsroom html urls redirect to the clean edition path', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/de/newsrooms.html') + ->assertRedirect('https://businessportal24.test/de/newsrooms'); + + $this->get('https://businessportal24.test/de/newsroom/acme.html') + ->assertRedirect('https://businessportal24.test/de/newsroom/acme'); +}); diff --git a/tests/Feature/Web/PreisePageTest.php b/tests/Feature/Web/PreisePageTest.php new file mode 100644 index 00000000..82630f7e --- /dev/null +++ b/tests/Feature/Web/PreisePageTest.php @@ -0,0 +1,57 @@ +seed(PlanSeeder::class); + + $this->get('https://businessportal24.test/de/preise') + ->assertSuccessful() + // Editorial-Masthead (Inhalte aus lang/de/pricing.php) + ->assertSeeText('Transparente Preise für jede Reichweite') + // 1) Abo-Tarife (echte Pläne aus dem System) + ->assertSeeText('Abo-Tarife mit Monatskontingent') + ->assertSeeText('Starter') + ->assertSeeText('Business') + ->assertSeeText('Pro') + ->assertSeeText('Agency') + // 2) Einzel-Pressemitteilung (config/billing) + ->assertSeeText('Einzel-Pressemitteilung') + ->assertSeeText('pro Veröffentlichung') + // 3) Add-ons (config/credits) + ->assertSeeText('Add-ons & Einzelkosten') + ->assertSeeText('Extra-Pressemitteilung') + ->assertSeeText('Boost-Platzierung') + ->assertSeeText('Veröffentlichungsnachweis (PDF)') + ->assertSeeText('Credit-Pakete') + ->assertSeeText('7 Tage') // boost 7d aus config + ->assertSeeText('10 Credits') // Credit-Paket p10 aus config + ->assertSeeText('Depublizieren') + ->assertSeeText('25 Credits'); // depublish aus config +}); + +test('the pricing page serves english content on the english edition', function () { + /** @var TestCase $this */ + $this->seed(PlanSeeder::class); + + $this->get('https://businessportal24.test/en/preise') + ->assertSuccessful() + ->assertSeeText('Transparent pricing for every reach') + ->assertSeeText('Subscriptions with a monthly quota') + ->assertSeeText('Single press release') + ->assertSeeText('Add-ons & individual costs'); +}); + +test('the pricing page still works and shows an empty hint without plans', function () { + /** @var TestCase $this */ + expect(Plan::query()->count())->toBe(0); + + $this->get('https://businessportal24.test/de/preise') + ->assertSuccessful() + ->assertSeeText('Tarife werden in Kürze veröffentlicht.') + // Add-ons sind config-basiert und unabhängig von Tarifen sichtbar. + ->assertSeeText('Add-ons & Einzelkosten'); +}); diff --git a/tests/Feature/Web/PricingTeaserTest.php b/tests/Feature/Web/PricingTeaserTest.php new file mode 100644 index 00000000..0aefab79 --- /dev/null +++ b/tests/Feature/Web/PricingTeaserTest.php @@ -0,0 +1,54 @@ +seed(PlanSeeder::class); + + $this->get('https://businessportal24.test/de') + ->assertSuccessful() + ->assertSeeText('Tarife & Pakete') + ->assertSeeText('Starter') + ->assertSeeText('Business') + ->assertSeeText('Pro') + ->assertSeeText('Agency') + // Jahrespreis-Kommunikation und Live-Preis (Agency 199 €). + ->assertSeeText('2 Monate gratis') + ->assertSee('199') + ->assertSeeText('Auswählen') + ->assertSeeText('Beliebt'); +}); + +test('the pricing teaser hides inactive plans', function () { + /** @var TestCase $this */ + Plan::factory()->create(['name' => 'Sichtbarer Tarif', 'is_active' => true]); + Plan::factory()->inactive()->create(['name' => 'Versteckter Tarif']); + + $this->get('https://businessportal24.test/de') + ->assertSuccessful() + ->assertSeeText('Sichtbarer Tarif') + ->assertDontSeeText('Versteckter Tarif'); +}); + +test('the pricing teaser is omitted when no plans exist but the page still works', function () { + /** @var TestCase $this */ + expect(Plan::query()->count())->toBe(0); + + $this->get('https://businessportal24.test/de') + ->assertSuccessful() + ->assertDontSeeText('Auswählen'); +}); + +test('the header links to the pricing page on every portal page', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/de') + ->assertSuccessful() + ->assertSee('/de/preise'); + + $this->get('https://presseecho.test/de') + ->assertSuccessful() + ->assertSee('/de/preise'); +}); diff --git a/tests/Feature/Web/StaticPagesTest.php b/tests/Feature/Web/StaticPagesTest.php new file mode 100644 index 00000000..918b7328 --- /dev/null +++ b/tests/Feature/Web/StaticPagesTest.php @@ -0,0 +1,45 @@ + ['de/impressum', 'Impressum'], + 'datenschutz' => ['de/datenschutz', 'Datenschutzerklärung'], + 'agb' => ['de/agb', 'Allgemeine Geschäftsbedingungen'], + 'cookies' => ['de/cookies', 'Cookie-Richtlinien'], + 'faq' => ['de/faq', 'Häufig gestellte Fragen'], + 'hilfe' => ['de/hilfe', 'Hilfe-Center'], + 'kontakt' => ['de/kontakt', 'Kontakt'], + 'ueber-uns' => ['de/ueber-uns', 'Über uns'], + 'api' => ['de/api', 'API & Integrationen'], +]); + +test('static pages render in the editorial design', function (string $path, string $heading) { + /** @var TestCase $this */ + $response = $this->get("https://businessportal24.test/{$path}"); + + $response->assertSuccessful() + ->assertSeeText($heading) + // Editorial-Shell (Token aus den neuen Komponenten) statt altem Theme. + ->assertSee('bg-bg', false) + // Altes Theme darf nicht mehr gerendert werden. + ->assertDontSee('gradient-hero'); +})->with('static pages'); + +test('the faq page lists questions and links to support', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/de/faq') + ->assertSuccessful() + ->assertSeeText('Wie veröffentliche ich meine erste Pressemitteilung?') + ->assertSeeText('Frage nicht gefunden?') + ->assertSee(route('kontakt'), false); +}); + +test('the help center shows quick links and topics', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/de/hilfe') + ->assertSuccessful() + ->assertSeeText('Hilfe-Themen') + ->assertSee(route('faq'), false) + ->assertSee(route('veroeffentlichen'), false); +});