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
27 changes: 27 additions & 0 deletions pwa/app/(common)/help/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,33 @@ export default async function Page() {
/>
</div>
</div>
<div>
<div className="container xl:max-w-5xl flex flew-row items-center relative gap-16 py-12">
<div className="flex-1 max-w-2xl py-6 mx-auto text-center">
<Heading size="lg" level="h2" overline="AI coding agents">
Teach AI agents the <strong>canonical way</strong>
</Heading>
<p className="mt-4">
The API Platform Skillset teaches AI coding agents the canonical
API Platform 4.x way, with 15 skills covering both Symfony and
Laravel. Install the Claude Code plugin to keep your agents
aligned with framework best practices.
</p>
<div className="mt-6 flex flex-wrap justify-center gap-4">
<Button
external
href="https://github.com/api-platform/skillset"
size="medium"
>
Get the skillset
</Button>
<Button href="/docs/core/ai-agents" size="medium" empty>
Learn more
</Button>
</div>
</div>
</div>
</div>
<div className="bg-blue-extralight/50 dark:bg-blue-dark dark:text-white after:absolute after:w-full after:h-80 after:top-full after:left-0 after:bg-blue-extralight/50 after:dark:bg-blue-dark">
<div className="container xl:max-w-5xl flex flew-row items-center relative gap-16 pt-12">
<img
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
export function Logos() {
return (
<>
<img src="/images/con/2025/cfp/api-platform.png" alt="" />
<img src="/images/con/2025/cfp/caddy.png" alt="" />
<img src="/images/con/2025/cfp/elastic.png" alt="" />
<img src="/images/con/2025/cfp/frankenphp.png" alt="" />
<img src="/images/con/2025/cfp/JS.png" alt="" />
<img src="/images/con/2025/cfp/laravel.png" alt="" />
<img src="/images/con/2025/cfp/php.png" alt="" />
<img src="/images/con/2025/cfp/rabbitmq.png" alt="" />
<img src="/images/con/2025/cfp/react-admin.png" alt="" />
<img src="/images/con/2025/cfp/xdebug.png" alt="" />
<img src="/images/con/2025/cfp/api-platform.png" alt="API Platform" />
<img src="/images/con/2025/cfp/caddy.png" alt="Caddy" />
<img src="/images/con/2025/cfp/elastic.png" alt="Elastic" />
<img src="/images/con/2025/cfp/frankenphp.png" alt="FrankenPHP" />
<img src="/images/con/2025/cfp/JS.png" alt="JavaScript" />
<img src="/images/con/2025/cfp/laravel.png" alt="Laravel" />
<img src="/images/con/2025/cfp/php.png" alt="PHP" />
<img src="/images/con/2025/cfp/rabbitmq.png" alt="RabbitMQ" />
<img src="/images/con/2025/cfp/react-admin.png" alt="React Admin" />
<img src="/images/con/2025/cfp/xdebug.png" alt="Xdebug" />
</>
);
}
1 change: 1 addition & 0 deletions pwa/app/(con)/[locale]/con/2025/tickets/RegisterPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export default function RegisterPage() {
<img
className="relative"
src="/images/con/2024/review/pic-06.jpg"
alt="API Platform Conference 2024"
/>
</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">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
export function Logos() {
return (
<>
<img src="/images/con/2025/cfp/api-platform.png" alt="" />
<img src="/images/con/2025/cfp/caddy.png" alt="" />
<img src="/images/con/2025/cfp/elastic.png" alt="" />
<img src="/images/con/2025/cfp/frankenphp.png" alt="" />
<img src="/images/con/2025/cfp/JS.png" alt="" />
<img src="/images/con/2025/cfp/laravel.png" alt="" />
<img src="/images/con/2025/cfp/php.png" alt="" />
<img src="/images/con/2025/cfp/rabbitmq.png" alt="" />
<img src="/images/con/2025/cfp/react-admin.png" alt="" />
<img src="/images/con/2025/cfp/xdebug.png" alt="" />
<img src="/images/con/2025/cfp/api-platform.png" alt="API Platform" />
<img src="/images/con/2025/cfp/caddy.png" alt="Caddy" />
<img src="/images/con/2025/cfp/elastic.png" alt="Elastic" />
<img src="/images/con/2025/cfp/frankenphp.png" alt="FrankenPHP" />
<img src="/images/con/2025/cfp/JS.png" alt="JavaScript" />
<img src="/images/con/2025/cfp/laravel.png" alt="Laravel" />
<img src="/images/con/2025/cfp/php.png" alt="PHP" />
<img src="/images/con/2025/cfp/rabbitmq.png" alt="RabbitMQ" />
<img src="/images/con/2025/cfp/react-admin.png" alt="React Admin" />
<img src="/images/con/2025/cfp/xdebug.png" alt="Xdebug" />
</>
);
}
17 changes: 14 additions & 3 deletions pwa/app/(con)/[locale]/con/2026/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import nav from "data/con/2026/nav";
import footer from "data/con/2026/footer";
import { Metadata } from "next";
import { getEditionEventData } from "utils/con";
import { i18n } from "i18n/i18n-config";
import { i18n, Locale } from "i18n/i18n-config";
import { getRootUrl } from "utils";
import { getAllSpeakers } from "api/con/speakers";
import { getAllConferences } from "api/con/conferences";

type Props = {
params: { edition: string; locale: string };
Expand Down Expand Up @@ -38,8 +40,17 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
};
}

function EditionLayout({ children }: { children: React.ReactNode }) {
const eventData = getEditionEventData("2026");
async function EditionLayout({
children,
params,
}: {
children: React.ReactNode;
params: { locale: string };
}) {
const locale = (params?.locale || i18n.defaultLocale) as Locale;
const speakers = await getAllSpeakers("2026", locale);
const talks = await getAllConferences("2026", true, locale);
const eventData = getEditionEventData("2026", speakers, talks);
return (
<LayoutBase edition="2026" nav={nav} footer={footer} isTicketingOpen>
<script
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export default function SpeakerPageListTemplate({
{conferences.map((conference, i) => {
const day = days.find((day: Day) => day.date === conference.date);
return (
<div
<article
key={conference.title}
className="flex flex-col-reverse md:flex-row text-center md:text-left gap-4 md:gap-12 bg-white p-4 pt-8 sm:p-8 text-blue-black relative"
>
Expand Down Expand Up @@ -206,6 +206,7 @@ export default function SpeakerPageListTemplate({
<SpeakerImage
image={speaker.image}
placeholder={speaker.placeholder}
alt={speaker.name}
/>
</div>
);
Expand Down Expand Up @@ -260,11 +261,19 @@ export default function SpeakerPageListTemplate({
) : null}
{conference.date ? (
<p className="text-sm font-title">
{getConferenceDate(
conference.date,
conference.start,
conference.end
)}
<time
dateTime={
conference.start
? `${conference.date}T${conference.start}`
: conference.date
}
>
{getConferenceDate(
conference.date,
conference.start,
conference.end
)}
</time>
</p>
) : null}
</>
Expand All @@ -283,7 +292,7 @@ export default function SpeakerPageListTemplate({
/>
) : null}
</div>
</div>
</article>
);
})}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const ConferencePageTemplate = ({ conference, day }: ConferencePageProps) => {
const languageContext = useContext(LanguageContext);

return (
<div className="container max-w-6xl flex flex-col items-center pt-10 pb-80 before:bg-wave before:absolute before:w-[2000px] before:h-[500px] before:bg-no-repeat before:opacity-30 before:-translate-x-1/2 before:top-[220px] before:left-[65%] | sm:pt-20">
<div className="text-white text-center mb-20">
<article className="container max-w-6xl flex flex-col items-center pt-10 pb-80 before:bg-wave before:absolute before:w-[2000px] before:h-[500px] before:bg-no-repeat before:opacity-30 before:-translate-x-1/2 before:top-[220px] before:left-[65%] | sm:pt-20">
<header className="text-white text-center mb-20">
<SectionTitle dark lined h1 small={50 < title.length}>
<strong>{title}</strong>
</SectionTitle>
Expand All @@ -34,11 +34,13 @@ const ConferencePageTemplate = ({ conference, day }: ConferencePageProps) => {
) : null}
{date ? (
<p className="text-sm font-semibold mt-3">
{getConferenceDate(date, start, end)}
<time dateTime={start ? `${date}T${start}` : date}>
{getConferenceDate(date, start, end)}
</time>
</p>
) : null}
</>
</div>
</header>
<div className="flex flex-col-reverse items-center w-full relative -mb-24 before:bg-grey before:absolute before:w-screen before:h-4/6 before:left-1/2 before:bottom-[50px] before:-translate-x-1/2 | md:flex-row md:items-start">
<ConferenceSpeaker conference={conference} />
<div className="bg-white p-12 shadow-md dotted-corner corner-bottom flex-1">
Expand All @@ -53,7 +55,7 @@ const ConferencePageTemplate = ({ conference, day }: ConferencePageProps) => {
) : null}
</div>
</div>
</div>
</article>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ const ConferenceSpeaker = ({ conference }: SpeakerProps) => {
placeholder={placeholder}
/>
) : (
<SpeakerImage image={image} placeholder={placeholder} />
<SpeakerImage
image={image}
placeholder={placeholder}
alt={name}
/>
)}
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
.replace("%name%", SPEAKERS)
.replace("%edition%", params.edition);

const URL = `${getRootUrl()}/${params.locale}/con/${params.edition}/${params.slug}`;
const URL = `${getRootUrl()}/${params.locale}/con/${params.edition}/${
params.slug
}`;

return {
title: conference.title,
Expand All @@ -39,13 +41,13 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
title: `${conference.title} - API Platform Conference`,
description: DESCRIPTION,
url: URL,
type: 'website',
type: "website",
images: [
{
url: `${getRootUrl()}/images/con/og-${params.edition}.png`,
width: 1200,
height: 630,
alt: `API Platform Conference ${params.edition}`
alt: `API Platform Conference ${params.edition}`,
},
],
},
Expand Down
21 changes: 18 additions & 3 deletions pwa/app/(con)/[locale]/con/[edition]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import LayoutBase from "components/con/layout/LayoutBase";
import ContactCard from "components/con/layout/ContactCard";
import type { Metadata } from "next";
import { getEditionEventData } from "utils/con";
import { i18n } from "i18n/i18n-config";
import { i18n, Locale } from "i18n/i18n-config";
import { getRootUrl } from "utils";
import { getAllSpeakers } from "api/con/speakers";
import { getAllConferences } from "api/con/conferences";
import { currentEdition } from "data/con/editions";

type Props = {
params: { edition: string; locale: string };
Expand Down Expand Up @@ -68,13 +71,25 @@ async function EditionLayout({
children: React.ReactNode;
params: {
edition: string;
locale: string;
};
}) {
const { edition } = params;
const { edition, locale } = params;
const nav = await import(`data/con/${edition}/nav`);
const footer = await import(`data/con/${edition}/footer`);

const eventData = getEditionEventData(edition);
const resolvedLocale = (locale || i18n.defaultLocale) as Locale;
// Only the upcoming edition benefits from rich speaker/talk structured data
// (Google does not surface past events). Skipping the fetch for past
// editions keeps the build lean.
const isCurrentEdition = edition === currentEdition;
const speakers = isCurrentEdition
? await getAllSpeakers(edition, resolvedLocale)
: [];
const talks = isCurrentEdition
? await getAllConferences(edition, true, resolvedLocale)
: [];
const eventData = getEditionEventData(edition, speakers, talks);

return (
<LayoutBase edition={edition} nav={nav.default} footer={footer.default}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function Avatar({
) : (
<Image
key={speaker.name}
alt=""
alt={speaker.name}
className="absolute rounded-full -translate-x-1/2 -translate-y-1/2 object-cover"
src={speaker.image}
width={getSize(speakers.length)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ export default function SlotItem({ conference, id }: SlotItemProps) {
: null}
</div>
<Overline className="opacity-70 lg:hidden">
{getConferenceTimes(date, start, end)}
<time dateTime={`${date}T${start}`}>
{getConferenceTimes(date, start, end)}
</time>
</Overline>
<h3 className="font-title font-bold lined-left leading-tight">
{title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export default function SpeakerPageTemplate({

return (
<>
<div className="container xl:max-w-8xl flex flex-col items-center pt-10 pb-32 | sm:pt-20">
<div className="text-white pb-14">
<article className="container xl:max-w-8xl flex flex-col items-center pt-10 pb-32 | sm:pt-20">
<header className="text-white pb-14">
<SectionTitle h1 dark lined>
<strong>{name}</strong>
</SectionTitle>
Expand All @@ -43,7 +43,7 @@ export default function SpeakerPageTemplate({
</>
) : null}
</p>
</div>
</header>
<div className="flex flex-col relative flex-wrap items-center bg-grey px-10 pb-10 pt-28 | lg:flex-row lg:items-start | sm:pb-20">
<div className="w-72 h-72 | md:w-80 md:h-80 | lg:w-[400px] lg:h-[400px]">
{edition === "2025" || edition === "2026" ? (
Expand All @@ -54,7 +54,12 @@ export default function SpeakerPageTemplate({
placeholder={placeholder}
/>
) : (
<SpeakerImage big image={image} placeholder={placeholder} />
<SpeakerImage
big
image={image}
placeholder={placeholder}
alt={name}
/>
)}
</div>
<div className="flex-1 | sm:px-6">
Expand All @@ -74,8 +79,8 @@ export default function SpeakerPageTemplate({
</div>
) : null}
</div>
</div>
<div className="container flex flex-col items-center pt-12 pb-44 max-w-6xl text-white">
</article>
<section className="container flex flex-col items-center pt-12 pb-44 max-w-6xl text-white">
<div className="lined-center lined-blue font-bold uppercase text-2xl text-white font-title">
<Translate translationKey="speakers.others" />
</div>
Expand All @@ -88,7 +93,7 @@ export default function SpeakerPageTemplate({
>
{t("speakers.see_all")}
</Button>
</div>
</section>
</>
);
}
Loading
Loading