@openeventkit/event-site 2.0.88 → 2.0.90

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openeventkit/event-site",
3
3
  "description": "Event Site",
4
- "version": "2.0.88",
4
+ "version": "2.0.90",
5
5
  "author": "Tipit LLC",
6
6
  "dependencies": {
7
7
  "@mui/base": "^5.0.0-alpha.114",
@@ -43,7 +43,6 @@
43
43
  "final-form": "4.20.7",
44
44
  "font-awesome": "^4.7.0",
45
45
  "formik": "^2.2.9",
46
- "fs-extra": "^9.0.1",
47
46
  "full-schedule-widget": "3.0.3",
48
47
  "gatsby": "^5.8.1",
49
48
  "gatsby-alias-imports": "^1.0.6",
@@ -78,7 +77,6 @@
78
77
  "moment-timezone": "^0.5.31",
79
78
  "netlify-cms-app": "^2.15.72",
80
79
  "netlify-cms-lib-widgets": "^1.8.0",
81
- "node-sass-utils": "^1.1.3",
82
80
  "openstack-uicore-foundation": "4.1.65",
83
81
  "path-browserify": "^1.0.1",
84
82
  "prop-types": "^15.6.0",
@@ -122,7 +120,7 @@
122
120
  "stream-browserify": "^3.0.0",
123
121
  "stream-chat": "^2.7.2",
124
122
  "stream-chat-react": "3.1.7",
125
- "summit-registration-lite": "5.0.18",
123
+ "summit-registration-lite": "5.0.19",
126
124
  "superagent": "8.0.9",
127
125
  "sweetalert2": "^9.17.0",
128
126
  "upcoming-events-widget": "3.0.5",
@@ -50,6 +50,9 @@ export const UNCAST_PRESENTATION_VOTE_RESPONSE = 'UNCAST_PRESENTATION_VOTE_RESPO
50
50
  export const TOGGLE_PRESENTATION_VOTE = 'TOGGLE_PRESENTATION_VOTE';
51
51
  export const GET_EXTRA_QUESTIONS = 'GET_EXTRA_QUESTIONS';
52
52
  export const TICKET_OWNER_CHANGED = 'TICKET_OWNER_CHANGED';
53
+ export const REQUEST_INVITATION = 'REQUEST_INVITATION';
54
+ export const RECEIVE_INVITATION = 'RECEIVE_INVITATION';
55
+ export const REJECT_INVITATION = 'REJECT_INVITATION';
53
56
 
54
57
  // shortName is the unique identifier assigned to a Disqus site.
55
58
  export const getDisqusSSO = (shortName) => async (dispatch, getState) => {
@@ -436,7 +439,7 @@ export const updatePassword = (password) => async (dispatch) => {
436
439
 
437
440
  export const saveAttendeeQuestions = (values, ticketId = null) => async (dispatch, getState) => {
438
441
 
439
- const { userState: { userProfile: { summit_tickets } } } = getState();
442
+ const { userState: { userProfile: { summit_tickets } } } = getState();
440
443
 
441
444
  const normalizedEntity = {...values};
442
445
 
@@ -453,7 +456,7 @@ export const saveAttendeeQuestions = (values, ticketId = null) => async (dispatc
453
456
  } catch (e) {
454
457
  console.log('getAccessToken error: ', e);
455
458
  return Promise.reject();
456
- }
459
+ }
457
460
 
458
461
  let params = {
459
462
  access_token: accessToken,
@@ -529,10 +532,11 @@ export const setUserOrder = (order) => (dispatch) => Promise.resolve().then(() =
529
532
  export const checkOrderData = (order) => (dispatch, getState) => {
530
533
  if (!order) return;
531
534
 
532
- const { userState: { idpProfile: { company, given_name, family_name } } } = getState();
533
- const { owner_company, owner_first_name, owner_last_name } = order || {};
535
+ const { userState: { idpProfile: { company, given_name, family_name, email } } } = getState();
536
+ const { owner_company, owner_first_name, owner_last_name, owner_email } = order || {};
534
537
 
535
- if (owner_company !== company || owner_first_name !== given_name || owner_last_name !== family_name) {
538
+ // only change data if I am the order owner
539
+ if (owner_email === email && (owner_company !== company || owner_first_name !== given_name || owner_last_name !== family_name)) {
536
540
  const newProfile = {
537
541
  first_name: owner_first_name,
538
542
  last_name: owner_last_name,
@@ -577,3 +581,44 @@ export const doVirtualCheckIn = (attendee) => async (dispatch, getState) => {
577
581
  return e;
578
582
  });
579
583
  };
584
+
585
+
586
+ /**
587
+ * Get invitation by token to allow reject
588
+ * @param token
589
+ * @returns {function(*=, *): *}
590
+ */
591
+ export const getInvitation = (token) => async (dispatch) => {
592
+ dispatch(startLoading());
593
+
594
+ return getRequest(
595
+ createAction(REQUEST_INVITATION),
596
+ createAction(RECEIVE_INVITATION),
597
+ `${window.API_BASE_URL}/api/public/v1/summits/${window.SUMMIT_ID}/registration-invitations/${token}`,
598
+ customErrorHandler
599
+ )({})(dispatch)
600
+ .finally(() => dispatch(stopLoading()));
601
+ }
602
+
603
+ /**
604
+ * Reject invitation by token
605
+ * @param token
606
+ * @returns {function(*=, *): *}
607
+ */
608
+ export const rejectInvitation = (token) => async (dispatch) => {
609
+ dispatch(startLoading());
610
+
611
+ return deleteRequest(
612
+ null,
613
+ createAction(REJECT_INVITATION),
614
+ `${window.API_BASE_URL}/api/public/v1/summits/${window.SUMMIT_ID}/registration-invitations/${token}/reject`,
615
+ {},
616
+ customErrorHandler
617
+ )({})(dispatch)
618
+ .then(() => {
619
+ //redirect ?
620
+ })
621
+ .finally(() => dispatch(stopLoading()));
622
+ }
623
+
624
+
@@ -5,6 +5,7 @@ import {
5
5
  import marketingPage from "./marketingPage";
6
6
  import lobbyPage from "./lobbyPage";
7
7
  import expoHallPage from "./expoHallPage";
8
+ import invitationsRejectPage from "./invitationsRejectPage";
8
9
 
9
10
  const defaultPagesCollection = {
10
11
  ...collectionDefaults({
@@ -14,7 +15,8 @@ const defaultPagesCollection = {
14
15
  files: [
15
16
  marketingPage,
16
17
  lobbyPage,
17
- expoHallPage
18
+ expoHallPage,
19
+ invitationsRejectPage
18
20
  ]
19
21
  };
20
22
 
@@ -0,0 +1,45 @@
1
+ import {stringField} from "../../../fields";
2
+
3
+ import {
4
+ INVITATIONS_REJECT_PAGE_FILE_PATH
5
+ } from "@utils/filePath";
6
+
7
+ const invitationsRejectPage = {
8
+ label: "Invitations Reject Page",
9
+ name: "invitations-reject-page",
10
+ file: INVITATIONS_REJECT_PAGE_FILE_PATH,
11
+ fields: [
12
+ stringField({
13
+ label: "Title",
14
+ name: "title",
15
+ default: "Reject Invitation"
16
+ }),
17
+ stringField({
18
+ label: "Not found text",
19
+ name: "notFoundText",
20
+ required: false,
21
+ default: "Invitation not found."
22
+ }),
23
+ stringField({
24
+ label: "Rejected text",
25
+ name: "rejectedText",
26
+ required: false,
27
+ default: "Invitation has already been rejected."
28
+ }),
29
+ stringField({
30
+ label: "Reject text",
31
+ name: "rejectText",
32
+ required: false,
33
+ default: "To reject please click on the button below."
34
+ }),
35
+ stringField({
36
+ label: "Reject CTA label",
37
+ name: "rejectCTALabel",
38
+ required: false,
39
+ default: "Reject Invitation"
40
+ }),
41
+ ]
42
+ };
43
+
44
+ export default invitationsRejectPage;
45
+
@@ -0,0 +1,10 @@
1
+
2
+ module.exports = `
3
+ type InvitationsRejectPageJson implements Node {
4
+ title: String
5
+ notFoundText: String
6
+ rejectedText: String
7
+ rejectText: String
8
+ rejectCTALabel: String
9
+ }
10
+ `;
@@ -116,7 +116,7 @@ const marketingPage = {
116
116
  name: "countdown",
117
117
  fields: [
118
118
  booleanField({
119
- label: "Should Display?",
119
+ label: "Display",
120
120
  name: "display",
121
121
  required: false
122
122
  }),
@@ -128,7 +128,7 @@ const marketingPage = {
128
128
  }),
129
129
  objectField({
130
130
  label: "Widgets",
131
- name: "leftColumn",
131
+ name: "widgets",
132
132
  fields: [
133
133
  objectField({
134
134
  label: "Text",
@@ -196,41 +196,52 @@ const marketingPage = {
196
196
  })
197
197
  ]
198
198
  }),
199
- numberField({
200
- label: "Redirect to Event",
201
- name: "eventRedirect",
202
- required: false,
203
- hint: "User will be redirected to this event after login"
204
- }),
205
- listField({
199
+ objectField({
206
200
  label: "Masonry",
207
201
  name: "masonry",
208
202
  fields: [
209
- stringField({
210
- label: "Placement",
211
- name: "placement",
203
+ booleanField({
204
+ label: "Display",
205
+ name: "display",
212
206
  required: false
213
207
  }),
214
- selectField({
215
- label: "Size",
216
- name: "size",
217
- options: [
218
- selectOption({
219
- label: "Single",
220
- value: 1
208
+ listField({
209
+ label: "Items",
210
+ name: "items",
211
+ fields: [
212
+ stringField({
213
+ label: "Placement",
214
+ name: "placement",
215
+ required: false
216
+ }),
217
+ selectField({
218
+ label: "Size",
219
+ name: "size",
220
+ options: [
221
+ selectOption({
222
+ label: "Single",
223
+ value: 1
224
+ }),
225
+ selectOption({
226
+ label: "Double",
227
+ value: 2
228
+ })
229
+ ]
221
230
  }),
222
- selectOption({
223
- label: "Double",
224
- value: 2
231
+ linkImagesField({
232
+ label: "Images",
233
+ name: "images",
234
+ imageRequired: true
225
235
  })
226
236
  ]
227
- }),
228
- linkImagesField({
229
- label: "Images",
230
- name: "images",
231
- imageRequired: true
232
237
  })
233
238
  ]
239
+ }),
240
+ numberField({
241
+ label: "Redirect to Event",
242
+ name: "eventRedirect",
243
+ required: false,
244
+ hint: "User will be redirected to this event after login"
234
245
  })
235
246
  ]
236
247
  };
@@ -9,29 +9,34 @@ module.exports = `
9
9
  alt: String
10
10
  link: String
11
11
  }
12
- type MarketingPageMasonry {
12
+ type MarketingPageMasonryItem {
13
+ display: Boolean
13
14
  placement: String
14
15
  size: Int
15
16
  images: [LinkImageWithAlt]
16
17
  }
17
- type MarketingPageLeftColumnTextWidget {
18
+ type MarketingPageMasonry {
18
19
  display: Boolean
19
- content: String
20
+ items: [MarketingPageMasonryItem]
20
21
  }
21
- type MarketingPageLeftColumnWidget {
22
+ type MarketingPageWidget {
22
23
  display: Boolean
23
24
  title: String
24
25
  }
25
- type MarketingPageLeftColumnImageWidget {
26
+ type MarketingPageTextWidget {
27
+ display: Boolean
28
+ content: String
29
+ }
30
+ type MarketingPageImageWidget {
26
31
  display: Boolean
27
32
  title: String
28
33
  image: ImageWithAlt
29
34
  }
30
- type MarketingPageLeftColumn {
31
- text: MarketingPageLeftColumnTextWidget
32
- schedule: MarketingPageLeftColumnWidget
33
- disqus: MarketingPageLeftColumnWidget
34
- image: MarketingPageLeftColumnImageWidget
35
+ type MarketingPageWidgets {
36
+ text: MarketingPageTextWidget
37
+ image: MarketingPageImageWidget
38
+ schedule: MarketingPageWidget
39
+ disqus: MarketingPageWidget
35
40
  }
36
41
  type MarketingPageCountdown {
37
42
  display: Boolean
@@ -57,9 +62,9 @@ module.exports = `
57
62
  }
58
63
  type MarketingPageJson implements Node {
59
64
  hero: MarketingPageHero
60
- leftColumn: MarketingPageLeftColumn
61
65
  countdown: MarketingPageCountdown
66
+ widgets: MarketingPageWidgets
67
+ masonry: MarketingPageMasonry
62
68
  eventRedirect: Int
63
- masonry: [MarketingPageMasonry]
64
69
  }
65
70
  `;
@@ -1,9 +1,11 @@
1
1
  const marketingPageTypeDefs = require("./marketingPage/typeDefs");
2
2
  const lobbyPageTypeDefs = require("./lobbyPage/typeDefs");
3
3
  const expoHallPageTypeDefs = require("./expoHallPage/typeDefs");
4
+ const invitationsRejectPageTypeDefs = require("./invitationsRejectPage/typeDefs");
4
5
 
5
6
  module.exports = [
6
7
  marketingPageTypeDefs,
7
8
  lobbyPageTypeDefs,
8
- expoHallPageTypeDefs
9
+ expoHallPageTypeDefs,
10
+ invitationsRejectPageTypeDefs
9
11
  ].join("");
@@ -138,7 +138,7 @@ const AuthComponent = ({
138
138
  allowsNativeAuth: allowsNativeAuth,
139
139
  allowsOtpAuth: allowsOtpAuth,
140
140
  initialEmailValue: initialEmailValue,
141
- title: 'Enter your email address to login with a one time code',
141
+ title: 'Sign in using the email associated with your account:',
142
142
  summitData: summit
143
143
  };
144
144
 
@@ -12,7 +12,7 @@ import styles from "../styles/marketing-hero.module.scss";
12
12
 
13
13
  const MarketingHeroComponent = ({
14
14
  location,
15
- marketingPageSettings,
15
+ data,
16
16
  }) => {
17
17
 
18
18
  const sliderRef = useRef(null);
@@ -32,21 +32,21 @@ const MarketingHeroComponent = ({
32
32
 
33
33
 
34
34
  const getButtons = () => {
35
- const { registerButton, loginButton } = marketingPageSettings.hero.buttons;
35
+ const {
36
+ registerButton,
37
+ loginButton
38
+ } = data?.buttons || {};
36
39
 
37
- return (
38
- <>
39
- {registerButton.display &&
40
- (
41
- <span className={styles.link}>
42
- <RegistrationLiteComponent location={location} />
43
- </span>
44
- )}
45
- {loginButton.display && (
46
- <AuthComponent location={location} />
47
- )}
48
- </>
49
- );
40
+ return <>
41
+ {registerButton?.display &&
42
+ <span className={styles.link}>
43
+ <RegistrationLiteComponent location={location} />
44
+ </span>
45
+ }
46
+ {loginButton?.display &&
47
+ <AuthComponent location={location} />
48
+ }
49
+ </>;
50
50
  };
51
51
 
52
52
  const sliderSettings = {
@@ -59,8 +59,8 @@ const MarketingHeroComponent = ({
59
59
  };
60
60
 
61
61
  let heroLeftColumnInlineStyles = {};
62
- if (marketingPageSettings.hero?.background?.src) {
63
- const imageSrc = getSrc(marketingPageSettings.hero.background.src);
62
+ if (data?.background?.src) {
63
+ const imageSrc = getSrc(data.background.src);
64
64
  heroLeftColumnInlineStyles.backgroundImage = `url(${imageSrc})`;
65
65
  }
66
66
 
@@ -73,53 +73,53 @@ const MarketingHeroComponent = ({
73
73
  >
74
74
  <div className={`${styles.heroMarketingContainer} hero-body`}>
75
75
  <div className="container">
76
- <h1 className="title">{marketingPageSettings.hero.title}</h1>
77
- <h2 className="subtitle">{marketingPageSettings.hero.subTitle}</h2>
76
+ <h1 className="title">{data?.title}</h1>
77
+ <h2 className="subtitle">{data?.subTitle}</h2>
78
78
  <div
79
79
  className={styles.date}
80
80
  style={{
81
- backgroundColor: marketingPageSettings.hero.dateLayout
81
+ backgroundColor: data?.dateLayout
82
82
  ? "var(--color_secondary)"
83
83
  : "",
84
- display: marketingPageSettings.hero.dateLayout
84
+ display: data?.dateLayout
85
85
  ? ""
86
86
  : "inline",
87
- transform: marketingPageSettings.hero.dateLayout
87
+ transform: data?.dateLayout
88
88
  ? "skew(-25deg)"
89
89
  : "skew(0deg)",
90
90
  }}
91
91
  >
92
- {marketingPageSettings.hero?.dateLayout ?
93
- <div style={{transform: "skew(25deg)"}}>{marketingPageSettings.hero?.date}</div>
92
+ {data?.dateLayout ?
93
+ <div style={{transform: "skew(25deg)"}}>{data?.date}</div>
94
94
  :
95
95
  <div style={{transform: "skew(0deg)"}}>
96
- <span>{marketingPageSettings.hero?.date}</span>
96
+ <span>{data?.date}</span>
97
97
  </div>
98
98
  }
99
99
  </div>
100
- <h4>{marketingPageSettings.hero?.time}</h4>
100
+ <h4>{data?.time}</h4>
101
101
  <div className={styles.heroButtons}>
102
102
  {getButtons()}
103
103
  </div>
104
104
  </div>
105
105
  </div>
106
106
  </div>
107
- {marketingPageSettings.hero?.images?.length > 0 &&
107
+ {data?.images?.length > 0 &&
108
108
  <div className={`${styles.rightColumn} column is-6 px-0`} id="marketing-slider" ref={sliderRef}>
109
- {marketingPageSettings.hero?.images?.length > 1 ?
110
- <Slider {...sliderSettings}>
111
- {marketingPageSettings.hero.images.map((image, index) => {
112
- const imageSrc = getSrc(image.src);
113
- return (
114
- <div key={index}>
115
- <div className={styles.imageSlider} aria-label={image.alt} style={{ backgroundImage: `url(${imageSrc})`, height: sliderHeight, marginBottom: -6 }} />
116
- </div>
117
- );
118
- })}
119
- </Slider>
120
- :
121
- <div className={styles.singleImage} aria-label={marketingPageSettings.hero.images[0].alt} style={{ backgroundImage: `url(${getSrc(marketingPageSettings.hero.images[0].src)})`}} >
122
- </div>
109
+ {data?.images?.length > 1 ?
110
+ <Slider {...sliderSettings}>
111
+ {data.images.map((image, index) => {
112
+ const imageSrc = getSrc(image.src);
113
+ return (
114
+ <div key={index}>
115
+ <div className={styles.imageSlider} aria-label={image.alt} style={{ backgroundImage: `url(${imageSrc})`, height: sliderHeight, marginBottom: -6 }} />
116
+ </div>
117
+ );
118
+ })}
119
+ </Slider>
120
+ :
121
+ <div className={styles.singleImage} aria-label={data.images[0].alt} style={{ backgroundImage: `url(${getSrc(data.images[0].src)})`}} >
122
+ </div>
123
123
  }
124
124
  </div>
125
125
  }
@@ -0,0 +1,7 @@
1
+ {
2
+ "title": "Invitation rejection",
3
+ "notFoundText": "Invitation not found.",
4
+ "rejectedText": "You have rejected your invitation. ",
5
+ "rejectText": "Be certain. This is your last chance to purchase this ticket...",
6
+ "rejectCTALabel": "Reject"
7
+ }
@@ -19,7 +19,7 @@
19
19
  "countdown": {
20
20
  "display": false
21
21
  },
22
- "leftColumn": {
22
+ "widgets": {
23
23
  "schedule": {
24
24
  "display": false
25
25
  },
@@ -30,5 +30,5 @@
30
30
  "display": false
31
31
  }
32
32
  },
33
- "masonry": []
33
+ "masonry": {}
34
34
  }
@@ -4,6 +4,7 @@ import { connect } from "react-redux";
4
4
  import EventPage from "../../templates/event-page";
5
5
  import PostersPage from "../../templates/posters-page";
6
6
  import SchedulePage from "../../templates/schedule-page";
7
+ import InvitationsRejectPage from "../../templates/invitations-reject-page";
7
8
  import SponsorPage from "../../templates/sponsor-page";
8
9
  import ExpoHallPage from "../../templates/expo-hall-page";
9
10
  import FullProfilePage from "../../templates/full-profile-page";
@@ -19,8 +20,21 @@ import withFeedsWorker from "../../utils/withFeedsWorker";
19
20
  import Seo from "../../components/Seo";
20
21
  import Link from "../../components/Link";
21
22
  import { titleFromPathname } from "../../utils/urlFormating";
23
+ import {graphql} from "gatsby";
22
24
 
23
- const App = ({ isLoggedUser, user, summitPhase, allowClick = true }) => {
25
+ export const appQuery = graphql`
26
+ query {
27
+ invitationsRejectPageJson {
28
+ title
29
+ notFoundText
30
+ rejectedText
31
+ rejectText
32
+ rejectCTALabel
33
+ }
34
+ }
35
+ `;
36
+
37
+ const App = ({ isLoggedUser, user, summitPhase, allowClick = true, data }) => {
24
38
  return (
25
39
  <Location>
26
40
  {({ location }) => (
@@ -34,6 +48,7 @@ const App = ({ isLoggedUser, user, summitPhase, allowClick = true }) => {
34
48
  }}
35
49
  allowClick={allowClick}
36
50
  />
51
+ <InvitationsRejectPage path="/invitations/reject/:invitationToken" location={location} data={data} />
37
52
  <WithAuthRoute path="/" isLoggedIn={isLoggedUser} location={location}>
38
53
  <MyTicketsPage path="/my-tickets" isLoggedIn={isLoggedUser} user={user} location={location} />
39
54
  <FullProfilePage path="/profile" summitPhase={summitPhase} isLoggedIn={isLoggedUser} user={user} location={location} />
@@ -54,7 +54,7 @@ export const marketingPageQuery = graphql`
54
54
  display
55
55
  text
56
56
  }
57
- leftColumn {
57
+ widgets {
58
58
  text {
59
59
  content
60
60
  display
@@ -84,24 +84,27 @@ export const marketingPageQuery = graphql`
84
84
  display
85
85
  }
86
86
  }
87
- eventRedirect
88
87
  masonry {
89
- placement
90
- size
91
- images {
92
- src {
93
- childImageSharp {
94
- gatsbyImageData (
95
- quality: 100
96
- placeholder: BLURRED
97
- layout: FULL_WIDTH
98
- )
88
+ display
89
+ items {
90
+ placement
91
+ size
92
+ images {
93
+ src {
94
+ childImageSharp {
95
+ gatsbyImageData (
96
+ quality: 100
97
+ placeholder: BLURRED
98
+ layout: FULL_WIDTH
99
+ )
100
+ }
99
101
  }
102
+ alt
103
+ link
100
104
  }
101
- alt
102
- link
103
105
  }
104
106
  }
107
+ eventRedirect
105
108
  }
106
109
  }
107
110
  `;
@@ -18,6 +18,7 @@ const INITIAL_STATE = {
18
18
  colorSource: 'track',
19
19
  is_my_schedule: false,
20
20
  only_events_with_attendee_access: false,
21
+ hide_past_events_with_show_always_on_schedule: false,
21
22
  };
22
23
 
23
24
  const scheduleReducer = (state = INITIAL_STATE, action) => {
@@ -41,6 +42,7 @@ const scheduleReducer = (state = INITIAL_STATE, action) => {
41
42
  filters,
42
43
  baseFilters,
43
44
  only_events_with_attendee_access,
45
+ hide_past_events_with_show_always_on_schedule,
44
46
  is_my_schedule,
45
47
  userProfile,
46
48
  isLoggedUser
@@ -48,15 +50,25 @@ const scheduleReducer = (state = INITIAL_STATE, action) => {
48
50
 
49
51
  const filterByAccessLevel = only_events_with_attendee_access && isLoggedUser;
50
52
  const filterByMySchedule = is_my_schedule && isLoggedUser;
51
- const allFilteredEvents = preFilterEvents(all_events, pre_filters, summitTimeZoneId, userProfile, filterByAccessLevel, filterByMySchedule);
53
+ const allFilteredEvents = preFilterEvents(all_events, pre_filters, summitTimeZoneId, userProfile, filterByAccessLevel, filterByMySchedule, hide_past_events_with_show_always_on_schedule);
52
54
  const newFilters = syncFilters(filters, state.filters);
53
- const events = getFilteredEvents(allFilteredEvents, newFilters, summitTimeZoneId);
55
+ const events = getFilteredEvents(allFilteredEvents, newFilters, summitTimeZoneId, hide_past_events_with_show_always_on_schedule);
54
56
 
55
- return {...state, allEvents: allFilteredEvents, baseFilters, filters: newFilters, colorSource: color_source.toLowerCase(), events, is_my_schedule, only_events_with_attendee_access};
57
+ return {
58
+ ...state,
59
+ allEvents: allFilteredEvents,
60
+ baseFilters,
61
+ filters: newFilters,
62
+ colorSource: color_source.toLowerCase(),
63
+ events,
64
+ is_my_schedule,
65
+ only_events_with_attendee_access,
66
+ hide_past_events_with_show_always_on_schedule
67
+ };
56
68
  }
57
69
  case `SCHED_UPDATE_FILTER`: {
58
70
 
59
- const { type : filterType, values } = payload;
71
+ const { type : filterType, values, hide_past_events_with_show_always_on_schedule } = payload;
60
72
  const { filters, allEvents } = state;
61
73
  // update the filters with new values
62
74
  const newFilters = {
@@ -70,24 +82,24 @@ const scheduleReducer = (state = INITIAL_STATE, action) => {
70
82
  return {...state,
71
83
  filters : newFilters ,
72
84
  // refilter events
73
- events: getFilteredEvents(allEvents, newFilters, summitTimeZoneId)}
85
+ events: getFilteredEvents(allEvents, newFilters, summitTimeZoneId, hide_past_events_with_show_always_on_schedule)}
74
86
  }
75
87
  case `SCHED_UPDATE_FILTERS`: {
76
88
  const {filters, view} = payload;
77
- const {allEvents} = state;
89
+ const {allEvents, hide_past_events_with_show_always_on_schedule} = state;
78
90
 
79
91
  // update events
80
- const events = getFilteredEvents(allEvents, filters, summitTimeZoneId);
92
+ const events = getFilteredEvents(allEvents, filters, summitTimeZoneId, hide_past_events_with_show_always_on_schedule);
81
93
 
82
94
  return {...state, filters, events, view}
83
95
  }
84
96
  case `SCHED_CLEAR_FILTERS`: {
85
- const { allEvents, baseFilters } = state;
97
+ const { allEvents, baseFilters, hide_past_events_with_show_always_on_schedule } = state;
86
98
 
87
99
  return {...state,
88
100
  filters : baseFilters ,
89
101
  // refilter events
90
- events: getFilteredEvents(allEvents, baseFilters, summitTimeZoneId)}
102
+ events: getFilteredEvents(allEvents, baseFilters, summitTimeZoneId, hide_past_events_with_show_always_on_schedule)}
91
103
  }
92
104
  case `SCHED_CHANGE_VIEW`: {
93
105
  const {view} = payload;
@@ -99,20 +111,20 @@ const scheduleReducer = (state = INITIAL_STATE, action) => {
99
111
  }
100
112
  case `SCHED_ADD_TO_SCHEDULE`: {
101
113
  const event = payload;
102
- const {allEvents, filters} = state;
114
+ const {allEvents, filters, hide_past_events_with_show_always_on_schedule} = state;
103
115
 
104
116
  allEvents.push(event);
105
- const events = getFilteredEvents(allEvents, filters, summitTimeZoneId);
117
+ const events = getFilteredEvents(allEvents, filters, summitTimeZoneId, hide_past_events_with_show_always_on_schedule);
106
118
 
107
119
  return {...state, allEvents, events};
108
120
 
109
121
  }
110
122
  case `SCHED_REMOVE_FROM_SCHEDULE`: {
111
123
  const event = payload;
112
- const {allEvents: allEventsCurrent, filters} = state;
124
+ const {allEvents: allEventsCurrent, filters, hide_past_events_with_show_always_on_schedule} = state;
113
125
 
114
126
  const allEvents = allEventsCurrent.filter(ev => ev.id !== event.id);
115
- const events = getFilteredEvents(allEvents, filters, summitTimeZoneId);
127
+ const events = getFilteredEvents(allEvents, filters, summitTimeZoneId, hide_past_events_with_show_always_on_schedule);
116
128
 
117
129
  return {...state, allEvents, events};
118
130
 
@@ -16,7 +16,10 @@ import {
16
16
  CAST_PRESENTATION_VOTE_RESPONSE,
17
17
  UNCAST_PRESENTATION_VOTE_RESPONSE,
18
18
  TOGGLE_PRESENTATION_VOTE,
19
- TICKET_OWNER_CHANGED
19
+ TICKET_OWNER_CHANGED,
20
+ RECEIVE_INVITATION,
21
+ REQUEST_INVITATION,
22
+ REJECT_INVITATION
20
23
  } from '../actions/user-actions';
21
24
  import { RESET_STATE } from '../actions/base-actions-definitions';
22
25
  import { isAuthorizedUser } from '../utils/authorizedGroups';
@@ -29,7 +32,8 @@ const DEFAULT_STATE = {
29
32
  idpProfile: null,
30
33
  isAuthorized: false,
31
34
  hasTicket: false,
32
- attendee: null
35
+ attendee: null,
36
+ invitation: null,
33
37
  }
34
38
 
35
39
  const userReducer = (state = DEFAULT_STATE, action) => {
@@ -107,6 +111,15 @@ const userReducer = (state = DEFAULT_STATE, action) => {
107
111
  }
108
112
  };
109
113
  }
114
+ case REQUEST_INVITATION: {
115
+ return {...state, invitation: null};
116
+ }
117
+ case RECEIVE_INVITATION: {
118
+ return {...state, invitation: payload.response}
119
+ }
120
+ case REJECT_INVITATION: {
121
+ return {...state, invitation: {...state.invitation, status: 'Rejected'}}
122
+ }
110
123
  default:
111
124
  return state;
112
125
  }
@@ -0,0 +1,24 @@
1
+ @import "colors.scss";
2
+
3
+ .container {
4
+ position: inherit;
5
+
6
+ .wrapper {
7
+ display: flex;
8
+ flex-direction: column;
9
+ max-width: 800px;
10
+ margin: 0 auto;
11
+
12
+ h1 {
13
+ margin-top: 40px;
14
+ }
15
+
16
+ div {
17
+ margin-top: 10px;
18
+ }
19
+
20
+ button {
21
+ margin-top: 40px;
22
+ }
23
+ }
24
+ }
@@ -13,6 +13,7 @@ const ExpoHallPage = ({
13
13
  }) => {
14
14
  const { expoHallPageJson: { hero } } = data;
15
15
  const style = hero?.background ? { backgroundImage: `url(${getSrc(hero.background.src)})` } : {};
16
+
16
17
  return (
17
18
  <Layout location={location}>
18
19
  <AttendanceTrackerComponent />
@@ -0,0 +1,106 @@
1
+ import React, {useEffect, useState, useCallback, useRef} from "react";
2
+ import PropTypes from "prop-types";
3
+ import {connect} from "react-redux";
4
+ import Layout from "../components/Layout";
5
+ import AttendanceTrackerComponent from "../components/AttendanceTrackerComponent";
6
+ import AccessTracker from "../components/AttendeeToAttendeeWidgetComponent";
7
+ import {getInvitation, rejectInvitation} from "../actions/user-actions";
8
+
9
+ import styles from "../styles/invitation-reject.module.scss";
10
+
11
+ const InvitationsRejectPage = ({data, location, invitationToken, invitation, loading, ...props}) => {
12
+ const [loaded, setLoaded] = useState(false);
13
+ const [rejecting, setRejecting] = useState(false);
14
+ const {invitationsRejectPageJson: {title, notFoundText, rejectedText, rejectText, rejectCTALabel}} = data;
15
+ const titleStr = title || "Reject invitation"
16
+ const rejectStr = rejectText || "To reject please click on the button below."
17
+ const rejectCTA = rejectCTALabel || "Reject"
18
+ const notFoundStr = notFoundText || "Invitation not found."
19
+ const rejectedStr = rejectedText || "Invitation has already been rejected."
20
+
21
+ useEffect(() => {
22
+ setLoaded(false);
23
+ if (invitationToken) {
24
+ props.getInvitation(invitationToken)
25
+ .finally(() => {
26
+ setLoaded(true)
27
+ });
28
+ }
29
+ }, []);
30
+
31
+ const rejectInvitation = () => {
32
+ setRejecting(true);
33
+ props.rejectInvitation(invitationToken)
34
+ .finally(() => {
35
+ setRejecting(false);
36
+ });
37
+ }
38
+
39
+ const getMessage = () => {
40
+ if (!invitationToken) {
41
+ return (
42
+ <>
43
+ <div>Missing token.</div>
44
+ </>
45
+ );
46
+ }
47
+ else if (loaded) {
48
+ if (!invitation) {
49
+ return (
50
+ <>
51
+ <div>{notFoundStr}</div>
52
+ </>
53
+ );
54
+ } else {
55
+ if (invitation.status === 'Rejected') {
56
+ return (
57
+ <>
58
+ <div>{rejectedStr}</div>
59
+ </>
60
+ )
61
+ } else {
62
+ return (
63
+ <>
64
+ <div>{rejectStr}</div>
65
+ <button className="button is-large" onClick={rejectInvitation} disabled={rejecting}>
66
+ {rejectCTA}
67
+ </button>
68
+ </>
69
+ )
70
+ }
71
+
72
+ }
73
+ } else {
74
+ return null;
75
+ }
76
+ }
77
+
78
+
79
+ return (
80
+ <Layout location={location}>
81
+ <div className={`container ${styles.container}`}>
82
+ <div className={styles.wrapper}>
83
+ <h1>{titleStr}</h1>
84
+ {getMessage()}
85
+ </div>
86
+ </div>
87
+ <AttendanceTrackerComponent/>
88
+ <AccessTracker/>
89
+ </Layout>
90
+ );
91
+ };
92
+
93
+ InvitationsRejectPage.propTypes = {
94
+ invitationToken: PropTypes.string.isRequired
95
+ };
96
+
97
+ const mapStateToProps = ({userState, globalState}) => ({
98
+ invitation: userState.invitation,
99
+ loading: globalState?.loading
100
+ });
101
+
102
+ export default connect(
103
+ mapStateToProps, {
104
+ getInvitation,
105
+ rejectInvitation
106
+ })(InvitationsRejectPage);
@@ -20,6 +20,15 @@ import { PHASES } from "../utils/phasesUtils";
20
20
 
21
21
  import styles from "../styles/marketing.module.scss";
22
22
 
23
+ const sliderSettings = {
24
+ autoplay: true,
25
+ autoplaySpeed: 5000,
26
+ infinite: true,
27
+ dots: false,
28
+ slidesToShow: 1,
29
+ slidesToScroll: 1
30
+ };
31
+
23
32
  const MarketingPageTemplate = ({
24
33
  location,
25
34
  data,
@@ -30,44 +39,42 @@ const MarketingPageTemplate = ({
30
39
  }) => {
31
40
 
32
41
  const {
33
- marketingPageJson
34
- } = data;
42
+ marketingPageJson: {
43
+ hero,
44
+ countdown,
45
+ widgets,
46
+ masonry
47
+ } = {}
48
+ } = data || {};
35
49
 
36
50
  let scheduleProps = {};
37
- if (marketingPageJson.leftColumn?.schedule && isLoggedUser && summitPhase !== PHASES.BEFORE) {
51
+ if (widgets?.schedule && isLoggedUser && summitPhase !== PHASES.BEFORE) {
38
52
  scheduleProps = {
39
53
  ...scheduleProps,
40
54
  onEventClick: (ev) => navigate(`/a/event/${ev.id}`),
41
55
  }
42
56
  }
43
57
 
44
- const sliderSettings = {
45
- autoplay: true,
46
- autoplaySpeed: 5000,
47
- infinite: true,
48
- dots: false,
49
- slidesToShow: 1,
50
- slidesToScroll: 1
51
- };
58
+ const shouldRenderMasonry = masonry?.display;
52
59
 
53
60
  return (
54
61
  <Layout marketing={true} location={location}>
55
62
  <AttendanceTrackerComponent />
56
63
  <MarketingHeroComponent
57
64
  location={location}
58
- marketingPageSettings={marketingPageJson}
65
+ data={hero}
59
66
  />
60
- {summit && marketingPageJson.countdown?.display && <Countdown summit={summit} text={marketingPageJson?.countdown?.text} />}
67
+ {summit && countdown?.display && <Countdown summit={summit} text={countdown?.text} />}
61
68
  <div className="columns">
62
- <div className={`column is-half mt-3 px-6 py-6 ${styles.leftColumn}`} style={{ position: 'relative' }}>
63
- {marketingPageJson.leftColumn?.text?.content && marketingPageJson.leftColumn?.text?.display &&
69
+ <div className={`column mt-3 px-6 py-6 ${shouldRenderMasonry ? "is-half" : ""} ${styles.leftColumn ? styles.leftColumn : ""}`} style={{ position: 'relative' }}>
70
+ {widgets?.text?.display && widgets?.text?.content &&
64
71
  <Markdown>
65
- {marketingPageJson.leftColumn.text.content}
72
+ {widgets.text.content}
66
73
  </Markdown>
67
74
  }
68
- {marketingPageJson.leftColumn?.schedule?.display &&
75
+ {widgets?.schedule?.display &&
69
76
  <>
70
- <h2><b>{marketingPageJson.leftColumn.schedule.title}</b></h2>
77
+ <h2><b>{widgets.schedule.title}</b></h2>
71
78
  <LiteScheduleComponent
72
79
  {...scheduleProps}
73
80
  lastDataSync={lastDataSync}
@@ -79,28 +86,28 @@ const MarketingPageTemplate = ({
79
86
  />
80
87
  </>
81
88
  }
82
- {marketingPageJson.leftColumn?.disqus?.display &&
89
+ {widgets?.disqus?.display &&
83
90
  <>
84
- <h2><b>{marketingPageJson.leftColumn.disqus.title}</b></h2>
91
+ <h2><b>{widgets.disqus.title}</b></h2>
85
92
  <DisqusComponent page="marketing-site"/>
86
93
  </>
87
94
  }
88
- {marketingPageJson.leftColumn?.image?.display &&
89
- marketingPageJson.leftColumn?.image?.image.src &&
95
+ {widgets?.image?.display &&
96
+ widgets?.image?.image.src &&
90
97
  <>
91
- <h2><b>{marketingPageJson.leftColumn.image.title}</b></h2>
98
+ <h2><b>{widgets.image.title}</b></h2>
92
99
  <br />
93
- <GatsbyImage image={getImage(marketingPageJson.leftColumn.image.image.src)} alt={marketingPageJson.leftColumn.image.image.alt ?? ""} />
100
+ <GatsbyImage image={getImage(widgets.image.image.src)} alt={widgets.image.image.alt ?? ""} />
94
101
  </>
95
102
  }
96
103
  </div>
97
- <div className={`column is-half px-0 pb-0 ${styles.rightColumn}`}>
104
+ {shouldRenderMasonry &&
105
+ <div className={`column is-half px-0 pb-0 ${styles.rightColumn ? styles.rightColumn : ""}`}>
98
106
  <Masonry
99
107
  breakpointCols={2}
100
108
  className="my-masonry-grid"
101
109
  columnClassName="my-masonry-grid_column">
102
- { marketingPageJson.masonry &&
103
- formatMasonry(marketingPageJson.masonry).map((item, index) => {
110
+ {masonry.items && formatMasonry(masonry.items).map((item, index) => {
104
111
  if (item.images && item.images.length === 1) {
105
112
  const image = getImage(item.images[0].src);
106
113
  return (
@@ -141,6 +148,7 @@ const MarketingPageTemplate = ({
141
148
  })}
142
149
  </Masonry>
143
150
  </div>
151
+ }
144
152
  </div>
145
153
  </Layout>
146
154
  )
@@ -35,6 +35,7 @@ const POSTERS_PAGES_FILE_PATH = `${STATIC_CONTENT_DIR_PATH}/posters-pages.json`;
35
35
  const MARKETING_SETTINGS_FILE_PATH = `${DATA_DIR_PATH}/marketing-settings.json`;
36
36
  const MAINTENANCE_FILE_PATH = `${STATIC_CONTENT_DIR_PATH}/maintenance.json`;
37
37
  const EXPO_HALL_PAGE_FILE_PATH = `${STATIC_CONTENT_DIR_PATH}/expo-hall-page/index.json`;
38
+ const INVITATIONS_REJECT_PAGE_FILE_PATH = `${STATIC_CONTENT_DIR_PATH}/invitations-reject-page/index.json`;
38
39
  const SPONSORS_FILE_NAME = "sponsors.json";
39
40
  const SPONSORS_FILE_PATH = `${STATIC_CONTENT_DIR_PATH}/${SPONSORS_FILE_NAME}`;
40
41
  const CMS_FONT_FILE_PATH = "/static/fonts/"
@@ -50,6 +51,7 @@ exports.REQUIRED_DIR_PATHS = [
50
51
  SITE_SETTINGS_DIR_PATH,
51
52
  MARKETING_PAGE_DIR_PATH,
52
53
  LOBBY_PAGE_DIR_PATH,
54
+ INVITATIONS_REJECT_PAGE_FILE_PATH,
53
55
  NAVBAR_DIR_PATH,
54
56
  FOOTER_DIR_PATH
55
57
  ];
@@ -83,6 +85,7 @@ exports.POSTERS_PAGES_FILE_PATH = POSTERS_PAGES_FILE_PATH;
83
85
  exports.MARKETING_SETTINGS_FILE_PATH = MARKETING_SETTINGS_FILE_PATH;
84
86
  exports.MAINTENANCE_FILE_PATH = MAINTENANCE_FILE_PATH;
85
87
  exports.EXPO_HALL_PAGE_FILE_PATH = EXPO_HALL_PAGE_FILE_PATH;
88
+ exports.INVITATIONS_REJECT_PAGE_FILE_PATH = INVITATIONS_REJECT_PAGE_FILE_PATH;
86
89
  exports.SPONSORS_FILE_PATH = SPONSORS_FILE_PATH;
87
90
  exports.CMS_FONT_FILE_PATH = CMS_FONT_FILE_PATH;
88
91
  exports.PAYMENTS_FILE_PATH = PAYMENTS_FILE_PATH;
@@ -7,13 +7,13 @@ export const getDefaultLocation = (eventRedirect, hasVirtualAccess = false) => {
7
7
 
8
8
  export const formatThirdPartyProviders = (providersArray) => {
9
9
  const providers = [
10
- { button_color: '#082238', provider_label: 'Continue with FNid', provider_param: '', provider_logo: '../img/logo_fn.svg', provider_logo_size: 35 },
10
+ { button_color: '#082238', provider_label: 'Sign in with FNid', provider_param: '', provider_logo: '../img/logo_fn.svg', provider_logo_size: 35 },
11
11
  ];
12
12
 
13
13
  const thirdPartyProviders = [
14
- { button_color: '#1877F2', provider_label: 'Continue with Facebook', provider_param: 'facebook', provider_logo: '../img/third-party-idp/logo_facebook.svg', provider_logo_size: 22 },
14
+ { button_color: '#1877F2', provider_label: 'Login with Facebook', provider_param: 'facebook', provider_logo: '../img/third-party-idp/logo_facebook.svg', provider_logo_size: 22 },
15
15
  { button_color: '#0A66C2', provider_label: 'Sign in with LinkedIn', provider_param: 'linkedin', provider_logo: '../img/third-party-idp/logo_linkedin.svg', provider_logo_size: 21 },
16
- { button_border_color:'var(--color_input_border_color)', button_color: '#000000', provider_label: 'Continue with Apple', provider_param: 'apple', provider_logo: '../img/third-party-idp/logo_apple.svg', provider_logo_size: 19 }
16
+ { button_border_color:'var(--color_input_border_color)', button_color: '#000000', provider_label: 'Sign in with Apple', provider_param: 'apple', provider_logo: '../img/third-party-idp/logo_apple.svg', provider_logo_size: 19 }
17
17
  ];
18
18
 
19
19
  return [...providers, ...thirdPartyProviders.filter(p => providersArray && providersArray.includes(p.provider_param))];
@@ -63,7 +63,7 @@ const filterMyEvents = (myEvents, events) => {
63
63
  return events.filter(ev => myEventsIds.includes(ev.id));
64
64
  };
65
65
 
66
- export const preFilterEvents = (events, filters, summitTimezone, userProfile, filterByAccessLevel, filterByMySchedule) => {
66
+ export const preFilterEvents = (events, filters, summitTimezone, userProfile, filterByAccessLevel, filterByMySchedule, hidePast) => {
67
67
  const {schedule_summit_events = []} = userProfile || {};
68
68
  let result = [...events];
69
69
 
@@ -75,10 +75,11 @@ export const preFilterEvents = (events, filters, summitTimezone, userProfile, fi
75
75
  result = filterEventsByAccessLevel(result, userProfile);
76
76
  }
77
77
 
78
- return getFilteredEvents(result, filters, summitTimezone);
78
+ return getFilteredEvents(result, filters, summitTimezone, hidePast);
79
79
  };
80
80
 
81
- export const getFilteredEvents = (events, filters, summitTimezone) => {
81
+ export const getFilteredEvents = (events, filters, summitTimezone, hidePast) => {
82
+ const localNow = Date.now() / 1000;
82
83
 
83
84
  return events.filter((ev) => {
84
85
  let valid = true;
@@ -90,8 +91,11 @@ export const getFilteredEvents = (events, filters, summitTimezone) => {
90
91
  valid = filters.date.values.includes(dateString);
91
92
  if (!valid) return false;
92
93
  }
93
-
94
- if (ev.type.show_always_on_schedule) return true;
94
+
95
+ if (ev.type.show_always_on_schedule) {
96
+ // hide past events when the flag is on
97
+ return !(hidePast && ev.end_date < localNow);
98
+ }
95
99
 
96
100
  if (filters.level?.values.length > 0) {
97
101
  valid = filters.level.values.some(l => l.toString().toLowerCase() === ev.level?.toLowerCase());