diff --git a/public/locales/fr/speakers.json b/public/locales/fr/speakers.json index 85549ff..9cc0e82 100644 --- a/public/locales/fr/speakers.json +++ b/public/locales/fr/speakers.json @@ -68,6 +68,7 @@ }, "sessions": [ { + "id": 1, "title": "Comment PSh est devenu le partenaire de Adobe.", "description": "Une plongée inspirante dans les coulisses d'un partenariat gagnant-gagnant, avec des enseignements applicables à d'autres collaborations stratégiques.", "lang": "fr", @@ -79,6 +80,7 @@ "speakers": [1] }, { + "id": 2, "title": "200 Domains on a Single Magento Instance with a $300 Hosting Bill", "description": "A fascinating story about a unique customer business model with a Magento store where each product segment has its dedicated domain and technology behind it.", "lang": "en", @@ -90,6 +92,7 @@ "speakers": [2] }, { + "id": 3, "title": "Comment étendre Magento/Adobe Commerce sans le surcharger - App Builder.", "description": "Et si vous personnalisiez Magento sans jamais toucher à son code ? Découvrez une approche innovante basée sur les addons, en exploitant Adobe App Builder et Runtime I/O pour transformer l’expérience Magento.", "lang": "fr", @@ -101,6 +104,7 @@ "speakers": [3] }, { + "id": 4, "title": "Le frontend Magento : vers une nouvelle dynamique communautaire.", "description": "Réinventer le frontend Magento en l'alignant sur les meilleures pratiques modernes : une opportunité d’optimiser la stack, d’attirer de nouveaux talents et de dynamiser la communauté grâce à une collaboration renforcée entre backend et frontend.", "lang": "fr", diff --git a/src/app/page.tsx b/src/app/page.tsx index 8fe55b6..6774851 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,4 +1,4 @@ -'use client'; +"use client"; import ContentMedia from "@/components/ContentMedia/ContentMedia"; import Place from "@/components/Place/Place"; @@ -7,6 +7,7 @@ import SponsorList from "@/components/SponsorList/SponsorList"; import Container from "@/layouts/Container"; import Hero from "@/components/Hero/Hero"; import Speakers from "@/components/Speakers/Speakers"; +import Program from "@/components/Program/Program"; import useDataProvider from "@/hooks/useDataProvider"; import { SpeakersProps } from "@/components/Speakers/SpeakersProps"; import { SponsorProps } from "@/components/SponsorList/Sponsor/Sponsor.types"; @@ -16,7 +17,7 @@ import Link from "next/link"; import { useTranslation } from "react-i18next"; export default function Home() { - const { t } = useTranslation(['common']); + const { t } = useTranslation(["common"]); const dataProvider = useDataProvider(); const speakers: SpeakersProps = dataProvider.useSpeakers(); const sponsors: SponsorProps[] = dataProvider.useSponsors(); @@ -25,6 +26,9 @@ export default function Home() { return (
+
+ +
@@ -49,12 +53,12 @@ export default function Home() { className="mt-6" > Pour toute autre question, consultez notre{" "} - - {t('common:faqLabel')} + + {t("common:faqLabel")} {" "} ou écrivez-nous via le{" "} - - {t('common:contactLabel')} + + {t("common:contactLabel")} . diff --git a/src/components/Person/Person.tsx b/src/components/Person/Person.tsx index baf44f9..3730f5e 100644 --- a/src/components/Person/Person.tsx +++ b/src/components/Person/Person.tsx @@ -5,65 +5,88 @@ import { GrFormView } from "react-icons/gr"; import { PersonProps } from "@/components/Person/PersonProps"; import classNames from "classnames"; -const PersonCard = ({ person }: { person: PersonProps }) => ( -
-
- {person.company} - {person.company} -
-

{person.name}

+const PersonCard = ({ + person, + appearance = "speaker", +}: { + person: PersonProps; + appearance?: "speaker" | "program"; +}) => ( +
+ {appearance === "speaker" && ( +
+ {person.company} + {person.company} +
+ )} +

{person.name}

{person.role}

); -const Person = ({ person }: { person: PersonProps }) => { +const Person = ({ + person, + appearance = "speaker", +}: { + person: PersonProps; + appearance?: "speaker" | "program"; +}) => { const { width } = useWindowSize(); const bgClass = `bg-cover bg-photo-${person.photoBg}`; + const imageClass = classNames( + "relative aspect-square object-cover overflow-hidden", + { + "w-20 h-24": appearance === "program", + "min-w-[135px]": appearance === "speaker" && width < 768, + "min-w-[165px]": appearance === "speaker" && width >= 768, + "rounded-full": appearance === "speaker", + "[clip-path:polygon(50%_0%,100%_25%,100%_75%,50%_100%,0%_75%,0%_25%)]": + appearance === "program", + }, + bgClass + ); + return ( <> {width < 768 ? ( -
+
{person.name} - +
) : ( - <> -
+
+
{person.name} -
-
- - Biographie + {appearance === "speaker" && ( +
+
+ + Biographie +
-
+ )}
- - + +
)} ); diff --git a/src/components/Program/Program.tsx b/src/components/Program/Program.tsx new file mode 100644 index 0000000..f9d6d55 --- /dev/null +++ b/src/components/Program/Program.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import Container from "@/layouts/Container"; +import BackgroundImage from "../BackgroundImage/BackgroundImage"; +import ProgramList from "./ProgramList"; + +const Program = () => { + return ( + + +
+ +
+
+
+ ); +}; + +export default Program; diff --git a/src/components/Program/ProgramList.tsx b/src/components/Program/ProgramList.tsx new file mode 100644 index 0000000..2f5921f --- /dev/null +++ b/src/components/Program/ProgramList.tsx @@ -0,0 +1,81 @@ +import React from "react"; +import TopBanner from "../TopBanner/TopBanner"; +import ButtonLink from "../ButtonLink/ButtonLink"; +import { Swiper, SwiperClass, SwiperSlide } from "swiper/react"; +import { Navigation } from "swiper/modules"; +import "swiper/css"; +import "swiper/css/navigation"; +import useDataProvider from "@/hooks/useDataProvider"; +import ClientOnly from "@/helpers/ClientOnly"; +import ProgramTile from "./ProgramTile"; + +const ProgramList = () => { + const swiperRef = React.useRef(null); + const sessions = useDataProvider().useSessions(); + + const handlePrev = () => { + if (swiperRef.current) { + swiperRef.current.slidePrev(); + } + }; + + const handleNext = () => { + if (swiperRef.current) { + swiperRef.current.slideNext(); + } + }; + return ( +
+
+ + <> +
+ + Voir tout + +
+
+ + Découvrir le Programme + +
+ +
+
+ + { + swiperRef.current = swiper; + }} + modules={[Navigation]} + spaceBetween={30} + breakpoints={{ + 0: { + slidesPerView: 1, + spaceBetween: 16, + }, + 768: { + slidesPerView: 2, + }, + 1024: { + slidesPerView: 3, + }, + }} + className="relative" + > + {sessions.map((session) => ( + + + + ))} + + +
+ ); +}; + +export default ProgramList; diff --git a/src/components/Program/ProgramTile.tsx b/src/components/Program/ProgramTile.tsx new file mode 100644 index 0000000..9190a38 --- /dev/null +++ b/src/components/Program/ProgramTile.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { SessionProps } from "../Speakers/Session/SessionProps"; +import Session from "../Speakers/Session/Session"; +import { PiCalendarPlus } from "react-icons/pi"; +import Person from "../Person/Person"; +import useDataProvider from "@/hooks/useDataProvider"; + +const ProgramTile = ({ session }: { session: SessionProps }) => { + const dataProvider = useDataProvider(); + const speakers = dataProvider + .usePersonList("speakers", "data.speakers") + .filter((speaker) => session.speakers.includes(speaker.id)); + + return ( +
+
+ +
+ {speakers.map((speaker) => ( + + ))} +
+
+
+
+ + + Ajouter à mon agenda + +
+
+
+ ); +}; + +export default ProgramTile; diff --git a/src/components/Speakers/Session/Session.tsx b/src/components/Speakers/Session/Session.tsx index 3cd4e05..c78dff5 100644 --- a/src/components/Speakers/Session/Session.tsx +++ b/src/components/Speakers/Session/Session.tsx @@ -1,4 +1,4 @@ -'use client'; +"use client"; import React from "react"; import Typography from "@/components/Typography/Typography"; @@ -9,10 +9,10 @@ import ButtonLink from "@/components/ButtonLink/ButtonLink"; import { useTranslation } from "react-i18next"; const Session = ({ session }: { session: SessionProps }) => { - const { t } = useTranslation(['speakers']); + const { t } = useTranslation(["speakers"]); return ( -
+
@@ -35,10 +35,7 @@ const Session = ({ session }: { session: SessionProps }) => {
{session.tags.map((tag, tagIndex) => ( - + { ))}
-
- {!!session.eventUrl && ( + {!!session.eventUrl && ( +
} + icon={} > - + Ajouter à mon agenda - )} -
+
+ )}
); }; diff --git a/src/components/Speakers/Session/SessionProps.ts b/src/components/Speakers/Session/SessionProps.ts index 03fa028..c158b59 100644 --- a/src/components/Speakers/Session/SessionProps.ts +++ b/src/components/Speakers/Session/SessionProps.ts @@ -1,4 +1,5 @@ export type SessionProps = { + id: number; title: string; description: string; lang: string; diff --git a/src/components/Speakers/Speaker/PersonPopIn.tsx b/src/components/Speakers/Speaker/PersonPopIn.tsx index 93bd97c..99fea35 100644 --- a/src/components/Speakers/Speaker/PersonPopIn.tsx +++ b/src/components/Speakers/Speaker/PersonPopIn.tsx @@ -1,4 +1,4 @@ -'use client'; +"use client"; import { PersonProps } from "@/components/Person/PersonProps"; import PersonPopIn from "@/components/Person/PersonPopIn"; @@ -14,7 +14,7 @@ interface SpeakerPopInProps { const SpeakerPopIn = ({ isOpen, onClose, - selectedSpeaker + selectedSpeaker, }: SpeakerPopInProps) => { const dataProvider = useDataProvider(); const sessions = dataProvider.useSessions(selectedSpeaker.id); @@ -26,8 +26,10 @@ const SpeakerPopIn = ({ selectedPerson={selectedSpeaker} > {sessions.map((session, index) => ( - - ))}; +
+ +
+ ))} ); }; diff --git a/src/components/Speakers/SpeakersList.tsx b/src/components/Speakers/SpeakersList.tsx index facf1ad..223d3b2 100644 --- a/src/components/Speakers/SpeakersList.tsx +++ b/src/components/Speakers/SpeakersList.tsx @@ -1,4 +1,4 @@ -'use client'; +"use client"; import React from "react"; import { Swiper, SwiperClass, SwiperSlide } from "swiper/react"; @@ -19,8 +19,8 @@ const SpeakersList = ({ speakers }: { speakers: PersonProps[] }) => { const swiperRef = React.useRef(null); const [selectedSpeaker, setSelectedSpeaker] = React.useState(null); - const { t } = useTranslation(['speakers']); - const actions = t('actions', { returnObjects: true }); + const { t } = useTranslation(["speakers"]); + const actions = t("actions", { returnObjects: true }); const handleSpeakerClick = (speaker: PersonProps) => { setSelectedSpeaker(speaker); @@ -51,7 +51,7 @@ const SpeakersList = ({ speakers }: { speakers: PersonProps[] }) => { onPrevClick={handlePrev} onNextClick={handleNext} > - {'seeAllUrl' in actions && typeof actions.seeAllUrl === 'string' && ( + {"seeAllUrl" in actions && typeof actions.seeAllUrl === "string" && ( <>
@@ -76,7 +76,7 @@ const SpeakersList = ({ speakers }: { speakers: PersonProps[] }) => { spaceBetween={30} breakpoints={{ 0: { - slidesPerView: 1.2, + slidesPerView: 1.1, spaceBetween: 16, }, 768: { diff --git a/src/components/TopBanner/TopBanner.tsx b/src/components/TopBanner/TopBanner.tsx index 2d86124..2217f88 100644 --- a/src/components/TopBanner/TopBanner.tsx +++ b/src/components/TopBanner/TopBanner.tsx @@ -29,7 +29,7 @@ const TopBanner = ({
{children} - {onPrevClick && onNextClick && width > 768 && ( + {onPrevClick && onNextClick && width > 1024 && (