Commit aa850f15 authored by Zhou Fang's avatar Zhou Fang
Browse files

#200 Added new icons and logic, updated filter and legend related components and content

parent 09809aa4
import React, { useState } from 'react'; import React, { useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { hasAddress, generateDates, generateTimes } from './EventHelpers'; import { hasAddress, generateDates, generateTimes } from './EventHelpers';
import inPersonIcon from '../../static/images/in_person_icon.svg';
import virtualIcon from '../../static/images/virtual_icon.svg';
const EventCard = ({ event }) => { const EventCard = ({ event }) => {
const [showDetails, setShowDetails] = useState(false) const [showDetails, setShowDetails] = useState(false);
return ( return (
<> <>
<div className={`event-card type-${event.attendance_type}`}> <div className={`event-card type-${event.attendance_type}`}>
<div className="all-margin-auto event-card-title-wrapper event-card-title-wrapper-display-IE"> <div className="all-margin-auto event-card-title-wrapper event-card-title-wrapper-display-IE">
<div className="event-card-decoration-dash"></div> <div className="event-card-decoration-dash"></div>
<h3 className="event-card-title">{event.title}</h3> <h3 className="event-card-title">{event.title}</h3>
<p> <p>
<i className={`fa fa-calendar-o fa-lg event-card-calendar-icon event-card-calendar-icon-${event.attendance_type}`} aria-hidden="true" /> <i
{ generateDates(new Date(event.date), new Date(event['end-date'])) } className={`fa fa-calendar-o fa-lg event-card-calendar-icon event-card-calendar-icon-${event.attendance_type}`}
</p> aria-hidden="true"
<p> />
<i className={`fa fa-clock-o fa-lg event-card-calendar-icon event-card-calendar-icon-${event.attendance_type}`} aria-hidden="true"></i> {generateDates(new Date(event.date), new Date(event['end-date']))}
{ generateTimes(new Date(event.date), new Date(event['end-date'])) } </p>
</p> <p>
<i
className={`fa fa-clock-o fa-lg event-card-calendar-icon event-card-calendar-icon-${event.attendance_type}`}
aria-hidden="true"
></i>
{generateTimes(new Date(event.date), new Date(event['end-date']))}
</p>
<div className="margin-top-5">
{(event.attendance_type === 'in_person' || event.attendance_type === 'hybrid') && (
<img className="margin-right-5 margin-left-5" src={inPersonIcon} width={40} alt='person event icon' />
)}
{(event.attendance_type === 'virtual' || event.attendance_type === 'hybrid') && (
<img className="margin-right-5 margin-left-5" src={virtualIcon} width={40} alt='virtual event icon' />
)}
</div>
</div>
<button className="btn event-card-button" onClick={() => setShowDetails(!showDetails)}>
Learn More
</button>
</div> </div>
<button className="btn event-card-button" onClick={() => setShowDetails(!showDetails)}>Learn More</button> {showDetails ? <EventDetails event={event} /> : null}
</div>
{ showDetails ? <EventDetails event={event} /> : null }
</> </>
) );
} };
const EventDetails = ({ event }) => { const EventDetails = ({ event }) => {
return (
return ( <div className="bordered-box event-details">
<div className="bordered-box event-details"> <div className="margin-bottom-20" data-testid="event-description">
<div className="margin-bottom-20" data-testid="event-description">{event.description}</div> {event.description}
{ hasAddress(event) && <div className="margin-bottom-20">Address: { event.address.city + " " + event.address.country } </div>} </div>
<div className="text-center"> {hasAddress(event) && (
{ (event.infoLink) && <a className="btn btn-default event-btn-more margin-right-20" href={event.infoLink} target="_blank">More Info</a> } <div className="margin-bottom-20">Address: {event.address.city + ' ' + event.address.country} </div>
{ (event.registration) && <a className="btn btn-primary" href={event.registration} target="_blank">Register</a> } )}
</div> <div className="text-center">
{event.infoLink && (
<a className="btn btn-default event-btn-more margin-right-20" href={event.infoLink} target="_blank">
More Info
</a>
)}
{event.registration && (
<a className="btn btn-primary" href={event.registration} target="_blank">
Register
</a>
)}
</div> </div>
) </div>
} );
};
EventCard.propTypes = { EventCard.propTypes = {
event: PropTypes.object.isRequired, event: PropTypes.object.isRequired,
} };
EventDetails.propTypes = { EventDetails.propTypes = {
event: PropTypes.object.isRequired, event: PropTypes.object.isRequired,
} };
export default EventCard export default EventCard;
\ No newline at end of file
export const WORKING_GROUPS = [ export const WORKING_GROUPS = [
{ {
id: "ascii_doc", id: "eclipse_org",
name: "AsciiDoc" name: "Eclipse.org"
}, },
{ {
id: "ecd_tools", id: "ecd_tools",
...@@ -9,15 +9,7 @@ export const WORKING_GROUPS = [ ...@@ -9,15 +9,7 @@ export const WORKING_GROUPS = [
}, },
{ {
id: "edge_native", id: "edge_native",
name: "Edge Native" name: "Eclipse Edge Native"
},
{
id: "research",
name: "Eclipse Research"
},
{
id: "gemoc_rc",
name: "GEMOC RC"
}, },
{ {
id: "eclipse_iot", id: "eclipse_iot",
...@@ -49,7 +41,11 @@ export const WORKING_GROUPS = [ ...@@ -49,7 +41,11 @@ export const WORKING_GROUPS = [
}, },
{ {
id: "openpass", id: "openpass",
name: "OpenPass" name: "OpenPASS"
},
{
id: "osgi",
name: "OSGI"
}, },
{ {
id: "science", id: "science",
...@@ -57,7 +53,7 @@ export const WORKING_GROUPS = [ ...@@ -57,7 +53,7 @@ export const WORKING_GROUPS = [
}, },
{ {
id: "sparkplug", id: "sparkplug",
name: "Sparkplug" name: "Eclipse Sparkplug"
}, },
{ {
id: "tangle_ee", id: "tangle_ee",
...@@ -68,39 +64,66 @@ export const WORKING_GROUPS = [ ...@@ -68,39 +64,66 @@ export const WORKING_GROUPS = [
name: "Eclipse IDE" name: "Eclipse IDE"
}, },
{ {
id: "eclipse_org", id: "asciidoc",
name: "Other" name: "AsciiDoc"
},
{
id: "research",
name: "Eclipse Research"
},
{
id: "ospo_zone",
name: "OSPO Zone"
},
{
id: "oniro",
name: "Oniro Project"
} }
] ]
export const EVENT_TYPES = [ export const EVENT_TYPES = [
{
id: "conference",
name: "Conference"
},
{
id: "dc",
name: "Demo Camps & Stammtisch"
},
{ {
id: "ec", id: "ec",
name: "EclipseCon" name: "EclipseCon"
}, },
{ {
id: "ve", id: "et",
name: "Virtual Events" name: "Training Series"
}, },
{ {
id: "dc", id: "webinar",
name: "Demo Camps & Stammtisch" name: "Webinar"
}, },
{ {
id: "wg", id: "wg",
name: "Working Group Events" name: "Working Group Events"
}, },
]
export const EVENT_ATTENDANCE_TYPE = [
{ {
id: "et", id: "virtual",
name: "Training Series" name: "Virtual",
}, },
{ {
id: "ee", id: "in_person",
name: "Other interesting Events" name: "In Person",
},
{
id: "hybrid",
name: "Hybrid",
}, },
] ]
export const EVENT_ATTENDANCE_TYPE = [ export const EVENT_PARTICIPATION_TYPE = [
{ {
id: "ef_event", id: "ef_event",
name: "Eclipse Foundation Events" name: "Eclipse Foundation Events"
...@@ -109,6 +132,10 @@ export const EVENT_ATTENDANCE_TYPE = [ ...@@ -109,6 +132,10 @@ export const EVENT_ATTENDANCE_TYPE = [
id: "ef_attending", id: "ef_attending",
name: "Events we are attending" name: "Events we are attending"
}, },
{
id: "member_org_participating",
name: "Member organization participating"
},
{ {
id: "other", id: "other",
name: "Other community events of interest" name: "Other community events of interest"
...@@ -195,16 +222,16 @@ export function hasSelectedItems(items) { ...@@ -195,16 +222,16 @@ export function hasSelectedItems(items) {
} }
export function getUrl(page, searchParas, groupParas, typeParas) { export function getUrl(page, searchParas, groupParas, typeParas, attendanceParas, participationParas) {
let url = `https://newsroom.eclipse.org/api/events?&page=${page}&pagesize=10&options[orderby][field_event_date]=custom` let url = `https://newsroom.eclipse.org/api/events?&page=${page}&pagesize=10&options[orderby][field_event_date]=custom`;
for (let i=0; i<groupParas.length; i++) {
url = url + "&parameters[publish_to][]=" + groupParas[i] groupParas && groupParas.forEach((item) => (url = url + '&parameters[publish_to][]=' + item));
} typeParas && typeParas.forEach((item) => (url = url + '&parameters[type][]=' + item));
for (let j=0; j<typeParas.length; j++) { participationParas && participationParas.forEach((item) => (url = url + '&parameters[ef_participation][]=' + item));
url = url + "&parameters[type][]=" + typeParas[j] attendanceParas && attendanceParas.forEach((item) => (url = url + '&parameters[attendance_type][]=' + item));
}
if (searchParas) { if (searchParas) {
url = url + "&parameters[search]=" + searchParas url = url + '&parameters[search]=' + searchParas;
} }
return url return url;
} }
...@@ -2,48 +2,67 @@ import React, { useState } from 'react'; ...@@ -2,48 +2,67 @@ import React, { useState } from 'react';
import CustomSearch from './CustomSearch'; import CustomSearch from './CustomSearch';
import EventsCheckboxFilters from './pastAndUpcomingEvents/EventsCheckboxFilters'; import EventsCheckboxFilters from './pastAndUpcomingEvents/EventsCheckboxFilters';
import EventsDataFetcher from './pastAndUpcomingEvents/EventsDataFetcher'; import EventsDataFetcher from './pastAndUpcomingEvents/EventsDataFetcher';
import Legend from "./Legend"; import { EVENT_ATTENDANCE_TYPE, EVENT_PARTICIPATION_TYPE, EVENT_TYPES, WORKING_GROUPS } from './EventHelpers';
const Wrapper = () => { const Wrapper = () => {
const [triggerSearchValue, setTriggerSearchValue] = useState('');
const [triggerSearchValue, setTriggerSearchValue] = useState("") const [checkedWorkingGroups, setCheckedWorkingGroups] = useState({});
const [checkedWorkingGroups, setCheckedWorkingGroups] = useState({}) const [checkedTypes, setCheckedTypes] = useState({});
const [checkedTypes, setCheckedTypes] = useState({}) const [checkedAttendance, setCheckedAttendance] = useState({});
const [checkedParticipation, setCheckedParticipation] = useState({});
const [upcomingReachEnd, setUpcomingReachEnd] = useState(false) const [upcomingReachEnd, setUpcomingReachEnd] = useState(false);
return ( return (
<> <>
<div className="container"> <div className="container">
<div className="row margin-bottom-20"> <div className="row margin-bottom-20">
<div className="col-md-6"> <div className="col-md-6 margin-top-20">
<a className="btn btn-primary" href="https://newsroom.eclipse.org/node/add/events">
Submit Your Event
</a>
<CustomSearch triggerSearchValue={triggerSearchValue} setTriggerSearchValue={setTriggerSearchValue} /> <CustomSearch triggerSearchValue={triggerSearchValue} setTriggerSearchValue={setTriggerSearchValue} />
<EventsCheckboxFilters <EventsCheckboxFilters
checkedTypes={checkedTypes} checkedBoxes={checkedTypes}
setCheckedTypes={setCheckedTypes} setCheckedBoxes={setCheckedTypes}
filterOptions={EVENT_TYPES}
filterGroupTitle="EVENT TYPE"
/> />
<EventsCheckboxFilters <EventsCheckboxFilters
checkedWorkingGroups={checkedWorkingGroups} checkedBoxes={checkedAttendance}
setCheckedWorkingGroups={setCheckedWorkingGroups} setCheckedBoxes={setCheckedAttendance}
filterOptions={EVENT_ATTENDANCE_TYPE}
filterGroupTitle="Attendance"
/>
<EventsCheckboxFilters
checkedBoxes={checkedParticipation}
setCheckedBoxes={setCheckedParticipation}
filterOptions={EVENT_PARTICIPATION_TYPE}
filterGroupTitle="PARTICIPATION"
/>
<EventsCheckboxFilters
checkedBoxes={checkedWorkingGroups}
setCheckedBoxes={setCheckedWorkingGroups}
filterOptions={WORKING_GROUPS}
filterGroupTitle="CATEGORIES"
/> />
<a className="btn btn-primary" href="https://newsroom.eclipse.org/node/add/events">Submit Your Event</a>
<Legend />
</div> </div>
<div className="col-md-18 event-list-wrapper"> <div className="col-md-18 event-list-wrapper">
<EventsDataFetcher <EventsDataFetcher
eventTime="upcoming" eventTime="upcoming"
searchValue={triggerSearchValue} searchValue={triggerSearchValue}
checkedWorkingGroups={checkedWorkingGroups} checkedWorkingGroups={checkedWorkingGroups}
checkedTypes={checkedTypes} checkedTypes={checkedTypes}
reachEnd={upcomingReachEnd} checkedAttendance={checkedAttendance}
setReachEnd={setUpcomingReachEnd} checkedParticipation={checkedParticipation}
/> reachEnd={upcomingReachEnd}
setReachEnd={setUpcomingReachEnd}
/>
</div> </div>
</div> </div>
</div> </div>
</> </>
) );
} };
export default Wrapper export default Wrapper;
\ No newline at end of file
...@@ -91,7 +91,7 @@ const EventLists = ({ events, isFetchingMore, fetchMore, reachEnd }) => { ...@@ -91,7 +91,7 @@ const EventLists = ({ events, isFetchingMore, fetchMore, reachEnd }) => {
) : ( ) : (
!isFetchingMore && ( !isFetchingMore && (
<button <button
className="btn btn-primary margin-top-10" className="btn btn-primary margin-top-30 margin-bottom-20"
onClick={fetchMore} onClick={fetchMore}
> >
Load More Load More
......
import React, { useState } from 'react'; import React, { useState } from 'react';
import Checkbox from '../Checkbox'; import Checkbox from '../Checkbox';
import { EVENT_TYPES, WORKING_GROUPS, alphaOrder } from '../EventHelpers'; import { alphaOrder } from '../EventHelpers';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const EventsCheckboxFilters = ({ const EventsCheckboxFilters = ({ checkedBoxes, setCheckedBoxes, filterOptions, filterGroupTitle }) => {
checkedTypes,
setCheckedTypes,
checkedWorkingGroups,
setCheckedWorkingGroups
}) => {
const determineInitialState = () => { const determineInitialState = () => {
return window.innerWidth > 991 return window.innerWidth > 991;
} };
const [showTypes, setShowTypes] = useState(determineInitialState()) const [showTypes, setShowTypes] = useState(determineInitialState());
const [showWorkingGroups, setShowWorkingGroups] = useState(determineInitialState())
const handleChange = (e) => { const handleChange = (e) => {
if (checkedWorkingGroups && setCheckedWorkingGroups) { if (e.target.checked) {
if (e.target.checked) { setCheckedBoxes({
setCheckedWorkingGroups({ ...checkedBoxes,
...checkedWorkingGroups, [e.target.name]: e.target.checked,
[e.target.name]: e.target.checked });
}); } else {
} else { setCheckedBoxes({
setCheckedWorkingGroups({ ...checkedBoxes,
...checkedWorkingGroups, [e.target.name]: undefined,
[e.target.name]: undefined });
})
}
} }
};
if (checkedTypes && setCheckedTypes) {
if (e.target.checked) {
setCheckedTypes({
...checkedTypes,
[e.target.name]: e.target.checked
});
} else {
setCheckedTypes({
...checkedTypes,
[e.target.name]: undefined
})
}
}
}
const toggleTypes = () => { const toggleTypes = () => {
setShowTypes(!showTypes) setShowTypes(!showTypes);
} };
const toggleWorkingGroups = () => {
setShowWorkingGroups(!showWorkingGroups)
}
function renderFilterComponent(checkedFilter, filterCheckFunc, filterShowingState, filterShowingFunc, filterDataArray, filterTypeName) {
if (checkedFilter && filterCheckFunc) {
return (
<>
<button
onClick={filterShowingFunc}
className="event-filter-title"
>
{ filterTypeName }
<i className="fa fa-angle-down event-filter-expandable-icon" aria-hidden="true"></i>
</button>
{ filterShowingState &&
<ul className="event-filter-checkbox-list">
{ filterDataArray.map(item => (
<li key={item.id}>
<label key={item.id}>
<Checkbox
name={item.id}
checked={checkedFilter[item.id]}
onChange={handleChange}
/>
{item.name}
</label>
</li>
))}
</ul>
}
</>
)
}
}
return ( return (
<div className="margin-bottom-10"> <div className="margin-bottom-10">
{renderFilterComponent(checkedTypes, setCheckedTypes, showTypes, toggleTypes, alphaOrder(EVENT_TYPES), "EVENT TYPE")} <button onClick={toggleTypes} className="event-filter-title">
{renderFilterComponent(checkedWorkingGroups, setCheckedWorkingGroups, showWorkingGroups, toggleWorkingGroups, alphaOrder(WORKING_GROUPS), "CATEGORIES")} {filterGroupTitle}
<i className="fa fa-angle-down event-filter-expandable-icon" aria-hidden="true"></i>
</button>
{showTypes && (
<ul className="event-filter-checkbox-list">
{alphaOrder(filterOptions).map((item) => (
<li key={item.id}>
<label key={item.id}>
<Checkbox name={item.id} checked={checkedBoxes[item.id]} onChange={handleChange} />
{item.name}
</label>
</li>
))}
</ul>
)}