Skip to content

Commit

Permalink
Add speakers page (#197)
Browse files Browse the repository at this point in the history
* Intialize Speaker page

* Add speaker page

* Modified js and tsx file to jsx

* Fix speaker page bio header alignment

* Improve accessibility

* Speakers page enhancements

* Add speaker modal and speakers data added in schedule

* Modify schedule based on the data modification

* Remove speakers data

* Remove speakers import statement

* Talk Deatils section added in speakers infor page
  • Loading branch information
RajatRajdeep authored Sep 20, 2023
1 parent d27b630 commit 50c580f
Show file tree
Hide file tree
Showing 58 changed files with 1,390 additions and 470 deletions.
21 changes: 21 additions & 0 deletions components/customModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Modal } from "react-bootstrap";

const CustomModal = ({ showModal, handleClose, children }) => {
return (
<Modal show={showModal} size="lg">
<Modal.Body>
<div className="row justify-content-end">
<button
type="button"
className="btn-close"
aria-label="Close"
onClick={handleClose}
></button>
</div>
{children}
</Modal.Body>
</Modal>
);
};

export default CustomModal;
12 changes: 6 additions & 6 deletions components/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ const navBarItems = [
},
],
},
{
name: "Keynotes",
href: "/#keynote",
id: "keynote",
openInNewTab: false,
},
{
name: "Schedule",
href: "/#schedule",
Expand All @@ -56,6 +50,12 @@ const navBarItems = [
id: "community-partners",
openInNewTab: false,
},
{
name: "Speakers",
href: "/speakers",
id: "speakers",
openInNewTab: false,
},
{
name: "Blog",
href: "https://in.pycon.org/blog/",
Expand Down
12 changes: 9 additions & 3 deletions components/icons.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FaBars, FaBriefcase, FaBuilding, FaCircleArrowRight, FaEnvelope, FaFacebook, FaGithub, FaLocationDot, FaGlobe, FaInstagram, FaLinkedin, FaMastodon, FaTwitter } from "react-icons/fa6";
import { FaWindowClose } from "react-icons/fa";
import { FaBars, FaBriefcase, FaBuilding, FaArrowUpRightFromSquare, FaMaximize, FaCircleArrowRight, FaEnvelope, FaFacebook, FaGithub, FaLocationDot, FaGlobe, FaInstagram, FaLinkedin, FaMastodon, FaTwitter, FaYoutube, FaMedium } from "react-icons/fa6";
import { SiZulip } from "react-icons/si";


export const icons = {
arrowRight: FaCircleArrowRight,
bars: FaBars,
Expand All @@ -12,11 +12,17 @@ export const icons = {
facebook: FaFacebook,
github: FaGithub,
instagram: FaInstagram,
medium: FaMedium,
linkedin: FaLinkedin,
mastodon: FaMastodon,
twitter: FaTwitter,
website: FaGlobe,
zulip: SiZulip
youtube: FaYoutube,
close: FaWindowClose,
maximizeWindow: FaMaximize,
newTab: FaArrowUpRightFromSquare,
zulip: SiZulip,

};

export const IconComponent = ({ ...props }) => {
Expand Down
60 changes: 0 additions & 60 deletions components/keynote.js

This file was deleted.

41 changes: 41 additions & 0 deletions components/nameAvatar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
function getRandomColor() {
// Function to generate a random color
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}


function getContrastColor(backgroundColor) {
// Function to get a contrasting text color based on the background color
const hex = backgroundColor.slice(1);
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
// Use black or white text based on brightness
return brightness > 128 ? '#000000' : '#ffffff';
}

const NameAvatar = (props) => {
const initials = props.name.split(' ').map(word => word[0]).join('').toUpperCase();
const backgroundColor = getRandomColor();
const textColor = getContrastColor(backgroundColor);

return (
<div
className={`name-initial-avatar ${props.className}`}
style={{
fontSize: 50,
color: textColor,
backgroundColor: backgroundColor
}}
>
{initials}
</div>)
};

export default NameAvatar;
50 changes: 34 additions & 16 deletions components/schedule.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Accordion, Card } from "react-bootstrap";
import ScheduleData from "../data/schedule.yml";
import React, { useState } from "react";
import Link from "next/link";

// group by array using a condition, param 1 -> array, param 2 -> function for filtering and grouping
const groupBy = (a, f) => a.reduce((x, c) => (x[f(c)] ??= []).push(c) && x, {});
Expand Down Expand Up @@ -39,9 +40,7 @@ const ConferenceSchedule = () => {
{ScheduleData.map((item, index) => (
<li className="nav-item" role="presentation" key={index}>
<button
className={`nav-link ${
index === selectedTab ? "active" : "nav-link-inactive"
}`}
className={`nav-link ${index === selectedTab ? "active" : "nav-link-inactive"}`}
id={`pills-tab-${index}`}
data-bs-toggle="pill"
data-bs-target={`#pills-${index}`}
Expand All @@ -67,7 +66,7 @@ const ConferenceSchedule = () => {
>
<div className="row">
{currentSchedule.schedule.map((scheduleItem, idx) => {
const { time, image, description, speaker, track } =
const { time, image, topic, speakers, track } =
scheduleItem;
return <ScheduleCard {...scheduleItem} key={idx} />;
})}
Expand Down Expand Up @@ -122,18 +121,30 @@ function ScheduleCard({ image, time, talks }) {
);
}

function ScheduleTalk({ description, speaker, track, size, proposalLink }) {
function ScheduleTalk({ title, speakers, track, size, proposalLink }) {
return (
<>
<div className={`col-${size} ${size == 1 || "text-center"}`}>
<p className="mb-0 date-content">
<a href={proposalLink} target="_blank" rel="noopener noreferrer">
{description}
</a>
{speaker && <span className="ft-weight"> <br /> {speaker} </span>}
{proposalLink ?
<Link
href={proposalLink}
target="_blank"
style={{ textDecoration: "none" }}
>
{title}
</Link> :
<span>{title}</span>
}
{speakers && speakers.map(speaker => (
<span key={speaker.id} className="ft-weight" >
<br />
{speaker.fullName}
</span>
))}
{/* {track && <span className="rt-green text-white">{track}</span>} */}
</p>
</div>
</div >
</>
);
}
Expand All @@ -143,20 +154,27 @@ function ScheduleAccordion({ date, currentSchedule, id, handleTabClick }) {
<Accordion key={id} className="d-block d-lg-none">
<Accordion.Item eventKey={id} onClick={() => handleTabClick(id)}>
<Accordion.Header>{date}</Accordion.Header>
<Accordion.Body style={{padding: "1rem 0rem"}}>
<Accordion.Body style={{ padding: "1rem 0rem" }}>
{currentSchedule.schedule.map((scheduleItem, idx) => {
return scheduleItem.talks.map(talk => {
return (
<Card style={{ margin: '0.8rem 0' }} key={idx}>
<Card.Body>
<Card.Title>
<a href={talk.proposalLink} target="_blank" rel="noopener noreferrer">
{talk.description}
{talk.title}
</a>
<br />
{talk.speaker && <>
<span> By {talk.speaker} </span>
</>}
{talk.speakersPlaceHolder ?
<span>By {talk.speakersPlaceHolder}</span> :
talk.speakers && <span>{"By "}</span>
}
{talk.speakers && talk.speakers.map((speaker, index) => (
<span key={speaker.id} className="ft-weight">
{speaker.fullName}
{index !== 0 && " & "}
</span>
))}
</Card.Title>
<Card.Subtitle className="mb-2 text-muted">{scheduleItem.time}</Card.Subtitle>
{/* <Card.Text>
Expand All @@ -176,7 +194,7 @@ function ScheduleAccordion({ date, currentSchedule, id, handleTabClick }) {
// </div>

// <div className="col-md-7 col-5" key={idx}>
// <p className="mb-0 date-content">{talk.description}</p>
// <p className="mb-0 date-content">{talk.title}</p>
// </div>
// </div>
);
Expand Down
103 changes: 103 additions & 0 deletions components/speakerDetail.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import Image from "next/image";
import Link from "next/link";

import IconComponent from "components/icons";
import NameAvatar from "components/nameAvatar";

const TalkDetail = (speaker) => {
return (
<>
<span>Talk Details:</span>
<ul className="ms-3">
{speaker.proposalLink ?
<li>Addressing: <Link target="_blank" href={speaker.proposalLink}>{speaker.proposalTitle}</Link></li> :
<li>Addressing: {speaker.proposalTitle}</li>
}
{speaker.talkDate && <li>Date: {speaker.talkDate}</li>}
{speaker.talkTime && <li>Time: {speaker.talkTime}</li>}
{speaker.track && <li>Track: {speaker.track}</li>}

</ul>
</>
)
}

const SpeakerDetail = ({ speaker, showHyperLink }) => {
return (
<div>
<div className="container bg-speaker-bio-box">
<div className="row bg-speaker-bio-header w-100">
<div className="d-flex col-12 py-4 justify-content-center">
{speaker.profilePicture ? (
<Image
src={speaker.profilePicture}
alt={`Image of ${speaker.fullName}`}
className="speaker-bio-image"
width={400}
height={400}
priority
/>
) : (
<NameAvatar
className="speaker-bio-image"
name={speaker.fullName}
/>
)}
</div>
<div className="col-12 py-2 text-center">
<h1>{speaker.fullName}</h1>
<p>{speaker.title}</p>
</div>
</div>
<div className="row bg-speaker-bio-about pt-4 px-4">
<p dangerouslySetInnerHTML={{ __html: speaker.about }}></p>
{speaker.proposalTitle &&
<TalkDetail {...speaker} />}
</div>
<div className="d-flex justify-content-between bg-speaker-bio-social py-2 px-4">
<div>
{speaker.social.map((item, index) => (
<span className="me-1" key={index}>
<Link
href={item.link}
target="_blank"
aria-label={`Hyperlink to speaker's ${item.icon} profile.`}
>
<IconComponent
className="my-1"
name={item.platform}
color="#fff"
backgroundColor="1f928d"
/>
</Link>
</span>
))}
</div>
{showHyperLink && (
<div>
<span className="me-1 d-flex flex-end">
<Link
href="/speakers/[id]"
as={`/speakers/${encodeURIComponent(
speaker.id.toLowerCase()
)}`}
style={{ textDecoration: "none" }}
target="_blank"
>
<IconComponent
className="my-1"
name="newTab"
color="#fff"
backgroundColor="1f928d"
/>
</Link>
</span>
</div>
)}
</div>
</div>
</div>
);
};

export default SpeakerDetail;
Loading

0 comments on commit 50c580f

Please sign in to comment.