From 9b245e9c51508c39ef962e71a036fd69c9bc44a0 Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Wed, 21 Jan 2026 12:48:34 +0100 Subject: [PATCH] Update marketing packages testimonials and demo --- database/seeders/_WeddingTasksSeeder.php | 112 ---------------- public/lang/de/marketing.json | 126 ++++++++++++++++-- public/lang/en/marketing.json | 122 ++++++++++++++++- resources/js/pages/marketing/Demo.tsx | 46 +++---- resources/js/pages/marketing/Packages.tsx | 34 ++++- resources/lang/de/marketing.json | 123 ++++++++++++++++- resources/lang/en/marketing.json | 123 ++++++++++++++++- .../Support/WatermarkConfigResolverTest.php | 7 +- 8 files changed, 533 insertions(+), 160 deletions(-) delete mode 100644 database/seeders/_WeddingTasksSeeder.php diff --git a/database/seeders/_WeddingTasksSeeder.php b/database/seeders/_WeddingTasksSeeder.php deleted file mode 100644 index 622c15c..0000000 --- a/database/seeders/_WeddingTasksSeeder.php +++ /dev/null @@ -1,112 +0,0 @@ -first(); - if (! $weddingType) { - return; - } - - // Helper to resolve emotion by English name (more stable given encoding issues) - $by = fn (string $en) => Emotion::where('name->en', $en)->first(); - - $emLove = $by('Love'); - $emJoy = $by('Joy'); - $emTouched = $by('Touched'); - $emNostalgia = $by('Nostalgia'); - $emSurprise = $by('Surprise'); - $emPride = $by('Pride'); - - $tasks = [ - // Liebe (10) - [$emLove, 'Kuss-Foto', 'Kiss Photo', 'Macht ein romantisches Kuss-Foto.', 'Take a romantic kiss photo.', 'easy'], - [$emLove, 'Herz mit Haenden', 'Hands Heart', 'Formt ein Herz mit euren Haenden.', 'Make a heart with your hands.', 'easy'], - [$emLove, 'Brautstrauss im Fokus', 'Bouquet Close-up', 'Brautstrauss nah an die Kamera halten.', 'Hold the bouquet close to the camera.', 'easy'], - [$emLove, 'Stirnkuss', 'Forehead Kiss', 'Sanfter Stirnkuss – ganz verliebt.', 'A gentle forehead kiss.', 'easy'], - [$emLove, 'Herzensblick', 'Loving Gaze', 'Schaut euch nur in die Augen.', 'Look only into each other’s eyes.', 'easy'], - [$emLove, 'Schleiermoment', 'Veil Moment', 'Schleier ueber beide Koepfe legen.', 'Drape the veil over both of you.', 'medium'], - [$emLove, 'Ringnahaufnahme', 'Ring Macro', 'Zeigt eure Ringe nah an der Kamera.', 'Show your rings close to the camera.', 'easy'], - [$emLove, 'Hand in Hand', 'Holding Hands', 'Haende greifen, Kamera im Hintergrund.', 'Hold hands with the camera behind.', 'easy'], - [$emLove, 'Tanzschritt', 'First Dance Step', 'Ein kleiner Tanzschritt fuer das Foto.', 'A small dance step for the photo.', 'medium'], - [$emLove, 'Kuss hinter dem Strauss', 'Peek-a-boo Kiss', 'Kuss hinter dem Brautstrauss verstecken.', 'Hide a kiss behind the bouquet.', 'easy'], - - // Freude (10) - [$emJoy, 'Sprung-Foto', 'Jump Photo', 'Alle springen gleichzeitig!', 'Everyone jump together!', 'medium'], - [$emJoy, 'Lachendes Gruppenfoto', 'Laughing Group', 'Erzaehlt einen Witz und klick!', 'Tell a joke and click!', 'easy'], - [$emJoy, 'Konfetti-Moment', 'Confetti Moment', 'Konfetti werfen (oder so tun).', 'Throw confetti (or pretend).', 'easy'], - [$emJoy, 'Cheers!', 'Cheers!', 'Glaser anstossen in die Kamera.', 'Clink glasses toward the camera.', 'easy'], - [$emJoy, 'Freudensprung zu zweit', 'Couple Jump', 'Brautpaar springt gemeinsam.', 'Couple jumps together.', 'medium'], - [$emJoy, 'Luftkuesse', 'Blowing Kisses', 'Luftkuesse in Richtung Kamera.', 'Blow kisses toward the camera.', 'easy'], - [$emJoy, 'Scherzbrillen', 'Silly Glasses', 'Accessoires aufsetzen und lachen.', 'Wear props and laugh.', 'easy'], - [$emJoy, 'Freudige Umarmung', 'Happy Hug', 'Grosse Umarmung in der Runde.', 'Big group hug.', 'easy'], - [$emJoy, 'Daumen hoch', 'Thumbs Up', 'Alle Daumen nach oben!', 'Thumbs up, everyone!', 'easy'], - [$emJoy, 'Victory-Zeichen', 'Peace Sign', 'Peace-Zeichen in die Kamera.', 'Peace sign to the camera.', 'easy'], - - // Touched (8) - [$emTouched, 'Traenen des Gluecks', 'Tears of Joy', 'Sanftes Traenchen abtupfen.', 'Dab a happy tear.', 'easy'], - [$emTouched, 'Eltern-Umarmung', 'Parents’ Hug', 'Umarmung mit Eltern oder Trauzeugen.', 'Hug with parents or witnesses.', 'easy'], - [$emTouched, 'Hand auf’s Herz', 'Hand on Heart', 'Hand aufs Herz – ehrlicher Moment.', 'Hand on heart — a sincere moment.', 'easy'], - [$emTouched, 'Danke-Geste', 'Thank You Gesture', '„Danke“-Geste in die Kamera.', 'A “thank you” gesture to the camera.', 'easy'], - [$emTouched, 'Enger Nasenstups', 'Nose Boop', 'Stirn an Stirn, sanfter Nasenstups.', 'Forehead to forehead, a soft nose boop.', 'easy'], - [$emTouched, 'Geliebtes Andenken', 'Keepsake', 'Ein bedeutsames Andenken zeigen.', 'Show a meaningful keepsake.', 'easy'], - [$emTouched, 'Leise Worte', 'Whisper', 'Ein leises Kompliment ins Ohr.', 'Whisper a compliment.', 'easy'], - [$emTouched, 'Ruhe vor dem Sturm', 'Quiet Moment', 'Augen schliessen, tief durchatmen.', 'Close eyes and take a deep breath.', 'easy'], - - // Nostalgia (8) - [$emNostalgia, 'Altes Foto nachstellen', 'Recreate Old Photo', 'Ein altes Familienfoto nachstellen.', 'Recreate an old family photo.', 'medium'], - [$emNostalgia, 'Kindheits-Pose', 'Childhood Pose', 'Lieblingspose aus der Kindheit.', 'Favorite childhood pose.', 'easy'], - [$emNostalgia, 'Erste Nachricht', 'First Message', 'Handys mit erster Nachricht zeigen.', 'Show your first message on phones.', 'medium'], - [$emNostalgia, 'Ringbox Vintage', 'Vintage Ring Box', 'Ringbox im Vintage-Stil inszenieren.', 'Stage the vintage ring box.', 'easy'], - [$emNostalgia, 'Familienerbstueck', 'Family Heirloom', 'Ein Familienerbstueck ins Bild.', 'Feature a family heirloom.', 'easy'], - [$emNostalgia, 'Schwarzweiss', 'Black & White', 'Schwarzweiss-Pose fuer klassisches Foto.', 'Pose for a black & white shot.', 'easy'], - [$emNostalgia, 'Erster Tanz (Mini)', 'Mini First Dance', 'Ein Schritt vom ersten Tanz.', 'One step of the first dance.', 'easy'], - [$emNostalgia, 'Gastebuch-Moment', 'Guestbook Moment', 'Eintrag ins Gaestebuch festhalten.', 'Capture a guestbook entry.', 'easy'], - - // Surprise (7) - [$emSurprise, 'Photobomb!', 'Photobomb!', 'Ueberraschung im Hintergrund.', 'Surprise in the background.', 'easy'], - [$emSurprise, 'Erster Blick', 'First Look', 'Reaktion beim First Look nachstellen.', 'Recreate a first-look reaction.', 'medium'], - [$emSurprise, 'Ueberraschungs-Dip', 'Surprise Dip', 'Ueberraschender Tanz-Dip.', 'A surprise dance dip.', 'medium'], - [$emSurprise, 'Ballon-Pop', 'Balloon Pop', 'Ballon zerplatzen (oder so tun).', 'Pop a balloon (or pretend).', 'easy'], - [$emSurprise, 'Hutwechsel', 'Hat Swap', 'Huete/Accessoires spontan tauschen.', 'Swap hats/props on the fly.', 'easy'], - [$emSurprise, 'Versteckspiel', 'Peekaboo', 'Hinter Deko kurz verstecken.', 'Peek from behind decor.', 'easy'], - [$emSurprise, 'Gespiegelte Pose', 'Mirror Pose', 'Gegensaetzliche, gespiegelte Pose.', 'Opposite mirrored pose.', 'easy'], - - // Pride (7) - [$emPride, 'Just Married', 'Just Married', '„Just Married“-Schild zeigen.', 'Show a “Just Married” sign.', 'easy'], - [$emPride, 'Ring zeigen', 'Show the Ring', 'Ring zur Kamera strecken.', 'Stretch ring toward the camera.', 'easy'], - [$emPride, 'Brautkleid-Detail', 'Dress Detail', 'Lieblingsdetail am Kleid zeigen.', 'Show a favorite dress detail.', 'easy'], - [$emPride, 'Anzug-Detail', 'Suit Detail', 'Manschette/Knopfloch zeigen.', 'Show cuff/ boutonniere.', 'easy'], - [$emPride, 'Team Braut', 'Team Bride', '„Team Braut“-Gruppenpose.', '“Team Bride” group pose.', 'easy'], - [$emPride, 'Team Braeutigam', 'Team Groom', '„Team Braeutigam“-Gruppenpose.', '“Team Groom” group pose.', 'easy'], - [$emPride, 'Siegesschrei', 'Victory Cheer', 'Arme hoch, Jubel in die Kamera.', 'Arms up, cheer to the camera.', 'easy'], - ]; - - $sort = 1; - foreach ($tasks as [$emotion, $titleDe, $titleEn, $descDe, $descEn, $difficulty]) { - if (! $emotion) { - continue; - } - Task::updateOrCreate([ - 'emotion_id' => $emotion->id, - 'title->de' => $titleDe, - ], [ - 'emotion_id' => $emotion->id, - 'event_type_id' => $weddingType->id, - 'title' => ['de' => $titleDe, 'en' => $titleEn], - 'description' => ['de' => $descDe, 'en' => $descEn], - 'difficulty' => $difficulty, - 'sort_order' => $sort++, - 'is_active' => true, - ]); - } - } -} diff --git a/public/lang/de/marketing.json b/public/lang/de/marketing.json index 1c76ac3..8884f8b 100644 --- a/public/lang/de/marketing.json +++ b/public/lang/de/marketing.json @@ -204,18 +204,128 @@ "no_watermark": "Kein Wasserzeichen", "max_tenants": "Max. Tenants", "max_events": "Events enthalten", - "faq_free": "Was ist das Free Package?", + "faq_branding": "Was bedeutet „Eigenes Branding“?", + "faq_branding_desc": "Du steuerst das Design der Gäste-App: Light/Dark/Auto-Modus, Farben, Schriften, Logo/Emoji und Button-Stil. Wenn dein Paket Branding erlaubt, kannst du es event-spezifisch überschreiben, sonst bleibt das Standard-Branding aktiv.", "faq_upgrade": "Kann ich upgraden?", + "faq_upgrade_desc": "Ja, du kannst dein Paket jederzeit upgraden. Änderungen wirken sofort für das laufende Event.", "faq_reseller": "Was für Partner / Agenturen?", + "faq_reseller_desc": "Partner-Pakete bündeln mehrere Events und enthalten Team-Features wie Reseller-Dashboard und Branding-Steuerung.", "faq_payment": "Zahlung sicher?", - "faq_free_desc": "Das Free Package bietet grundlegende Features für kleine Events mit begrenzter Anzahl an Fotos und Gästen.", - "faq_upgrade_desc": "Ja, Sie können jederzeit upgraden, um mehr Features und Limits zu erhalten. Der Upgrade ist nahtlos und Ihre Daten bleiben erhalten.", - "faq_reseller_desc": "Partner-Pakete sind Event-Kontingente für Agenturen, die mehrere Events verwalten. Inklusive Dashboard und Branding-Optionen.", - "faq_payment_desc": "Alle Zahlungen werden über sichere Provider wie Paddle abgewickelt. Ihre Daten sind GDPR-konform geschützt.", + "faq_payment_desc": "Ja. Zahlungen werden über sichere Zahlungsanbieter abgewickelt und mit TLS verschlüsselt übertragen.", + "testimonials_title": "Erfahrungen zum Paket", "testimonials": { - "anna": "Fotospiel hat unsere Hochzeit perfekt gemacht! Die Gäste konnten einfach Fotos teilen, und die Galerie war ein Hit.", - "max": "Als Event-Organisator liebe ich die Analytics und das einfache Branding. Super für Firmenevents!", - "lisa": "Kostenloses Paket für Geburtstage – einfach und sicher. Kein Stress mit Apps!" + "starter": [ + { + "name": "Sarah K.", + "text": "Für unsere Gartenparty mit rund 80 Gästen war Starter ideal. Foto-Limits und Aufgaben haben gut gepasst." + }, + { + "name": "Tom H.", + "text": "Wir wollten eine einfache Lösung für QR-Uploads und Galerie. Starter war genau richtig." + }, + { + "name": "Maja L.", + "text": "Die 6 Monate Galerie reichen uns, und die 600 Fotos waren ein guter Rahmen." + } + ], + "standard": [ + { + "name": "Lena & Jonas", + "text": "Standard fühlt sich wie das Allround-Paket an: genug Gäste und Fotos plus ein Jahr Galerie." + }, + { + "name": "Marco P.", + "text": "Mit Branding und Live-Slideshow wirkte das Event richtig stimmig." + }, + { + "name": "Nadine R.", + "text": "Kein Wasserzeichen und eigene Farben – für Hochzeiten einfach passend." + } + ], + "pro": [ + { + "name": "Aylin B.", + "text": "Bei großen Events ist Premium entspannt: keine Gästegrenze und viel Foto-Puffer." + }, + { + "name": "Robert M.", + "text": "Die Analytics sind hilfreich für den Nachbericht, und der Support reagierte schnell." + }, + { + "name": "Clara F.", + "text": "Zwei Jahre Galerie geben uns Ruhe, wenn Fotos später noch gebraucht werden." + } + ], + "s-small-reseller": [ + { + "name": "Agentur Huber", + "text": "Ein guter Einstieg für kleine Studios. Fünf Events lassen sich sauber planen." + }, + { + "name": "Studio Meyer", + "text": "Das Partner-Dashboard spart Zeit, wenn mehrere Kunden parallel laufen." + }, + { + "name": "Kathrin T.", + "text": "Für kleinere Feiern reicht Starter-Level völlig aus – der Ablauf ist zuverlässig." + } + ], + "m-medium-reseller": [ + { + "name": "Eventbüro Lenz", + "text": "15 Events decken unsere Saison meistens ab. Standard-Level ist für viele Kunden passend." + }, + { + "name": "Jasmin & Co.", + "text": "Das Reporting unterstützt die Nachbereitung und Kunden-Recaps." + }, + { + "name": "Agentur Nord", + "text": "Branding und Slideshow machen die Umsetzung professionell, ohne kompliziert zu werden." + } + ], + "l-large-reseller": [ + { + "name": "Studio Westend", + "text": "Bei hoher Auslastung ist Premium angenehm – keine Diskussionen über Limits." + }, + { + "name": "Eventagentur Krämer", + "text": "Die Live-Slideshow kommt bei großen Events immer gut an." + }, + { + "name": "Fritz P.", + "text": "Viele Events im Jahr lassen sich hier sauber bündeln." + } + ], + "partner-premium-5": [ + { + "name": "Hochzeitsstudio Weiß", + "text": "Perfekt für wenige, aber große Produktionen im Jahr." + }, + { + "name": "Agentur Rosen", + "text": "Premium-Features wirken bei Kunden sehr hochwertig, ohne großes Kontingent." + }, + { + "name": "Laura S.", + "text": "Wir nutzen es für unsere Top-Events – zuverlässig und planbar." + } + ], + "studio-annual": [ + { + "name": "Studio Alster", + "text": "Das Jahreskontingent passt gut zu wiederkehrenden Kunden." + }, + { + "name": "Eventservice Hahn", + "text": "24 Events geben ausreichend Spielraum über die Saison." + }, + { + "name": "Agentur Süd", + "text": "Standard-Level ist ein guter Mittelweg für verschiedene Event-Typen." + } + ] }, "what_customers_say": "Was unsere Kunden sagen", "close": "Schließen", diff --git a/public/lang/en/marketing.json b/public/lang/en/marketing.json index 2231d6c..42e1f74 100644 --- a/public/lang/en/marketing.json +++ b/public/lang/en/marketing.json @@ -195,14 +195,128 @@ "standard_support": "Standard support", "max_tenants": "Max. Tenants", "max_events": "Events included", - "faq_free": "What is the Free Package?", + "faq_branding": "What does “Custom Branding” mean?", + "faq_branding_desc": "You control the guest app’s look: Light/Dark/Auto mode, colors, fonts, logo/emoji, and button style. If your package allows branding, you can override per event; otherwise the default branding stays active.", "faq_upgrade": "Can I upgrade?", + "faq_upgrade_desc": "Yes, you can upgrade at any time. Changes apply immediately to the current event.", "faq_reseller": "What for Partner / Agencies?", + "faq_reseller_desc": "Partner packages bundle multiple events and include team features like reseller dashboards and branding controls.", "faq_payment": "Payment secure?", + "faq_payment_desc": "Yes. Payments are processed by secure providers and transmitted with TLS encryption.", + "testimonials_title": "Package experiences", "testimonials": { - "anna": "Fotospiel made our wedding perfect! Guests could easily share photos, and the gallery was a hit.", - "max": "As an event organizer, I love the analytics and easy branding. Great for corporate events!", - "lisa": "Free package for birthdays – simple and secure. No app hassle!" + "starter": [ + { + "name": "Sarah K.", + "text": "Starter was ideal for our garden party of about 80 guests. The photo and task limits fit well." + }, + { + "name": "Tom H.", + "text": "We wanted a simple QR upload + gallery flow. Starter did exactly that." + }, + { + "name": "Maja L.", + "text": "Six months of gallery access was enough for us, and 600 photos was a good frame." + } + ], + "standard": [ + { + "name": "Lena & Jonas", + "text": "Standard feels like the all-round package: plenty of guests and photos plus a full year of gallery." + }, + { + "name": "Marco P.", + "text": "Branding and the live slideshow made the event feel cohesive." + }, + { + "name": "Nadine R.", + "text": "No watermark and custom colors fit our wedding perfectly." + } + ], + "pro": [ + { + "name": "Aylin B.", + "text": "For large events, Premium is stress-free: no guest limit and lots of photo headroom." + }, + { + "name": "Robert M.", + "text": "Analytics help with post-event reporting, and support was quick." + }, + { + "name": "Clara F.", + "text": "Two years of gallery access is a relief when photos are needed later." + } + ], + "s-small-reseller": [ + { + "name": "Agency Huber", + "text": "A solid entry for small studios. Five events per year are easy to plan." + }, + { + "name": "Studio Meyer", + "text": "The partner dashboard saves time when running several clients." + }, + { + "name": "Kathrin T.", + "text": "Starter-level coverage is enough for smaller events, and the flow is reliable." + } + ], + "m-medium-reseller": [ + { + "name": "Event Bureau Lenz", + "text": "Fifteen events usually cover our season. Standard level fits most clients." + }, + { + "name": "Jasmin & Co.", + "text": "Reporting helps with recaps and client summaries." + }, + { + "name": "Agency North", + "text": "Branding and slideshow make delivery feel professional without complexity." + } + ], + "l-large-reseller": [ + { + "name": "Studio Westend", + "text": "At high volume, Premium is comfortable—no back-and-forth about limits." + }, + { + "name": "Agency Krämer", + "text": "The live slideshow is a consistent hit at larger events." + }, + { + "name": "Fritz P.", + "text": "Plenty of events per year bundled in one plan." + } + ], + "partner-premium-5": [ + { + "name": "Wedding Studio Weiß", + "text": "Perfect for a few large productions each year." + }, + { + "name": "Agency Rosen", + "text": "Premium features feel high-end without committing to a huge bundle." + }, + { + "name": "Laura S.", + "text": "We use it for our top events—reliable and predictable." + } + ], + "studio-annual": [ + { + "name": "Studio Alster", + "text": "The annual bundle fits recurring clients well." + }, + { + "name": "Event Service Hahn", + "text": "24 events give enough room across the season." + }, + { + "name": "Agency South", + "text": "Standard level is a solid middle ground for varied event types." + } + ] }, "what_customers_say": "What our customers say", "close": "Close", diff --git a/resources/js/pages/marketing/Demo.tsx b/resources/js/pages/marketing/Demo.tsx index 7275d91..6589c5c 100644 --- a/resources/js/pages/marketing/Demo.tsx +++ b/resources/js/pages/marketing/Demo.tsx @@ -23,6 +23,7 @@ const DemoPage: React.FC = ({ demoToken }) => { const locale = useLocale(); const embedUrl = demoToken ? `/e/${demoToken}` : '/e/demo?demo=1'; const [isDemoOpen, setIsDemoOpen] = React.useState(false); + const demoOpenLabel = t('labels.demoOpenOverlay', locale === 'en' ? 'Open demo overlay' : 'Demo im Overlay öffnen'); const demo = t('demo_page', { returnObjects: true }) as { title: string; @@ -35,17 +36,6 @@ const DemoPage: React.FC = ({ demoToken }) => { }; const demoFeatures = Array.isArray(demo.features) ? demo.features : []; const handleOpenDemo = (): void => { - if (typeof window === 'undefined') { - return; - } - - const isMobile = window.matchMedia('(max-width: 768px)').matches; - - if (isMobile) { - window.open(embedUrl, '_blank', 'noopener,noreferrer'); - return; - } - setIsDemoOpen(true); }; @@ -84,18 +74,28 @@ const DemoPage: React.FC = ({ demoToken }) => {
{embedUrl ? ( <> -
+
-