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

Merge branch 'zhoufang/master/200' into 'master'

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

See merge request !247
parents 09809aa4 aa850f15
Pipeline #1873 passed with stage
in 0 seconds
import React, { useState } from 'react';
import PropTypes from 'prop-types';
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 [showDetails, setShowDetails] = useState(false)
const [showDetails, setShowDetails] = useState(false);
return (
<>
<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="event-card-decoration-dash"></div>
<h3 className="event-card-title">{event.title}</h3>
<p>
<i className={`fa fa-calendar-o fa-lg event-card-calendar-icon event-card-calendar-icon-${event.attendance_type}`} aria-hidden="true" />
{ generateDates(new Date(event.date), new Date(event['end-date'])) }
</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={`event-card type-${event.attendance_type}`}>
<div className="all-margin-auto event-card-title-wrapper event-card-title-wrapper-display-IE">
<div className="event-card-decoration-dash"></div>
<h3 className="event-card-title">{event.title}</h3>
<p>
<i
className={`fa fa-calendar-o fa-lg event-card-calendar-icon event-card-calendar-icon-${event.attendance_type}`}
aria-hidden="true"
/>
{generateDates(new Date(event.date), new Date(event['end-date']))}
</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>
<button className="btn event-card-button" onClick={() => setShowDetails(!showDetails)}>Learn More</button>
</div>
{ showDetails ? <EventDetails event={event} /> : null }
{showDetails ? <EventDetails event={event} /> : null}
</>
)
}
);
};
const EventDetails = ({ event }) => {
return (
<div className="bordered-box event-details">
<div className="margin-bottom-20" data-testid="event-description">{event.description}</div>
{ hasAddress(event) && <div className="margin-bottom-20">Address: { event.address.city + " " + event.address.country } </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>
const EventDetails = ({ event }) => {
return (
<div className="bordered-box event-details">
<div className="margin-bottom-20" data-testid="event-description">
{event.description}
</div>
{hasAddress(event) && (
<div className="margin-bottom-20">Address: {event.address.city + ' ' + event.address.country} </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>
);
};
EventCard.propTypes = {
event: PropTypes.object.isRequired,
}
};
EventDetails.propTypes = {
event: PropTypes.object.isRequired,
}
};
export default EventCard
\ No newline at end of file
export default EventCard;
export const WORKING_GROUPS = [
{
id: "ascii_doc",
name: "AsciiDoc"
id: "eclipse_org",
name: "Eclipse.org"
},
{
id: "ecd_tools",
......@@ -9,15 +9,7 @@ export const WORKING_GROUPS = [
},
{
id: "edge_native",
name: "Edge Native"
},
{
id: "research",
name: "Eclipse Research"
},
{
id: "gemoc_rc",
name: "GEMOC RC"
name: "Eclipse Edge Native"
},
{
id: "eclipse_iot",
......@@ -49,7 +41,11 @@ export const WORKING_GROUPS = [
},
{
id: "openpass",
name: "OpenPass"
name: "OpenPASS"
},
{
id: "osgi",
name: "OSGI"
},
{
id: "science",
......@@ -57,7 +53,7 @@ export const WORKING_GROUPS = [
},
{
id: "sparkplug",
name: "Sparkplug"
name: "Eclipse Sparkplug"
},
{
id: "tangle_ee",
......@@ -68,39 +64,66 @@ export const WORKING_GROUPS = [
name: "Eclipse IDE"
},
{
id: "eclipse_org",
name: "Other"
id: "asciidoc",
name: "AsciiDoc"
},
{
id: "research",
name: "Eclipse Research"
},
{
id: "ospo_zone",
name: "OSPO Zone"
},
{
id: "oniro",
name: "Oniro Project"
}
]
export const EVENT_TYPES = [
{
id: "conference",
name: "Conference"
},
{
id: "dc",
name: "Demo Camps & Stammtisch"
},
{
id: "ec",
name: "EclipseCon"
},
{
id: "ve",
name: "Virtual Events"
id: "et",
name: "Training Series"
},
{
id: "dc",
name: "Demo Camps & Stammtisch"
id: "webinar",
name: "Webinar"
},
{
id: "wg",
name: "Working Group Events"
},
]
export const EVENT_ATTENDANCE_TYPE = [
{
id: "et",
name: "Training Series"
id: "virtual",
name: "Virtual",
},
{
id: "ee",
name: "Other interesting Events"
id: "in_person",
name: "In Person",
},
{
id: "hybrid",
name: "Hybrid",
},
]
export const EVENT_ATTENDANCE_TYPE = [
export const EVENT_PARTICIPATION_TYPE = [
{
id: "ef_event",
name: "Eclipse Foundation Events"
......@@ -109,6 +132,10 @@ export const EVENT_ATTENDANCE_TYPE = [
id: "ef_attending",
name: "Events we are attending"
},
{
id: "member_org_participating",
name: "Member organization participating"
},
{
id: "other",
name: "Other community events of interest"
......@@ -195,16 +222,16 @@ export function hasSelectedItems(items) {
}
export function getUrl(page, searchParas, groupParas, typeParas) {
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]
}
for (let j=0; j<typeParas.length; j++) {
url = url + "&parameters[type][]=" + typeParas[j]
}
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`;
groupParas && groupParas.forEach((item) => (url = url + '&parameters[publish_to][]=' + item));
typeParas && typeParas.forEach((item) => (url = url + '&parameters[type][]=' + item));
participationParas && participationParas.forEach((item) => (url = url + '&parameters[ef_participation][]=' + item));
attendanceParas && attendanceParas.forEach((item) => (url = url + '&parameters[attendance_type][]=' + item));
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';
import CustomSearch from './CustomSearch';
import EventsCheckboxFilters from './pastAndUpcomingEvents/EventsCheckboxFilters';
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 [triggerSearchValue, setTriggerSearchValue] = useState("")
const [checkedWorkingGroups, setCheckedWorkingGroups] = useState({})
const [checkedTypes, setCheckedTypes] = useState({})
const [upcomingReachEnd, setUpcomingReachEnd] = useState(false)
const [triggerSearchValue, setTriggerSearchValue] = useState('');
const [checkedWorkingGroups, setCheckedWorkingGroups] = useState({});
const [checkedTypes, setCheckedTypes] = useState({});
const [checkedAttendance, setCheckedAttendance] = useState({});
const [checkedParticipation, setCheckedParticipation] = useState({});
const [upcomingReachEnd, setUpcomingReachEnd] = useState(false);
return (
<>
<div className="container">
<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} />
<EventsCheckboxFilters
checkedTypes={checkedTypes}
setCheckedTypes={setCheckedTypes}
checkedBoxes={checkedTypes}
setCheckedBoxes={setCheckedTypes}
filterOptions={EVENT_TYPES}
filterGroupTitle="EVENT TYPE"
/>
<EventsCheckboxFilters
checkedWorkingGroups={checkedWorkingGroups}
setCheckedWorkingGroups={setCheckedWorkingGroups}
checkedBoxes={checkedAttendance}
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 className="col-md-18 event-list-wrapper">
<EventsDataFetcher
eventTime="upcoming"
searchValue={triggerSearchValue}
checkedWorkingGroups={checkedWorkingGroups}
checkedTypes={checkedTypes}
reachEnd={upcomingReachEnd}
setReachEnd={setUpcomingReachEnd}
/>
<EventsDataFetcher
eventTime="upcoming"
searchValue={triggerSearchValue}
checkedWorkingGroups={checkedWorkingGroups}
checkedTypes={checkedTypes}
checkedAttendance={checkedAttendance}
checkedParticipation={checkedParticipation}
reachEnd={upcomingReachEnd}
setReachEnd={setUpcomingReachEnd}
/>
</div>
</div>
</div>
</>
)
}
);
};
export default Wrapper
\ No newline at end of file
export default Wrapper;
......@@ -91,7 +91,7 @@ const EventLists = ({ events, isFetchingMore, fetchMore, reachEnd }) => {
) : (
!isFetchingMore && (
<button
className="btn btn-primary margin-top-10"
className="btn btn-primary margin-top-30 margin-bottom-20"
onClick={fetchMore}
>
Load More
......
import React, { useState } from 'react';
import Checkbox from '../Checkbox';
import { EVENT_TYPES, WORKING_GROUPS, alphaOrder } from '../EventHelpers';
import { alphaOrder } from '../EventHelpers';
import PropTypes from 'prop-types';
const EventsCheckboxFilters = ({
checkedTypes,
setCheckedTypes,
checkedWorkingGroups,
setCheckedWorkingGroups
}) => {
const EventsCheckboxFilters = ({ checkedBoxes, setCheckedBoxes, filterOptions, filterGroupTitle }) => {
const determineInitialState = () => {
return window.innerWidth > 991
}
return window.innerWidth > 991;
};
const [showTypes, setShowTypes] = useState(determineInitialState())
const [showWorkingGroups, setShowWorkingGroups] = useState(determineInitialState())
const [showTypes, setShowTypes] = useState(determineInitialState());
const handleChange = (e) => {
if (checkedWorkingGroups && setCheckedWorkingGroups) {
if (e.target.checked) {
setCheckedWorkingGroups({
...checkedWorkingGroups,
[e.target.name]: e.target.checked
});
} else {
setCheckedWorkingGroups({
...checkedWorkingGroups,
[e.target.name]: undefined
})
}
if (e.target.checked) {
setCheckedBoxes({
...checkedBoxes,
[e.target.name]: e.target.checked,
});
} else {
setCheckedBoxes({
...checkedBoxes,
[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 = () => {
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>
}
</>
)
}
}
setShowTypes(!showTypes);
};
return (
<div className="margin-bottom-10">
{renderFilterComponent(checkedTypes, setCheckedTypes, showTypes, toggleTypes, alphaOrder(EVENT_TYPES), "EVENT TYPE")}
{renderFilterComponent(checkedWorkingGroups, setCheckedWorkingGroups, showWorkingGroups, toggleWorkingGroups, alphaOrder(WORKING_GROUPS), "CATEGORIES")}
<button onClick={toggleTypes} className="event-filter-title">
{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>
)}
</div>
)
}
);
};
EventsCheckboxFilters.propTypes = {
checkedTypes: PropTypes.object,
setCheckedTypes: PropTypes.func,
checkedWorkingGroups: PropTypes.object,
setCheckedWorkingGroups: PropTypes.func
}
checkedBoxes: PropTypes.object,
setCheckedBoxes: PropTypes.func,
};
export default EventsCheckboxFilters
\ No newline at end of file
export default EventsCheckboxFilters;
......@@ -6,96 +6,129 @@ import { hasSelectedItems, getUrl } from '../EventHelpers';
import PropTypes from 'prop-types';
import Loading from '../Loading';
const EventsDataFetcher = ({ searchValue, checkedWorkingGroups, checkedTypes, reachEnd, setReachEnd }) => {
const EventsDataFetcher = ({
searchValue,
checkedWorkingGroups,
checkedTypes,
checkedAttendance,
checkedParticipation,
reachEnd,
setReachEnd,
}) => {
const [error, setError] = useState(null);
const [isFetchingMore, setIsFetchingMore] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [error, setError] = useState(null)
const [isFetchingMore, setIsFetchingMore] = useState(false)
const [currentPage, setCurrentPage] = useState(1)
const [isLoading, setIsLoading] = useState(true)
const [eventsData, setEventsData] = useState([])
const fetchingDataWithParas = (signal, page, searchParas, groupParas, typeParas, forceUpdate) => {
const [isLoading, setIsLoading] = useState(true);
const [eventsData, setEventsData] = useState([]);
const fetchingDataWithParas = (
signal,
page,
searchParas,
groupParas,
typeParas,
attendanceParas,
participation,
forceUpdate
) => {
if (page === 1) {
setIsLoading(true);
}
let url = getUrl(page, searchParas, groupParas, typeParas)
fetch(url, {signal: signal})
.then((res) => res.json())
.then(
(result) => {
let url = getUrl(page, searchParas, groupParas, typeParas, attendanceParas, participation);
fetch(url, { signal: signal })
.then((res) => res.json())
.then((result) => {
if (result.events.length < 10) {
setReachEnd(true)
setReachEnd(true);
if (forceUpdate) {
setEventsData(result.events)