You can build a Freshteam app by using the following steps.
- Install NVM
- Install Node
- Install the CLI
- Create an App
- Test the App
- Validate and Pack
Apps built on the Freshworks platform are compatible with the latest and immediately preceding version of the following browsers.
- Google Chrome
- Firefox
- Edge
- Safari
Install NVM
Node Version Manager (NVM) enables you to install and work with multiple versions of Node.js. You can use NVM to install Node.
- To install NVM:
- On Mac or Linux, follow the installation and upgrade steps.
- On Windows, navigate to the release channel > Assets, download the
nvm-setup.zip
file, extract the contents of the file, and use the installer in the extracted files.
- To verify the NVM installation, run the following command.
nvm --version
Install Node
To install Node using NVM, run the following command.
nvm install 14
Note
With the latest FDK version, support for building apps on earlier versions of Node.js is unavailable.
Run the following command to verify the Node installation.
node --version
Note
On Windows, if the Node version is not displayed, run the nvm on command to enable NVM.
Run the following command to set the default Node version.
nvm alias default 14
On Windows, to install the required tools and configurations to start using Node, use the following command.
npm install --global --production windows-build-tools
For more information, see the write-up on node-gyp.
Install the Freshworks CLI
Note
- Ensure to use npm for CLI installation. Also, ensure to use the npm version that is shipped with Node. For information on supported Node versions, see FDK and compatible Node.js versions. Use of any other npm version or use of alternative package managers such as YARN can affect the CLI installation and dependencies management.
- Uninstall the previous CLI version by using the
npm uninstall fdk -g
command. - The Developer portal and SDK Terms of Use apply to the use of the CLI.
- Run the following command to install the latest CLI version.
npm install https://dl.freshdev.io/cli/fdk.tgz -g
- Run the following command to verify the CLI installation.
fdk version
Create an App
To create an app on the Job Details page:
- From the command line, navigate to the empty directory in which you want to create an app.
- Run the following command.A prompt to choose a product is displayed.
fdk create
- Select
freshteam
and press Enter. A new app is created based on theyour_first_app
template.
The following directories and files are created as a result of the fdk create
command.
Directory/File | Description |
---|---|
app/ | Contains all the files required for the front-end component of an app. The JS file follows the ES5 standard. |
app/icon.svg | Contains the product icon. If you intend to extend the app, you can replace the icon.svg file. The icon file should be of SVG type with a resolution of 64 x 64 pixels. |
app/template.html | Contains the HTML code required for the app's UI, which is rendered in an IFrame. |
config/ | Contains the installation parameters and OAuth configuration files. |
config/iparams.json | Specifies all the installation parameters whose values are set when the app is installed. For more information, see Installation Parameters. |
manifest.json | Contains details such as the platform version the app uses, product to which the app belongs, event listeners for serverless apps, SMI functions that can be invoked from the app's front end component, and npm packages (dependencies) that the app uses. |
README.md | Contains additional instructions, information, and specifications. |
Note
- The
iparam_test_data.json
file has been deprecated. Before testing the app, navigate tohttp://localhost:10001/custom_configs
and enter appropriate values for the configured installation parameters. - If you use React, Ember, Vue, or any other front-end framework, include the source files of your app in the src directory to ensure quick app reviews.
Test the App
Note
- Use the latest version of Chrome browser.
- Ensure to sign up for a Freshteam account.
- From the command line, navigate to the directory that contains the app related files and run the following command.
fdk run
- Log in to your Freshteam account.
- To the Freshteam account URL, append
?dev=true
. Example URL:https://domain.freshteam.com/dashboard/hire/jobs/0000000001/info?dev=true
. - To allow the Chrome browser to connect to the test server that runs on HTTP,
- On Chrome 79 and higher versions:
- Navigate to Settings > Advanced > Privacy and security > Site settings > Insecure content.
- In the Allow section, click Add and enter the Freshteam account URL. Example URL:
https://domain.freshteam.com
- On Chrome 78 and lower versions:
- In the address bar, if a shield icon is displayed, click the icon. A warning message is displayed as the Support portal runs on HTTPS and the test server runs on HTTP.
- Click Load Unsafe Scripts to continue testing.
- On Chrome 79 and higher versions:
- Navigate to the Recruit > Jobs > Details page. If the app is created successfully, the app is displayed in the Job Boards > Other Job Boards section, as shown in the image.
Note
- Each component in the coverage summary should be at least 80% for apps to be submitted in Freshworks Marketplace. See Code coverage for more information.
- If you have any issues when testing your app, attach detailed logs of the output in your support ticket for quick resolution from the support team.
Code
1import Cookie from 'js-cookie';
2import { SiteUtilities } from '../utils';
3import formSsoSignup from '../form-sso-signup';
4import fchatFormHandler from './fchat-form-handlers';
5// import fsalesFormHandler from './fsales-form-handlers';
6// import fmarketerFormHandler from './fmarketer-form-handlers';
7import fcallerFormHandler from './fcaller-form-handlers';
8import fdeskFormHandler from './fdesk-form-handlers';
9import fpingFormHandler from './fping-form-handlers';
10import fworksFormHandler from './fworks-form-handlers';
11import fstatusFormHandler from './fstatus-form-handlers';
12import fteamFormHandler from './fteam-form-handlers';
13import fwCrmFormHandler from './fwcrm-form-handlers';
14import GA4Utilities from '../utils/ga4-utilities';
15const { getFchatCurrencyUnit, fchatSignupFormSuccess } = fchatFormHandler;
16// const { fsalesSignupFormSuccess } = fsalesFormHandler;
17// const { fmarketerSignupFormSuccess } = fmarketerFormHandler;
18const { fcallerSignupFormSuccess } = fcallerFormHandler;
19const { fdeskSignupFormSuccess } = fdeskFormHandler;
20const { fpingSignupFormSuccess } = fpingFormHandler;
21const { fworksCrmSignupForm, fworksCrmSignupFormSuccess } = fworksFormHandler;
22const { fstatusSignupFormSuccess } = fstatusFormHandler;
23const { fteamSignupFormSuccess } = fteamFormHandler;
24const { fwCrmSignupForm, fwCrmSignupFormSuccess } = fwCrmFormHandler;
25const { handleGoogleSingupErrHandler } = formSsoSignup;
26
27const alohaForm = (classname, form) => {
28 const $form = $(form);
29 const product = $form.data('product');
30 SiteUtilities.resetSignupPerformanceLog();
31 localStorage.setItem('signupStartTime', new Date().toISOString());
32 // control form section in sso signup
33 const $formSection = $($form.parent('[data-form-section]'));
34 const $submitButton = $form.find('.button');
35 const $emailField = $form.find('input[name^="email"]');
36 const email = $emailField.val() || null;
37 const isEmailBusiness = email && !(email.includes('@gmail'));
38 let apiDomain = $form.data('signup-url');
39 const firstName = $form.find('.first-name-form').val() || null;
40 const lastName = $form.find('.last-name-form').val() || null;
41 const domain = $form.find('.domain-form').val() || null;
42 const phone = $form.find('.phone-form').val() || null;
43 const company = $form.find('.company-form').val() || null;
44 const plan = $form.find('input[name="plan-id"]').val() || null;
45 let remindEndPoint = $form.data('remind-path');
46 const $miscPayload = $form.find('.aloha-misc-payload');
47 let growSumo = false;
48 const isExternalIframeSignup = $form.parents('.external-iframe-signup').length;
49 const currentReferrer = SiteUtilities.getCurrentReferrer();
50 let currentLoc = SiteUtilities.getCurrentLocation();
51 const location = session.simpleGeoLocation;
52 const productDesign = $form.hasClass('product-signupform');
53 const planId = $form.data('plan_id') || null;
54 if (planId && !$form.find('.fm-asset-id').val() && !$form.data('fm_asset_id')) {
55 SiteUtilities.fwcrmAssetId($form, planId);
56 }
57 const fmAssetId = $form.find('.fm-asset-id').val() || $form.data('fm_asset_id') || 3649313;
58 const tacticId = SiteUtilities.getTacticID() || null;
59 const compositeKey = SiteUtilities.getCompositeKey(email, fmAssetId, tacticId);
60 const gclid = SiteUtilities.getGclId();
61 const latestPageVisits = localStorage.getItem('latest_page_visits') || null;
62
63 session.ga_client_id = SiteUtilities.getCombinedGaClientId(session, gclid, compositeKey);
64
65 let fsCookie;
66 try {
67 fsCookie = freshsales.anonymous_id || '';
68 } catch (exception) {
69 SiteUtilities.log(
70 'Freshsales session exception.',
71 'warning',
72 'submitHandlers.fsalesForm (fsCookie - Catch Exception)',
73 0,
74 exception
75 );
76 }
77
78 if (productDesign) {
79 $submitButton.addClass('button--product-disabled').attr('disabled', 'disabled');
80 if (product === 'fworks') {
81 fworksCrmSignupFormSuccess($form, true);
82 }
83 if (product === 'fwcrm' || product === 'fmarketer' || product === 'fsales') {
84 fwCrmSignupFormSuccess($form, true);
85 }
86 } else {
87 $submitButton.addClass('button--loading').attr('disabled', 'disabled');
88 }
89
90 if (currentReferrer && isExternalIframeSignup) {
91 currentLoc = currentReferrer;
92 if (session.current_session) {
93 session.current_session.url = `${SiteUtilities.getCurrentLocation()}?first_referrer=${currentReferrer}`;
94 }
95 }
96
97 let ipAddress;
98 const country = session.simpleGeoLocation.countryName;
99 let sessionObj = (typeof (session) === 'object') ? JSON.parse(JSON.stringify(session)) : null;
100 if (SiteUtilities.GDPRFunctionalCookieDisabled()) {
101 const { simpleGeoLocation: { countryCode, countryName, country: { iso_code: isoCode } = {} } = {} } = session || {};
102 if (sessionObj) {
103 sessionObj.location = {
104 country: {
105 iso_code: isoCode || ''
106 },
107 ipAddress: '',
108 countryCode: countryCode || '',
109 countryName: countryName || ''
110 };
111 sessionObj.simpleGeoLocation = sessionObj.location;
112 sessionObj.geoLocation = sessionObj.location;
113 }
114 } else {
115 // Binding maxmind_session
116 session.location = session.simpleGeoLocation;
117 ipAddress = (location && location.ipAddress) ? location.ipAddress : null;
118 sessionObj = session;
119 }
120
121 if (sessionObj) {
122 // attach Google click ID (gclid) in sessionObj
123 sessionObj.gclid = gclid;
124 }
125
126 const postData = {
127 user: {
128 first_name: firstName,
129 middle_name: null,
130 last_name: lastName,
131 email,
132 phone,
133 mobile: null,
134 job_title: null,
135 company_name: company
136 },
137 organisation: null,
138 session_json: sessionObj,
139 currency: null,
140 plan: plan,
141 first_referrer: (isExternalIframeSignup && currentReferrer) ? currentReferrer : (Cookie.get('fw_fr') || currentLoc),
142 first_landing_page: Cookie.get('fw_flu') || '',
143 misc: {
144 fs_cookie: fsCookie,
145 composite_stitching_key: compositeKey
146 }
147 };
148
149 $miscPayload.each((index, ele) => {
150 postData.misc[$(ele).data('key')] = $(ele).data('value');
151 });
152
153 const marketingCampaign = $form.find('[name="MarketingCampaign"],[name="Marketing Campaign"],[name="marketing_campaign"],[name="marketingcampaign"],[name="marketing campaign"]').data('custom-value');
154
155 if (marketingCampaign) {
156 postData.misc.marketing_campaign = marketingCampaign;
157 }
158
159 if (tacticId) {
160 postData.misc.allocadia_tactic_id = tacticId;
161 }
162
163 if (fmAssetId) {
164 postData.misc.allocadia_asset_id = fmAssetId;
165 }
166 const shopifyJWT = SiteUtilities.getParameterByName('shopify_jwt');
167 const JWTToken = $form.find('.google-jwt-token').val();
168 if (JWTToken && JWTToken.length) {
169 postData.user.oauth_details = {
170 oauth_provider: 'google',
171 token: JWTToken
172 };
173 }
174
175 switch (product) {
176 case 'fchat':
177 /**
178 * BUG:WEB-921
179 * Browser version is obtained from navigation.userAgent using `session.json`.
180 * when the browser version is, say, "13.0.4", parseFloat returns "13". But when it is "13.1.4",
181 * it will return "13.1". So it is because of parseFloat implementation, the payload is varying.
182 * Fchat expects this datatype to be "long". When "float" is sent, it fails.
183 */
184 if (postData.session_json.browser && postData.session_json.browser.version) {
185 postData.session_json.browser.version = parseInt(postData.session_json.browser.version);
186 }
187 const currencyUnit = getFchatCurrencyUnit(session.simpleGeoLocation.countryName, session.simpleGeoLocation.countryCode);
188 postData.misc.preferredDomain = company;
189 postData.currency = currencyUnit;
190 if (latestPageVisits) postData.misc.latest_page_visits = latestPageVisits;
191 break;
192
193 case 'fdesk':
194 const getSignupEndpointLang = (workflowLocale) => {
195 return {
196 de: 'de',
197 fr: 'fr',
198 es: 'es',
199 pt: 'pt-BR',
200 'latam-es': 'es-LA',
201 nl: 'nl',
202 se: 'sv-SE',
203 en: 'en',
204 ja: 'ja-JP',
205 ru: 'ru-RU',
206 ko: 'ko',
207 it: 'it',
208 cn: 'zh-CN',
209 th: 'th',
210 vi: 'vi',
211 pl: 'pl',
212 hk: 'zh-TW',
213 fi: 'fi',
214 tr: 'tr'
215 }[workflowLocale];
216 };
217 const currentLang = $('body').attr('data-lang');
218 postData.misc.account_name = company;
219 postData.misc.account_lang = getSignupEndpointLang(currentLang) || 'en';
220 growSumo = true;
221 /** ** Conditionally add mandatory params for aloha internal environment ** **/
222 if (window.location.href.includes('/signup-int')) {
223 postData.cloud_type = window.location.href.includes('omnichannel') ? 'omnichannel' : 'messaging';
224 postData.misc.account_domain = company;
225 postData.misc.preferredDomain = company;
226 }
227 if (latestPageVisits) postData.misc.latest_page_visits = latestPageVisits;
228 break;
229
230 case 'fcaller':
231 remindEndPoint = remindEndPoint || 'https://signup.freshcaller.com/find_domain?user_email=';
232 if (latestPageVisits) postData.misc.latest_page_visits = latestPageVisits;
233 break;
234
235 case 'fping':
236 postData.misc.application_url = $form.find('input[name^=url]').val() || null;
237 break;
238
239 case 'fworks':
240 postData.misc.account_domain = company ? company.replace(/[^a-zA-Z0-9-]/g, '') : null;
241 postData.misc.account_name = company;
242 growSumo = true;
243 break;
244
245 case 'fstatus':
246 postData.misc.website_url = $form.find('input[name^=url]').val() || null;
247 break;
248
249 case 'fteam':
250 postData.misc.source = SiteUtilities.getParameterByName('utm_source');
251 postData.misc.medium = SiteUtilities.getParameterByName('utm_medium');
252 postData.misc.campaign = SiteUtilities.getParameterByName('utm_campaign');
253 postData.misc.validate_duplicate = true;
254 postData.misc.time_zone = session.time.tz_offset;
255 postData.misc.gdpr_first_opt_in = ($form.find('input[name="send_promotions"]').is(':checked'));
256 postData.misc.country = country;
257 postData.misc.pre_visits = Cookie.get('fw_vi') || 0;
258 break;
259
260 case 'fsales':
261 case 'fmarketer':
262 case 'fwcrm':
263 const isShopifySignup = (shopifyJWT) || SiteUtilities.getParameterByName('att_referrer').includes('shopify');
264 let cloudType = $form.data('cloud_type') || null;
265 const currencies = {
266 US: 'USD',
267 EU: 'EUR',
268 GB: 'GBP',
269 AU: 'AUD',
270 IN: 'INR'
271 };
272 const selectedCurrency = SiteUtilities.selectCountry(currencies, location.countryCode, location.countryName);
273 const currency = currencies[selectedCurrency];
274 let planvalue = `sales360_sales_enterprise_${currency}_Yearly`;
275 const marketplaceProduct = $form.data('marketplace-product') || product;
276 if (planId) {
277 const planObj = {
278 mc: { name: `sales360_marketing_enterprise_${currency}_Yearly` },
279 c4l: { name: `sales360_clc_enterprise_${currency}_Yearly` },
280 suite: { name: `sales360_clc_enterprise_${currency}_Yearly` }
281 };
282 planvalue = (planObj[planId]) ? planObj[planId].name : planvalue;
283 }
284 if (isShopifySignup) {
285 const marketplacePlanValues = {
286 fmarketer: {
287 planId: `sales360_marketing_growth_${currency}_Yearly`,
288 cloudType: 'marketing_cloud'
289 },
290 fdmessaging: {
291 planId: `crm_messaging_growth_${currency}_Yearly`,
292 cloudType: 'freshdesk_messaging_cloud'
293 }
294 };
295 const planValues = marketplacePlanValues[marketplaceProduct];
296 planvalue = planValues.planId;
297 cloudType = planValues.cloudType;
298 }
299 if (cloudType) {
300 postData.cloud_type = cloudType;
301 }
302 const attReferrer = localStorage.getItem('att_referrer');
303 if (attReferrer) {
304 postData.misc.att_referrer = `${attReferrer}_${marketplaceProduct}`;
305 }
306 if (shopifyJWT) {
307 postData.user.oauth_details = {
308 oauth_provider: 'shopify',
309 token: shopifyJWT
310 };
311 }
312 postData.misc.plan_id = planvalue;
313 postData.misc.account_domain = company ? company.replace(/[^a-zA-Z0-9-]/g, '') : null;
314 postData.misc.account_name = company;
315 if (latestPageVisits) postData.misc.latest_page_visits = latestPageVisits;
316 growSumo = true;
317 break;
318 }
319
320 /** **ONLY NEEDED IN STAGING STARTS** **/
321 const currentLocation = window.location.href;
322 if (currentLocation.includes('/signup-int')) {
323 const apiURLFinal = new URL(apiDomain);
324 apiURLFinal.host = 'aloha-int.freshid.io';
325 apiDomain = apiURLFinal.href;
326 }
327 /** **ONLY NEEDED IN STAGING ENDS** **/
328
329 // Handling growsumo data
330 if (growSumo) {
331 const gsdata = {
332 first_name: firstName,
333 email_id: email,
334 domain_name: domain
335 };
336 localStorage.setItem('gsdata', JSON.stringify(gsdata));
337 }
338
339 const responseErrorHandler = (errorMsg, form) => {
340 if (errorMsg) {
341 errorMsg = typeof (errorMsg) === 'object' ? JSON.stringify(errorMsg) : errorMsg;
342 if (errorMsg.includes('only one signup is allowed per email id')) {
343 $('.login-trigger-button').trigger('click');
344
345 if (remindEndPoint) {
346 $submitButton.removeClass('button--loading').removeAttr('disabled');
347
348 const $signupLoginModal = $('.signup-login-modal');
349 $signupLoginModal.find('.email-reminder-button').on('click', function () {
350 $(this).addClass('button--loading').attr('disabled', 'disabled');
351 $.ajax({
352 url: remindEndPoint + encodeURIComponent(email),
353 type: 'GET',
354 success: (response) => {
355 $(this).removeClass('button--loading').removeAttr('disabled');
356 $signupLoginModal.find('.form-field-container,.thank-you-card').addClass('active');
357 $signupLoginModal.find('.thank-you-card').addClass('small-card');
358 },
359 error: () => {
360 $(this).removeClass('button--loading').removeAttr('disabled').text('Retry');
361 }
362 });
363 });
364 }
365
366 if (product === 'fteam') {
367 $('.new-account-button').on('click', function () {
368 postData.misc.validate_duplicate = false;
369 $(this).addClass('button--loading product-specific');
370 $.ajax({
371 url: apiDomain,
372 type: 'POST',
373 data: JSON.stringify(postData),
374 accept: 'application/json; charset=UTF-8',
375 contentType: 'application/json; charset=UTF-8',
376 dataType: 'json',
377 success: (response, textStatus, jqXHR) => {
378 const status = jqXHR.status;
379 if ((status >= 200 && status < 300) || status === 304) {
380 fteamSignupFormSuccess($form, response, true);
381 }
382 },
383 error: (jqXHR) => {
384 $(this).removeClass('button--loading').removeAttr('disabled').text('Retry');
385 failureLogData['message'] = `Aloha v2 Signup API call Failed. StatusCode: ${jqXHR.status}, Error: ${jqXHR.responseText || 'jqXHR response text is undefined'}`;
386 SiteUtilities.signupErrorLog(failureLogData, form);
387 }
388 });
389 });
390 }
391 }
392 if (errorMsg.includes('domain_taken') || errorMsg.includes('The Full domain has already been taken')) {
393 $('.domain-taken-trigger-button').trigger('click');
394 }
395 if (errorMsg.includes('spam_email') || errorMsg.includes('invalid_email') || errorMsg.includes('blocked_domain')) {
396 $('.login-spam-trigger-button').trigger('click');
397 }
398 if (product === 'fping' && errorMsg.includes('User already Registered')) {
399 $('.email-form').parent('.form-field').addClass('error').find('.error-wrapper').html(`<em id="email-error" class="error">User already Registered</em>`);
400 }
401 if (product === 'fstatus' && errorMsg.includes('User already Registered')) {
402 form.find('.email-form').parent('.form-field').addClass('error').find('.error-wrapper').html(`<em id="email-error" class="error">Please try using a different email address</em>`);
403 }
404 if (errorMsg.includes('company_name parameter length should not be more than 128')) {
405 $('.company-form').parent('.form-field').addClass('error').find('.error-wrapper').html(`<em id="email-error" class="error">Company length should not be more than 128 characters</em>`);
406 }
407 if (errorMsg.includes('Register using your work email')) {
408 $('.email-form').parent('.form-field').addClass('error').find('.error-wrapper').html(`<em id="email-error" class="error">Register using your work email</em>`);
409 }
410 if (errorMsg.includes('Name cannot contain URLs or dots')) {
411 $('.first-name-form').parent('.form-field').addClass('error').find('.error-wrapper').html(`<em id="email-error" class="error">Name cannot contain URLs or dots</em>`);
412 $('.last-name-form').parent('.form-field').addClass('error');
413 }
414 }
415 $submitButton.removeClass('button--loading').val('Retry').removeAttr('disabled');
416 };
417
418 const checkErrorValidity = errorMsg => {
419 if (errorMsg) {
420 errorMsg = typeof (errorMsg) === 'object' ? JSON.stringify(errorMsg) : errorMsg;
421 if (
422 errorMsg.includes('only one signup is allowed per email id') ||
423 errorMsg.includes('domain_taken') ||
424 errorMsg.includes('The Full domain has already been taken') ||
425 errorMsg.includes('spam_email') ||
426 errorMsg.includes('invalid_email') ||
427 errorMsg.includes('blocked_domain') ||
428 errorMsg.includes('User already Registered')
429 ) {
430 return false;
431 }
432 }
433 return true;
434 };
435
436 const failureLogData = {
437 email: email,
438 source: 'FW.Submit.alohaForm',
439 product: product,
440 ip_address: ipAddress
441 };
442
443 const signupLogData = {
444 email: email,
445 source: 'FW.Submit.alohaForm',
446 ip_address: ipAddress,
447 type: 'signup',
448 signup_payload_payload: postData,
449 api_endpoint: apiDomain
450 };
451
452 localStorage.setItem('signupPerformanceLogData', JSON.stringify(signupLogData));
453
454 if ($formSection.length) $formSection.css('pointer-events', 'none');
455
456 const params = {
457 url: apiDomain,
458 type: 'POST',
459 data: JSON.stringify(postData),
460 dataType: 'json',
461 crossDomain: true,
462 timeout: 100000, // Increase timeout to 100 seconds
463 accept: 'application/json; charset=UTF-8',
464 contentType: 'application/json; charset=UTF-8',
465 success: (response, textStatus, jqXHR) => {
466 response.composite_stitching_key = compositeKey;
467 const status = jqXHR.status;
468 SiteUtilities.configureAssetId($form, product);
469 if ((status >= 200 && status < 300) || status === 304) {
470 localStorage.setItem('alohaEndTime', new Date().toISOString());
471 switch (product) {
472 case 'fchat':
473 fchatSignupFormSuccess($form, response, true);
474 break;
475
476 case 'fcaller':
477 fcallerSignupFormSuccess($form, response, true);
478 break;
479
480 case 'fdesk':
481 fdeskSignupFormSuccess($form, response, true);
482 break;
483
484 case 'fping':
485 fpingSignupFormSuccess($form, response, true);
486 break;
487
488 case 'fworks':
489 fworksCrmSignupForm($form, response, true);
490 break;
491
492 case 'fstatus':
493 fstatusSignupFormSuccess($form, response, true);
494 break;
495
496 case 'fteam':
497 fteamSignupFormSuccess($form, response, true);
498 break;
499
500 case 'fwcrm':
501 case 'fsales':
502 case 'fmarketer':
503 const redirectUrl = response['product_signup_response']['redirect_url'].replace(/\/$/, '');
504 const redirectUrlObj = new window.URL(redirectUrl);
505 const domainLink = redirectUrlObj.hostname;
506 if (growSumo) {
507 const gsdata = JSON.parse(localStorage.getItem('gsdata'));
508 gsdata.domain_link = domainLink;
509 localStorage.setItem('gsdata', JSON.stringify(gsdata));
510 }
511 fwCrmSignupForm($form, response, true);
512 break;
513 }
514 if (JWTToken && JWTToken.length) {
515 GA4Utilities.googleSSOEvent('fwFormSubmit', (isEmailBusiness ? 'Business' : 'Generic'), 'Form Submission', classname);
516 } else if ($('[data-idp="GOOGLE"]').length) {
517 GA4Utilities.googleSSOEvent('fwFormSubmit', 'Normal Signup', 'Normal Signup', classname);
518 } else if (shopifyJWT) {
519 GA4Utilities.postGAEvent('fwFormSubmit', 'Shopify Signup Page', 'Shopify Signup Page', classname, 'Shopify Signup Page');
520 } else {
521 GA4Utilities.fwFormSubmit(classname);
522 }
523 localStorage.setItem('signupStatusCode', status);
524 localStorage.setItem('signupStatusText', textStatus);
525 localStorage.setItem('signupStatusMsg', 'Aloha v2 Signup API call successful');
526 SiteUtilities.signupLog(signupLogData, form, status, { message: 'Aloha v2 Signup API call successful', response: `${jqXHR.responseText || 'jqXHR response text is undefined'}`, textStatus: `${textStatus}` });
527 } else {
528 localStorage.setItem('alohaEndTime', new Date().toISOString());
529 const responseObject = {
530 jqXHR,
531 textStatus,
532 response
533 };
534 $form.find('.button').removeClass('button--loading').attr('disabled', null);
535
536 // sso signup section
537 if ($formSection.length) {
538 $formSection.css('pointer-events', 'auto');
539 handleGoogleSingupErrHandler($formSection);
540 }
541
542 if (product === 'fworks') {
543 fworksCrmSignupFormSuccess($form, false);
544 }
545 if (product === 'fwcrm' || product === 'fmarketer' || product === 'fsales') {
546 fwCrmSignupFormSuccess($form, false);
547 }
548 SiteUtilities.classifyStatusCode(status, 'Aloha v2 Signup API call succeeded - Received Error Response', textStatus, responseObject);
549 responseErrorHandler(jqXHR.responseText, $form);
550 if (JWTToken && JWTToken.length) {
551 GA4Utilities.googleSSOEvent('retry', 'Google SSO', 'Unsuccessful form submission');
552 } else if ($('[data-idp="GOOGLE"]').length) {
553 GA4Utilities.googleSSOEvent('retry', 'Normal Signup', 'Unsuccessful form submission');
554 } else {
555 GA4Utilities.signupRetryEvent('retry', 'Form Unsuccessful Submission', 'Retry option shown');
556 }
557 // Log valid error scenarios in db(filter out spam errors from getting logged)
558 const errorValidity = checkErrorValidity(jqXHR.responseText);
559 if (errorValidity) {
560 failureLogData['message'] = `Aloha v2 Signup API call succeeded - Received Error Response. StatusCode: ${status}, Error: ${jqXHR.responseText || 'jqXHR response text is undefined'}`;
561 SiteUtilities.signupErrorLog(failureLogData, form);
562 }
563 SiteUtilities.signupLog(signupLogData, form, status, { message: 'Aloha v2 Signup API call succeeded - Received Error Response', error: `${jqXHR.responseText || 'jqXHR response text is undefined'}`, textStatus: `${textStatus}` });
564 SiteUtilities.signupPerformanceLog(signupLogData, product, textStatus, status, 'Aloha v2 Signup API call succeeded - Received Error Response');
565 }
566 },
567 error: (jqXHR, textStatus, err) => {
568 localStorage.setItem('alohaEndTime', new Date().toISOString());
569 const responseObject = {
570 jqXHR,
571 textStatus,
572 err
573 };
574 $form.find('.button').removeClass('button--loading').attr('disabled', null);
575
576 // sso signup section
577 if ($formSection.length) {
578 $formSection.css('pointer-events', 'auto');
579 handleGoogleSingupErrHandler($formSection);
580 }
581
582 if (product === 'fworks') {
583 fworksCrmSignupFormSuccess($form, false);
584 }
585 if (product === 'fwcrm' || product === 'fmarketer' || product === 'fsales') {
586 fwCrmSignupFormSuccess($form, false);
587 }
588 SiteUtilities.classifyStatusCode(jqXHR.status, 'Aloha v2 Signup API call Failed', textStatus, responseObject);
589 responseErrorHandler(jqXHR.responseText, $form);
590 if (JWTToken && JWTToken.length) {
591 GA4Utilities.googleSSOEvent('retry', 'Google SSO', 'Form Unsuccessful Submission');
592 } else if ($('[data-idp="GOOGLE"]').length) {
593 GA4Utilities.googleSSOEvent('retry', 'Normal Signup', 'Unsuccessful form submission');
594 } else {
595 GA4Utilities.signupRetryEvent('retry', 'Form Unsuccessful Submission', 'Retry option shown');
596 }
597 // Log valid error scenarios in db(filter out spam errors from getting logged)
598 const errorValidity = checkErrorValidity(jqXHR.responseText);
599 if (errorValidity) {
600 failureLogData['message'] = `Aloha v2 Signup API call Failed. StatusCode: ${jqXHR.status}, Error: ${jqXHR.responseText || 'jqXHR response text is undefined'}`;
601 SiteUtilities.signupErrorLog(failureLogData, form);
602 }
603 SiteUtilities.signupLog(signupLogData, form, jqXHR.status, { message: 'Aloha v2 Signup API call Failed', error: `${jqXHR.responseText || 'jqXHR response text is undefined'}`, textStatus: `${textStatus}` });
604 SiteUtilities.signupPerformanceLog(signupLogData, product, textStatus, jqXHR.status, 'Aloha v2 Signup API call Failed');
605 }
606 };
607
608 if (ipAddress) {
609 params['headers'] = {
610 'X-Forwarded-For': ipAddress
611 };
612 }
613
614 localStorage.setItem('alohaStartTime', new Date().toISOString());
615
616 // Add additional headers to signup int environment
617 if (product === 'fdesk' && window.location.href.includes('/signup-int')) {
618 const alohaBundle = 'freshdesk:omniint|freshchat:bots1|freshcaller:branch2';
619 if (params['headers']) {
620 params['headers']['x-fw-aloha-bundle'] = alohaBundle;
621 } else {
622 params['headers'] = { 'x-fw-aloha-bundle': alohaBundle };
623 }
624 }
625
626 $.ajax(params);
627};
628
629export default {
630 alohaForm
631};