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
1 change: 0 additions & 1 deletion pwa/api/con/conferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export const getConferencesBySpeaker = async (
locale: Locale
) => {
const conferences = await getAllConferences(edition, true, locale);

return conferences
.filter((conference) =>
conference.speakers.find((speaker: Speaker) => speaker.id === speakerId)
Expand Down
13 changes: 2 additions & 11 deletions pwa/app/(con)/[locale]/con/2025/components/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ const HomePage = ({ speakers, partners, images }: HomePageProps) => {
{t("buy_tickets")}
</BuyButton>
)}
<Button empty to={`/${locale}/con/2025/call-for-papers`}>
{t("2025.cfp.title")}
</Button>
</div>
</div>
</div>
Expand Down Expand Up @@ -86,12 +83,6 @@ const HomePage = ({ speakers, partners, images }: HomePageProps) => {
{t("speakers.see_all")}
</Button>
) : null}
<Button
className="mx-auto my-7"
to={`/${locale}/con/2025/call-for-papers`}
>
{t("2025.our_speakers.cfp")}
</Button>
</div>
</Section>
{currentEdition === "2025" && (
Expand Down Expand Up @@ -206,9 +197,9 @@ const HomePage = ({ speakers, partners, images }: HomePageProps) => {
<div className="bg-white text-center relative z-10 pt-40 pb-40">
<div className="container text-center">
<div className="lined-center lined-blue font-bold uppercase text-2xl text-blue font-title">
{t("sponsorship.they_trust_us", { year: "2024" })}
<Translate translationKey="partners.title" />
</div>
<Partners data={partners} edition="2024" />
<Partners data={partners} edition="2025" />
</div>
</div>
</Section>
Expand Down
2 changes: 1 addition & 1 deletion pwa/app/(con)/[locale]/con/2025/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getAllSpeakers } from "api/con/speakers";
import { getAllEditionPictures } from "api/con/editions";
import partners from "data/con/2024/partners";
import partners from "data/con/2025/partners";
import HomePage from "./components/HomePage";
import { Locale, i18n } from "i18n/i18n-config";
import { Metadata } from "next";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Link from "components/common/Link";
import SpeakerImage from "components/con/speakers/SpeakerImage";
import Button from "components/con/common/Button";
import { LanguageContext } from "contexts/con/LanguageContext";
import SpeakerImage2025 from "components/con/speakers/SpeakerImage2025";

interface SpeakerProps {
conference: Conference;
Expand All @@ -21,69 +22,71 @@ const ConferenceSpeaker = ({ conference }: SpeakerProps) => {
{speakers.map((speaker: Speaker) => {
const { name, job, company, image, placeholder, url } = speaker;
return (
<div
key={speaker.name}
className={classNames(
"bg-blue-gradient bg-blue text-center px-5 py-5 border-b-2 border-dotted border-b-white/50 last:border-b-0 group hover:bg-blue-dark transition-colors",
speakers.length === 1 && "md:hover:bg-blue md:pt-10 md:pb-5"
)}
>
<Link
href={url}
<>
<div
key={speaker.name}
className={classNames(
"flex flex-row items-center",
speakers.length === 1 && "md:flex-col"
"bg-blue-gradient bg-blue text-center px-5 py-5 border-b-2 border-dotted border-b-white/50 last:border-b-0 group hover:bg-blue-dark transition-colors",
speakers.length === 1 && "md:hover:bg-blue md:pt-10 md:pb-5"
)}
>
<div
<Link
href={url}
className={classNames(
"w-20 h-20",
speakers.length === 1 && "md:mx-auto md:w-60 md:h-60"
"flex flex-row items-center",
speakers.length === 1 && "md:flex-col"
)}
>
<SpeakerImage
image={image}
placeholder={placeholder}
hoverable={false}
/>
</div>
<div
className={classNames(
"w-20 h-20 relative",
speakers.length === 1 && "md:mx-auto md:w-60 md:h-60"
)}
>
{conference.edition === "2025" ? (
<SpeakerImage2025 speaker={speaker} image={image} />
) : (
<SpeakerImage image={image} placeholder={placeholder} />
)}
</div>

<div
className={classNames(
"text-inherit uppercase font-title ml-5 flex-1 transition-colors group-hover:text-white",
speakers.length === 1 &&
"md:mt-7 md:ml-0 md:group-hover:text-blue-black"
)}
>
<span
<div
className={classNames(
"inline-block leading-tight font-semibold text-lg",
speakers.length === 1 && "md:text-xl"
"text-inherit uppercase font-title ml-5 flex-1 transition-colors group-hover:text-white",
speakers.length === 1 &&
"md:mt-7 md:ml-0 md:group-hover:text-blue-black"
)}
>
{name}
</span>
<Overline className="text-white lined-center lined-white/50">
{job}
<br />
{company ? (
<>
@ <strong className="font-normal">{company}</strong>
</>
) : null}
</Overline>
</div>
</Link>
{speakers.length === 1 && (
<Button
className="mt-5 white square hidden md:inline-block"
size="small"
to={speaker.url}
>
{t("conferences.see_speaker_details")}
</Button>
)}
</div>
<span
className={classNames(
"inline-block leading-tight font-semibold text-lg",
speakers.length === 1 && "md:text-xl"
)}
>
{name}
</span>
<Overline className="text-white lined-center lined-white/50">
{job}
<br />
{company ? (
<>
@ <strong className="font-normal">{company}</strong>
</>
) : null}
</Overline>
</div>
</Link>
{speakers.length === 1 && (
<Button
className="mt-5 white square hidden md:inline-block"
size="small"
to={speaker.url}
>
{t("conferences.see_speaker_details")}
</Button>
)}
</div>
</>
);
})}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,28 @@ export default function SpeakerConferenceSlot({
</span>
{<Overline>{getConferenceDate(date)}</Overline>}
</>
) : null}
) : (
<div className="text-white flex flex-col gap-4 items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="size-12 opacity-60"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5m-9-6h.008v.008H12v-.008ZM12 15h.008v.008H12V15Zm0 2.25h.008v.008H12v-.008ZM9.75 15h.008v.008H9.75V15Zm0 2.25h.008v.008H9.75v-.008ZM7.5 15h.008v.008H7.5V15Zm0 2.25h.008v.008H7.5v-.008Zm6.75-4.5h.008v.008h-.008v-.008Zm0 2.25h.008v.008h-.008V15Zm0 2.25h.008v.008h-.008v-.008Zm2.25-4.5h.008v.008H16.5v-.008Zm0 2.25h.008v.008H16.5V15Z"
/>
</svg>

<span className="uppercase text-white font-bold font-title">
{t("speakers.no_date")}
</span>
</div>
)}
</div>
<div className="flex flex-col flex-1 items-start py-5 px-12">
<div className="lined-left flex flex-col">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SpeakerConferenceSlot from "./SpeakerConferenceSlot";
import { LanguageContext } from "contexts/con/LanguageContext";
import Button from "components/con/common/Button";
import SpeakerList from "components/con/speakers/SpeakerList";
import SpeakerImage2025 from "components/con/speakers/SpeakerImage2025";

interface SpeakerProps {
speakerData: Speaker;
Expand Down Expand Up @@ -45,7 +46,11 @@ export default function SpeakerPageTemplate({
</div>
<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]">
<SpeakerImage big image={image} placeholder={placeholder} />
{edition === "2025" ? (
<SpeakerImage2025 speaker={speakerData} big image={image} />
) : (
<SpeakerImage big image={image} placeholder={placeholder} />
)}
</div>
<div className="flex-1 | sm:px-6">
<SpeakerDescription speaker={speakerData} />
Expand Down
1 change: 1 addition & 0 deletions pwa/app/(con)/[locale]/con/[edition]/speakers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const generateStaticParams = async () => {
{ edition: "2022" },
{ edition: "2023" },
{ edition: "2024" },
{ edition: "2025" },
];
};

Expand Down
148 changes: 148 additions & 0 deletions pwa/components/con/speakers/SpeakerImage2025.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React from "react";
import classNames from "classnames";
import { Speaker } from "types/con";

interface SpeakerImageProps {
image: string;
placeholder?: string;
hoverable?: boolean;
big?: boolean;
speaker: Speaker;
}

function nameToAngle(name: string): number {
// Calcule l'angle (en degrés) de 0 à 360
let hash = 0;
for (let i = 0; i < name.length; i++) {
hash += name.charCodeAt(i);
}
let angle = hash % 360;

// Plages interdites (en degrés)
const forbiddenZones: [number, number][] = [
[60, 120],
[240, 300],
];

// Corrige si dans une zone interdite
for (const [start, end] of forbiddenZones) {
if (angle >= start && angle < end) {
angle = end; // Déplace à la fin de la zone interdite
}
}

return angle * (Math.PI / 180);
}

function nameToSize(name: string, min = 10, max = 30) {
let hash = 0;
for (let i = 0; i < name.length; i++) {
hash += (i + 1) * name.charCodeAt(i);
}
const range = max - min;
return min + (hash % range);
}

function cssPositionOnCircle(angle: number) {
// angle en radians
return {
x: `calc(50% + ${Math.cos(angle) * 50}%)`,
y: `calc(50% + ${Math.sin(angle) * 50}%)`,
x2: `calc(50% + ${Math.cos(angle + Math.PI) * 50}%)`,
y2: `calc(50% + ${Math.sin(angle + Math.PI) * 50}%)`,
};
}

export default function SpeakerImage({
image,
placeholder,
big = false,
hoverable = true,
speaker,
}: SpeakerImageProps) {
if (speaker.edition === "2025") {
const { name } = speaker;
const angle = nameToAngle(name);
const size = nameToSize(name);
const pos = cssPositionOnCircle(angle);
return (
<>
<svg width="0" height="0">
<clipPath id={`clip-${speaker.id}`} clipPathUnits="objectBoundingBox">
<path
d={
speaker.path ||
"M1,0 H0 V0.55125 H0.13225 c-0.00475,0.02385,-0.00725,0.0485,-0.00725,0.07375 c0,0.2071,0.1679,0.375,0.375,0.375 s0.375,-0.1679,0.375,-0.375 c0,-0.02525,-0.0025,-0.0499,-0.00725,-0.07375 H1 V0 Z"
}
/>
</clipPath>
</svg>
<div
className={classNames(
"w-full h-full relative bg-white rounded-full transition-all duration-500",
hoverable && "group-hover:bg-blue-light "
)}
>
<div
className={classNames(
"relative w-full h-full before:absolute before:w-[110%] before:h-[110%] before:left-1/2 before:top-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:pointer-events-none before:transition-all before:bg-circle before:bg-no-repeat before:duration-700 before:ease-out",
hoverable && "group-hover:before:rotate-45"
)}
>
<div
className="aspect-square overflow-hidden w-[calc(400%/3)] pointer-events-none max-w-none absolute z-10 bottom-0 left-1/2 -translate-x-1/2"
style={{ clipPath: `url(#clip-${speaker.id})` }}
>
<img
src={image}
className={classNames(
"w-full h-full transition-all duration-500 will-change-transform origin-center",
big && "scale-110",
hoverable && "group-hover:-rotate-2 group-hover:scale-110 "
)}
alt=""
/>
</div>
<div
className={classNames(
"absolute aspect-square -translate-x-1/2 -translate-y-1/2 transition-all duration-700",
big && "scale-[150%]",
hoverable && "group-hover:scale-[200%]"
)}
style={{
left: pos.x,
top: pos.y,
width: `${size}%`,
}}
>
<div
className={classNames(
"bg-pink/20 animate-float rounded-full size-full",
big && "scale-[150%]"
)}
/>
</div>
<div
className={classNames(
"absolute z-20 aspect-square -translate-x-1/2 -translate-y-1/2 transition-all duration-700",
hoverable && "group-hover:scale-[40%]"
)}
style={{
left: pos.x2,
top: pos.y2,
width: `${50 - size}%`,
}}
>
<div
className={classNames(
"bg-blue/30 animate-float2 rounded-full size-full",
big && "scale-[60%]"
)}
/>
</div>
</div>
</div>
</>
);
}
}
Loading
Loading