feat: Onepage nach Filmcamp-Gliederung umgebaut
- Figtree als einzige Schrift, Logo mittig in fester Kopfzeile - Headerbild ueber volle Breite und Hoehe, ohne Preis, mit Terminleiste - "Was dich erwartet" stellt die Villa mit animierter Bilderfolge vor, darunter Tagesablauf als Karussell (Programm folgt als PDF) - Preis: regulaerer Preis durchgestrichen, kein Verkaufsstartdatum, Ueberschrift "Jetzt Platz sichern mit Early Bird" - Team als "Mit dabei", danach drei Reels im Hochformat - Logo, Headerbild, Portraets und Reels ueber feste Dateinamen im Medienverzeichnis (docs/MEDIEN.md), ohne Neustart austauschbar - Widerrufsbelehrung im Admin pflegbar, Seite /widerruf, Live-Verkauf ohne Freigabe gesperrt - Anreise: Abholung am Bahnhof Firenze Santa Maria Novella - Booking-Link entfernt, Gedankenstriche aus Kundentexten entfernt Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
3cba3e2341
commit
7203cd1e6e
37 files changed
+962
-517
No files matched your search
@@ -4,20 +4,47 @@ import { formatEuroShort } from "@/lib/money";
|
||||
import type { PublicPricing } from "@/lib/public-page";
|
||||
import { BookButton } from "../booking/BookingProvider";
|
||||
|
||||
// Bewusst ohne Startdatum des Verkaufs (Wunsch des Auftraggebers).
|
||||
const CLOSED_TEXT: Record<Exclude<PublicPricing["closedReason"], null>, string> = {
|
||||
PREVIEW: "Die Buchung öffnet am 1. November 2026.",
|
||||
PREVIEW: "Bald buchbar.",
|
||||
NOT_RELEASED: "Die Buchung ist noch nicht freigeschaltet. Wir veröffentlichen alle verbindlichen Angaben vor dem Verkaufsstart.",
|
||||
CAMP_STARTED: "Die Buchung ist geschlossen.",
|
||||
NOT_CONFIGURED: "Die Buchung ist derzeit nicht verfügbar.",
|
||||
};
|
||||
|
||||
function PriceDisplay({ pricing }: { pricing: PublicPricing }) {
|
||||
if (pricing.phase === "REGULAR" && pricing.currentCents !== null) {
|
||||
return (
|
||||
<>
|
||||
<p className="text-[clamp(4rem,12vw,7.5rem)] font-extrabold leading-none tracking-[-0.03em] tabular-nums">{formatEuroShort(pricing.currentCents)}</p>
|
||||
<p className="mt-3 text-lg">pro Person</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
// Vorschau und Early Bird: regulaerer Preis durchgestrichen, Early Bird gross.
|
||||
const earlyBird = pricing.currentCents ?? pricing.earlyBirdCents;
|
||||
return (
|
||||
<>
|
||||
<p className="text-lg text-stone">
|
||||
Regulärer Preis <s className="tabular-nums">{formatEuroShort(pricing.regularCents)}</s>
|
||||
</p>
|
||||
<p className="mt-2 flex flex-wrap items-baseline gap-x-4 gap-y-2">
|
||||
<span className="text-[clamp(4rem,12vw,7.5rem)] font-extrabold leading-none tracking-[-0.03em] tabular-nums">{formatEuroShort(earlyBird)}</span>
|
||||
<span className="rounded-full bg-rosso px-3 py-1 text-sm font-bold uppercase tracking-[0.12em] text-paper">Early Bird</span>
|
||||
</p>
|
||||
<p className="mt-3 text-lg">Early-Bird-Preis pro Person, gültig bis einschließlich 31. Dezember 2026.</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function PricingSection({ pricing, facts, showDrafts }: { pricing: PublicPricing; facts: PublicCampFacts; showDrafts: boolean }) {
|
||||
const included = site.services.included.filter((s) => s.confirmed);
|
||||
const title = pricing.phase === "REGULAR" ? "Jetzt Platz sichern" : "Jetzt Platz sichern mit Early Bird";
|
||||
return (
|
||||
<section id="preis" aria-labelledby="preis-title" className="bg-paper py-20 md:py-32">
|
||||
<div className="container-edge grid gap-14 md:grid-cols-12">
|
||||
<div className="md:col-span-7">
|
||||
<h2 id="preis-title" className="section-title">Preis und Buchung</h2>
|
||||
<h2 id="preis-title" className="section-title">{title}</h2>
|
||||
|
||||
{pricing.mode === "test" && (
|
||||
<p className="mt-6 rounded-lg border border-line bg-plaster p-4 text-[0.9375rem]">
|
||||
@@ -27,33 +54,7 @@ export function PricingSection({ pricing, facts, showDrafts }: { pricing: Public
|
||||
)}
|
||||
|
||||
<div className="mt-10">
|
||||
{pricing.phase === "EARLY_BIRD" && pricing.currentCents !== null && (
|
||||
<>
|
||||
<p className="text-stone">
|
||||
Regulärer Preis <s className="tabular-nums">{formatEuroShort(pricing.regularCents)}</s>
|
||||
</p>
|
||||
<p className="display mt-2 text-[clamp(4rem,12vw,8rem)] leading-none tabular-nums">{formatEuroShort(pricing.currentCents)}</p>
|
||||
<p className="mt-3 text-lg">Early-Bird-Preis pro Person, gültig bis einschließlich 31. Dezember 2026.</p>
|
||||
</>
|
||||
)}
|
||||
{pricing.phase === "REGULAR" && pricing.currentCents !== null && (
|
||||
<>
|
||||
<p className="display text-[clamp(4rem,12vw,8rem)] leading-none tabular-nums">{formatEuroShort(pricing.currentCents)}</p>
|
||||
<p className="mt-3 text-lg">pro Person</p>
|
||||
</>
|
||||
)}
|
||||
{pricing.phase === "PREVIEW" && (
|
||||
<dl className="grid gap-6 sm:grid-cols-2">
|
||||
<div className="border-t border-ink pt-4">
|
||||
<dt className="text-stone">Early Bird, 1. November bis 31. Dezember 2026</dt>
|
||||
<dd className="display mt-2 text-6xl tabular-nums">{formatEuroShort(pricing.earlyBirdCents)}</dd>
|
||||
</div>
|
||||
<div className="border-t border-line pt-4">
|
||||
<dt className="text-stone">Regulär, ab 1. Januar 2027</dt>
|
||||
<dd className="display mt-2 text-6xl tabular-nums">{formatEuroShort(pricing.regularCents)}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
)}
|
||||
<PriceDisplay pricing={pricing} />
|
||||
</div>
|
||||
|
||||
<ul className="mt-8 max-w-xl space-y-2 text-[0.9375rem] text-ink/85">
|
||||
@@ -77,7 +78,7 @@ export function PricingSection({ pricing, facts, showDrafts }: { pricing: Public
|
||||
|
||||
<div className="mt-10">
|
||||
{pricing.checkoutOpen ? (
|
||||
<BookButton className="btn btn-primary px-8 text-lg">Platz buchen</BookButton>
|
||||
<BookButton className="btn btn-primary px-8 text-lg">Jetzt Platz sichern</BookButton>
|
||||
) : (
|
||||
<p className="max-w-xl text-lg font-semibold">{CLOSED_TEXT[pricing.closedReason ?? "NOT_CONFIGURED"]}</p>
|
||||
)}
|
||||
@@ -91,12 +92,13 @@ export function PricingSection({ pricing, facts, showDrafts }: { pricing: Public
|
||||
<li key={item.text} className="py-3">{item.text}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="mt-6 text-[0.9375rem] text-stone">
|
||||
Nicht enthalten: {site.services.excluded.join(", ")}.
|
||||
</p>
|
||||
<p className="mt-6 text-[0.9375rem] text-stone">Nicht enthalten: {site.services.excluded.join(", ")}.</p>
|
||||
<p className="mt-4 text-[0.9375rem] text-stone">
|
||||
Vor der Zahlung siehst du Gesamtpreis, heute fälligen Betrag, alle weiteren Raten mit Terminen und die Beschreibung der Unterbringung auf einen Blick.
|
||||
</p>
|
||||
<p className="mt-4 text-[0.9375rem] text-stone">
|
||||
Hinweise zu deinem <a href="/widerruf" className="link-quiet">Widerrufsrecht</a>.
|
||||
</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in new issue
Block a user