feat: Tagesablauf laut Wochenplan, Termin 03. bis 09. Mai 2027
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 2m51s
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 2m51s
- Alle sieben Tage aus dem Wochenplan-PDF mit Uhrzeit, Coach und Foto-Kennzeichnung im Karussell - Migration: Camp-Termin 03.05. bis 09.05.2027 (ersetzt 08. bis 15.05.) - Leistungen laut PDF: 20 Workshops und Shootings, sechs Naechte, Bustransfer ab und bis Florenz, Fruehstueck, Snacks und Abendessen - FAQ Anreise mit Transferzeiten - Hinweise "Programmentwurf" und "Laut Beschreibung der Unterkunft" entfernt Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
3f5a2554ee
commit
84c8f2f32b
7 files changed
+138
-52
No files matched your search
@@ -2,11 +2,11 @@
|
||||
|
||||
Stand 01.10.2026. Bereits bestätigt und per Migration `20261001120000_camp_2027_bestaetigt` eingetragen (im Admin änderbar):
|
||||
|
||||
- Termin 08.05.–15.05.2027, Villa La Chiesina, Arcidosso (Toskana)
|
||||
- Termin 03.05. bis 09.05.2027 (laut Wochenplan vom 02.10.2026), Villa La Chiesina, Arcidosso (Toskana)
|
||||
- Unterbringung: eigenes Bett im geteilten Zimmer, keine Einzelzimmer
|
||||
- 13 verkaufbare Plätze
|
||||
- Team (Namen und Rollen) und Unterkunftsbilder; die Booking-Seite wird bewusst nicht verlinkt
|
||||
- Abholung am Anreisetag vom Bahnhof Firenze Santa Maria Novella
|
||||
- Tagesablauf aller sieben Tage, Bustransfer ab und bis Florenz, Verpflegung (Frühstück, Snacks, Abendessen)
|
||||
- Early Bird 01.11. bis 31.12.2026, ohne Anzeige eines Verkaufsstarts
|
||||
|
||||
Solange die mit **(Freigabe)** markierten Punkte fehlen, bleibt der Live-Verkauf serverseitig gesperrt. Der aktuelle Stand steht im Admin-Dashboard unter „Fehlt für den Live-Verkauf“.
|
||||
@@ -18,8 +18,6 @@ Solange die mit **(Freigabe)** markierten Punkte fehlen, bleibt der Live-Verkauf
|
||||
| Logo (`logo.svg`, `logo-hell.svg`) und Headerbild (`header.jpg`) | Medienverzeichnis, Dateinamen in `docs/MEDIEN.md` |
|
||||
| Porträts der neun Teammitglieder | Medienverzeichnis `team/<name>.jpg`, siehe `docs/MEDIEN.md` |
|
||||
| Freigegebene Kurzbeschreibungen der Teammitglieder | `site.ts` → `team` |
|
||||
| Tagesablauf (Programm als PDF angekündigt) | `site.ts` → `schedule.days` |
|
||||
| Uhrzeit der Abholung in Florenz | `site.ts` → `schedule.days[0]` |
|
||||
| Schriftliche Bestätigung des Vermieters, dass die Booking-Bilder genutzt werden dürfen (Urheber ist der Vermieter bzw. Fotograf) | Ablage beim Veranstalter |
|
||||
| Link zur ARTISTA-Stammmodel-Seite und daraus abgeleiteter Text „Über ARTISTA“ | `site.ts` → `about` |
|
||||
| Drei Reels (Video + Vorschaubild) mit Freigabe der gezeigten Personen | Medienverzeichnis `reels/reel-1.mp4` usw., Namen in `site.ts` → `reels` |
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
-- Termin laut Wochenplan des Auftraggebers (02.10.2026):
|
||||
-- Montag, 03.05.2027 bis Sonntag, 09.05.2027.
|
||||
-- Ersetzt nur den per Migration 20261001120000 gesetzten Termin; ein im
|
||||
-- Adminbereich bereits anders gepflegter Termin bleibt unveraendert.
|
||||
UPDATE "Camp" SET
|
||||
"campStartDate" = DATE '2027-05-03',
|
||||
"campEndDate" = DATE '2027-05-09',
|
||||
"dateConfirmed" = true,
|
||||
"updatedAt" = CURRENT_TIMESTAMP,
|
||||
"updatedBy" = 'migration'
|
||||
WHERE "id" = 'modelcamp-2027'
|
||||
AND ("campStartDate" IS NULL OR "campStartDate" = DATE '2027-05-08');
|
||||
@@ -1,15 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { ScheduleItem } from "@/content/site";
|
||||
|
||||
export type ScheduleCard = { label: string; date: string | null; title: string | null; items: string[] };
|
||||
export type ScheduleCard = { label: string; date: string | null; title: string; items: ScheduleItem[] };
|
||||
|
||||
/**
|
||||
* Tagesablauf als waagerechte Kartenreihe mit Einrasten (scroll-snap).
|
||||
* Wischen auf dem Smartphone, Pfeile und Punkte auf allen Groessen. Die
|
||||
* Liste bleibt eine echte Liste: ohne JavaScript scrollt sie trotzdem.
|
||||
*/
|
||||
export function ScheduleCarousel({ days, emptyText }: { days: ScheduleCard[]; emptyText: string }) {
|
||||
export function ScheduleCarousel({ days }: { days: ScheduleCard[] }) {
|
||||
const track = useRef<HTMLOListElement>(null);
|
||||
const cards = useRef<(HTMLLIElement | null)[]>([]);
|
||||
const [current, setCurrent] = useState(0);
|
||||
@@ -52,23 +53,28 @@ export function ScheduleCarousel({ days, emptyText }: { days: ScheduleCard[]; em
|
||||
cards.current[index] = el;
|
||||
}}
|
||||
data-index={index}
|
||||
className="flex w-[85%] shrink-0 snap-start flex-col rounded-2xl bg-paper p-7 shadow-[0_1px_0_var(--color-line)] sm:w-[60%] md:w-[44%] md:p-9 lg:w-[31%]"
|
||||
className="flex w-[88%] shrink-0 snap-start flex-col rounded-2xl bg-paper p-6 shadow-[0_1px_0_var(--color-line)] sm:w-[62%] md:w-[46%] md:p-8 lg:w-[34%]"
|
||||
>
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.16em] text-rosso">{day.label}</p>
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.16em] text-rosso">
|
||||
{day.label} · {day.title}
|
||||
</p>
|
||||
{day.date && <p className="mt-1 text-xl font-bold md:text-2xl">{day.date}</p>}
|
||||
{day.title && <p className="mt-1 text-stone">{day.title}</p>}
|
||||
{day.items.length > 0 ? (
|
||||
<ul className="mt-6 space-y-3 border-t border-line pt-5">
|
||||
{day.items.map((item) => (
|
||||
<li key={item} className="flex gap-3">
|
||||
<span aria-hidden className="mt-[0.6em] h-1.5 w-1.5 shrink-0 rounded-full bg-rosso" />
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p className="mt-6 border-t border-line pt-5 text-stone">{emptyText}</p>
|
||||
)}
|
||||
<ul className="mt-5 divide-y divide-line border-t border-line">
|
||||
{day.items.map((item) => (
|
||||
<li key={`${item.time} ${item.text}`} className="grid grid-cols-[5.75rem_1fr] gap-x-3 py-3 text-[0.9375rem] leading-snug">
|
||||
<span className="font-semibold tabular-nums text-ink/75">{item.time}</span>
|
||||
<span>
|
||||
{item.text}
|
||||
{item.photo && (
|
||||
<span className="ml-2 inline-block translate-y-[-1px] rounded bg-sand/60 px-1.5 py-px align-middle text-[0.6875rem] font-bold uppercase tracking-[0.08em]">
|
||||
Foto
|
||||
</span>
|
||||
)}
|
||||
{item.coach && <span className="mt-0.5 block text-sm text-stone">mit {item.coach}</span>}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
@@ -29,7 +29,6 @@ export function AccommodationSection({ facts, showDrafts }: { facts: PublicCampF
|
||||
<li key={feature} className="py-2.5">{feature}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="mt-2 text-sm text-stone">Laut Beschreibung der Unterkunft.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -52,13 +52,11 @@ export function ScheduleSection({ facts }: { facts: PublicCampFacts }) {
|
||||
return (
|
||||
<section id="ablauf" aria-labelledby="ablauf-title" className="bg-sand/35 py-24 md:py-32">
|
||||
<div className="container-edge">
|
||||
<div className="flex flex-wrap items-end justify-between gap-6">
|
||||
<h2 id="ablauf-title" className="section-title">{schedule.title}</h2>
|
||||
<p className="max-w-md text-ink/80">{schedule.note}</p>
|
||||
</div>
|
||||
<h2 id="ablauf-title" className="section-title">{schedule.title}</h2>
|
||||
<div className="mt-12">
|
||||
<ScheduleCarousel days={scheduleCards(facts)} emptyText="Programm folgt." />
|
||||
<ScheduleCarousel days={scheduleCards(facts)} />
|
||||
</div>
|
||||
<p className="mt-8 text-ink/80">{schedule.footnote}</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ function faqs(facts: PublicCampFacts, pricing: PublicPricing): Faq[] {
|
||||
},
|
||||
{
|
||||
q: "Wie reise ich an?",
|
||||
a: `Du reist selbst nach Florenz an. Am Anreisetag holen wir dich am Bahnhof Firenze Santa Maria Novella ab und bringen dich zur Villa. Kommst du mit dem Auto, fährst du direkt zur Unterkunft. Die Anreise nach Italien und zurück ist nicht im Preis enthalten.${facts.location ? ` Das Camp findet in der ${facts.location} statt.` : ""}`,
|
||||
a: `Du reist selbst nach Florenz an. Am Anreisetag fährt um 15:30 Uhr ein Bus vom Bahnhof Firenze Santa Maria Novella zur Villa. Kommst du mit dem Auto, fährst du direkt zur Unterkunft und bist zwischen 17:30 und 18:00 Uhr da. Am Abreisetag bringt dich der Bus zwischen 09:00 und 12:00 Uhr zurück nach Florenz. Die Anreise nach Italien und zurück ist nicht im Preis enthalten.${facts.location ? ` Das Camp findet in der ${facts.location} statt.` : ""}`,
|
||||
},
|
||||
{
|
||||
q: "Habe ich ein Widerrufsrecht?",
|
||||
|
||||
+97
-24
@@ -30,7 +30,8 @@ export type Media =
|
||||
|
||||
export type Confirmable<T> = T & { confirmed: boolean };
|
||||
|
||||
export type ScheduleDay = { title: string | null; items: string[] };
|
||||
export type ScheduleItem = { time: string; text: string; coach?: string; photo?: boolean };
|
||||
export type ScheduleDay = { title: string; items: ScheduleItem[] };
|
||||
|
||||
const portrait = (name: string, initials: string): Media => ({ kind: "placeholder", label: `Porträt ${name}`, tone: "portrait", glyph: initials });
|
||||
/** Lokales Bild aus public/images/unterkunft (1280 x 853 px). */
|
||||
@@ -55,7 +56,7 @@ export const site = {
|
||||
"Die Villa liegt zwischen Zypressen oberhalb von Arcidosso, mit Blick über die Hügel am Monte Amiata. Hier wohnen, arbeiten und essen wir eine Woche lang zusammen.",
|
||||
"Garten, Pool, alte Steintreppen und Räume mit Holzbalkendecken: Die Villa bietet viele Orte zum Fotografieren und zum Durchatmen.",
|
||||
],
|
||||
highlights: ["Eigenes Bett im geteilten Zimmer", "Abholung am Bahnhof in Florenz", "Kleine Gruppe, ein ganzes Team"],
|
||||
highlights: ["Eigenes Bett im geteilten Zimmer", "Bustransfer ab und bis Florenz", "20 Workshops und Shootings mit dem Team"],
|
||||
slides: [
|
||||
villa("villa-eingang", "Die Villa La Chiesina mit Steintreppe und Rasen im Frühling"),
|
||||
villa("pool-zypressen", "Pool zwischen hohen Zypressen, im Hintergrund die Hügel der Toskana"),
|
||||
@@ -67,39 +68,111 @@ export const site = {
|
||||
|
||||
schedule: {
|
||||
title: "Tagesablauf",
|
||||
note: "Programmentwurf. Das finale Programm kann sich noch ändern.",
|
||||
// Ein Eintrag pro Tag ab Anreisetag; das Datum kommt aus dem
|
||||
// Adminbereich. Das Programm liefert der Auftraggeber als PDF nach.
|
||||
// Leere Tage zeigen "Programm folgt". Nichts erfinden.
|
||||
// Quelle: Modelcamp_2027_Wochenplan.pdf vom Auftraggeber (02.10.2026),
|
||||
// Seiten 2 bis 4. Ein Eintrag pro Tag ab Anreisetag; Wochentag und
|
||||
// Datum kommen aus dem Camp-Termin im Adminbereich.
|
||||
days: [
|
||||
{
|
||||
title: "Anreise",
|
||||
items: [
|
||||
"Eigene Anreise nach Florenz",
|
||||
"Abholung am Bahnhof Firenze Santa Maria Novella und gemeinsame Fahrt zur Villa",
|
||||
"Mit dem Auto? Dann fährst du direkt zur Villa",
|
||||
{ time: "Vormittag", text: "Eigene Anreise nach Florenz" },
|
||||
{ time: "15:30", text: "Bustransfer vom Bahnhof Santa Maria Novella zur Unterkunft" },
|
||||
{ time: "17:30 bis 18:00", text: "Ankunft der Selbstfahrer" },
|
||||
{ time: "18:00", text: "Zimmerverteilung" },
|
||||
{ time: "18:30", text: "Kennenlernen" },
|
||||
{ time: "19:00", text: "Abendessen" },
|
||||
{ time: "20:00", text: "Abendausklang am Kamin" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Fundament",
|
||||
items: [
|
||||
{ time: "08:30 bis 09:30", text: "Frühstück, ab 09:00 Einführung und Ziele der Woche" },
|
||||
{ time: "10:00 bis 11:15", text: "Stretching & Mobility: Yoga und Quick-Routine", coach: "Lola" },
|
||||
{ time: "11:15 bis 13:00", text: "Catwalk-Grundlagen, inkl. Vorher-Video (gefilmt von Eric und Saskia)", coach: "Eileen" },
|
||||
{ time: "13:30 bis 14:30", text: "Make-up-Basics: Clean / Natural Look", coach: "Gabi" },
|
||||
{ time: "15:00 bis 16:30", text: "Social-Media-Grundlagen", coach: "Saskia" },
|
||||
{ time: "17:30 bis 18:45", text: "Stimmtraining", coach: "Eric" },
|
||||
{ time: "19:00", text: "Abendessen" },
|
||||
{ time: "20:00 bis 21:00", text: "Toastmasters Runde 1: Stegreif-Reden, erste Wirkung", coach: "Eric" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Ausdruck & Kamera",
|
||||
items: [
|
||||
{ time: "07:30 bis 08:15", text: "Frühsport (optional)", coach: "Gabi" },
|
||||
{ time: "08:30 bis 09:30", text: "Frühstück" },
|
||||
{ time: "10:00 bis 11:30", text: "Mimikkontrolle", coach: "Eric", photo: true },
|
||||
{ time: "11:30 bis 13:30", text: "Range: Angel or Devil Shoot, Emotion vor der Kamera", coach: "Lola, Eric, Michele", photo: true },
|
||||
{ time: "14:00 bis 15:00", text: "Commercial-Posing", coach: "Gabi" },
|
||||
{ time: "15:00 bis 16:00", text: "Testimonial-Look", coach: "Gabi", photo: true },
|
||||
{ time: "16:15 bis 17:15", text: "Runway-Posen am Laufstegende", coach: "Eileen", photo: true },
|
||||
{ time: "17:30 bis 18:30", text: "Tennisshoot", coach: "Michele" },
|
||||
{ time: "19:00", text: "Abendessen" },
|
||||
{ time: "20:00", text: "Angel-or-Devil-Bildbesprechung und Spieleabend", coach: "Lola" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Ausflugstag Arcidosso",
|
||||
items: [
|
||||
{ time: "08:30 bis 09:30", text: "Frühstück / Sandwichstation" },
|
||||
{ time: "10:00 bis 11:00", text: "Spaziergang nach Arcidosso" },
|
||||
{ time: "11:00 bis 12:30", text: "Foto-Content fürs eigene Profil, vor Ort", coach: "Saskia", photo: true },
|
||||
{ time: "12:30 bis 14:00", text: "Freies Shooten in Arcidosso" },
|
||||
{ time: "14:00", text: "Treffen und Austausch" },
|
||||
{ time: "14:30 bis 15:30", text: "Spaziergang zurück zum Camp" },
|
||||
{ time: "16:30 bis 18:00", text: "Range: Acting & Performance", coach: "Lola" },
|
||||
{ time: "19:00", text: "Abendessen" },
|
||||
{ time: "20:00", text: "Lagerfeuer und Musik" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Walk, Content & Pool",
|
||||
items: [
|
||||
{ time: "08:30 bis 09:30", text: "Frühstück" },
|
||||
{ time: "10:00 bis 13:00", text: "Walk: Runway-Analyse, High-Fashion & Commercial, Fashion Cities, Runway Practice", coach: "Lola" },
|
||||
{ time: "13:30 bis 14:15", text: "Walk mit Musik & Rhythmus", coach: "Eileen", photo: true },
|
||||
{ time: "14:15 bis 16:15", text: "Content & Reels produzieren", coach: "Saskia", photo: true },
|
||||
{ time: "16:30 bis 18:30", text: "Poolparty mit Cocktails und Fingerfood" },
|
||||
{ time: "18:30", text: "Gruppenfoto" },
|
||||
{ time: "19:00", text: "Abendessen" },
|
||||
{ time: "20:00 bis 21:00", text: "Toastmasters Runde 2: Fortschritt sichtbar machen", coach: "Eric" },
|
||||
{ time: "21:30", text: "Special-Programm für Mutige" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Editorial, Pitch & Finale",
|
||||
items: [
|
||||
{ time: "07:30 bis 08:15", text: "Frühsport (optional)", coach: "Gabi" },
|
||||
{ time: "08:30 bis 09:30", text: "Frühstück" },
|
||||
{ time: "10:00 bis 13:00", text: "Editorial Posing inkl. Test-Shoot", coach: "Lola, Michele", photo: true },
|
||||
{ time: "13:45 bis 14:30", text: "Elevator Pitch: erarbeiten", coach: "Eric" },
|
||||
{ time: "14:30 bis 15:30", text: "Elevator Pitch: vorstellen, als Vorstellungsvideo (gefilmt von Saskia)", coach: "Eric", photo: true },
|
||||
{ time: "16:00 bis 17:30", text: "Bildauswahl und Einzelfeedback", coach: "Team" },
|
||||
{ time: "17:30 bis 19:00", text: "Pause und Styling" },
|
||||
{ time: "19:00 bis 20:30", text: "Golden-Hour-Finalshooting", coach: "Michele", photo: true },
|
||||
{ time: "20:30", text: "Abschiedsdinner, Reel- und Bildervorstellung, Abschiedsfeier" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Abreise",
|
||||
items: [
|
||||
{ time: "08:00 bis 08:45", text: "Sandwichstation" },
|
||||
{ time: "09:00 bis 12:00", text: "Bustransfer nach Florenz" },
|
||||
],
|
||||
},
|
||||
{ title: null, items: [] },
|
||||
{ title: null, items: [] },
|
||||
{ title: null, items: [] },
|
||||
{ title: null, items: [] },
|
||||
{ title: null, items: [] },
|
||||
{ title: null, items: [] },
|
||||
{ title: "Abreise", items: [] },
|
||||
] as ScheduleDay[],
|
||||
footnote: "Frühstück, Snacks über den Tag und Abendessen sind für alle inklusive.",
|
||||
},
|
||||
|
||||
services: {
|
||||
included: [
|
||||
// Bestaetigt durch das Buchungsprodukt bzw. den Auftraggeber:
|
||||
{ text: "Teilnahme am Modelcamp 2027", confirmed: true },
|
||||
{ text: "Sieben Nächte in der Villa La Chiesina, eigenes Bett im geteilten Zimmer", confirmed: true },
|
||||
{ text: "Abholung am Anreisetag vom Bahnhof Firenze Santa Maria Novella", confirmed: true },
|
||||
// Entwuerfe: erst nach Bestaetigung (Admin: "Leistungen bestaetigt")
|
||||
// auf confirmed: true setzen.
|
||||
{ text: "Workshops und Coachings mit dem ARTISTA-Team", confirmed: false },
|
||||
{ text: "Verpflegung vor Ort (Umfang folgt)", confirmed: false },
|
||||
// Bestaetigt durch das Buchungsprodukt bzw. den Auftraggeber
|
||||
// (Wochenplan-PDF vom 02.10.2026):
|
||||
{ text: "Teilnahme am Modelcamp 2027 mit 20 Workshops und Shootings", confirmed: true },
|
||||
{ text: "Sechs Nächte in der Villa La Chiesina, eigenes Bett im geteilten Zimmer", confirmed: true },
|
||||
{ text: "Bustransfer vom Bahnhof Firenze Santa Maria Novella zur Villa und zurück", confirmed: true },
|
||||
{ text: "Frühstück, Snacks über den Tag und Abendessen", confirmed: true },
|
||||
] satisfies Confirmable<{ text: string }>[],
|
||||
excluded: [
|
||||
// Laut Vorlage ausdruecklich nicht enthalten.
|
||||
|
||||
Reference in new issue
Block a user