PluginProbe
seQura / 3.2.2
seQura v3.2.2
4.3.4 4.3.3 4.3.2 4.3.1 trunk 2.0.0 2.0.10 2.0.11 2.0.12 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 3.0.0 3.0.2 3.0.5 3.0.6 3.0.7 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 4.0.0 All 30 releases
sequra / assets / js / src / core / ConnectionSettingsForm.js

ConnectionSettingsForm.js in seQura 3.2.2, at assets/js/src/core/ConnectionSettingsForm.js

418 lines 15.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 if (!window.SequraFE) {
2 window.SequraFE = {};
3 }
4
5 (function () {
6 /**
7 * @typedef ConnectionSettings
8 * @property {'live' | 'sandbox'} environment
9 * @property {string} username
10 * @property {string} password
11 * @property {boolean} sendStatisticalData
12 */
13
14 /**
15 * Handles connection settings form logic.
16 *
17 * @param {{
18 * connectionSettings: ConnectionSettings,
19 * countrySettings: CountrySettings[]
20 * }} data
21 * @param {{
22 * saveConnectionDataUrl: string,
23 * validateConnectionDataUrl: string,
24 * disconnectUrl: string,
25 * page: string,
26 * appState: string
27 * }} configuration
28 * @constructor
29 */
30 function ConnectionSettingsForm(data, configuration) {
31 /** @type AjaxServiceType */
32 const api = SequraFE.ajaxService;
33
34 const {
35 elementGenerator: generator,
36 validationService: validator,
37 utilities,
38 components
39 } = SequraFE;
40
41 let navigateToOnboarding = false;
42 /** @type ConnectionSettings */
43 let activeSettings;
44 /** @type ConnectionSettings */
45 let changedSettings;
46 /** @type ConnectionSettings */
47 const defaultFormData = {
48 environment: 'sandbox',
49 username: '',
50 password: '',
51 sendStatisticalData: true
52 };
53
54 /**
55 * Handles form rendering.
56 */
57 this.render = () => {
58 if (!activeSettings) {
59 activeSettings = utilities.cloneObject(defaultFormData);
60 for (let key in activeSettings) {
61 activeSettings[key] = data.connectionSettings?.[key] ?? defaultFormData[key];
62 }
63 }
64
65 changedSettings = utilities.cloneObject(activeSettings);
66
67 initForm();
68 disableFooter(true);
69 utilities.hideLoader();
70 }
71
72 /**
73 * Initializes the form structure.
74 */
75 const initForm = () => {
76 const pageContent = document.querySelector('.sq-content');
77 pageContent?.append(
78 generator.createElement('div', 'sq-content-inner sqv--connect', '', null, [
79 generator.createElement('div', 'sqp-flash-message-wrapper'),
80 generator.createPageHeading({
81 title: `connection.title.${configuration.appState}`,
82 text: `connection.description.${configuration.appState}`
83 }),
84 generator.createRadioGroupField({
85 value: changedSettings.environment,
86 label: 'connection.environment.label',
87 options: [
88 { label: 'connection.environment.options.live', value: 'live' },
89 { label: 'connection.environment.options.sandbox', value: 'sandbox' }
90 ],
91 onChange: (value) => handleChange('environment', value)
92 }),
93 generator.createTextField({
94 name: 'username-input',
95 value: changedSettings.username,
96 className: 'sq-text-input',
97 label: 'connection.username.label',
98 description: 'connection.username.description',
99 onChange: (value) => handleChange('username', value)
100 }),
101 generator.createPasswordField({
102 name: 'password-input',
103 value: changedSettings.password,
104 className: 'sq-password-input',
105 label: 'connection.password.label',
106 description: 'connection.password.description',
107 onChange: (value) => handleChange('password', value)
108 }),
109 ])
110 );
111
112 document.querySelector('.sqp-description').append(
113 generator.createButtonLink({
114 className: 'sq-link-button',
115 text: 'connection.description.endLink',
116 href: 'https://en.sequra.com/en/contact',
117 openInNewTab: true
118 })
119 )
120
121 renderByAppState();
122 }
123
124 /**
125 * Renders form parts that are dependant of application state.
126 */
127 const renderByAppState = () => {
128 const pageContent = document.querySelector('.sq-content');
129 const pageInnerContent = document.querySelector('.sq-content-inner');
130
131 if (configuration.appState === SequraFE.appStates.ONBOARDING) {
132 pageInnerContent?.append(
133 SequraFE.isPromotional ? [] : generator.createCheckboxField({
134 className: 'sq-statistics',
135 value: changedSettings.sendStatisticalData,
136 description: 'connection.sendStatisticalData.description.text',
137 onChange: (value) => handleChange('sendStatisticalData', value)
138 }),
139 generator.createButtonField({
140 className: 'sqm--block',
141 buttonType: 'primary',
142 buttonLabel: 'general.continue',
143 onClick: handleSave
144 })
145 )
146
147 !SequraFE.isPromotional && document.querySelector('.sq-statistics .sqp-field-subtitle').append(
148 generator.createButtonLink({
149 className: 'sq-info-button',
150 text: 'connection.sendStatisticalData.description.endLink',
151 href: 'https://en.sequra.com/',
152 openInNewTab: true
153 })
154 )
155
156 return;
157 }
158
159 pageInnerContent?.append(
160 generator.createButtonField({
161 className: 'sqm--block',
162 buttonType: 'danger',
163 buttonSize: 'medium',
164 buttonLabel: 'general.disconnect',
165 onClick: handleDisconnect
166 })
167 );
168
169 pageContent?.append(
170 generator.createPageFooter({
171 onCancel: () => {
172 const pageContent = document.querySelector('.sq-content');
173 while (pageContent?.firstChild) {
174 pageContent?.removeChild(pageContent.firstChild);
175 }
176
177 this.render();
178 },
179 onSave: handleSave
180 })
181 );
182 }
183
184 /**
185 * Validates form inputs.
186 *
187 * @returns {boolean}
188 */
189 const isFormValid = () => {
190 let errorCount = 0;
191
192 !validator.validateRequiredField(
193 document.querySelector('[name="username-input"]'),
194 'validation.requiredField'
195 ) && errorCount++;
196
197 !validator.validateRequiredField(
198 document.querySelector('[name="password-input"]'),
199 'validation.requiredField'
200 ) && errorCount++;
201
202 return errorCount === 0;
203 }
204
205 /**
206 * Handles form input changes.
207 *
208 * @param name
209 * @param value
210 */
211 const handleChange = (name, value) => {
212 if (['username', 'password'].includes(name)) {
213 validator.validateRequiredField(
214 document.querySelector(`[name="${name}-input"]`),
215 'validation.requiredField'
216 );
217 }
218
219 changedSettings[name] = value;
220 disableFooter(false);
221 }
222
223 /**
224 * Handles form saving.
225 */
226 const handleSave = () => {
227 if (!isFormValid()) {
228 return;
229 }
230
231 if (!hasChange() && configuration.appState !== SequraFE.appStates.ONBOARDING) {
232 disableFooter(true);
233
234 return;
235 }
236
237 if (hasChange() && activeSettings.environment !== changedSettings.environment
238 && configuration.appState !== SequraFE.appStates.ONBOARDING) {
239 showConfirmModal().then((confirmed) => {
240 if (!confirmed) {
241 return;
242 }
243
244 utilities.showLoader();
245
246 const merchantId = configuration.appState === SequraFE.appStates.ONBOARDING ? 'test' : data.countrySettings[0]?.merchantId;
247 api.post(configuration.validateConnectionDataUrl, { ...changedSettings, merchantId: merchantId }, SequraFE.customHeader)
248 .then((result) => areCredentialsValid(result) ? saveChangedData() : handleValidationError())
249 })
250 } else {
251 utilities.showLoader();
252 api.post(configuration.validateConnectionDataUrl, changedSettings, SequraFE.customHeader)
253 .then((result) => areCredentialsValid(result) ? saveChangedData() : handleValidationError());
254 }
255 }
256
257 const hasChange = () => {
258 return activeSettings.environment !== changedSettings.environment ||
259 activeSettings.password !== changedSettings.password ||
260 activeSettings.username !== changedSettings.username ||
261 activeSettings.sendStatisticalData !== changedSettings.sendStatisticalData
262 }
263
264 /**
265 * Returns true if username and password are valid.
266 *
267 * @param {{isValid: boolean, reason: string|null}} result
268 */
269 const areCredentialsValid = (result) => {
270 if (!result.isValid && result.reason.includes('merchantId')) {
271 navigateToOnboarding = true;
272 }
273
274 return result.isValid || result.reason.includes('merchantId');
275 }
276
277 /**
278 * Handle connection validation error.
279 */
280 const handleValidationError = () => {
281 SequraFE.responseService.errorHandler({ errorCode: 'general.errors.connection.invalidUsernameOrPassword' }).catch(() => {
282 });
283
284 utilities.hideLoader();
285 }
286
287 const saveChangedData = () => {
288 utilities.showLoader();
289 api.post(configuration.saveConnectionDataUrl, changedSettings, SequraFE.customHeader)
290 .then(() => {
291 if (configuration.appState === SequraFE.appStates.ONBOARDING) {
292 if (activeSettings.username.length !== 0) {
293 SequraFE.state.setCredentialsChanged();
294 }
295
296 const index = SequraFE.pages.onboarding.indexOf(SequraFE.appPages.ONBOARDING.CONNECT)
297 SequraFE.pages.onboarding.length > index + 1 ?
298 window.location.hash = configuration.appState + '-' + SequraFE.pages.onboarding[index + 1] :
299 window.location.hash = SequraFE.appStates.PAYMENT + '-' + SequraFE.appPages.PAYMENT.METHODS;
300 }
301
302 activeSettings = utilities.cloneObject(changedSettings);
303 SequraFE.state.setData('connectionSettings', activeSettings);
304
305 disableFooter(true);
306
307 if (configuration.appState === SequraFE.appStates.SETTINGS && navigateToOnboarding) {
308 SequraFE.state.setCredentialsChanged();
309 SequraFE.state.goToState(SequraFE.appStates.ONBOARDING);
310 } else {
311 utilities.hideLoader();
312 }
313 }).finally(() => utilities.hideLoader());
314 }
315
316 /**
317 * Handles the disconnect button click.
318 */
319 const handleDisconnect = () => {
320 showDisconnectModal().then((confirmed) => {
321 if (!confirmed) {
322 return;
323 }
324
325 utilities.showLoader();
326 api.post(configuration.disconnectUrl, null, SequraFE.customHeader)
327 .then(() => SequraFE.state.display())
328 .finally(utilities.hideLoader);
329 })
330 }
331
332 /**
333 * Disables the form footer controls.
334 *
335 * @param disable
336 */
337 const disableFooter = (disable) => {
338 if (configuration.appState !== SequraFE.appStates.ONBOARDING) {
339 utilities.disableFooter(disable);
340 }
341 }
342
343 /**
344 * Shows the disconnect modal dialog.
345 *
346 * @returns {Promise}
347 */
348 const showDisconnectModal = () => {
349 return new Promise((resolve) => {
350 const modal = components.Modal.create({
351 title: `connection.disconnect.title`,
352 className: `sq-modal sqv--connection-modal`,
353 content: [generator.createElement('p', '', `connection.disconnect.message`)],
354 footer: true,
355 buttons: [
356 {
357 type: 'secondary',
358 label: 'general.cancel',
359 onClick: () => {
360 modal.close();
361 resolve(false);
362 }
363 },
364 {
365 type: 'primary',
366 label: 'general.confirm',
367 onClick: () => {
368 modal.close();
369 resolve(true);
370 }
371 }
372 ]
373 });
374
375 modal.open();
376 });
377 }
378
379 /**
380 * Shows the confirmation modal dialog.
381 *
382 * @returns {Promise}
383 */
384 const showConfirmModal = () => {
385 return new Promise((resolve) => {
386 const modal = components.Modal.create({
387 title: `connection.modal.title`,
388 className: `sq-modal sqv--connection-modal`,
389 content: [generator.createElement('p', '', `connection.modal.message`)],
390 footer: true,
391 buttons: [
392 {
393 type: 'secondary',
394 label: 'general.cancel',
395 onClick: () => {
396 modal.close();
397 resolve(false);
398 }
399 },
400 {
401 type: 'primary',
402 label: 'general.confirm',
403 onClick: () => {
404 modal.close();
405 resolve(true);
406 }
407 }
408 ]
409 });
410
411 modal.open();
412 });
413 };
414 }
415
416 SequraFE.ConnectionSettingsForm = ConnectionSettingsForm;
417 })();
418