diff --git a/Dockerfile b/Dockerfile index 3fb4fddb3..3d280e6aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ ARG NEXT_PUBLIC_DEFAULT_LOCALE=de ARG NEXT_PUBLIC_ENABLE_FISCHEREI=true ARG NEXT_PUBLIC_ENABLE_MEETING_PROTOCOLS=true ARG NEXT_PUBLIC_ENABLE_VERBANDSVERWALTUNG=true +ARG NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= ENV NEXT_PUBLIC_CI=${NEXT_PUBLIC_CI} ENV NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL} ENV NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL} @@ -29,6 +30,7 @@ ENV NEXT_PUBLIC_DEFAULT_LOCALE=${NEXT_PUBLIC_DEFAULT_LOCALE} ENV NEXT_PUBLIC_ENABLE_FISCHEREI=${NEXT_PUBLIC_ENABLE_FISCHEREI} ENV NEXT_PUBLIC_ENABLE_MEETING_PROTOCOLS=${NEXT_PUBLIC_ENABLE_MEETING_PROTOCOLS} ENV NEXT_PUBLIC_ENABLE_VERBANDSVERWALTUNG=${NEXT_PUBLIC_ENABLE_VERBANDSVERWALTUNG} +ENV NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY} RUN pnpm --filter web build # --- Run --- diff --git a/apps/web/.env.development b/apps/web/.env.development index 3507b798a..fcb4754eb 100644 --- a/apps/web/.env.development +++ b/apps/web/.env.development @@ -21,7 +21,9 @@ EMAIL_PASSWORD=password CONTACT_EMAIL=test@makerkit.dev # STRIPE -NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= +NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51SMbesKttnWb7SsFOR7cJ1jshdEaZiHmCflWndgLtL3gx1Cu8N4p5qxSJY8PHmpEJL8gf4VrqqX2Fr7pxJtQILUS00yYQ7Tx8V # MAILER -MAILER_PROVIDER=nodemailer \ No newline at end of file +MAILER_PROVIDER=nodemailer +# STRIPE SECRET KEY +STRIPE_SECRET_KEY=sk_test_51SMbesKttnWb7SsFTjCsPZMlxVe3WjrsDnpLDAQehVdzSoDaWMFmc3hiZOTp2IAKB1cleMPIOW9GmEJHEkhazJsq00FEfTr6BI diff --git a/apps/web/.env.production b/apps/web/.env.production index 417308e17..249edeba2 100644 --- a/apps/web/.env.production +++ b/apps/web/.env.production @@ -9,4 +9,4 @@ NEXT_PUBLIC_SUPABASE_URL= # STRIPE -NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= +NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51SMbesKttnWb7SsFOR7cJ1jshdEaZiHmCflWndgLtL3gx1Cu8N4p5qxSJY8PHmpEJL8gf4VrqqX2Fr7pxJtQILUS00yYQ7Tx8V diff --git a/apps/web/app/[locale]/(marketing)/_components/feature-carousel.tsx b/apps/web/app/[locale]/(marketing)/_components/feature-carousel.tsx new file mode 100644 index 000000000..eefa86011 --- /dev/null +++ b/apps/web/app/[locale]/(marketing)/_components/feature-carousel.tsx @@ -0,0 +1,584 @@ +'use client'; + +import { useCallback, useEffect, useState } from 'react'; + +import { + BedDouble, + CalendarDays, + ChevronLeft, + ChevronRight, + FileText, + Globe, + GraduationCap, + LayoutDashboard, + Mail, + Users, + Wallet, +} from 'lucide-react'; + +import { Badge } from '@kit/ui/badge'; +import { Button } from '@kit/ui/button'; +import { cn } from '@kit/ui/utils'; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function MiniStat({ label, value }: { label: string; value: string }) { + return ( +
+
{value}
+
{label}
+
+ ); +} + +function MiniRow({ + cells, + highlighted, +}: { + cells: string[]; + highlighted?: boolean; +}) { + return ( +
+ {cells.map((c, i) => ( + + {c} + + ))} +
+ ); +} + +// --------------------------------------------------------------------------- +// Slide data +// --------------------------------------------------------------------------- + +interface Slide { + id: string; + label: string; + icon: React.ReactNode; + content: React.ReactNode; +} + +const IC = 'h-4 w-4'; + +const SLIDES: Slide[] = [ + { + id: 'dashboard', + label: 'Dashboard', + icon: , + content: ( +
+
+ + + + +
+
+
+
Letzte Aktivität
+ {[ + 'Max Müller — Beitritt', + 'SEPA-Einzug #42 — erstellt', + 'Schwimmkurs — 3 neue Teilnehmer', + ].map((t) => ( +
+
+ {t} +
+ ))} +
+
+
Schnellaktionen
+ {['Neues Mitglied', 'Neuer Kurs', 'Newsletter erstellen'].map( + (t) => ( +
+ {t} +
+ ), + )} +
+
+
+ ), + }, + { + id: 'members', + label: 'Mitglieder', + icon: , + content: ( +
+
+
+ 🔍 Mitglied suchen… +
+
+ + CSV + + + Excel + + + + Neues Mitglied + +
+
+
+
+ Nr + Name + E-Mail + Status + Eintritt +
+ {( + [ + [ + 'M-001', + 'Müller, Hans', + 'h.mueller@web.de', + 'Aktiv', + '15.01.2024', + ], + [ + 'M-002', + 'Schmidt, Anna', + 'a.schmidt@gmx.de', + 'Aktiv', + '01.03.2024', + ], + [ + 'M-003', + 'Weber, Thomas', + 'weber@t-online.de', + 'Passiv', + '22.06.2023', + ], + [ + 'M-004', + 'Fischer, Maria', + 'm.fischer@mail.de', + 'Aktiv', + '08.09.2024', + ], + ] as string[][] + ).map((row, i) => ( + + ))} +
+
+ ← Zurück   Seite 1 von 52   Weiter → +
+
+ ), + }, + { + id: 'courses', + label: 'Kurse', + icon: , + content: ( +
+
+ + + + +
+
+
+ Kurs-Nr + Name + Beginn + Status + Gebühr +
+ {( + [ + ['SK-001', 'Schwimmkurs Anfänger', '01.05.2026', 'Aktiv', '50 €'], + ['YG-003', 'Yoga für Senioren', '15.03.2026', 'Geplant', '35 €'], + ['TN-012', 'Tenniskurs Jugend', '01.06.2026', 'Aktiv', '80 €'], + ] as string[][] + ).map((row, i) => ( + + ))} +
+
+ ), + }, + { + id: 'finance', + label: 'Finanzen', + icon: , + content: ( +
+
+ + + +
+
+
+ Rechnungs-Nr + Mitglied + Betrag + Status +
+ {( + [ + ['RE-2026-001', 'Hans Müller', '120,00 €', 'Bezahlt'], + ['RE-2026-002', 'Anna Schmidt', '85,00 €', 'Offen'], + ['RE-2026-003', 'Thomas Weber', '120,00 €', 'Überfällig'], + ] as string[][] + ).map((row, i) => ( + + ))} +
+
+ ), + }, + { + id: 'events', + label: 'Veranstaltungen', + icon: , + content: ( +
+
+ + + +
+
+ {[ + { + name: 'Sommerfest 2026', + date: '21.06.2026', + spots: '84/120', + badge: 'Offen' as const, + }, + { + name: 'Jahreshauptversammlung', + date: '15.03.2026', + spots: '200/200', + badge: 'Ausgebucht' as const, + }, + { + name: 'Weihnachtsfeier', + date: '20.12.2026', + spots: '0/80', + badge: 'Geplant' as const, + }, + ].map((e) => ( +
+
+
{e.name}
+
+ {e.date} · {e.spots} Plätze +
+
+ + {e.badge} + +
+ ))} +
+
+ ), + }, + { + id: 'newsletter', + label: 'Newsletter', + icon: , + content: ( +
+
+ + + +
+
+
+ Betreff + Empfänger + Datum + Status +
+ {( + [ + ['Frühjahrsnewsletter', '1.180', '01.03.2026', 'Gesendet'], + ['Einladung Sommerfest', '1.180', '15.05.2026', 'Entwurf'], + ['Beitragsinfo 2026', '1.180', '15.01.2026', 'Gesendet'], + ] as string[][] + ).map((row, i) => ( + + ))} +
+
+ ), + }, + { + id: 'website', + label: 'Website', + icon: , + content: ( +
+
+ + + +
+
+
+ Titel + URL + Status + Aktualisiert +
+ {( + [ + ['Startseite', '/home', 'Veröffentlicht', '30.03.2026'], + ['Über uns', '/ueber-uns', 'Entwurf', '28.03.2026'], + ['Kurse', '/kurse', 'Veröffentlicht', '25.03.2026'], + ['Kontakt', '/kontakt', 'Veröffentlicht', '20.03.2026'], + ] as string[][] + ).map((row, i) => ( + + ))} +
+
+ ), + }, + { + id: 'bookings', + label: 'Buchungen', + icon: , + content: ( +
+
+ + + +
+
+
+ Raum + Gast + Check-in + Status +
+ {( + [ + ['Seminarraum A', 'TV Musterstadt', '15.04.2026', 'Bestätigt'], + ['Vereinsheim', 'Karin Bauer', '18.04.2026', 'Ausstehend'], + ['Turnhalle', 'TSV Neustadt', '20.04.2026', 'Bestätigt'], + ] as string[][] + ).map((row, i) => ( + + ))} +
+
+ ), + }, + { + id: 'documents', + label: 'Dokumente', + icon: , + content: ( +
+
+ + + +
+
+ {[ + { name: 'Mitgliedsausweis', count: '1.247 generiert', type: 'PDF' }, + { name: 'Beitragsrechnung', count: '324 generiert', type: 'PDF' }, + { name: 'SEPA-Mandat', count: '890 generiert', type: 'PDF' }, + { name: 'Mitgliederliste', count: '12 Exporte', type: 'Excel' }, + ].map((d) => ( +
+
+
{d.name}
+
+ {d.count} +
+
+ + {d.type} + +
+ ))} +
+
+ ), + }, +]; + +// --------------------------------------------------------------------------- +// Component +// --------------------------------------------------------------------------- + +export function FeatureCarousel() { + const [active, setActive] = useState(0); + const slide = SLIDES[active]!; + + const next = useCallback( + () => setActive((i) => (i + 1) % SLIDES.length), + [], + ); + const prev = useCallback( + () => setActive((i) => (i - 1 + SLIDES.length) % SLIDES.length), + [], + ); + + useEffect(() => { + const id = setInterval(next, 6000); + return () => clearInterval(id); + }, [next]); + + return ( +
+