This repository has been archived on 2026-02-28. You can view files and clone it, but cannot push or open issues or pull requests.
site-2025/src/components/landing-page.tsx
Dariusz Niemczyk bcc01476c3
Some checks failed
/ deploy (push) Failing after 2s
feat: add new pages texts
2025-04-04 17:15:43 +02:00

315 lines
9.5 KiB
TypeScript

"use client"
import dynamic from 'next/dynamic';
import { jgs7 } from '@/fonts';
import { Translations } from "@/i18n/translations";
import { cn } from "@/lib/utils";
import { ReactElement, useEffect, useRef } from "react";
import { MainpageNav } from './nav';
import { NewsletterPopup } from './newsletter-form';
import { useTheme } from "./providers";
import { Skeleton } from './ui/skeleton';
function Heading({ children }: { children: ReactElement | string }) {
return <h2 className="text-5xl font-bold tracking-tighter max-w-3xl mx-auto">{children}</h2>
}
function Subheading({ children }: { children: ReactElement | string }) {
return <h3 className="text-3xl font-bold tracking-tighter max-w-3xl mx-auto">{children}</h3>
}
function TinyHeading({ children }: { children: ReactElement | string }) {
return <h4 className="text-lg font-bold tracking-tighter max-w-3xl mx-auto mb-2">{children}</h4>
}
function TinyTextWrapper({ children }: { children: ReactElement }) {
return <div className="text-sm text-muted-foreground max-w-3xl mx-auto whitespace-pre-line">
{children}
</div>
}
function TextWrapper({ children }: { children: ReactElement }) {
return <div className="text-lg text-muted-foreground max-w-3xl mx-auto whitespace-pre-line">
{children}
</div>
}
function NewSection({
id,
children,
after
}: {
id: string
children: ReactElement;
after?: ReactElement;
}) {
return (<section id={id} className="bg-background">
<div className="container mx-auto px-4 gap-6 flex flex-col">
{children}
{after}
</div>
</section>)
}
function Section({
id,
title,
paragraphs,
after
}: {
id: string
title: string;
paragraphs: ReactElement;
after?: ReactElement;
}) {
return (<section id={id} className="bg-background">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold tracking-tighter max-w-3xl mx-auto mb-2">{title}</h2>
<div className="text-lg text-muted-foreground max-w-3xl mx-auto whitespace-pre-line">
{paragraphs}
</div>
{after}
</div>
</section>)
}
function getSource({
src,
type
}: {
src: string;
type: 'mp4' | 'webm' | 'ogv'
}) {
const sourceType = `video/${type}`
return [
<source
key={`mobile-${type}`}
media="(max-width: 640px)"
src={src.replace('.mp4', `_mobile.${type}`)}
type={sourceType}
/>,
<source
key={`tablet-${type}`}
media="(max-width: 1024px)"
src={src.replace('.mp4', `_tablet.${type}`)}
type={sourceType}
/>,
<source
key={`hd-${type}`}
media="(max-width: 1920px)"
src={src.replace('.mp4', `_hd.${type}`)}
type={sourceType}
/>,
<source
key={`twok-${type}`}
media="(max-width: 2560px)"
src={src.replace('.mp4', `_twok.${type}`)}
type={sourceType}
/>,
<source
key={`uhd-${type}`}
media="(max-width: 3840px)"
src={src.replace('.mp4', `_uhd.${type}`)}
type={sourceType}
/>,
<source
key={`full-${type}`}
media="(min-width: 3841px)"
src={src.replace('.mp4', `_full.${type}`)}
type={sourceType}
/>,
];
}
function Video({ sourceBase, hidden }: {
sourceBase: string;
hidden: boolean;
}) {
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
if (!videoRef.current || hidden) return;
const handleScroll = () => {
if (!videoRef.current || hidden) return;
videoRef.current.play();
const scrolled = window.scrollY;
videoRef.current.style.willChange = "transform";
videoRef.current.style.transform = `translateY(${scrolled * 0.5}px)`;
videoRef.current.style.willChange = "unset";
};
const throttledHandleScroll = () => {
requestAnimationFrame(handleScroll);
};
window.addEventListener("scroll", throttledHandleScroll);
handleScroll();
return () => window.removeEventListener("scroll", throttledHandleScroll);
}, [hidden]);
const sources = [...getSource({ src: sourceBase, type: 'mp4' }), ...getSource({ src: sourceBase, type: 'ogv' }), ...getSource({ src: sourceBase, type: 'webm' })]
return (
<video
ref={videoRef}
preload="auto"
autoPlay
muted
loop
playsInline
webkit-playsinline="true"
x5-playsinline="true"
className={cn("w-full h-full object-contain parallax-video", {
hidden,
})}
>
{sources.map(x => x)}
</video>
);
}
const LazyLeafletMap = dynamic(() => import('./event-map.lazy'), {
ssr: false,
loading: () => <Skeleton className="mt-8 h-[368px] w-full" />
})
export default function LandingPage(
{ t }: { t: Translations }
) {
const { theme } = useTheme()
return (
<div>
<MainpageNav t={t} />
<main className="flex flex-col min-h-screen grid-gap-10 gap-10 pb-12">
<section id="hero" className="h-screen relative overflow-hidden dark:bg-black light:bg-white ">
<div className="absolute inset-0 opacity-80">
<Video sourceBase="/videos/ceboola_gradient.mp4" hidden={theme === 'light'} />
<Video sourceBase="/videos/ceboola_gradient-white.mp4" hidden={theme === "dark"} />
</div>
<div className="relative z-10 container mx-auto px-4 h-full flex items-center justify-center">
<div className={`text-center ${jgs7.className}`}>
<h1 className="text-5xl sm:text-6xl md:text-8xl font-bold tracking-tighter light:text-background">{t.hero.title}</h1>
<p className="mt-2 text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl-text:7xl 2xl:text-8xl text-primary">{t.hero.subtitle}</p>
<p className="mt-2 text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl-text:7xl 2xl:text-8xl text-primary ">{t.details.when.date}</p>
<div className='flex flex-col space-y-4 max-w-20 items-center justify-center m-auto'>
<NewsletterPopup t={t} />
</div>
</div>
</div>
</section>
<NewSection id="about">
<>
<div>
<Heading>{t.about.title}</Heading>
</div>
<section>
<TextWrapper><p>{t.about.description}</p></TextWrapper>
</section>
<section>
<Subheading>{t.details.when.title}</Subheading>
<TextWrapper><p className={`text-primary text-3xl ${jgs7.className}`}>{t.details.when.date}</p></TextWrapper>
<TextWrapper><p>{t.details.when.extra}</p></TextWrapper>
</section>
<section>
<Subheading>{t.details.where.title}</Subheading>
<TextWrapper><p>{t.details.where.location}</p></TextWrapper>
<LazyLeafletMap t={t} />
</section>
</>
</NewSection>
<NewSection id="tickets">
<>
<div>
<Heading>{t.tickets.title}</Heading>
</div>
<section>
<TextWrapper><p>{t.tickets.status}</p></TextWrapper>
</section>
</>
</NewSection>
<NewSection id="cfp">
<>
<div>
<Heading>{t.cfp.title}</Heading>
</div>
<section>
<TextWrapper><p>{t.cfp.status}</p></TextWrapper>
</section>
</>
</NewSection>
<NewSection id="faq">
<>
<div>
<Heading>{t.details.title}</Heading>
</div>
<section>
<Subheading>{t.faq.accommodation.title}</Subheading>
<TextWrapper><p>{t.faq.accommodation.description}</p></TextWrapper>
</section>
<section>
<Subheading>{t.faq.accesibility.title}</Subheading>
<TextWrapper><p>{t.faq.accesibility.description} <a href="mailto:orga@cebula.camp">{t.contact.email}</a></p></TextWrapper>
</section>
<section>
<Subheading>{t.faq.food.title}</Subheading>
<TextWrapper><p>{t.faq.food.description}</p></TextWrapper>
</section>
<section>
<Subheading>{t.faq.transport.title}</Subheading>
<TextWrapper><p>{t.faq.transport.description}</p></TextWrapper>
</section>
<section>
<Subheading>{t.faq.documents.title}</Subheading>
<TextWrapper><p>
<a className='hover:text-primary' href={`/pages/rules`}>📜 {t.faq.documents.rules}</a></p></TextWrapper>
<TextWrapper><p>
<a className='hover:text-primary' href={`/pages/privacy`}>📜 {t.faq.documents.privacyPolicy}</a></p></TextWrapper>
</section>
</>
</NewSection>
<NewSection id="contact">
<>
<div>
<Heading>{t.contact.title}</Heading>
</div>
<section>
<TextWrapper><p><a href={`mailto:${t.contact.email}`}>{t.contact.email}</a></p></TextWrapper>
</section>
</>
</NewSection>
<NewSection id="credits">
<>
<section className='text-sm'>
<TinyHeading>{t.credits.title}</TinyHeading>
<TinyTextWrapper><p>{t.credits.usedFonts}</p></TinyTextWrapper>
<TinyTextWrapper><p><a className="hover:underline" href="https://velvetyne.fr/fonts/jgs-font/">{t.credits.jgs7}</a></p></TinyTextWrapper>
<TinyTextWrapper><p><a className="hover:underline" href="https://fonts.google.com/specimen/Oxanium">{t.credits.oxanium}</a></p></TinyTextWrapper>
</section>
</>
</NewSection>
</main>
</div>
)
}