PluginProbe
seQura / 3.0.0
seQura v3.0.0
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.0.0, at assets/js/src/core/ConnectionSettingsForm.js

420 lines 15.2 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
253 const merchantId = configuration.appState === SequraFE.appStates.ONBOARDING ? 'test' : data.countrySettings[0]?.merchantId;
254 api.post(configuration.validateConnectionDataUrl, { ...changedSettings, merchantId: merchantId }, SequraFE.customHeader)
255 .then((result) => areCredentialsValid(result) ? saveChangedData() : handleValidationError());
256 }
257 }
258
259 const hasChange = () => {
260 return activeSettings.environment !== changedSettings.environment ||
261 activeSettings.password !== changedSettings.password ||
262 activeSettings.username !== changedSettings.username ||
263 activeSettings.sendStatisticalData !== changedSettings.sendStatisticalData
264 }
265
266 /**
267 * Returns true if username and password are valid.
268 *
269 * @param {{isValid: boolean, reason: string|null}} result
270 */
271 const areCredentialsValid = (result) => {
272 if (!result.isValid && result.reason.includes('merchantId')) {
273 navigateToOnboarding = true;
274 }
275
276 return result.isValid || result.reason.includes('merchantId');
277 }
278
279 /**
280 * Handle connection validation error.
281 */
282 const handleValidationError = () => {
283 SequraFE.responseService.errorHandler({ errorCode: 'general.errors.connection.invalidUsernameOrPassword' }).catch(() => {
284 });
285
286 utilities.hideLoader();
287 }
288
289 const saveChangedData = () => {
290 utilities.showLoader();
291 api.post(configuration.saveConnectionDataUrl, changedSettings, SequraFE.customHeader)
292 .then(() => {
293 if (configuration.appState === SequraFE.appStates.ONBOARDING) {
294 if (activeSettings.username.length !== 0) {
295 SequraFE.state.setCredentialsChanged();
296 }
297
298 const index = SequraFE.pages.onboarding.indexOf(SequraFE.appPages.ONBOARDING.CONNECT)
299 SequraFE.pages.onboarding.length > index + 1 ?
300 window.location.hash = configuration.appState + '-' + SequraFE.pages.onboarding[index + 1] :
301 window.location.hash = SequraFE.appStates.PAYMENT + '-' + SequraFE.appPages.PAYMENT.METHODS;
302 }
303
304 activeSettings = utilities.cloneObject(changedSettings);
305 SequraFE.state.setData('connectionSettings', activeSettings);
306
307 disableFooter(true);
308
309 if (configuration.appState === SequraFE.appStates.SETTINGS && navigateToOnboarding) {
310 SequraFE.state.setCredentialsChanged();
311 SequraFE.state.goToState(SequraFE.appStates.ONBOARDING);
312 } else {
313 utilities.hideLoader();
314 }
315 }).finally(() => utilities.hideLoader());
316 }
317
318 /**
319 * Handles the disconnect button click.
320 */
321 const handleDisconnect = () => {
322 showDisconnectModal().then((confirmed) => {
323 if (!confirmed) {
324 return;
325 }
326
327 utilities.showLoader();
328 api.post(configuration.disconnectUrl, null, SequraFE.customHeader)
329 .then(() => SequraFE.state.display())
330 .finally(utilities.hideLoader);
331 })
332 }
333
334 /**
335 * Disables the form footer controls.
336 *
337 * @param disable
338 */
339 const disableFooter = (disable) => {
340 if (configuration.appState !== SequraFE.appStates.ONBOARDING) {
341 utilities.disableFooter(disable);
342 }
343 }
344
345 /**
346 * Shows the disconnect modal dialog.
347 *
348 * @returns {Promise}
349 */
350 const showDisconnectModal = () => {
351 return new Promise((resolve) => {
352 const modal = components.Modal.create({
353 title: `connection.disconnect.title`,
354 className: `sq-modal sqv--connection-modal`,
355 content: [generator.createElement('p', '', `connection.disconnect.message`)],
356 footer: true,
357 buttons: [
358 {
359 type: 'secondary',
360 label: 'general.cancel',
361 onClick: () => {
362 modal.close();
363 resolve(false);
364 }
365 },
366 {
367 type: 'primary',
368 label: 'general.confirm',
369 onClick: () => {
370 modal.close();
371 resolve(true);
372 }
373 }
374 ]
375 });
376
377 modal.open();
378 });
379 }
380
381 /**
382 * Shows the confirmation modal dialog.
383 *
384 * @returns {Promise}
385 */
386 const showConfirmModal = () => {
387 return new Promise((resolve) => {
388 const modal = components.Modal.create({
389 title: `connection.modal.title`,
390 className: `sq-modal sqv--connection-modal`,
391 content: [generator.createElement('p', '', `connection.modal.message`)],
392 footer: true,
393 buttons: [
394 {
395 type: 'secondary',
396 label: 'general.cancel',
397 onClick: () => {
398 modal.close();
399 resolve(false);
400 }
401 },
402 {
403 type: 'primary',
404 label: 'general.confirm',
405 onClick: () => {
406 modal.close();
407 resolve(true);
408 }
409 }
410 ]
411 });
412
413 modal.open();
414 });
415 };
416 }
417
418 SequraFE.ConnectionSettingsForm = ConnectionSettingsForm;
419 })();
420