Skip to content

Commit

Permalink
Implement session popin content
Browse files Browse the repository at this point in the history
  • Loading branch information
thomas-kl1 committed Feb 2, 2025
1 parent a24f7e6 commit 73879f5
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 93 deletions.
3 changes: 3 additions & 0 deletions public/images/speakers/rooms/boreal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/speakers/rooms/orion.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/speakers/rooms/vega.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
107 changes: 55 additions & 52 deletions public/locales/fr/speakers.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,7 @@
"role": "CEO",
"photo": "/images/speakers/fred_plais.jpg",
"photoBg": 3,
"biography": "Fred Plais est un serial entrepreneur et cofondateur & CEO de Platform.sh, localisé en Californie depuis 2015. Passioné de tech, d'open source et d'entreprenariat, Fred est aussi investisseur et board member de plusieurs startups et scaleups francaises et americaines.",
"sessions": [
{
"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",
"track": "Business",
"room": "Salle Orion",
"start": "9h00",
"end": "10h30",
"tags": ["Ouverture", "Business", "Expertise"]
}
]
"biography": "Fred Plais est un serial entrepreneur et cofondateur & CEO de Platform.sh, localisé en Californie depuis 2015. Passioné de tech, d'open source et d'entreprenariat, Fred est aussi investisseur et board member de plusieurs startups et scaleups francaises et americaines."
},
{
"id": 2,
Expand All @@ -45,19 +33,7 @@
"role": "Magento Performance Expert",
"photo": "/images/speakers/ivan_chepurnyi.jpg",
"photoBg": 6,
"biography": "Ivan Chepurnyi stands as a recognized expert in the Magento platform, a status he’s earned since his start in 2007. His deep technical understanding of the system has enabled him to build and fine-tune Magento websites for clients all around the globe.\n\nBeyond his technical skills, Ivan excels in breaking down complex ideas about performance optimization, code quality, and development workflows. His knack for making these intricate topics easy to understand has made him a regular speaker at Magento conferences and events.",
"sessions": [
{
"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",
"track": "Technical",
"room": "Salle Orion",
"start": "9h00",
"end": "10h30",
"tags": ["Technical", "Expertise"]
}
]
"biography": "Ivan Chepurnyi stands as a recognized expert in the Magento platform, a status he’s earned since his start in 2007. His deep technical understanding of the system has enabled him to build and fine-tune Magento websites for clients all around the globe.\n\nBeyond his technical skills, Ivan excels in breaking down complex ideas about performance optimization, code quality, and development workflows. His knack for making these intricate topics easy to understand has made him a regular speaker at Magento conferences and events."
},
{
"id": 3,
Expand All @@ -69,19 +45,7 @@
"role": "Tech Expert",
"photo": "/images/speakers/adrien_illy.jpg",
"photoBg": 4,
"biography": "",
"sessions": [
{
"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",
"track": "Technical",
"room": "Salle Orion",
"start": "9h00",
"end": "10h30",
"tags": ["Technical", "Expertise"]
}
]
"biography": ""
},
{
"id": 4,
Expand All @@ -93,19 +57,58 @@
"role": "Tech Expert",
"photo": "/images/speakers/herve_guetin.jpg",
"photoBg": 1,
"biography": "",
"sessions": [
{
"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",
"track": "Technical",
"room": "Salle Orion",
"start": "9h00",
"end": "10h30",
"tags": ["Technical", "Expertise"]
}
]
"biography": ""
}
],
"rooms": {
"orion": "Salle Orion",
"vega": "Salle Véga",
"boreal": "Salle Boréal"
},
"sessions": [
{
"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",
"track": "Business",
"room": "orion",
"start": "9h00",
"end": "10h30",
"tags": ["Ouverture", "Business", "Expertise"],
"speakers": [1]
},
{
"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",
"track": "Technical",
"room": "orion",
"start": "9h00",
"end": "10h30",
"tags": ["Technical", "Expertise"],
"speakers": [2]
},
{
"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",
"track": "Technical",
"room": "orion",
"start": "9h00",
"end": "10h30",
"tags": ["Technical", "Expertise"],
"speakers": [3]
},
{
"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",
"track": "Technical",
"room": "orion",
"start": "9h00",
"end": "10h30",
"tags": ["Technical", "Expertise"],
"speakers": [4]
}
]
}
Expand Down
6 changes: 3 additions & 3 deletions src/app/speakers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import useDataProvider from "@/hooks/useDataProvider";
import Push from "@/layouts/Push/Push";
import Person from "@/components/Person/Person";
import React from "react";
import PersonPopIn from "@/components/Person/PersonPopIn";
import { PersonProps } from "@/components/Person/PersonProps";
import SpeakerPopIn from "@/components/Speakers/Speaker/PersonPopIn";

export default function Page() {
const { t } = useTranslation(['speakers']);
Expand Down Expand Up @@ -58,10 +58,10 @@ export default function Page() {
</div>
<Push/>
{selectedSpeaker && (
<PersonPopIn
<SpeakerPopIn
selectedSpeaker={selectedSpeaker}
isOpen={!!selectedSpeaker}
onClose={handleCloseModal}
selectedPerson={selectedSpeaker}
/>
)}
</Container>
Expand Down
2 changes: 1 addition & 1 deletion src/app/staff/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Typography from "@/components/Typography/Typography";
import ContentMedia from "@/components/ContentMedia/ContentMedia";
import Container from "@/layouts/Container";
import {useTranslation} from "react-i18next";
import { useTranslation } from "react-i18next";
import Image from "next/image";
import React from "react";
import { PersonProps } from "@/components/Person/PersonProps";
Expand Down
46 changes: 32 additions & 14 deletions src/components/Speakers/Session/Session.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
'use client';

import React from "react";
import Typography from "@/components/Typography/Typography";
import { SessionProps } from "@/components/Speakers/Session/SessionProps";
import Image from "next/image";
import {PiCalendarPlus} from "react-icons/pi";
import { PiCalendarPlus } from "react-icons/pi";
import ButtonLink from "@/components/ButtonLink/ButtonLink";
import { useTranslation } from "react-i18next";

const Session = ({ session }: { session: SessionProps }) => {
const { t } = useTranslation(['speakers']);

return (
<div className="flex flex-col md:flex-row justify-between rounded-xl gap-6 p-6 bg-white">
<div className="flex flex-col gap-6">
Expand All @@ -13,11 +19,17 @@ const Session = ({ session }: { session: SessionProps }) => {
{session.title}
</Typography>
<div className="flex items-center font-medium text-black">
<Image src={session.room} alt="Room" width={24} height={24} />
<span>{session.room}</span>
<span className="mx-2">|</span>
<Image
src={`/images/speakers/rooms/${session.room}.svg`}
alt={t(`speakers:data.rooms.${session.room}`)}
width={18}
height={20}
className="object-fit mr-1"
/>
<span>{t(`speakers:data.rooms.${session.room}`)}</span>
<span className="mx-2 text-primary">|</span>
<span>{session.start}</span>
<span className="mx-1">|</span>
<span className="mx-1 text-primary">&bull;</span>
<span>{session.end}</span>
</div>
</div>
Expand All @@ -39,16 +51,22 @@ const Session = ({ session }: { session: SessionProps }) => {
</div>
</div>
<div className="flex flex-col justify-end items-start md:items-end w-full">
<button className="bg-primary text-white px-4 py-2 rounded-xl flex items-center gap-2 w-fit">
<PiCalendarPlus size={24} className="text-white" />
<Typography
variant="body1"
weight="semibold"
color="light"
{!!session.eventUrl && (
<ButtonLink
variant="secondary-invert"
href={session.eventUrl}
iconPosition="left"
icon={<PiCalendarPlus size={24} className="text-white"/>}
>
Ajouter à mon agenda
</Typography>
</button>
<Typography
variant="body1"
weight="semibold"
color="light"
>
Ajouter à mon agenda
</Typography>
</ButtonLink>
)}
</div>
</div>
);
Expand Down
6 changes: 5 additions & 1 deletion src/components/Speakers/Session/SessionProps.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
export type SessionProps = {
speakerId: number;
title: string;
description: string;
lang: string;
track: string;
room: string;
start: string;
end: string;
tags: string[];
speakers: number[];
eventUrl?: string;
};
35 changes: 35 additions & 0 deletions src/components/Speakers/Speaker/PersonPopIn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client';

import { PersonProps } from "@/components/Person/PersonProps";
import PersonPopIn from "@/components/Person/PersonPopIn";
import useDataProvider from "@/hooks/useDataProvider";
import Session from "@/components/Speakers/Session/Session";

interface SpeakerPopInProps {
selectedSpeaker: PersonProps;
isOpen: boolean;
onClose: () => void;
}

const SpeakerPopIn = ({
isOpen,
onClose,
selectedSpeaker
}: SpeakerPopInProps) => {
const dataProvider = useDataProvider();
const sessions = dataProvider.useSessions(selectedSpeaker.id);

return (
<PersonPopIn
isOpen={isOpen}
onClose={onClose}
selectedPerson={selectedSpeaker}
>
{sessions.map((session, index) => (
<Session session={session} key={index} />
))};
</PersonPopIn>
);
};

export default SpeakerPopIn;
24 changes: 4 additions & 20 deletions src/components/Speakers/SpeakersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Person from "@/components/Person/Person";
import { PersonProps } from "@/components/Person/PersonProps";
import PersonPopIn from "@/components/Person/PersonPopIn";
import Typography from "@/components/Typography/Typography";
import SpeakerPopIn from "@/components/Speakers/Speaker/PersonPopIn";

const SpeakersList = ({ speakers }: { speakers: PersonProps[] }) => {
const { width } = useWindowSize();
Expand Down Expand Up @@ -139,29 +140,12 @@ const SpeakersList = ({ speakers }: { speakers: PersonProps[] }) => {
)}
</Swiper>
</ClientOnly>

{selectedSpeaker && (
<PersonPopIn
<SpeakerPopIn
selectedSpeaker={selectedSpeaker}
isOpen={!!selectedSpeaker}
onClose={handleCloseModal}
selectedPerson={selectedSpeaker}
>
{/* Render Session.tsx List here */}
<Typography
color="dark"
variant="body1"
weight="semibold"
>
Plus d’informations à venir !
</Typography>
<Typography
color="dark"
variant="body1"
weight="semibold"
>
Restez connecté !
</Typography>
</PersonPopIn>
/>
)}
</section>
);
Expand Down
20 changes: 18 additions & 2 deletions src/hooks/useDataProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import { useTranslation } from "react-i18next";
import { SpeakersProps } from "@/components/Speakers/SpeakersProps";
import { SponsorProps } from "@/components/SponsorList/Sponsor/Sponsor.types";
import {PlaceProps} from "@/components/Place/PlaceProps";
import {PersonProps} from "@/components/Person/PersonProps";
import { PlaceProps } from "@/components/Place/PlaceProps";
import { PersonProps } from "@/components/Person/PersonProps";
import {SessionProps} from "@/components/Speakers/Session/SessionProps";

const useData = (ns: string, key: string = 'data') => {
return useTranslation([ns]).t(key, { returnObjects: true });
Expand All @@ -14,6 +15,10 @@ const isSpeakersProps = (content: object): content is SpeakersProps => {
return content !== null && typeof content === 'object';
}

const isSessionProps = (content: object): content is SessionProps[] => {
return content !== null && typeof content === 'object';
}

const isSponsors = (content: object): content is SponsorProps[] => {
return content !== null && typeof content === 'object';
}
Expand All @@ -36,6 +41,16 @@ const useSpeakers = (): SpeakersProps =>
return content;
}

const useSessions = (speakerId: number): SessionProps[] =>
{
const content = useData('speakers', 'data.sessions');
if (!isSessionProps(content)) {
throw new Error('Content is not a valid Speakers Type');
}

return content.filter((session) => session.speakers.includes(speakerId));
}

const useSponsors = (): SponsorProps[] =>
{
const content = useData('sponsors', 'partners');
Expand Down Expand Up @@ -70,6 +85,7 @@ const useDataProvider = () => {
return {
useSponsors: useSponsors,
useSpeakers: useSpeakers,
useSessions: useSessions,
usePlace: usePlace,
usePersonList: usePersonList,
}
Expand Down

0 comments on commit 73879f5

Please sign in to comment.