@openeventkit/event-site 2.0.75 → 2.0.77

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/gatsby-config.js CHANGED
@@ -22,182 +22,184 @@ catch (e) {
22
22
 
23
23
  const title = siteSettings?.siteMetadata?.title || process.env.GATSBY_METADATA_TITLE || "Event Site";
24
24
  const description = siteSettings?.siteMetadata?.description || process.env.GATSBY_METADATA_DESCRIPTION || "Event Site";
25
- const manifestFaviconSettings = siteSettings?.favicon?.asset ? {
26
- icon: `${SITE_SETTINGS_DIR_PATH}/${siteSettings.favicon.asset}`,
27
- include_favicon: true
28
- } : {};
25
+ const faviconAsset = siteSettings?.favicon?.asset;
29
26
 
30
- module.exports = {
31
- siteMetadata: {
32
- title: title,
33
- description: description
34
- },
35
- plugins: [
36
- {
37
- resolve: "gatsby-plugin-manifest",
38
- options: {
39
- name: title,
40
- short_name: title,
41
- description: description,
42
- start_url: "/",
43
- // Enables "Add to Homescreen" prompt and disables browser UI (including back button)
44
- // see https://developers.google.com/web/fundamentals/web-app-manifest/#display
45
- display: "minimal-ui",
46
- ...manifestFaviconSettings
27
+ const manifestPlugin = faviconAsset ? [
28
+ {
29
+ resolve: "gatsby-plugin-manifest",
30
+ options: {
31
+ name: title,
32
+ short_name: title,
33
+ description: description,
34
+ start_url: "/",
35
+ display: "minimal-ui",
36
+ icon: path.join(SITE_SETTINGS_DIR_PATH, faviconAsset),
37
+ include_favicon: true
38
+ }
39
+ }
40
+ ] : [];
41
+
42
+ const plugins = [
43
+ ...manifestPlugin,
44
+ {
45
+ resolve: "gatsby-alias-imports",
46
+ options: {
47
+ aliases: {
48
+ "@utils": `${__dirname}/src/utils`
47
49
  }
48
- },
49
- {
50
- resolve: "gatsby-alias-imports",
51
- options: {
52
- aliases: {
53
- "@utils": `${__dirname}/src/utils`
50
+ }
51
+ },
52
+ {
53
+ /**
54
+ * Gatsby v4 uses ES Modules for importing cssModules by default.
55
+ * Disabling this to avoid needing to update in all files and for compatibility
56
+ * with other plugins/packages that have not yet been updated.
57
+ * @see https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v2-to-v3/#css-modules-are-imported-as-es-modules
58
+ *
59
+ * Also, since libSass was deprecated in October 2020, the Node Sass package has also been deprecated.
60
+ * As such, we have migrated from Node Sass to Dart Sass in package.json.
61
+ * @see https://www.gatsbyjs.com/plugins/gatsby-plugin-sass/#v300
62
+ * @see https://sass-lang.com/blog/libsass-is-deprecated#how-do-i-migrate
63
+ */
64
+ resolve: "gatsby-plugin-sass",
65
+ options: {
66
+ cssLoaderOptions: {
67
+ esModule: false,
68
+ modules: {
69
+ namedExport: false
54
70
  }
55
71
  }
56
- },
57
- {
58
- /**
59
- * Gatsby v4 uses ES Modules for importing cssModules by default.
60
- * Disabling this to avoid needing to update in all files and for compatibility
61
- * with other plugins/packages that have not yet been updated.
62
- * @see https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v2-to-v3/#css-modules-are-imported-as-es-modules
63
- *
64
- * Also, since libSass was deprecated in October 2020, the Node Sass package has also been deprecated.
65
- * As such, we have migrated from Node Sass to Dart Sass in package.json.
66
- * @see https://www.gatsbyjs.com/plugins/gatsby-plugin-sass/#v300
67
- * @see https://sass-lang.com/blog/libsass-is-deprecated#how-do-i-migrate
68
- */
69
- resolve: "gatsby-plugin-sass",
70
- options: {
71
- cssLoaderOptions: {
72
- esModule: false,
73
- modules: {
74
- namedExport: false
72
+ }
73
+ },
74
+ {
75
+ // Add image assets before markdown or json files
76
+ resolve: "gatsby-source-filesystem",
77
+ options: {
78
+ path: `${__dirname}/static/img`,
79
+ name: "uploads"
80
+ }
81
+ },
82
+ {
83
+ // Add image assets before markdown or json files
84
+ resolve: "gatsby-source-filesystem",
85
+ options: {
86
+ path: `${__dirname}/src/img`,
87
+ name: "images"
88
+ }
89
+ },
90
+ {
91
+ // Add font assets before markdown or json files
92
+ resolve: "gatsby-source-filesystem",
93
+ options: {
94
+ path: `${__dirname}/static/fonts`,
95
+ name: "fonts"
96
+ }
97
+ },
98
+ {
99
+ resolve: "gatsby-source-filesystem",
100
+ options: {
101
+ path: `${__dirname}/src/pages`,
102
+ name: "pages"
103
+ }
104
+ },
105
+ {
106
+ resolve: "gatsby-source-filesystem",
107
+ options: {
108
+ path: path.resolve(CONTENT_PAGES_DIR_PATH),
109
+ name: "contentPages"
110
+ }
111
+ },
112
+ {
113
+ resolve: "gatsby-source-filesystem",
114
+ options: {
115
+ path: path.resolve(STATIC_CONTENT_DIR_PATH),
116
+ name: "content"
117
+ }
118
+ },
119
+ {
120
+ resolve: "gatsby-source-filesystem",
121
+ options: {
122
+ path: path.resolve(MARKETING_SETTINGS_FILE_PATH),
123
+ name: "marketingSettings"
124
+ }
125
+ },
126
+ "gatsby-plugin-image",
127
+ "gatsby-plugin-sharp",
128
+ "gatsby-transformer-sharp",
129
+ "gatsby-transformer-json",
130
+ {
131
+ resolve: "gatsby-transformer-remark",
132
+ options: {
133
+ plugins: [
134
+ {
135
+ resolve: "gatsby-remark-images",
136
+ options: {
137
+ // It"s important to specify the maxWidth (in pixels) of
138
+ // the content container as this plugin uses this as the
139
+ // base for generating different widths of each image.
140
+ maxWidth: 2048
75
141
  }
76
142
  }
77
- }
78
- },
79
- {
80
- // Add image assets before markdown or json files
81
- resolve: "gatsby-source-filesystem",
82
- options: {
83
- path: `${__dirname}/static/img`,
84
- name: "uploads"
85
- }
86
- },
87
- {
88
- // Add image assets before markdown or json files
89
- resolve: "gatsby-source-filesystem",
90
- options: {
91
- path: `${__dirname}/src/img`,
92
- name: "images"
93
- }
94
- },
95
- {
96
- // Add font assets before markdown or json files
97
- resolve: "gatsby-source-filesystem",
98
- options: {
99
- path: `${__dirname}/static/fonts`,
100
- name: "fonts"
101
- }
102
- },
103
- {
104
- resolve: "gatsby-source-filesystem",
105
- options: {
106
- path: `${__dirname}/src/pages`,
107
- name: "pages"
108
- }
109
- },
110
- {
111
- resolve: "gatsby-source-filesystem",
112
- options: {
113
- path: path.resolve(CONTENT_PAGES_DIR_PATH),
114
- name: "contentPages"
115
- }
116
- },
117
- {
118
- resolve: "gatsby-source-filesystem",
119
- options: {
120
- path: path.resolve(STATIC_CONTENT_DIR_PATH),
121
- name: "content"
122
- }
123
- },
124
- {
125
- resolve: "gatsby-source-filesystem",
126
- options: {
127
- path: path.resolve(MARKETING_SETTINGS_FILE_PATH),
128
- name: "marketingSettings"
129
- }
130
- },
131
- "gatsby-plugin-image",
132
- "gatsby-plugin-sharp",
133
- "gatsby-transformer-sharp",
134
- "gatsby-transformer-json",
135
- {
136
- resolve: "gatsby-transformer-remark",
137
- options: {
138
- plugins: [
139
- {
140
- resolve: "gatsby-remark-images",
141
- options: {
142
- // It"s important to specify the maxWidth (in pixels) of
143
- // the content container as this plugin uses this as the
144
- // base for generating different widths of each image.
145
- maxWidth: 2048
146
- }
147
- }
148
- ]
149
- }
150
- },
151
- {
152
- resolve: "gatsby-plugin-netlify-cms",
153
- options: {
154
- modulePath: `${__dirname}/src/cms/cms.js`,
155
- manualInit: true,
156
- enableIdentityWidget: false,
157
- customizeWebpackConfig: (config) => {
158
- const jsTestString = "\\.(js|mjs|jsx|ts|tsx)$";
159
- const jsTest = new RegExp(jsTestString);
160
- const jsRule = config.module.rules.find(
161
- (rule) => String(rule.test) === String(jsTest)
162
- );
163
- // is it running standalone? or is it running as a module/package?
164
- const standalone = __dirname === path.resolve();
165
- if (!standalone) {
166
- /**
167
- * Force transpiliation of solution js files; required for theming.
168
- * @see https://www.gatsbyjs.com/docs/how-to/custom-configuration/add-custom-webpack-config/#modifying-the-babel-loader
169
- */
170
- const solutionJsTest = new RegExp(`${__dirname}(.*)${jsTestString}`);
171
- const jsRuleInclude = jsRule.include;
172
- jsRule.include = (modulePath) => {
173
- if (solutionJsTest.test(modulePath)) return true;
174
- return jsRuleInclude(modulePath);
175
- }
176
- }
177
- config.module.rules = [
178
- ...config.module.rules.filter(
179
- (rule) => String(rule.test) !== String(jsTest)
180
- ),
181
- jsRule
182
- ];
183
- /**
184
- * Fixes Module not found: Error: Can"t resolve "path" bug.
185
- * Webpack 5 doesn"t include browser polyfills for node APIs by default anymore,
186
- * so we need to provide them ourselves.
187
- * @see https://github.com/postcss/postcss/issues/1509#issuecomment-772097567
188
- * @see https://github.com/gatsbyjs/gatsby/issues/31475
189
- * @see https://github.com/gatsbyjs/gatsby/issues/31179#issuecomment-844588682
143
+ ]
144
+ }
145
+ },
146
+ {
147
+ resolve: "gatsby-plugin-netlify-cms",
148
+ options: {
149
+ modulePath: `${__dirname}/src/cms/cms.js`,
150
+ manualInit: true,
151
+ enableIdentityWidget: false,
152
+ customizeWebpackConfig: (config) => {
153
+ const jsTestString = "\\.(js|mjs|jsx|ts|tsx)$";
154
+ const jsTest = new RegExp(jsTestString);
155
+ const jsRule = config.module.rules.find(
156
+ (rule) => String(rule.test) === String(jsTest)
157
+ );
158
+ // is it running standalone? or is it running as a module/package?
159
+ const standalone = __dirname === path.resolve();
160
+ if (!standalone) {
161
+ /**
162
+ * Force transpiliation of solution js files; required for theming.
163
+ * @see https://www.gatsbyjs.com/docs/how-to/custom-configuration/add-custom-webpack-config/#modifying-the-babel-loader
190
164
  */
191
- config.resolve = {
192
- ...config.resolve,
193
- fallback: {
194
- ...config.resolve.fallback,
195
- path: require.resolve("path-browserify")
196
- }
197
- };
165
+ const solutionJsTest = new RegExp(`${__dirname}(.*)${jsTestString}`);
166
+ const jsRuleInclude = jsRule.include;
167
+ jsRule.include = (modulePath) => {
168
+ if (solutionJsTest.test(modulePath)) return true;
169
+ return jsRuleInclude(modulePath);
170
+ }
198
171
  }
172
+ config.module.rules = [
173
+ ...config.module.rules.filter(
174
+ (rule) => String(rule.test) !== String(jsTest)
175
+ ),
176
+ jsRule
177
+ ];
178
+ /**
179
+ * Fixes Module not found: Error: Can"t resolve "path" bug.
180
+ * Webpack 5 doesn"t include browser polyfills for node APIs by default anymore,
181
+ * so we need to provide them ourselves.
182
+ * @see https://github.com/postcss/postcss/issues/1509#issuecomment-772097567
183
+ * @see https://github.com/gatsbyjs/gatsby/issues/31475
184
+ * @see https://github.com/gatsbyjs/gatsby/issues/31179#issuecomment-844588682
185
+ */
186
+ config.resolve = {
187
+ ...config.resolve,
188
+ fallback: {
189
+ ...config.resolve.fallback,
190
+ path: require.resolve("path-browserify")
191
+ }
192
+ };
199
193
  }
200
- },
201
- "gatsby-plugin-netlify", // make sure to keep it last in the array
202
- ],
203
- }
194
+ }
195
+ },
196
+ "gatsby-plugin-netlify", // make sure to keep it last in the array
197
+ ];
198
+
199
+ module.exports = {
200
+ siteMetadata: {
201
+ title,
202
+ description
203
+ },
204
+ plugins
205
+ };
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.75",
4
+ "version": "2.0.77",
5
5
  "author": "Tipit LLC",
6
6
  "dependencies": {
7
7
  "@mui/base": "^5.0.0-alpha.114",
@@ -78,7 +78,7 @@
78
78
  "netlify-cms-app": "^2.15.72",
79
79
  "netlify-cms-lib-widgets": "^1.8.0",
80
80
  "node-sass-utils": "^1.1.3",
81
- "openstack-uicore-foundation": "4.1.55",
81
+ "openstack-uicore-foundation": "4.1.57",
82
82
  "path-browserify": "^1.0.1",
83
83
  "prop-types": "^15.6.0",
84
84
  "react": "^18.2.0",
@@ -120,7 +120,7 @@
120
120
  "stream-browserify": "^3.0.0",
121
121
  "stream-chat": "^2.7.2",
122
122
  "stream-chat-react": "3.1.7",
123
- "summit-registration-lite": "5.0.13",
123
+ "summit-registration-lite": "5.0.15",
124
124
  "superagent": "8.0.9",
125
125
  "sweetalert2": "^9.17.0",
126
126
  "upcoming-events-widget": "3.0.5",
@@ -0,0 +1,51 @@
1
+ import {
2
+ getAccessToken,
3
+ clearAccessToken,
4
+ } from 'openstack-uicore-foundation/lib/security/methods';
5
+
6
+ import {
7
+ getRequest,
8
+ createAction,
9
+ startLoading,
10
+ stopLoading,
11
+ } from 'openstack-uicore-foundation/lib/utils/actions';
12
+
13
+ import { customErrorHandler } from '../utils/customErrorHandler';
14
+
15
+ export const GET_ATTENDEE_DATA = 'GET_ATTENDEE_DATA';
16
+ export const REQUEST_ATTENDEE_DATA = 'REQUEST_ATTENDEE_DATA';
17
+
18
+ export const getAttendeeData = (attendeeId) => async (dispatch, getState) => {
19
+
20
+ let accessToken;
21
+ try {
22
+ accessToken = await getAccessToken();
23
+ } catch (e) {
24
+ console.log('getAccessToken error: ', e);
25
+ return Promise.reject();
26
+ }
27
+
28
+ let params = {
29
+ access_token: accessToken,
30
+ expand: 'tickets,tickets.owner,extra_questions'
31
+ };
32
+
33
+ dispatch(startLoading());
34
+
35
+ return getRequest(
36
+ createAction(REQUEST_ATTENDEE_DATA),
37
+ createAction(GET_ATTENDEE_DATA),
38
+ `${window.API_BASE_URL}/api/v1/summits/${window.SUMMIT_ID}/attendees/${attendeeId}/me`,
39
+ customErrorHandler
40
+ )(params)(dispatch)
41
+ .then((payload) => {
42
+ dispatch(stopLoading())
43
+ return payload;
44
+ })
45
+ .catch((e) => {
46
+ dispatch(stopLoading());
47
+ console.log('ERROR: ', e);
48
+ clearAccessToken();
49
+ return (e);
50
+ });
51
+ }
@@ -16,7 +16,7 @@ import { customErrorHandler } from '../utils/customErrorHandler';
16
16
 
17
17
  export const GET_EXTRA_QUESTIONS = 'GET_EXTRA_QUESTIONS';
18
18
 
19
- export const getExtraQuestions = () => async (dispatch, getState) => {
19
+ export const getExtraQuestions = (attendeeId = null) => async (dispatch, getState) => {
20
20
 
21
21
  dispatch(startLoading());
22
22
 
@@ -28,7 +28,7 @@ export const getExtraQuestions = () => async (dispatch, getState) => {
28
28
  return Promise.reject(e);
29
29
  }
30
30
 
31
- let apiUrl = URI(`${window.API_BASE_URL}/api/v1/summits/${window.SUMMIT_ID}/attendees/me/allowed-extra-questions`);
31
+ let apiUrl = URI(`${window.API_BASE_URL}/api/v1/summits/${window.SUMMIT_ID}/attendees/${attendeeId ? attendeeId : 'me'}/allowed-extra-questions`);
32
32
  apiUrl.addQuery('expand', '*sub_question_rules,*sub_question,*values')
33
33
  apiUrl.addQuery('access_token', accessToken);
34
34
  apiUrl.addQuery('order', 'order');
@@ -434,7 +434,7 @@ export const updatePassword = (password) => async (dispatch) => {
434
434
  });
435
435
  }
436
436
 
437
- export const saveAttendeeQuestions = (values) => async (dispatch, getState) => {
437
+ export const saveAttendeeQuestions = (values, ticketId = null) => async (dispatch, getState) => {
438
438
 
439
439
  const { userState: { userProfile: { summit_tickets } } } = getState();
440
440
 
@@ -465,7 +465,7 @@ export const saveAttendeeQuestions = (values) => async (dispatch, getState) => {
465
465
  return putRequest(
466
466
  null,
467
467
  createAction(UPDATE_EXTRA_QUESTIONS),
468
- `${window.API_BASE_URL}/api/v1/summits/all/orders/all/tickets/${summit_tickets[0].id}`,
468
+ `${window.API_BASE_URL}/api/v1/summits/all/orders/all/tickets/${ticketId ? ticketId : summit_tickets[0].id}`,
469
469
  normalizedEntity,
470
470
  customErrorHandler
471
471
  )(params)(dispatch).then(() => {
@@ -112,14 +112,14 @@ const RegistrationLiteComponent = ({
112
112
  const inPersonDisclaimer = getSettingByKey(MARKETING_SETTINGS_KEYS.registrationInPersonDisclaimer);
113
113
  const allowPromoCodes = !!Number(getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteAllowPromoCodes));
114
114
  const companyDDLPlaceholder = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteCompanyDDLPlaceholder);
115
- const showCompanyInputDefaultOptions = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteShowCompanyInputDefaultOptions)
116
- const showCompanyInput = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteShowCompanyInput)
117
- const initialOrderComplete1stParagraph = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteInitialOrderComplete1stParagraph)
118
- const initialOrderComplete2ndParagraph = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteInitialOrderComplete2ndParagraph)
119
- const initialOrderCompleteButton = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteInitialOrderCompleteButton)
120
- const orderComplete1stParagraph = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteOrderComplete1stParagraph)
121
- const orderComplete2ndParagraph = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteOrderComplete2ndParagraph)
122
- const orderCompleteButton = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteOrderCompleteButton)
115
+ const showCompanyInputDefaultOptions = !!Number(getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteShowCompanyInputDefaultOptions));
116
+ const showCompanyInput = !!Number(getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteShowCompanyInput));
117
+ const initialOrderComplete1stParagraph = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteInitialOrderComplete1stParagraph);
118
+ const initialOrderComplete2ndParagraph = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteInitialOrderComplete2ndParagraph);
119
+ const initialOrderCompleteButton = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteInitialOrderCompleteButton);
120
+ const orderComplete1stParagraph = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteOrderComplete1stParagraph);
121
+ const orderComplete2ndParagraph = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteOrderComplete2ndParagraph);
122
+ const orderCompleteButton = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteOrderCompleteButton);
123
123
  const noAllowedTicketsMessage = getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteNoAllowedTicketsMessage);
124
124
 
125
125
  const widgetProps = {
@@ -143,18 +143,16 @@ const RegistrationLiteComponent = ({
143
143
  getUserProfile().catch((e) => console.log("getUserProfile error. Not logged in?"));
144
144
  setIsActive(false);
145
145
  },
146
- goToExtraQuestions: () => {
147
- navigate("/a/extra-questions");
146
+ goToExtraQuestions: (attendeeId) => {
147
+ navigate(`/a/extra-questions${attendeeId ? `/#attendee=${attendeeId}` : ''}`);
148
148
  },
149
149
  goToEvent: () => navigate("/a/"),
150
150
  goToRegistration: () => navigate(`${getEnvVariable(REGISTRATION_BASE_URL)}/a/${summit.slug}`),
151
151
  goToMyOrders: () => navigate("/a/my-tickets"),
152
- completedExtraQuestions: async (order) => {
153
- const currentUserTicket = order?.tickets.find(t => t?.owner?.email == userProfile?.email);
154
- const currentAttendee = attendee ? attendee : (currentUserTicket ? currentUserTicket?.owner : null);
155
- if(!currentAttendee) return true;
156
- await getExtraQuestions();
157
- return checkRequireExtraQuestionsByAttendee(currentAttendee);
152
+ completedExtraQuestions: async (attendee) => {
153
+ if(!attendee) return true;
154
+ await getExtraQuestions(attendee?.id);
155
+ return checkRequireExtraQuestionsByAttendee(attendee);
158
156
  },
159
157
  onPurchaseComplete: (order) => {
160
158
  // check if it"s necessary to update profile
@@ -188,8 +186,8 @@ const RegistrationLiteComponent = ({
188
186
  orderComplete2ndParagraph: orderComplete2ndParagraph,
189
187
  orderCompleteButton: orderCompleteButton,
190
188
  noAllowedTicketsMessage: noAllowedTicketsMessage,
191
- showCompanyInput: showCompanyInput.toString().toLowerCase() == "1",
192
- showCompanyInputDefaultOptions: showCompanyInputDefaultOptions.toString().toLowerCase() == "1",
189
+ showCompanyInput: showCompanyInput,
190
+ showCompanyInputDefaultOptions: showCompanyInputDefaultOptions,
193
191
  };
194
192
 
195
193
  const { registerButton } = marketingPageSettings.hero.buttons;
@@ -14,7 +14,6 @@ import ShowOpenRoute from "../../routes/ShowOpenRoute";
14
14
  import WithBadgeRoute from "../../routes/WithBadgeRoute";
15
15
  import PosterDetailPage from "../../templates/poster-detail-page";
16
16
  import MyTicketsPage from "../../templates/my-tickets-page";
17
- import WithTicketRoute from "../../routes/WithTicketRoute";
18
17
  import withRealTimeUpdates from "../../utils/real_time_updates/withRealTimeUpdates";
19
18
  import withFeedsWorker from "../../utils/withFeedsWorker";
20
19
  import Seo from "../../components/Seo";
@@ -38,9 +37,7 @@ const App = ({ isLoggedUser, user, summitPhase, allowClick = true }) => {
38
37
  <WithAuthRoute path="/" isLoggedIn={isLoggedUser} location={location}>
39
38
  <MyTicketsPage path="/my-tickets" isLoggedIn={isLoggedUser} user={user} location={location} />
40
39
  <FullProfilePage path="/profile" summitPhase={summitPhase} isLoggedIn={isLoggedUser} user={user} location={location} />
41
- <WithTicketRoute path="/extra-questions" location={location}>
42
- <ExtraQuestionsPage path="/" isLoggedIn={isLoggedUser} user={user} location={location} />
43
- </WithTicketRoute>
40
+ <ExtraQuestionsPage path="/extra-questions" isLoggedIn={isLoggedUser} user={user} location={location} />
44
41
  <WithAuthzRoute path="/" summitPhase={summitPhase} isLoggedIn={isLoggedUser} user={user} location={location}>
45
42
  <PostersPage path="/posters" trackGroupId={0} location={location} />
46
43
  <PostersPage path="/posters/:trackGroupId" location={location} />
@@ -0,0 +1,38 @@
1
+ import { LOGOUT_USER } from 'openstack-uicore-foundation/lib/security/actions';
2
+ import { START_LOADING, STOP_LOADING } from "openstack-uicore-foundation/lib/utils/actions";
3
+ import {
4
+ GET_ATTENDEE_DATA, REQUEST_ATTENDEE_DATA
5
+ } from '../actions/extra-questions-actions';
6
+ import { RESET_STATE } from '../actions/base-actions-definitions';
7
+ import { UPDATE_EXTRA_QUESTIONS } from '../actions/user-actions';
8
+
9
+ const DEFAULT_STATE = {
10
+ loading: false,
11
+ attendee: null
12
+ }
13
+
14
+ const extraQuestionReducer = (state = DEFAULT_STATE, action) => {
15
+ const { type, payload } = action;
16
+ switch (type) {
17
+ case RESET_STATE:
18
+ case UPDATE_EXTRA_QUESTIONS:
19
+ case LOGOUT_USER: {
20
+ return DEFAULT_STATE;
21
+ }
22
+ case START_LOADING:
23
+ return { ...state, loading: true };
24
+ case STOP_LOADING:
25
+ return { ...state, loading: false };
26
+ case REQUEST_ATTENDEE_DATA: {
27
+ return { ...state, attendee: null }
28
+ }
29
+ case GET_ATTENDEE_DATA: {
30
+ const attendee = payload.response;
31
+ return { ...state, attendee }
32
+ }
33
+ default:
34
+ return state;
35
+ }
36
+ };
37
+
38
+ export default extraQuestionReducer;
@@ -13,6 +13,7 @@ import presentationsReducer from "../reducers/presentations-reducer";
13
13
  import eventReducer from "../reducers/event-reducer";
14
14
  import speakerReducer from "../reducers/speaker-reducer";
15
15
  import sponsorReducer from "../reducers/sponsor-reducer";
16
+ import extraQuestionReducer from "../reducers/extra-questions-reducer";
16
17
 
17
18
  // get from process.env bc window is not set yet
18
19
  const clientId = process.env.GATSBY_OAUTH2_CLIENT_ID;
@@ -44,6 +45,7 @@ const persistedReducers = persistCombineReducers(config, {
44
45
  summitState: summitReducer,
45
46
  speakerState: speakerReducer,
46
47
  sponsorState: sponsorReducer,
48
+ extraQuestionState: extraQuestionReducer
47
49
  });
48
50
 
49
51
  function appendLoggedUser({ getState }) {
@@ -3,6 +3,12 @@
3
3
  @import "bulma/sass/form/_all.sass";
4
4
 
5
5
  @import "colors.scss";
6
+
7
+ .extraQuestionsAttendeeWarning {
8
+ padding: 2rem 6rem;
9
+ font-weight: bold;
10
+ }
11
+
6
12
  .extraQuestionsContainer {
7
13
 
8
14
  h2, h1, h3{
@@ -1,32 +1,35 @@
1
1
  import React, { useEffect, useState, useRef, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { connect } from 'react-redux';
4
- import { navigate } from "gatsby";
5
4
  import Layout from '../components/Layout';
6
5
  import { useFormik } from 'formik';
7
6
  import { useTranslation } from "react-i18next";
8
7
  import * as Yup from 'yup';
8
+ import { isEmpty } from "lodash";
9
9
 
10
10
  import { getExtraQuestions } from '../actions/summit-actions';
11
11
  import { saveAttendeeQuestions } from '../actions/user-actions';
12
12
  import { TICKET_ATTENDEE_KEYS as TicketKeys } from '../components/summit-my-orders-tickets/store/actions/ticket-actions';
13
13
  import { Input, RegistrationCompanyInput, RawHTML } from 'openstack-uicore-foundation/lib/components';
14
+ import FragmentParser from "openstack-uicore-foundation/lib/utils/fragment-parser";
14
15
  import QuestionsSet from 'openstack-uicore-foundation/lib/utils/questions-set';
15
16
  import ExtraQuestionsForm from 'openstack-uicore-foundation/lib/components/extra-questions';
16
17
 
17
18
  import { DefaultScrollBehaviour as ScrollBehaviour } from '@utils/scroll';
18
19
 
19
20
  import styles from '../styles/extra-questions.module.scss';
21
+ import { getAttendeeData } from '../actions/extra-questions-actions';
22
+ import HeroComponent from "../components/HeroComponent";
20
23
 
21
24
  const noOpFn = () => {};
22
25
 
23
- export const ExtraQuestionsPageTemplate = ({ user, summit, extraQuestions, saveAttendeeQuestions }) => {
26
+ export const ExtraQuestionsPageTemplate = ({ user, summit, extraQuestions, attendee, attendeeId, saveAttendeeQuestions, someoneElseLoaded }) => {
24
27
 
25
28
  const { t } = useTranslation();
26
29
  const formRef = useRef(null);
27
30
  const [triedSubmitting, setTriedSubmitting] = useState(false);
28
31
 
29
- const ticket = user.summit_tickets.length > 0 ? user.summit_tickets[user.summit_tickets.length - 1] : null;
32
+ const ticket = attendee ? attendee.tickets[0] : user.summit_tickets.length > 0 ? user.summit_tickets[user.summit_tickets.length - 1] : null;
30
33
  const hasExtraQuestions = extraQuestions.length > 0;
31
34
 
32
35
  const initialValues = useMemo(() => {
@@ -69,7 +72,8 @@ export const ExtraQuestionsPageTemplate = ({ user, summit, extraQuestions, saveA
69
72
 
70
73
  const handleSubmit = (values, formikHelpers) => {
71
74
  formikHelpers.setSubmitting(true);
72
- saveAttendeeQuestions(values).then(() => {
75
+ const ticketId = attendee ? attendee.tickets[0]?.id : null;
76
+ saveAttendeeQuestions(values, ticketId).then(() => {
73
77
  formikHelpers.setSubmitting(false);
74
78
  });
75
79
  };
@@ -141,133 +145,145 @@ export const ExtraQuestionsPageTemplate = ({ user, summit, extraQuestions, saveA
141
145
  validateForm();
142
146
  };
143
147
 
144
- if (!ticket) {
145
- navigate('/');
146
- return null;
148
+ if ((!ticket && !attendeeId) || (attendeeId && someoneElseLoaded === false)) {
149
+ return <HeroComponent title={"Sorry. You don't have a ticket for this event."} redirectTo={"/"} />;
150
+ }
151
+
152
+ const getAttendeeFullname = (attendee) => {
153
+ return !isEmpty(attendee.first_name) && !isEmpty(attendee.last_name) ? `${attendee.first_name} ${attendee.last_name}` : attendee.email
147
154
  }
148
155
 
149
156
  return (
150
- <div className={`content columns ${styles.extraQuestionsContainer}`}>
151
- <div className="column is-three-fifths is-offset-one-fifth px-6-desktop py-6-desktop mb-6">
152
- <h2>Attendee Information</h2>
153
- <div className="columns is-multiline pt-4 pb-5">
154
- <div className={`column is-full-mobile is-half ${styles.extraQuestion}`}>
155
- <label htmlFor={TicketKeys.firstName}>First Name</label>
156
- <Input
157
- id={TicketKeys.firstName}
158
- name={TicketKeys.firstName}
159
- className="form-control"
160
- type="text"
161
- placeholder={'Your First Name'}
162
- value={formik.values[TicketKeys.firstName]}
163
- onBlur={formik.handleBlur}
164
- onChange={!!initialValues[TicketKeys.firstName] ? noOpFn : formik.handleChange}
165
- disabled={!!initialValues[TicketKeys.firstName]}
166
- />
167
- {(formik.touched[TicketKeys.firstName] || triedSubmitting) && formik.errors[TicketKeys.firstName] &&
168
- <p className={styles.errorLabel}>{t("ticket_popup.edit_required")}</p>
169
- }
170
- </div>
171
- <div className={`column is-full-mobile is-half ${styles.extraQuestion}`}>
172
- <label htmlFor={TicketKeys.lastName}>Last Name</label>
173
- <Input
174
- id={TicketKeys.lastName}
175
- name={TicketKeys.lastName}
176
- className="form-control"
177
- type="text"
178
- placeholder={'Your Last Name'}
179
- value={formik.values[TicketKeys.lastName]}
180
- onBlur={formik.handleBlur}
181
- onChange={!!initialValues[TicketKeys.lastName] ? noOpFn : formik.handleChange}
182
- disabled={!!initialValues[TicketKeys.lastName]}
183
- />
184
- {(formik.touched[TicketKeys.lastName] || triedSubmitting) && formik.errors[TicketKeys.lastName] &&
185
- <p className={styles.errorLabel}>{t("ticket_popup.edit_required")}</p>
186
- }
187
- </div>
188
- <div className={`column is-full-mobile is-half ${styles.extraQuestion}`}>
189
- <label htmlFor={TicketKeys.email}>Email</label>
190
- <Input
191
- id={TicketKeys.email}
192
- name={TicketKeys.email}
193
- className="form-control"
194
- type="text"
195
- value={initialValues[TicketKeys.email]}
196
- disabled={true}
197
- />
198
- </div>
199
- <div className={`column is-full-mobile is-half ${styles.extraQuestion}`}>
200
- <label htmlFor={TicketKeys.company}>Company</label>
201
- <RegistrationCompanyInput
202
- id={TicketKeys.company}
203
- name={TicketKeys.company}
204
- summitId={summit.id}
205
- placeholder={'Your Company'}
206
- value={formik.values[TicketKeys.company]}
207
- onBlur={formik.handleBlur}
208
- onChange={!!initialValues[TicketKeys.company].name ? noOpFn : formik.handleChange}
209
- disabled={!!initialValues[TicketKeys.company].name}
210
- tabSelectsValue={false}
211
- />
212
- {(formik.touched[TicketKeys.company] || triedSubmitting) && formik.errors[TicketKeys.company] &&
213
- <p className={styles.errorLabel}>{t("ticket_popup.edit_required")}</p>
214
- }
215
- </div>
157
+ <>
158
+ {attendee &&
159
+ <div className={styles.extraQuestionsAttendeeWarning}>
160
+ {`Attention: The info below is for ${getAttendeeFullname(attendee)}. No additional action is required if you
161
+ prefer ${attendee.first_name || attendee.email} to complete this info; they have received an email with instructions.
162
+ You can manage this ticket on the "My Orders / Tickets" page.`}
216
163
  </div>
217
- { hasExtraQuestions &&
218
- <>
219
- <h2 className="mb-3">Additional Information</h2>
220
- <p>Please answer these additional questions.</p>
221
- <ExtraQuestionsForm
222
- extraQuestions={extraQuestions}
223
- userAnswers={formik.values[TicketKeys.extraQuestions]}
224
- onAnswerChanges={onExtraQuestionsAnswersSet}
225
- ref={formRef}
226
- allowExtraQuestionsEdit={summit.allow_update_attendee_extra_questions}
227
- questionContainerClassName={`columns is-multiline ${styles.extraQuestion} pt-3`}
228
- questionLabelContainerClassName={'column is-full pb-0'}
229
- questionControlContainerClassName={`column is-full pt-0`}
230
- shouldScroll2FirstError={false}
231
- onError={handleExtraQuestionError}
232
- />
233
- </>
234
- }
235
- { summit.registration_disclaimer_content &&
236
- <div className="columns">
237
- <div className={`column ${styles.extraQuestion} abc-checkbox`}>
238
- <input
239
- id={TicketKeys.disclaimerAccepted}
240
- name={TicketKeys.disclaimerAccepted}
241
- type="checkbox"
242
- onBlur={formik.handleBlur}
243
- onChange={(e) =>
244
- formik.setFieldTouched(TicketKeys.disclaimerAccepted, true) && formik.handleChange(e)
164
+ }
165
+ <div className={`content columns ${styles.extraQuestionsContainer}`}>
166
+ <div className="column is-three-fifths is-offset-one-fifth px-6-desktop py-6-desktop mb-6">
167
+ <h2>Attendee Information</h2>
168
+ <div className="columns is-multiline pt-4 pb-5">
169
+ <div className={`column is-full-mobile is-half ${styles.extraQuestion}`}>
170
+ <label htmlFor={TicketKeys.firstName}>First Name</label>
171
+ <Input
172
+ id={TicketKeys.firstName}
173
+ name={TicketKeys.firstName}
174
+ className="form-control"
175
+ type="text"
176
+ placeholder={'Your First Name'}
177
+ value={formik.values[TicketKeys.firstName]}
178
+ onBlur={formik.handleBlur}
179
+ onChange={!!initialValues[TicketKeys.firstName] ? noOpFn : formik.handleChange}
180
+ disabled={!!initialValues[TicketKeys.firstName]}
181
+ />
182
+ {(formik.touched[TicketKeys.firstName] || triedSubmitting) && formik.errors[TicketKeys.firstName] &&
183
+ <p className={styles.errorLabel}>{t("ticket_popup.edit_required")}</p>
245
184
  }
246
- checked={formik.values[TicketKeys.disclaimerAccepted]}
185
+ </div>
186
+ <div className={`column is-full-mobile is-half ${styles.extraQuestion}`}>
187
+ <label htmlFor={TicketKeys.lastName}>Last Name</label>
188
+ <Input
189
+ id={TicketKeys.lastName}
190
+ name={TicketKeys.lastName}
191
+ className="form-control"
192
+ type="text"
193
+ placeholder={'Your Last Name'}
194
+ value={formik.values[TicketKeys.lastName]}
195
+ onBlur={formik.handleBlur}
196
+ onChange={!!initialValues[TicketKeys.lastName] ? noOpFn : formik.handleChange}
197
+ disabled={!!initialValues[TicketKeys.lastName]}
198
+ />
199
+ {(formik.touched[TicketKeys.lastName] || triedSubmitting) && formik.errors[TicketKeys.lastName] &&
200
+ <p className={styles.errorLabel}>{t("ticket_popup.edit_required")}</p>
201
+ }
202
+ </div>
203
+ <div className={`column is-full-mobile is-half ${styles.extraQuestion}`}>
204
+ <label htmlFor={TicketKeys.email}>Email</label>
205
+ <Input
206
+ id={TicketKeys.email}
207
+ name={TicketKeys.email}
208
+ className="form-control"
209
+ type="text"
210
+ value={initialValues[TicketKeys.email]}
211
+ disabled={true}
212
+ />
213
+ </div>
214
+ <div className={`column is-full-mobile is-half ${styles.extraQuestion}`}>
215
+ <label htmlFor={TicketKeys.company}>Company</label>
216
+ <RegistrationCompanyInput
217
+ id={TicketKeys.company}
218
+ name={TicketKeys.company}
219
+ summitId={summit.id}
220
+ placeholder={'Your Company'}
221
+ value={formik.values[TicketKeys.company]}
222
+ onBlur={formik.handleBlur}
223
+ onChange={!!initialValues[TicketKeys.company].name ? noOpFn : formik.handleChange}
224
+ disabled={!!initialValues[TicketKeys.company].name}
225
+ tabSelectsValue={false}
226
+ />
227
+ {(formik.touched[TicketKeys.company] || triedSubmitting) && formik.errors[TicketKeys.company] &&
228
+ <p className={styles.errorLabel}>{t("ticket_popup.edit_required")}</p>
229
+ }
230
+ </div>
231
+ </div>
232
+ { hasExtraQuestions &&
233
+ <>
234
+ <h2 className="mb-3">Additional Information</h2>
235
+ <p>Please answer these additional questions.</p>
236
+ <ExtraQuestionsForm
237
+ extraQuestions={extraQuestions}
238
+ userAnswers={formik.values[TicketKeys.extraQuestions]}
239
+ onAnswerChanges={onExtraQuestionsAnswersSet}
240
+ ref={formRef}
241
+ allowExtraQuestionsEdit={summit.allow_update_attendee_extra_questions}
242
+ questionContainerClassName={`columns is-multiline ${styles.extraQuestion} pt-3`}
243
+ questionLabelContainerClassName={'column is-full pb-0'}
244
+ questionControlContainerClassName={`column is-full pt-0`}
245
+ shouldScroll2FirstError={false}
246
+ onError={handleExtraQuestionError}
247
247
  />
248
- <label htmlFor={TicketKeys.disclaimerAccepted}>
249
- {summit.registration_disclaimer_mandatory && <b> *</b>}
250
- </label>
251
- {(formik.touched[TicketKeys.disclaimerAccepted] || triedSubmitting) && formik.errors[TicketKeys.disclaimerAccepted] &&
252
- <p className={styles.errorLabel}>{t("ticket_popup.edit_required")}</p>
253
- }
254
- <div className="mt-3">
255
- <RawHTML>
256
- {summit.registration_disclaimer_content}
257
- </RawHTML>
248
+ </>
249
+ }
250
+ { summit.registration_disclaimer_content &&
251
+ <div className="columns">
252
+ <div className={`column ${styles.extraQuestion} abc-checkbox`}>
253
+ <input
254
+ id={TicketKeys.disclaimerAccepted}
255
+ name={TicketKeys.disclaimerAccepted}
256
+ type="checkbox"
257
+ onBlur={formik.handleBlur}
258
+ onChange={(e) =>
259
+ formik.setFieldTouched(TicketKeys.disclaimerAccepted, true) && formik.handleChange(e)
260
+ }
261
+ checked={formik.values[TicketKeys.disclaimerAccepted]}
262
+ />
263
+ <label htmlFor={TicketKeys.disclaimerAccepted}>
264
+ {summit.registration_disclaimer_mandatory && <b> *</b>}
265
+ </label>
266
+ {(formik.touched[TicketKeys.disclaimerAccepted] || triedSubmitting) && formik.errors[TicketKeys.disclaimerAccepted] &&
267
+ <p className={styles.errorLabel}>{t("ticket_popup.edit_required")}</p>
268
+ }
269
+ <div className="mt-3">
270
+ <RawHTML>
271
+ {summit.registration_disclaimer_content}
272
+ </RawHTML>
273
+ </div>
258
274
  </div>
259
275
  </div>
276
+ }
277
+ <button
278
+ className={`${styles.buttonSave} button is-large`}
279
+ disabled={formik.isSubmitting}
280
+ onClick={triggerSubmit}>
281
+ {!formik.isSubmitting && <>Save and Continue</>}
282
+ {formik.isSubmitting && <>Saving...</>}
283
+ </button>
260
284
  </div>
261
- }
262
- <button
263
- className={`${styles.buttonSave} button is-large`}
264
- disabled={formik.isSubmitting}
265
- onClick={triggerSubmit}>
266
- {!formik.isSubmitting && <>Save and Continue</>}
267
- {formik.isSubmitting && <>Saving...</>}
268
- </button>
269
285
  </div>
270
- </div>
286
+ </>
271
287
  )
272
288
  };
273
289
 
@@ -277,13 +293,22 @@ const ExtraQuestionsPage = (
277
293
  user,
278
294
  summit,
279
295
  extraQuestions,
296
+ attendee,
280
297
  saveAttendeeQuestions,
281
298
  getExtraQuestions,
299
+ getAttendeeData
282
300
  }
283
301
  ) => {
284
302
 
303
+ const fragmentParser = new FragmentParser();
304
+
305
+ const attendeeId = fragmentParser.getParam('attendee') || null;
306
+
307
+ const [someoneElseLoaded, setSomeoneElseLoaded] = useState(null);
308
+
285
309
  useEffect(() => {
286
- getExtraQuestions();
310
+ getExtraQuestions(attendeeId);
311
+ if(attendeeId) getAttendeeData(attendeeId).then(()=> setSomeoneElseLoaded(true)).catch(()=> setSomeoneElseLoaded(false));
287
312
  }, [])
288
313
 
289
314
  return (
@@ -292,7 +317,11 @@ const ExtraQuestionsPage = (
292
317
  user={user}
293
318
  summit={summit}
294
319
  extraQuestions={extraQuestions}
295
- saveAttendeeQuestions={saveAttendeeQuestions} />
320
+ attendeeId={attendeeId || null}
321
+ attendee={attendeeId ? attendee : null}
322
+ saveAttendeeQuestions={saveAttendeeQuestions}
323
+ someoneElseLoaded={someoneElseLoaded}
324
+ />
296
325
  </Layout>
297
326
  )
298
327
  }
@@ -307,16 +336,18 @@ ExtraQuestionsPageTemplate.propTypes = {
307
336
  saveAttendeeQuestions: PropTypes.func,
308
337
  }
309
338
 
310
- const mapStateToProps = ({ userState, summitState }) => ({
339
+ const mapStateToProps = ({ userState, summitState, extraQuestionState }) => ({
311
340
  user: userState.userProfile,
312
341
  loading: userState.loading,
313
342
  summit: summitState.summit,
314
343
  extraQuestions: summitState.extra_questions,
344
+ attendee: extraQuestionState.attendee
315
345
  })
316
346
 
317
347
  export default connect(mapStateToProps,
318
348
  {
319
349
  saveAttendeeQuestions,
320
350
  getExtraQuestions,
351
+ getAttendeeData
321
352
  }
322
- )(ExtraQuestionsPage);
353
+ )(ExtraQuestionsPage);