Skip to content

Commit 1f0a5ce

Browse files
authored
feat: add 2025 infos (#599)
* feat: add speakers for 2025
1 parent d0a0546 commit 1f0a5ce

154 files changed

Lines changed: 3277 additions & 1480 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pwa/api/con/conferences.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ export const getConferencesBySpeaker = async (
3333
locale: Locale
3434
) => {
3535
const conferences = await getAllConferences(edition, true, locale);
36-
3736
return conferences
3837
.filter((conference) =>
3938
conference.speakers.find((speaker: Speaker) => speaker.id === speakerId)

pwa/app/(con)/[locale]/con/2025/components/HomePage.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ const HomePage = ({ speakers, partners, images }: HomePageProps) => {
4545
{t("buy_tickets")}
4646
</BuyButton>
4747
)}
48-
<Button empty to={`/${locale}/con/2025/call-for-papers`}>
49-
{t("2025.cfp.title")}
50-
</Button>
5148
</div>
5249
</div>
5350
</div>
@@ -86,12 +83,6 @@ const HomePage = ({ speakers, partners, images }: HomePageProps) => {
8683
{t("speakers.see_all")}
8784
</Button>
8885
) : null}
89-
<Button
90-
className="mx-auto my-7"
91-
to={`/${locale}/con/2025/call-for-papers`}
92-
>
93-
{t("2025.our_speakers.cfp")}
94-
</Button>
9586
</div>
9687
</Section>
9788
{currentEdition === "2025" && (
@@ -206,9 +197,9 @@ const HomePage = ({ speakers, partners, images }: HomePageProps) => {
206197
<div className="bg-white text-center relative z-10 pt-40 pb-40">
207198
<div className="container text-center">
208199
<div className="lined-center lined-blue font-bold uppercase text-2xl text-blue font-title">
209-
{t("sponsorship.they_trust_us", { year: "2024" })}
200+
<Translate translationKey="partners.title" />
210201
</div>
211-
<Partners data={partners} edition="2024" />
202+
<Partners data={partners} edition="2025" />
212203
</div>
213204
</div>
214205
</Section>

pwa/app/(con)/[locale]/con/2025/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getAllSpeakers } from "api/con/speakers";
22
import { getAllEditionPictures } from "api/con/editions";
3-
import partners from "data/con/2024/partners";
3+
import partners from "data/con/2025/partners";
44
import HomePage from "./components/HomePage";
55
import { Locale, i18n } from "i18n/i18n-config";
66
import { Metadata } from "next";

pwa/app/(con)/[locale]/con/[edition]/conferences/[slug]/components/ConferenceSpeaker.tsx

Lines changed: 56 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Link from "components/common/Link";
77
import SpeakerImage from "components/con/speakers/SpeakerImage";
88
import Button from "components/con/common/Button";
99
import { LanguageContext } from "contexts/con/LanguageContext";
10+
import SpeakerImage2025 from "components/con/speakers/SpeakerImage2025";
1011

1112
interface SpeakerProps {
1213
conference: Conference;
@@ -21,69 +22,71 @@ const ConferenceSpeaker = ({ conference }: SpeakerProps) => {
2122
{speakers.map((speaker: Speaker) => {
2223
const { name, job, company, image, placeholder, url } = speaker;
2324
return (
24-
<div
25-
key={speaker.name}
26-
className={classNames(
27-
"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",
28-
speakers.length === 1 && "md:hover:bg-blue md:pt-10 md:pb-5"
29-
)}
30-
>
31-
<Link
32-
href={url}
25+
<>
26+
<div
27+
key={speaker.name}
3328
className={classNames(
34-
"flex flex-row items-center",
35-
speakers.length === 1 && "md:flex-col"
29+
"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",
30+
speakers.length === 1 && "md:hover:bg-blue md:pt-10 md:pb-5"
3631
)}
3732
>
38-
<div
33+
<Link
34+
href={url}
3935
className={classNames(
40-
"w-20 h-20",
41-
speakers.length === 1 && "md:mx-auto md:w-60 md:h-60"
36+
"flex flex-row items-center",
37+
speakers.length === 1 && "md:flex-col"
4238
)}
4339
>
44-
<SpeakerImage
45-
image={image}
46-
placeholder={placeholder}
47-
hoverable={false}
48-
/>
49-
</div>
40+
<div
41+
className={classNames(
42+
"w-20 h-20 relative",
43+
speakers.length === 1 && "md:mx-auto md:w-60 md:h-60"
44+
)}
45+
>
46+
{conference.edition === "2025" ? (
47+
<SpeakerImage2025 speaker={speaker} image={image} />
48+
) : (
49+
<SpeakerImage image={image} placeholder={placeholder} />
50+
)}
51+
</div>
5052

51-
<div
52-
className={classNames(
53-
"text-inherit uppercase font-title ml-5 flex-1 transition-colors group-hover:text-white",
54-
speakers.length === 1 &&
55-
"md:mt-7 md:ml-0 md:group-hover:text-blue-black"
56-
)}
57-
>
58-
<span
53+
<div
5954
className={classNames(
60-
"inline-block leading-tight font-semibold text-lg",
61-
speakers.length === 1 && "md:text-xl"
55+
"text-inherit uppercase font-title ml-5 flex-1 transition-colors group-hover:text-white",
56+
speakers.length === 1 &&
57+
"md:mt-7 md:ml-0 md:group-hover:text-blue-black"
6258
)}
6359
>
64-
{name}
65-
</span>
66-
<Overline className="text-white lined-center lined-white/50">
67-
{job}
68-
<br />
69-
{company ? (
70-
<>
71-
@ <strong className="font-normal">{company}</strong>
72-
</>
73-
) : null}
74-
</Overline>
75-
</div>
76-
</Link>
77-
{speakers.length === 1 && (
78-
<Button
79-
className="mt-5 white square hidden md:inline-block"
80-
size="small"
81-
to={speaker.url}
82-
>
83-
{t("conferences.see_speaker_details")}
84-
</Button>
85-
)}
86-
</div>
60+
<span
61+
className={classNames(
62+
"inline-block leading-tight font-semibold text-lg",
63+
speakers.length === 1 && "md:text-xl"
64+
)}
65+
>
66+
{name}
67+
</span>
68+
<Overline className="text-white lined-center lined-white/50">
69+
{job}
70+
<br />
71+
{company ? (
72+
<>
73+
@ <strong className="font-normal">{company}</strong>
74+
</>
75+
) : null}
76+
</Overline>
77+
</div>
78+
</Link>
79+
{speakers.length === 1 && (
80+
<Button
81+
className="mt-5 white square hidden md:inline-block"
82+
size="small"
83+
to={speaker.url}
84+
>
85+
{t("conferences.see_speaker_details")}
86+
</Button>
87+
)}
88+
</div>
89+
</>
8790
);
8891
})}
8992
</div>

pwa/app/(con)/[locale]/con/[edition]/speakers/[slug]/SpeakerConferenceSlot.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,28 @@ export default function SpeakerConferenceSlot({
3030
</span>
3131
{<Overline>{getConferenceDate(date)}</Overline>}
3232
</>
33-
) : null}
33+
) : (
34+
<div className="text-white flex flex-col gap-4 items-center">
35+
<svg
36+
xmlns="http://www.w3.org/2000/svg"
37+
fill="none"
38+
viewBox="0 0 24 24"
39+
strokeWidth={1.5}
40+
stroke="currentColor"
41+
className="size-12 opacity-60"
42+
>
43+
<path
44+
strokeLinecap="round"
45+
strokeLinejoin="round"
46+
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"
47+
/>
48+
</svg>
49+
50+
<span className="uppercase text-white font-bold font-title">
51+
{t("speakers.no_date")}
52+
</span>
53+
</div>
54+
)}
3455
</div>
3556
<div className="flex flex-col flex-1 items-start py-5 px-12">
3657
<div className="lined-left flex flex-col">

pwa/app/(con)/[locale]/con/[edition]/speakers/[slug]/SpeakerPage.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import SpeakerConferenceSlot from "./SpeakerConferenceSlot";
88
import { LanguageContext } from "contexts/con/LanguageContext";
99
import Button from "components/con/common/Button";
1010
import SpeakerList from "components/con/speakers/SpeakerList";
11+
import SpeakerImage2025 from "components/con/speakers/SpeakerImage2025";
1112

1213
interface SpeakerProps {
1314
speakerData: Speaker;
@@ -45,7 +46,11 @@ export default function SpeakerPageTemplate({
4546
</div>
4647
<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">
4748
<div className="w-72 h-72 | md:w-80 md:h-80 | lg:w-[400px] lg:h-[400px]">
48-
<SpeakerImage big image={image} placeholder={placeholder} />
49+
{edition === "2025" ? (
50+
<SpeakerImage2025 speaker={speakerData} big image={image} />
51+
) : (
52+
<SpeakerImage big image={image} placeholder={placeholder} />
53+
)}
4954
</div>
5055
<div className="flex-1 | sm:px-6">
5156
<SpeakerDescription speaker={speakerData} />

pwa/app/(con)/[locale]/con/[edition]/speakers/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const generateStaticParams = async () => {
2424
{ edition: "2022" },
2525
{ edition: "2023" },
2626
{ edition: "2024" },
27+
{ edition: "2025" },
2728
];
2829
};
2930

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import React from "react";
2+
import classNames from "classnames";
3+
import { Speaker } from "types/con";
4+
5+
interface SpeakerImageProps {
6+
image: string;
7+
placeholder?: string;
8+
hoverable?: boolean;
9+
big?: boolean;
10+
speaker: Speaker;
11+
}
12+
13+
function nameToAngle(name: string): number {
14+
// Calcule l'angle (en degrés) de 0 à 360
15+
let hash = 0;
16+
for (let i = 0; i < name.length; i++) {
17+
hash += name.charCodeAt(i);
18+
}
19+
let angle = hash % 360;
20+
21+
// Plages interdites (en degrés)
22+
const forbiddenZones: [number, number][] = [
23+
[60, 120],
24+
[240, 300],
25+
];
26+
27+
// Corrige si dans une zone interdite
28+
for (const [start, end] of forbiddenZones) {
29+
if (angle >= start && angle < end) {
30+
angle = end; // Déplace à la fin de la zone interdite
31+
}
32+
}
33+
34+
return angle * (Math.PI / 180);
35+
}
36+
37+
function nameToSize(name: string, min = 10, max = 30) {
38+
let hash = 0;
39+
for (let i = 0; i < name.length; i++) {
40+
hash += (i + 1) * name.charCodeAt(i);
41+
}
42+
const range = max - min;
43+
return min + (hash % range);
44+
}
45+
46+
function cssPositionOnCircle(angle: number) {
47+
// angle en radians
48+
return {
49+
x: `calc(50% + ${Math.cos(angle) * 50}%)`,
50+
y: `calc(50% + ${Math.sin(angle) * 50}%)`,
51+
x2: `calc(50% + ${Math.cos(angle + Math.PI) * 50}%)`,
52+
y2: `calc(50% + ${Math.sin(angle + Math.PI) * 50}%)`,
53+
};
54+
}
55+
56+
export default function SpeakerImage({
57+
image,
58+
placeholder,
59+
big = false,
60+
hoverable = true,
61+
speaker,
62+
}: SpeakerImageProps) {
63+
if (speaker.edition === "2025") {
64+
const { name } = speaker;
65+
const angle = nameToAngle(name);
66+
const size = nameToSize(name);
67+
const pos = cssPositionOnCircle(angle);
68+
return (
69+
<>
70+
<svg width="0" height="0">
71+
<clipPath id={`clip-${speaker.id}`} clipPathUnits="objectBoundingBox">
72+
<path
73+
d={
74+
speaker.path ||
75+
"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"
76+
}
77+
/>
78+
</clipPath>
79+
</svg>
80+
<div
81+
className={classNames(
82+
"w-full h-full relative bg-white rounded-full transition-all duration-500",
83+
hoverable && "group-hover:bg-blue-light "
84+
)}
85+
>
86+
<div
87+
className={classNames(
88+
"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",
89+
hoverable && "group-hover:before:rotate-45"
90+
)}
91+
>
92+
<div
93+
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"
94+
style={{ clipPath: `url(#clip-${speaker.id})` }}
95+
>
96+
<img
97+
src={image}
98+
className={classNames(
99+
"w-full h-full transition-all duration-500 will-change-transform origin-center",
100+
big && "scale-110",
101+
hoverable && "group-hover:-rotate-2 group-hover:scale-110 "
102+
)}
103+
alt=""
104+
/>
105+
</div>
106+
<div
107+
className={classNames(
108+
"absolute aspect-square -translate-x-1/2 -translate-y-1/2 transition-all duration-700",
109+
big && "scale-[150%]",
110+
hoverable && "group-hover:scale-[200%]"
111+
)}
112+
style={{
113+
left: pos.x,
114+
top: pos.y,
115+
width: `${size}%`,
116+
}}
117+
>
118+
<div
119+
className={classNames(
120+
"bg-pink/20 animate-float rounded-full size-full",
121+
big && "scale-[150%]"
122+
)}
123+
/>
124+
</div>
125+
<div
126+
className={classNames(
127+
"absolute z-20 aspect-square -translate-x-1/2 -translate-y-1/2 transition-all duration-700",
128+
hoverable && "group-hover:scale-[40%]"
129+
)}
130+
style={{
131+
left: pos.x2,
132+
top: pos.y2,
133+
width: `${50 - size}%`,
134+
}}
135+
>
136+
<div
137+
className={classNames(
138+
"bg-blue/30 animate-float2 rounded-full size-full",
139+
big && "scale-[60%]"
140+
)}
141+
/>
142+
</div>
143+
</div>
144+
</div>
145+
</>
146+
);
147+
}
148+
}

0 commit comments

Comments
 (0)