@openeventkit/event-site 2.1.18 → 2.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/jest.yml +1 -1
- package/babel.config.json +9 -9
- package/gatsby-node.js +67 -125
- package/jest.setup.js +2 -0
- package/netlify.toml +1 -1
- package/package.json +25 -16
- package/src/__mocks__/@mdx-js/mdx.js +32 -0
- package/src/__mocks__/@mdx-js/react.js +15 -0
- package/src/__mocks__/rehype-external-links.js +3 -0
- package/src/__mocks__/remark-gfm.js +3 -0
- package/src/actions/fetch-entities-actions.js +45 -87
- package/src/actions/update-data-actions.js +2 -2
- package/src/actions/user-actions.js +578 -430
- package/src/cms/config/collections/configurationsCollection/siteSettings/index.js +2 -0
- package/src/cms/config/collections/configurationsCollection/siteSettings/typeDefs.js +10 -0
- package/src/cms/preview-templates/ContentPagePreview.js +27 -29
- package/src/components/AvatarEditorModal/index.js +10 -0
- package/src/components/CertificatePDF.js +313 -0
- package/src/components/CertificateSection.js +139 -0
- package/src/components/FullSchedule.js +83 -66
- package/src/components/Mdx.js +39 -0
- package/src/components/__tests__/Mdx.test.jsx +70 -0
- package/src/content/site-settings/index.json +1 -1
- package/src/content/sponsors.json +1 -1
- package/src/i18n/locales/en.json +9 -1
- package/src/pages/a/[...].js +3 -0
- package/src/reducers/user-reducer.js +89 -27
- package/src/routes/authorization-callback-route.js +20 -2
- package/src/styles/rsvp-page.module.scss +63 -0
- package/src/templates/full-profile-page.js +61 -2
- package/src/templates/marketing-page-template/MainColumn.js +40 -42
- package/src/templates/rsvp-page.js +144 -0
- package/src/utils/alerts.js +1 -1
- package/src/utils/build-json/BaseAPIRequest.js +25 -0
- package/src/utils/build-json/EventsAPIRequest.js +171 -0
- package/src/utils/build-json/SpeakersAPIRequest.js +62 -0
- package/src/utils/build-json/SummitAPIRequest.js +115 -0
- package/src/utils/build-json/constants.js +5 -0
- package/src/utils/certificateSettings.js +45 -0
- package/src/utils/customErrorHandler.js +40 -1
- package/src/utils/rsvpConstants.js +7 -0
- package/src/utils/useMarketingSettings.js +48 -1
- package/src/utils/useSiteSettings.js +11 -0
- package/src/workers/feeds.worker.js +85 -90
- package/src/workers/sync_strategies/activity_synch_strategy.js +147 -102
- package/src/workers/sync_strategies/speaker_synch_strategy.js +3 -3
- package/src/workers/sync_strategies/track_synch_strategy.js +149 -48
- package/src/workers/synch.worker.js +123 -88
- package/static/fonts/fonts.css +120 -20
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-200.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-200italic.ttf +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-200italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-300.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-300italic.ttf +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-300italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-400.ttf +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-400.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-400italic.ttf +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-400italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-500.ttf +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-500.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-500italic.ttf +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-500italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-600.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-600italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-700.ttf +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-700.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-700italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-800.ttf +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-800.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-800italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-900.ttf +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-900.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v18-latin-900italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-300.woff +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-300.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-300italic.woff +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-300italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-600.woff +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-600.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-600italic.woff +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-600italic.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-700.woff +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-700.woff2 +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-700italic.woff +0 -0
- package/static/fonts/nunito-sans/nunito-sans-v12-latin-700italic.woff2 +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
.speakerWrapper {
|
|
2
|
+
color: var(--color_text_dark);
|
|
3
|
+
display: flex;
|
|
4
|
+
margin-top: 15px;
|
|
5
|
+
|
|
6
|
+
.name {
|
|
7
|
+
width: max-content;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.moderator {
|
|
11
|
+
font-size: 12px;
|
|
12
|
+
margin-top: -2px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.job {
|
|
16
|
+
font-size: 10px;
|
|
17
|
+
|
|
18
|
+
.company {
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
text-overflow: ellipsis;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
font-size: inherit;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.speaker {
|
|
27
|
+
height: 55px;
|
|
28
|
+
margin: 0 15px 15px 0;
|
|
29
|
+
border-radius: 4px;
|
|
30
|
+
display: flex;
|
|
31
|
+
flex: 1 0 auto;
|
|
32
|
+
|
|
33
|
+
.picWrapper {
|
|
34
|
+
height: 100%;
|
|
35
|
+
width: 55px;
|
|
36
|
+
|
|
37
|
+
.pic {
|
|
38
|
+
height: 100%;
|
|
39
|
+
width: 100%;
|
|
40
|
+
background-size: cover;
|
|
41
|
+
border-radius: 50%;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.nameWrapper {
|
|
46
|
+
background-color: var(--color_background_light);
|
|
47
|
+
height: 100%;
|
|
48
|
+
padding: 5px;
|
|
49
|
+
border-radius: 0 4px 4px 0;
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
flex: 1 0;
|
|
52
|
+
min-width: 150px;
|
|
53
|
+
max-width: 300px;
|
|
54
|
+
margin-left: 5px;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.buttonWrapper {
|
|
60
|
+
margin-top: 15px;
|
|
61
|
+
display: flex;
|
|
62
|
+
gap: 15px;
|
|
63
|
+
}
|
|
@@ -17,6 +17,11 @@ import LiteScheduleComponent from '../components/LiteScheduleComponent'
|
|
|
17
17
|
import AvatarEditorModal from '../components/AvatarEditorModal'
|
|
18
18
|
import ChangePasswordComponent from '../components/ChangePasswordComponent';
|
|
19
19
|
import AccessTracker from "../components/AttendeeToAttendeeWidgetComponent";
|
|
20
|
+
import CertificateSection from '../components/CertificateSection';
|
|
21
|
+
import useMarketingSettings from '../utils/useMarketingSettings';
|
|
22
|
+
import { MARKETING_SETTINGS_KEYS, DISPLAY_OPTIONS } from '../utils/useMarketingSettings';
|
|
23
|
+
import { getAccessTokenSafely } from '../utils/loginUtils';
|
|
24
|
+
import { getEnvVariable, SUMMIT_API_BASE_URL } from '../utils/envVariables';
|
|
20
25
|
|
|
21
26
|
import { updateProfilePicture, updateProfile, getIDPProfile, updatePassword } from '../actions/user-actions'
|
|
22
27
|
|
|
@@ -24,9 +29,12 @@ import styles from '../styles/full-profile.module.scss'
|
|
|
24
29
|
|
|
25
30
|
import "openstack-uicore-foundation/lib/css/components/inputs/datetimepicker.css";
|
|
26
31
|
|
|
27
|
-
export const FullProfilePageTemplate = ({ user, getIDPProfile, updateProfile, updateProfilePicture, updatePassword }) => {
|
|
32
|
+
export const FullProfilePageTemplate = ({ user, getIDPProfile, updateProfile, updateProfilePicture, updatePassword, summit }) => {
|
|
28
33
|
|
|
29
34
|
const [showProfile, setShowProfile] = useState(false);
|
|
35
|
+
const [freshTickets, setFreshTickets] = useState([]);
|
|
36
|
+
const [ticketsFetched, setTicketsFetched] = useState(false);
|
|
37
|
+
const { getSettingByKey } = useMarketingSettings();
|
|
30
38
|
const [personalProfile, setPersonalProfile] = useState({
|
|
31
39
|
firstName: '',
|
|
32
40
|
lastName: '',
|
|
@@ -193,6 +201,51 @@ export const FullProfilePageTemplate = ({ user, getIDPProfile, updateProfile, up
|
|
|
193
201
|
navigate('/a/my-schedule')
|
|
194
202
|
};
|
|
195
203
|
|
|
204
|
+
// Fetch fresh tickets for certificate validation
|
|
205
|
+
const fetchFreshTickets = async () => {
|
|
206
|
+
try {
|
|
207
|
+
const accessToken = await getAccessTokenSafely();
|
|
208
|
+
if (!accessToken || !summit) return;
|
|
209
|
+
|
|
210
|
+
const params = new URLSearchParams({
|
|
211
|
+
access_token: accessToken,
|
|
212
|
+
fields: 'id,status',
|
|
213
|
+
expand: 'owner'
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const apiBaseUrl = getEnvVariable(SUMMIT_API_BASE_URL);
|
|
217
|
+
const url = `${apiBaseUrl}/api/v1/summits/${summit.id}/orders/all/tickets/me?${params}`;
|
|
218
|
+
|
|
219
|
+
const response = await fetch(url);
|
|
220
|
+
|
|
221
|
+
if (response.ok) {
|
|
222
|
+
const data = await response.json();
|
|
223
|
+
setFreshTickets(data.data || []);
|
|
224
|
+
}
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.error('Profile page - Error fetching tickets:', err);
|
|
227
|
+
} finally {
|
|
228
|
+
setTicketsFetched(true);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
if (summit && user.idpProfile && !ticketsFetched) {
|
|
234
|
+
fetchFreshTickets();
|
|
235
|
+
}
|
|
236
|
+
}, [summit, user.idpProfile, ticketsFetched]);
|
|
237
|
+
|
|
238
|
+
// Check if certificates are enabled and user has checked-in tickets
|
|
239
|
+
const certificatesEnabled = getSettingByKey(MARKETING_SETTINGS_KEYS.certificateEnabled) !== DISPLAY_OPTIONS.hide;
|
|
240
|
+
const checkedInTickets = freshTickets.filter(ticket => {
|
|
241
|
+
const isCheckedIn = ticket.owner?.summit_hall_checked_in === true;
|
|
242
|
+
console.log(ticket)
|
|
243
|
+
const isValidTicket = ticket.status === 'Paid';
|
|
244
|
+
return isCheckedIn && isValidTicket;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const showCertificate = certificatesEnabled && checkedInTickets.length > 0;
|
|
248
|
+
|
|
196
249
|
const discardChanges = (state) => {
|
|
197
250
|
switch (state) {
|
|
198
251
|
case 'profile':
|
|
@@ -264,6 +317,9 @@ export const FullProfilePageTemplate = ({ user, getIDPProfile, updateProfile, up
|
|
|
264
317
|
<h4>
|
|
265
318
|
@{user.idpProfile?.nickname}
|
|
266
319
|
</h4>
|
|
320
|
+
{showCertificate && (
|
|
321
|
+
<CertificateSection freshTickets={freshTickets} />
|
|
322
|
+
)}
|
|
267
323
|
<ChangePasswordComponent updatePassword={handlePasswordUpdate} />
|
|
268
324
|
</div>
|
|
269
325
|
<div className="column">
|
|
@@ -661,6 +717,7 @@ const FullProfilePage = (
|
|
|
661
717
|
{
|
|
662
718
|
location,
|
|
663
719
|
user,
|
|
720
|
+
summit,
|
|
664
721
|
getIDPProfile,
|
|
665
722
|
updateProfile,
|
|
666
723
|
updateProfilePicture,
|
|
@@ -671,6 +728,7 @@ const FullProfilePage = (
|
|
|
671
728
|
<Layout location={location}>
|
|
672
729
|
<OrchestedTemplate
|
|
673
730
|
user={user}
|
|
731
|
+
summit={summit}
|
|
674
732
|
getIDPProfile={getIDPProfile}
|
|
675
733
|
updateProfile={updateProfile}
|
|
676
734
|
updateProfilePicture={updateProfilePicture}
|
|
@@ -695,8 +753,9 @@ FullProfilePageTemplate.propTypes = {
|
|
|
695
753
|
updatePassword: PropTypes.func
|
|
696
754
|
};
|
|
697
755
|
|
|
698
|
-
const mapStateToProps = ({ userState }) => ({
|
|
756
|
+
const mapStateToProps = ({ userState, summitState }) => ({
|
|
699
757
|
user: userState,
|
|
758
|
+
summit: summitState.summit,
|
|
700
759
|
});
|
|
701
760
|
|
|
702
761
|
export default connect(mapStateToProps,
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { navigate } from "gatsby";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { GatsbyImage, getImage } from "gatsby-plugin-image";
|
|
5
|
-
import Mdx from "
|
|
5
|
+
import Mdx from "../../components/Mdx";
|
|
6
6
|
import Container from "./Container";
|
|
7
7
|
import LiteScheduleComponent from "../../components/LiteScheduleComponent";
|
|
8
8
|
import DisqusComponent from "../../components/DisqusComponent";
|
|
@@ -22,49 +22,47 @@ const onEventClick = (ev) => navigate(`/a/event/${ev.id}`);
|
|
|
22
22
|
|
|
23
23
|
const MainColumn = ({ widgets, summitPhase, isLoggedUser, onEventClick, lastDataSync, fullWidth, maxHeight }) => {
|
|
24
24
|
const { content, schedule, disqus, image } = widgets || {};
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
const scheduleProps = schedule && isLoggedUser && summitPhase !== PHASES.BEFORE ? { onEventClick } : {};
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
</Container>
|
|
67
|
-
</div>
|
|
29
|
+
<div
|
|
30
|
+
className={`column pt-6 pb-5 px-6 ${!fullWidth ? "is-half" : ""} ${styles.mainColumn || ""}`}
|
|
31
|
+
style={{ maxHeight: !fullWidth && maxHeight ? maxHeight : "none", overflowY: "auto" }}
|
|
32
|
+
>
|
|
33
|
+
<Container>
|
|
34
|
+
{content?.display && content?.body && (
|
|
35
|
+
<Mdx shortcodes={shortcodes} content={content.body}/>
|
|
36
|
+
)}
|
|
37
|
+
{schedule?.display && (
|
|
38
|
+
<>
|
|
39
|
+
<h2><b>{schedule.title}</b></h2>
|
|
40
|
+
<LiteScheduleComponent
|
|
41
|
+
{...scheduleProps}
|
|
42
|
+
lastDataSync={lastDataSync}
|
|
43
|
+
id={`marketing_lite_schedule_${lastDataSync}`}
|
|
44
|
+
page="marketing-site"
|
|
45
|
+
showAllEvents={true}
|
|
46
|
+
showSearch={false}
|
|
47
|
+
showNav={true}
|
|
48
|
+
/>
|
|
49
|
+
</>
|
|
50
|
+
)}
|
|
51
|
+
{disqus?.display && (
|
|
52
|
+
<>
|
|
53
|
+
<h2><b>{disqus.title}</b></h2>
|
|
54
|
+
<DisqusComponent page="marketing-site" />
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
{image?.display && image?.image?.src && (
|
|
58
|
+
<>
|
|
59
|
+
<h2><b>{image.title}</b></h2>
|
|
60
|
+
<br/>
|
|
61
|
+
<GatsbyImage image={getImage(image.image.src)} alt={image.image.alt ?? ""} />
|
|
62
|
+
</>
|
|
63
|
+
)}
|
|
64
|
+
</Container>
|
|
65
|
+
</div>
|
|
68
66
|
);
|
|
69
67
|
};
|
|
70
68
|
|
|
@@ -77,4 +75,4 @@ MainColumn.propTypes = {
|
|
|
77
75
|
maxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
|
|
78
76
|
};
|
|
79
77
|
|
|
80
|
-
export default MainColumn;
|
|
78
|
+
export default MainColumn;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
3
|
+
import { Redirect } from "@gatsbyjs/reach-router";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import AjaxLoader from 'openstack-uicore-foundation/lib/components/ajaxloader'
|
|
6
|
+
import Layout from "../components/Layout";
|
|
7
|
+
import { acceptRSVPInvitation, declineRSVPInvitation, getRSVPInvitation } from "../actions/user-actions";
|
|
8
|
+
import { getEventById } from "../actions/event-actions";
|
|
9
|
+
import styles from "../styles/rsvp-page.module.scss"
|
|
10
|
+
import { RSVP_STATUS } from "@utils/rsvpConstants";
|
|
11
|
+
import { Badge } from "react-bootstrap";
|
|
12
|
+
import "../i18n";
|
|
13
|
+
|
|
14
|
+
const RSVPPage = ({ location, rsvpInvitation, event, getRSVPInvitation, acceptRSVPInvitation, declineRSVPInvitation, getEventById }) => {
|
|
15
|
+
|
|
16
|
+
const { t } = useTranslation();
|
|
17
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
18
|
+
|
|
19
|
+
const queryParams = useMemo(() => {
|
|
20
|
+
const search = location?.search || "";
|
|
21
|
+
const searchParams = new URLSearchParams(search);
|
|
22
|
+
const k = searchParams.get("k");
|
|
23
|
+
const eventId = searchParams.get("event_id");
|
|
24
|
+
return {
|
|
25
|
+
invitationToken: k ? k : null,
|
|
26
|
+
eventId: eventId ? Number(eventId) : null,
|
|
27
|
+
};
|
|
28
|
+
}, [location?.search]);
|
|
29
|
+
|
|
30
|
+
const { invitationToken, eventId } = queryParams;
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!invitationToken) return;
|
|
34
|
+
getRSVPInvitation(invitationToken, eventId)
|
|
35
|
+
.then(() => getEventById(eventId))
|
|
36
|
+
.finally(() => setIsLoading(false));
|
|
37
|
+
}, [invitationToken, eventId]);
|
|
38
|
+
|
|
39
|
+
const moderatorId = event?.moderator_speaker_id;
|
|
40
|
+
const errorMessage = rsvpInvitation?.errorMessage;
|
|
41
|
+
|
|
42
|
+
const handleConfirmRSVP = (isAccepted) => {
|
|
43
|
+
setIsLoading(true);
|
|
44
|
+
const action = isAccepted ? acceptRSVPInvitation : declineRSVPInvitation;
|
|
45
|
+
return action(invitationToken, eventId).finally(() => setIsLoading(false));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!invitationToken || !eventId) {
|
|
49
|
+
return <Redirect to="/" noThrow />;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Layout location={location}>
|
|
54
|
+
<AjaxLoader show={isLoading} size={120} />
|
|
55
|
+
{!isLoading && (
|
|
56
|
+
<div className={`container`}>
|
|
57
|
+
{event ?
|
|
58
|
+
(<>
|
|
59
|
+
<h2>{t("rsvp_page.invite_message", { event: event?.title })} </h2>
|
|
60
|
+
|
|
61
|
+
<br />
|
|
62
|
+
|
|
63
|
+
<div dangerouslySetInnerHTML={{ __html: event?.description || "" }} />
|
|
64
|
+
|
|
65
|
+
<br />
|
|
66
|
+
|
|
67
|
+
{event?.speakers?.length > 0 &&
|
|
68
|
+
<div>
|
|
69
|
+
<b>{t("rsvp_page.speakers")}</b>
|
|
70
|
+
<div className={styles.speakerWrapper}>
|
|
71
|
+
{event.speakers.map((speaker) => {
|
|
72
|
+
const isModerator = speaker.id === moderatorId;
|
|
73
|
+
return (
|
|
74
|
+
<div className={styles.speaker}>
|
|
75
|
+
{speaker.pic && (
|
|
76
|
+
<div className={styles.picWrapper}>
|
|
77
|
+
<div className={styles.pic} style={{ backgroundImage: `url(${speaker.pic})` }} />
|
|
78
|
+
</div>
|
|
79
|
+
)}
|
|
80
|
+
<div className={styles.nameWrapper}>
|
|
81
|
+
<div className={styles.name}>
|
|
82
|
+
{speaker.first_name} {speaker.last_name} {isModerator && <Badge className={styles.moderator} pill>Moderator</Badge>}
|
|
83
|
+
</div>
|
|
84
|
+
{speaker.title &&
|
|
85
|
+
<div className={styles.job}>
|
|
86
|
+
<span>{speaker.title}</span>
|
|
87
|
+
{speaker.company && <span className={styles.company}> - {speaker.company}</span>}
|
|
88
|
+
</div>
|
|
89
|
+
}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
)
|
|
93
|
+
})}
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
}
|
|
97
|
+
<div className={styles.buttonWrapper}>
|
|
98
|
+
{errorMessage && (
|
|
99
|
+
<h3 dangerouslySetInnerHTML={{ __html: errorMessage }} />
|
|
100
|
+
)}
|
|
101
|
+
{rsvpInvitation?.status === RSVP_STATUS.rejected && (
|
|
102
|
+
<h4>{t("rsvp_page.decline_message")} </h4>
|
|
103
|
+
)}
|
|
104
|
+
{rsvpInvitation?.status === RSVP_STATUS.accepted && (
|
|
105
|
+
<h4>{t("rsvp_page.confirm_message")} </h4>
|
|
106
|
+
)}
|
|
107
|
+
{rsvpInvitation?.status === RSVP_STATUS.pending && (
|
|
108
|
+
<>
|
|
109
|
+
<button className="button is-large" onClick={() => handleConfirmRSVP(true)}>
|
|
110
|
+
{t("rsvp_page.accept_button")}
|
|
111
|
+
</button>
|
|
112
|
+
<button className="button is-large" onClick={() => handleConfirmRSVP(false)}>
|
|
113
|
+
{t("rsvp_page.decline_button")}
|
|
114
|
+
</button>
|
|
115
|
+
</>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
</>)
|
|
119
|
+
:
|
|
120
|
+
(
|
|
121
|
+
<>
|
|
122
|
+
<h3>Activity not found.</h3>
|
|
123
|
+
</>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
</Layout>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const mapStateToProps = ({ userState, eventState }) => ({
|
|
133
|
+
rsvpInvitation: userState.rsvpInvitation,
|
|
134
|
+
event: eventState.event,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
export default connect(mapStateToProps,
|
|
138
|
+
{
|
|
139
|
+
getRSVPInvitation,
|
|
140
|
+
acceptRSVPInvitation,
|
|
141
|
+
declineRSVPInvitation,
|
|
142
|
+
getEventById
|
|
143
|
+
}
|
|
144
|
+
)(RSVPPage);
|
package/src/utils/alerts.js
CHANGED
|
@@ -30,8 +30,8 @@ export const alertPopup = (title, html, confirmLabel, confirmAction, cancelLabel
|
|
|
30
30
|
})
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
34
33
|
export const needsLogin = (action, msg = null) => {
|
|
34
|
+
|
|
35
35
|
const defaultMessage = "Please log in to add sessions to My Schedule.";
|
|
36
36
|
|
|
37
37
|
const login = () => {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class BaseAPIRequest {
|
|
2
|
+
constructor(fields = [], relations = [], expands = []) {
|
|
3
|
+
this.fields = fields;
|
|
4
|
+
this.relations = relations;
|
|
5
|
+
this.expands = expands;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
getFields() {
|
|
9
|
+
return this.fields.join(",");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getExpands() {
|
|
13
|
+
return this.expands.join(",");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getRelations() {
|
|
17
|
+
return this.relations.join(",");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static build(apiUrl) {
|
|
21
|
+
throw new Error("This method should not be used from BaseAPIRequest class.");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = BaseAPIRequest;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
const BaseAPIRequest = require("./BaseAPIRequest");
|
|
2
|
+
|
|
3
|
+
const SPEAKER_MODERATOR_FIELDS = ['id', 'first_name', 'last_name', 'title', 'bio', 'member_id', 'pic', 'big_pic', 'company', 'featured'];
|
|
4
|
+
const DOCUMENTS_FIELDS = ['display_on_site', 'name', 'order', 'class_name', 'type', 'public_url', 'link'];
|
|
5
|
+
const CURRENT_ATTENDANCE_FIELDS = ['member_first_name', 'member_last_name', 'member_pic'];
|
|
6
|
+
|
|
7
|
+
class EventAPIRequest extends BaseAPIRequest {
|
|
8
|
+
static instance;
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
const primary_fields = [
|
|
12
|
+
"id",
|
|
13
|
+
"created",
|
|
14
|
+
"start_date",
|
|
15
|
+
"end_date",
|
|
16
|
+
"title",
|
|
17
|
+
"abstract",
|
|
18
|
+
"description",
|
|
19
|
+
"level",
|
|
20
|
+
"image",
|
|
21
|
+
"stream_thumbnail",
|
|
22
|
+
"location_id",
|
|
23
|
+
"class_name",
|
|
24
|
+
"allow_feedback",
|
|
25
|
+
"head_count",
|
|
26
|
+
"attendance_count",
|
|
27
|
+
"current_attendance_count",
|
|
28
|
+
"to_record",
|
|
29
|
+
"etherpad_link",
|
|
30
|
+
"streaming_url",
|
|
31
|
+
"streaming_type",
|
|
32
|
+
"meeting_url",
|
|
33
|
+
"current_attendance",
|
|
34
|
+
"attendees_expected_learnt",
|
|
35
|
+
"show_sponsors",
|
|
36
|
+
"duration",
|
|
37
|
+
// RSVP fields
|
|
38
|
+
'rsvp_type',
|
|
39
|
+
'rsvp_capacity',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const type_fields = ["type.id", "type.name", "type.allows_publishing_dates", "type.color"];
|
|
43
|
+
const tags_fields = ["tags.id", "tags.tag"];
|
|
44
|
+
const location_fields = ["location.id", "location.class_name", "location.name", "location.venue.name", "location.floor.name"];
|
|
45
|
+
const track_fields = ["track.id", "track.name", "track.icon_url", "track.color", "track.text_color", "track.parent_id"];
|
|
46
|
+
const track_groups_fields = ["track_groups.id", "track_groups.name", "track_groups.parent_id", "track_groups.color", "track_groups.order"];
|
|
47
|
+
const speakers_badge_feature_fields = ["speakers.badge_features.id", "speakers.badge_features.name", "speakers.badge_features.image"];
|
|
48
|
+
const sponsors_fields = ["sponsors.id", "sponsors.name", "sponsors.logo"];
|
|
49
|
+
|
|
50
|
+
const speakers_fields = SPEAKER_MODERATOR_FIELDS.map((e) => `speakers.${e}`);
|
|
51
|
+
const moderator_fields = SPEAKER_MODERATOR_FIELDS.map((e) => `moderator.${e}`);
|
|
52
|
+
const media_upload_fields = DOCUMENTS_FIELDS.map((e) => `media_uploads.${e}`)
|
|
53
|
+
const slides_fields = DOCUMENTS_FIELDS.map((e) => `slides.${e}`)
|
|
54
|
+
const links_fields = DOCUMENTS_FIELDS.map((e) => `links.${e}`)
|
|
55
|
+
const video_fields = DOCUMENTS_FIELDS.map((e) => `videos.${e}`)
|
|
56
|
+
const current_attendance_fields = CURRENT_ATTENDANCE_FIELDS.map((e) => `current_attendance.${e}`)
|
|
57
|
+
|
|
58
|
+
const primary_relations = [
|
|
59
|
+
"slides",
|
|
60
|
+
"links",
|
|
61
|
+
"videos",
|
|
62
|
+
"media_uploads",
|
|
63
|
+
"type",
|
|
64
|
+
"track",
|
|
65
|
+
"location",
|
|
66
|
+
"speakers",
|
|
67
|
+
"moderator",
|
|
68
|
+
"sponsors",
|
|
69
|
+
"tags",
|
|
70
|
+
"current_attendance",
|
|
71
|
+
"allowed_ticket_types"
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const speakers_relations = ["speakers.badge_features", "speakers.presentations", "speakers.moderated_presentations"];
|
|
75
|
+
|
|
76
|
+
const track_relations = ["track.track_groups"];
|
|
77
|
+
|
|
78
|
+
const type_relations = ["type.allowed_ticket_types"];
|
|
79
|
+
|
|
80
|
+
const locations_relations = ["location.venue", "location.floor", "location.venue.none", "location.floor.none"];
|
|
81
|
+
|
|
82
|
+
// TODO: review relations for "current_attendance"
|
|
83
|
+
|
|
84
|
+
const relations = [
|
|
85
|
+
...track_relations,
|
|
86
|
+
...type_relations,
|
|
87
|
+
...speakers_relations,
|
|
88
|
+
...locations_relations,
|
|
89
|
+
...primary_relations,
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const speakers_expand = ["speakers.badge_features"];
|
|
93
|
+
|
|
94
|
+
const location_expand = ["location.venue", "location.floor"];
|
|
95
|
+
|
|
96
|
+
const track_expand = [
|
|
97
|
+
"track.subtracks",
|
|
98
|
+
"track.allowed_access_levels",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
const expands = [
|
|
102
|
+
"slides",
|
|
103
|
+
"links",
|
|
104
|
+
"videos",
|
|
105
|
+
"media_uploads",
|
|
106
|
+
"type",
|
|
107
|
+
"track",
|
|
108
|
+
"location",
|
|
109
|
+
"speakers",
|
|
110
|
+
"moderator",
|
|
111
|
+
"sponsors",
|
|
112
|
+
"tags",
|
|
113
|
+
"current_attendance",
|
|
114
|
+
...track_expand,
|
|
115
|
+
...speakers_expand,
|
|
116
|
+
...location_expand
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
super(
|
|
120
|
+
[
|
|
121
|
+
...primary_fields,
|
|
122
|
+
...type_fields,
|
|
123
|
+
...tags_fields,
|
|
124
|
+
...location_fields,
|
|
125
|
+
...track_fields,
|
|
126
|
+
...track_groups_fields,
|
|
127
|
+
...sponsors_fields,
|
|
128
|
+
...speakers_fields,
|
|
129
|
+
...moderator_fields,
|
|
130
|
+
...media_upload_fields,
|
|
131
|
+
...slides_fields,
|
|
132
|
+
...links_fields,
|
|
133
|
+
...video_fields,
|
|
134
|
+
...current_attendance_fields,
|
|
135
|
+
...speakers_badge_feature_fields
|
|
136
|
+
],
|
|
137
|
+
relations,
|
|
138
|
+
expands
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (!EventAPIRequest.instance) {
|
|
142
|
+
EventAPIRequest.instance = this;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static getInstance() {
|
|
147
|
+
if (!EventAPIRequest.instance) {
|
|
148
|
+
new EventAPIRequest();
|
|
149
|
+
}
|
|
150
|
+
return EventAPIRequest.instance;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static getParams(apiUrl) {
|
|
154
|
+
const instance = EventAPIRequest.getInstance();
|
|
155
|
+
apiUrl.addQuery("fields", instance.getFields());
|
|
156
|
+
apiUrl.addQuery("expand", instance.getExpands());
|
|
157
|
+
apiUrl.addQuery("relations", instance.getRelations());
|
|
158
|
+
return apiUrl.query(true);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
static build(apiUrl) {
|
|
162
|
+
const instance = EventAPIRequest.getInstance();
|
|
163
|
+
apiUrl.addQuery("fields", instance.getFields());
|
|
164
|
+
apiUrl.addQuery("expand", instance.getExpands());
|
|
165
|
+
apiUrl.addQuery("relations", instance.getRelations());
|
|
166
|
+
|
|
167
|
+
return apiUrl.toString();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = EventAPIRequest;
|