Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pwa/app/(con)/[locale]/con/2025/tickets/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
},
alternates: {
languages: {
en: locale === "en" ? undefined : "/con/2025/call-for-papers",
fr: locale === "fr" ? undefined : "/fr/con/2025/call-for-papers",
en: locale === "en" ? undefined : "/con/2025/tickets",
fr: locale === "fr" ? undefined : "/fr/con/2025/tickets",
},
},
};
Expand Down
80 changes: 49 additions & 31 deletions pwa/app/(con)/[locale]/con/2026/components/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import { LanguageContext } from "contexts/con/LanguageContext";
import Section from "components/con/home/Section";
import PictureGallery from "components/con/common/PictureGallery";
import AfterMovie from "../../2025/components/AfterMovie";
import BuyButton from "components/con/common/BuyButton";
import { currentEdition } from "data/con/editions";
import prices from "data/con/2026/prices";
import PricingCard from "components/con/home/Pricing/PricingCard";

type HomePageProps = {
speakers: Speaker[];
Expand Down Expand Up @@ -42,8 +46,13 @@ const HomePage = ({ speakers, partners, images }: HomePageProps) => {
{t("2026.baseline")}
</p>
<div className="flex gap-2">
{currentEdition === "2026" && (
<BuyButton className="mr-2" id="cover">
{t("buy_tickets")}
</BuyButton>
)}
<Button
className="pink"
className="empty"
to={`/${locale}/con/2026/call-for-papers`}
>
{t("2026.cfp.button")}
Expand Down Expand Up @@ -91,38 +100,9 @@ const HomePage = ({ speakers, partners, images }: HomePageProps) => {
</PictureGallery>
</div>
</Section>
<div className="pb-12">
<AfterMovie />
</div>
<Section
section="missing"
className="relative z-10 text-center overflow-y-clip"
>
<div className="container text-center">
<SectionTitle dark>
<Translate
translationKey="missing_conferences.title"
translationParams={{ edition: "2025" }}
/>
</SectionTitle>
<SectionSubTitle dark>
<Translate
translationKey="2026.missing_conferences.subtitle"
translationParams={{ link: t("2026.missing_conferences.link") }}
/>
</SectionSubTitle>
<Button
className="mx-auto mb-10"
external
to="https://www.youtube.com/playlist?list=PL3hoUDjLa7eSppJSvwSIeBexYZQWkN0bm"
>
{t("2026.missing_conferences.subscribe")}
</Button>
</div>
</Section>
<Section
section="speakers"
className="bg-white z-10 relative py-4 overflow-x-hidden"
className="bg-grey z-10 relative py-4 overflow-x-hidden"
>
<div className="container text-center">
<SectionTitle h1>
Expand Down Expand Up @@ -155,6 +135,44 @@ const HomePage = ({ speakers, partners, images }: HomePageProps) => {
) : null}
</div>
</Section>
{currentEdition === "2026" && (
<Section
className="relative py-10 before:bg-grey before:h-[calc(100%-500px)] before:absolute before:left-0 before:bottom-0 before:w-full after:bg-wave2 after:w-[1300px] after:h-[800px] after:absolute after:top-24 after:left-1/2 after:bg-top after:bg-contain after:opacity-50 after:bg-no-repeat after:-translate-x-1/2 after:rotate-6"
section="pricing"
>
<div className="container relative z-10">
<SectionTitle dark>
<Translate translationKey="pricing.title" />
</SectionTitle>
<div className="max-w-4xl mx-auto flex flex-row flex-wrap justify-center">
{prices.map((price) => (
<PricingCard key={price.id} price={price} />
))}
<div className="w-full self-center max-w-md mt-10 | lg:pl-10 lg:mt-0 lg:w-1/3">
<div className="p-5 dotted-corner flex flex-col items-center text-center bg-blue bg-blue-gradient shadow-md border-blue-dark border-4">
<span className="font-bold text-white leading-tight font-title uppercase lined-center lined-white relative">
{t("pricing.student")}
</span>
<div className="mt-2 text-blue-black/80 font-semibold">
<Translate translationKey="pricing.free_ticket" />
</div>
<Button
size="small"
square
className="white mt-2 mb-5"
to="mailto:events@les-tilleuls.coop"
>
{t("contact_us")}
</Button>
<small className="text-xs text-blue-black/50 font-bold">
*{t("pricing.certificate_needed")}
</small>
</div>
</div>
</div>
</div>
</Section>
)}
<Venue subtitle={t("2026.venue.subtitle")} />
<Section section="sponsorship" className="py-8">
<div className="container text-center">
Expand Down
7 changes: 1 addition & 6 deletions pwa/app/(con)/[locale]/con/2026/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
function EditionLayout({ children }: { children: React.ReactNode }) {
const eventData = getEditionEventData("2026");
return (
<LayoutBase
edition="2026"
nav={nav}
footer={footer}
isTicketingOpen={false}
>
<LayoutBase edition="2026" nav={nav} footer={footer} isTicketingOpen>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(eventData) }}
Expand Down
173 changes: 173 additions & 0 deletions pwa/app/(con)/[locale]/con/2026/tickets/RegisterPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
"use client";
import { useContext, useEffect } from "react";
import { LanguageContext } from "contexts/con/LanguageContext";
import SectionTitle from "components/con/common/typography/SectionTitle";
import SectionSubTitle from "components/con/common/typography/SectionSubtitle";
import Script from "next/script";
import prices from "data/con/2026/prices";
import { Offer } from "types/con";
import dayjs from "dayjs";
import classNames from "classnames";
import { toLocaleDate } from "utils/con";

export default function RegisterPage() {
const { t, Translate, getLocaleDictionary } = useContext(LanguageContext);
const timelinePrices = prices
.find((p) => p.id === 1)
?.offers.filter((o) => o.type);

const isActiveOffer = (offer: Offer) => {
if (offer.limitDate && dayjs(offer.limitDate).isBefore(dayjs(), "day"))
return false;
if (offer.startDate && dayjs(offer.startDate).isAfter(dayjs(), "day"))
return false;
return true;
};

const isPastOffer = (offer: Offer) => {
if (isActiveOffer(offer)) return false;
if (offer.startDate && dayjs(offer.startDate).isBefore(dayjs(), "day"))
return true;
if (!offer.startDate) return true;
return false;
};

const expectations =
getLocaleDictionary?.()[2026].tickets.expect.points || [];

useEffect(() => {
const iframe = document.getElementById(
"yurplan-widget-141690"
) as HTMLIFrameElement | null;
if (!iframe) return;
const handleLoad = () => {
const loader = document.getElementById("loader");
loader?.classList.add("hidden");
};
iframe.addEventListener("load", handleLoad, true);
}, []);

return (
<>
<div className="container max-w-5xl flex flex-col items-center py-12 relative z-10">
<SectionTitle small h1 dark lined>
<Translate translationKey="2026.tickets.title" />
</SectionTitle>
<SectionSubTitle dark>{t("2026.tickets.subtitle")}</SectionSubTitle>
</div>
<div className="">
<div className="container max-w-6xl relative z-10">
<div className="flex flex-col lg:flex-row w-full max-w-6xl items-center lg:items-start">
<div className="translate-y-12 relative z-10 w-4/5 lg:w-2/5 max-w-md before:absolute before:w-full before:h-full before:bg-blue before:-translate-x-3 before:-translate-y-3 before:left-0 before:top-0">
<img
className="relative"
src="/images/con/2025/review/pic-01.jpg"
alt=""
/>
</div>
<div className="flex-1 relative bg-white shadow-floating dotted-corner p-12 pt-24 lg:pt-12 lg:pl-24 lg:-translate-x-12 leading-relaxed font-light">
<Translate translationKey="2026.tickets.description" />
<p className="mt-4 text-lg font-bold">
{t("2026.tickets.description2")}
</p>
</div>
</div>
</div>
</div>
<div className="">
<div className="container relative z-10 max-w-4xl py-12">
<SectionTitle dark small lined>
<strong>{t("2026.tickets.buy")}</strong>
</SectionTitle>
<div className="hidden relative w-full gap-0 md:grid grid-cols-3 py-12 mb-12 overflow-hidden">
<div className="absolute w-1.5 h-full left-4 -translate-x-1/2 md:-translate-x-0 top-0 md:h-1.5 md:w-full md:top-1/2 md:left-0 md:-translate-y-1/2 bg-white/30"></div>
{timelinePrices?.map((p) => (
<div
key={p.type}
className="relative flex flex-col items-center gap-2"
>
<div className="ml-8 md:ml-0 whitespace-nowrap absolute left-full md:left-1/2 top-1/2 -translate-y-1/2 -translate-x-1/2">
<div
className={classNames(
"font-bold uppercase md:mb-12 text-left md:text-center",
isActiveOffer(p)
? "text-blue md:mb-12"
: isPastOffer(p)
? "text-white/30 md:mb-4"
: "text-white md:mb-4"
)}
>
{p.type}
</div>
<p
className={classNames(
isActiveOffer(p)
? "text-blue"
: isPastOffer(p)
? "text-white/30"
: "text-white"
)}
>
{t("2026.tickets.until_date", {
date: toLocaleDate(p.limitDate as string),
})}
</p>
</div>
<div
className={classNames(
"relative rounded-full border-4 size-8",
isActiveOffer(p)
? "border-blue bg-blue before:h-screen before:w-1.5 before:md:w-screen before:absolute before:md:h-1.5 before:bottom-full before:-translate-x-1/2 before:md:-translate-x-0 before:md:right-full before:bg-blue before:left-1/2 before:md:left-auto before:md:top-1/2 before:md:-translate-y-1/2"
: "hidden"
)}
/>
</div>
))}
</div>
<a
title="Vente de billets en ligne"
href="https://www.billetweb.fr/shop.php?event=api-platform-conference-2026"
className="shop_frame"
target="_blank"
data-src="https://www.billetweb.fr/shop.php?event=api-platform-conference-2026"
data-max-width="100%"
data-initial-height="600"
data-scrolling="no"
data-id="api-platform-conference-2026"
data-resize="1"
>
Vente de billets en ligne
</a>
<Script
src="https://www.billetweb.fr/js/export.js"
strategy="afterInteractive"
/>
</div>
</div>
<div className="bg-white pt-12 pb-48">
<div className="container max-w-6xl -mt-12">
<SectionTitle small lined>
<Translate translationKey="2026.tickets.expect.title" />
</SectionTitle>
<div className="mx-auto max-w-64 sm:max-w-xl grid grid-cols-1 sm:grid-cols-2 xl:max-w-none xl:grid-cols-4 gap-8 text-white">
{expectations.map((e, i) => (
<div
key={i}
className={classNames(
"p-8 aspect-square flex flex-col justify-center",
i === 0 && "bg-blue-dark",
i === 1 && "bg-blue-black/80",
i === 2 && "bg-blue-darkest",
i === 3 && "bg-blue-dark"
)}
>
<p className="uppercase font-bold text-xl">{e.title}</p>
<p>{e.text}</p>
</div>
))}
</div>
</div>
</div>
</>
);
}
37 changes: 37 additions & 0 deletions pwa/app/(con)/[locale]/con/2026/tickets/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Locale, i18n } from "i18n/i18n-config";
import { Metadata } from "next";
import RegisterPage from "./RegisterPage";

type Props = {
params: { locale: Locale };
};
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const locale = params.locale || i18n.defaultLocale;
const dictionary = await import(`i18n/meta/${locale}.json`);

return {
title: {
absolute: dictionary.tickets.title,
template: `%s - API Platform Conference 2026`,
},
description: dictionary.tickets.description,
openGraph: {
title: `${dictionary.tickets.title} - API Platform Conference`,
description: dictionary.tickets.description,
},
twitter: {
title: `${dictionary.tickets.title} - API Platform Conference`,
description: dictionary.tickets.description,
},
alternates: {
languages: {
en: locale === "en" ? undefined : "/con/2026/tickets",
fr: locale === "fr" ? undefined : "/fr/con/2026/tickets",
},
},
};
}

export default async function Page({ params }: { params: { locale: Locale } }) {
return <RegisterPage />;
}
2 changes: 1 addition & 1 deletion pwa/components/con/common/BuyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function BuyButton({ children, id, className, ...props }: BuyButtonProps) {
return (
<Button
id={id}
to={`/${locale}/con/2025/tickets`}
to={`/${locale}/con/2026/tickets`}
className={classNames("pink flex flex-row gap-1", className)}
{...props}
>
Expand Down
4 changes: 4 additions & 0 deletions pwa/data/con/2026/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const nav = {
to: "/{{locale}}/con/2025/review",
text: "footer.previous_edition.links.review",
},
{
to: "/{{locale}}/con/2026/#pricing",
text: "nav.links.pricing",
},
{
to: "/{{locale}}/con/2026/#sponsorship",
text: "nav.links.sponsorship",
Expand Down
Loading
Loading