- 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]>
93 lines
3.7 KiB
TypeScript
93 lines
3.7 KiB
TypeScript
import type { Camp } from "@/generated/prisma/client";
|
|
import { CAMP_ID, type Db, type Tx } from "./db";
|
|
import { evaluateSales, type CampGateFields, type SalesState, type StripeMode } from "./sales-gate";
|
|
import { dbDateToIso } from "./time";
|
|
|
|
/**
|
|
* Laedt die Camp-Konfiguration. Fehlt die Zeile (frische Datenbank), wird
|
|
* sie mit sicheren Standardwerten angelegt: Live-Verkauf aus, keine
|
|
* Freigaben, 13 Plaetze und 4 Stammmodel-Buchungen. Die Migration
|
|
* 20261001120000_camp_2027_bestaetigt legt Termin, Ort und Zimmer fest.
|
|
*/
|
|
export async function getCamp(db: Db | Tx): Promise<Camp> {
|
|
return db.camp.upsert({ where: { id: CAMP_ID }, create: { id: CAMP_ID }, update: {} });
|
|
}
|
|
|
|
export function campStartIso(camp: Pick<Camp, "campStartDate">): string | null {
|
|
return camp.campStartDate ? dbDateToIso(camp.campStartDate) : null;
|
|
}
|
|
|
|
export function toGateFields(camp: Camp): CampGateFields {
|
|
return {
|
|
salesEnabled: camp.salesEnabled,
|
|
capacityConfirmed: camp.capacityConfirmed,
|
|
stammmodelQuotaConfirmed: camp.stammmodelQuotaConfirmed,
|
|
campStartDate: campStartIso(camp),
|
|
dateConfirmed: camp.dateConfirmed,
|
|
locationName: camp.locationName,
|
|
locationConfirmed: camp.locationConfirmed,
|
|
accommodationText: camp.accommodationText,
|
|
accommodationConfirmed: camp.accommodationConfirmed,
|
|
servicesConfirmed: camp.servicesConfirmed,
|
|
organizerName: camp.organizerName,
|
|
organizerAddress: camp.organizerAddress,
|
|
organizerEmail: camp.organizerEmail,
|
|
organizerConfirmed: camp.organizerConfirmed,
|
|
taxNote: camp.taxNote,
|
|
taxConfirmed: camp.taxConfirmed,
|
|
termsVersion: camp.termsVersion,
|
|
termsApproved: camp.termsApproved,
|
|
withdrawalText: camp.withdrawalText,
|
|
withdrawalApproved: camp.withdrawalApproved,
|
|
travelLawChecked: camp.travelLawChecked,
|
|
installmentsApproved: camp.installmentsApproved,
|
|
installmentRuleApproved: camp.installmentRuleApproved,
|
|
testPhaseOverride: camp.testPhaseOverride,
|
|
};
|
|
}
|
|
|
|
export function salesStateFor(camp: Camp, mode: StripeMode, now: Date = new Date()): SalesState {
|
|
return evaluateSales(now, toGateFields(camp), mode);
|
|
}
|
|
|
|
/**
|
|
* Oeffentlich anzeigbare, bestaetigte Fakten. Unbestaetigte Werte werden
|
|
* hier bewusst zu null — die Seite zeigt dann "wird bekanntgegeben" statt
|
|
* eines ungeprueften Datums oder Orts.
|
|
*/
|
|
export type PublicCampFacts = {
|
|
startDate: string | null;
|
|
endDate: string | null;
|
|
location: string | null;
|
|
region: string | null;
|
|
accommodationText: string | null;
|
|
taxNote: string | null;
|
|
organizer: { name: string; address: string; email: string; phone: string | null } | null;
|
|
termsUrl: string | null;
|
|
termsVersion: string | null;
|
|
withdrawalText: string | null;
|
|
};
|
|
|
|
export function publicCampFacts(camp: Camp): PublicCampFacts {
|
|
return {
|
|
startDate: camp.dateConfirmed ? campStartIso(camp) : null,
|
|
endDate: camp.dateConfirmed && camp.campEndDate ? dbDateToIso(camp.campEndDate) : null,
|
|
location: camp.locationConfirmed ? camp.locationName : null,
|
|
region: camp.locationConfirmed ? camp.locationRegion : null,
|
|
accommodationText: camp.accommodationConfirmed ? camp.accommodationText : null,
|
|
taxNote: camp.taxConfirmed ? camp.taxNote : null,
|
|
organizer:
|
|
camp.organizerConfirmed && camp.organizerName && camp.organizerAddress && camp.organizerEmail
|
|
? {
|
|
name: camp.organizerName,
|
|
address: camp.organizerAddress,
|
|
email: camp.organizerEmail,
|
|
phone: camp.organizerPhone,
|
|
}
|
|
: null,
|
|
termsUrl: camp.termsApproved ? camp.termsUrl : null,
|
|
termsVersion: camp.termsApproved ? camp.termsVersion : null,
|
|
withdrawalText: camp.withdrawalApproved ? camp.withdrawalText : null,
|
|
};
|
|
}
|