PageSourceSearch

https://www.icicihfc.com/scripts/gtag-utils.js

js icicihfc.com collected 2026-09-26 10:07:28 UTC 27,304 bytes, 797 lines download raw bytes

1/**
2 * GTM Analytics tracking utility
3 * Implements ICICI HFC GA4 Phase 1 GTM Tag Guidelines
4 */
5
6// Prevent multiple initializations
7let isInitialized = false;
8// Initialize dataLayer if it doesn't exist
9
10import { getMetadata } from "./aem.js";
11
12const pageName = getPageName().toLowerCase();
13const loginInfo = getLoginInfo();
14
15function hashCustomerID(id) {
16  if (!id) return null;
17  return btoa(new TextEncoder().encode(id).reduce((acc, byte) => acc + String.fromCharCode(byte), ''));
18}
19
20function getPageName() {
21  const pathname = window.location.pathname;
22  const segments = pathname.split('/').filter(segment => segment && segment !== 'en');
23  return segments.length === 0 ? 'home' : segments.join(':');
24}
25
26// function getJourneyName() {
27//   const pathname = window.location.pathname;
28//   const segments = pathname.split('/').filter(segment => segment && segment !== 'en');
29//   return segments.length === 0 ? 'home' : segments[0];
30// }
31
32function getJourneyName() {
33  const keywords = getMetadata("keywords")?.toLowerCase() || "";
34
35  const keywordMap = [
36    { keyword: "lsp-dashboard",          name: "loan-service" },
37    { keyword: "lsp-header",             name: "loan-service" },
38    { keyword: "fd-customer-dashboard",  name: "fixed deposit" },
39    { keyword: "fd-broker-registration", name: "fixed deposit" },
40    { keyword: "fd-security",            name: "fixed deposit" },
41    { keyword: "fd-login",               name: "fixed deposit" },
42    { keyword: "fd-dashboard",           name: "fixed deposit" },
43    { keyword: "app-track-dash",         name: "view my application" },
44    { keyword: "app-track-login",        name: "view my application" },
45    { keyword: "vma-dashboard",          name: "view my application" },
46    { keyword: "vma-login",             name: "view my application" },
47    { keyword: "dma-rp-header",          name: "dma-rp" },
48    { keyword: "dma-dashboard",          name: "dma" },
49    { keyword: "rp-dashboard",           name: "rp" },
50    { keyword: "payments-dashboard",     name: "payments" },
51    { keyword: "payments-header",        name: "payments" },
52    { keyword: "employee-assisted",      name: "employee-assisted" },
53    { keyword: "broker-assisted",        name: "broker-assisted" },
54  ];
55
56  for (const { keyword, name } of keywordMap) {
57    if (keywords.includes(keyword)) return name;
58  }
59
60  // Fallback to first URL path segment
61  const segments = window.location.pathname
62    .split("/")
63    .filter((s) => s && s !== "en");
64
65  return segments[0] ?? "home";
66}
67
68function getPageChannel() {
69  const journeyName = getJourneyName();
70  return `web:${journeyName}`;
71}
72
73function getLoginInfo() {
74  const token = sessionStorage.getItem('kyc-token') || localStorage.getItem('user-token') || sessionStorage.getItem("accessToken") || sessionStorage.getItem('vmlToken') || (sessionStorage.getItem("lspSession") ? JSON.parse(sessionStorage.getItem("lspSession")).secureToken : null);
75  const customerId = sessionStorage.getItem('customer-id') || localStorage.getItem('customer-id') || sessionStorage.getItem("loginCustId") || (sessionStorage.getItem("lspSession") ? JSON.parse(sessionStorage.getItem("lspSession")).mobileNo : null) || sessionStorage.getItem("LoggingCusID");
76
77  return {
78    login_status: token ? 'logged-in' : 'guest',
79    customerID: hashCustomerID(token && customerId ? customerId : null)
80  };
81}
82
83function trackPageView() {
84
85  const eventData = {
86    event: 'page_view',
87    page_name: pageName,
88    page_channel: getPageChannel().toLowerCase(),
89    login_status: loginInfo.login_status,
90    customerID: loginInfo.customerID
91  };
92
93  if (pageName !== 'home') {
94    eventData.journey_name = getJourneyName().toLowerCase();
95  }
96
97  console.log('📡 GTM page_view:', eventData);
98  window.dataLayer.push(eventData);
99}
100
101function trackClick(ctaName) {
102
103  const eventData = {
104    event: 'click',
105    cta_name: ctaName.toLowerCase(),
106    page_name: pageName,
107    login_status: loginInfo.login_status,
108    customerID: loginInfo.customerID
109  };
110
111  if (pageName !== 'home') {
112    eventData.journey_name = getJourneyName().toLowerCase();
113  }
114
115  console.log('🖱️ GTM click:', eventData);
116  window.dataLayer.push(eventData);
117}
118
119function trackBannerClick(bannerName, ctaName, bannerPosition = null) {
120
121  const eventData = {
122    event: 'banner_click',
123    event_name: 'banner_click',
124    link_pagename: pageName,
125    banner_name: bannerName ? bannerName.toLowerCase() : null,
126    cta_name: ctaName ? ctaName.toLowerCase() : null,
127    login_status: loginInfo.login_status,
128    journey_name: getJourneyName().toLowerCase(),
129    customerID: loginInfo.customerID
130  };
131
132  if (bannerPosition !== null) {
133    eventData.banner_position = String(bannerPosition);
134  }
135
136  console.log('🎯 GTM banner_click:', eventData);
137  window.dataLayer.push(eventData);
138}
139
140function trackScroll(scrollPercent) {
141
142  const eventData = {
143    event: 'scroll',
144    page_name: pageName,
145    page_channel: getPageChannel().toLowerCase(),
146    login_status: loginInfo.login_status,
147    scrollPercent: scrollPercent,
148    customerID: loginInfo.customerID
149  };
150
151  console.log('📜 GTM scroll:', eventData);
152  window.dataLayer.push(eventData);
153}
154
155function trackSearch(searchTerm, customerID) {
156  
157  const eventData = {
158    event: 'search',
159    cta_name: 'search initiated',
160    page_name: pageName,
161    journey_name: getJourneyName().toLowerCase(),
162    searchTerm: searchTerm.toLowerCase(),
163    login_status: loginInfo.login_status,
164    customerID: loginInfo.customerID || hashCustomerID(customerID)
165  };
166
167  console.log('🔍 GTM search:', eventData);
168  window.dataLayer.push(eventData);
169}
170
171function trackSearchResults(searchTerm) {
172
173  const eventData = {
174    event: 'view_search_results',
175    page_name: pageName,
176    page_channel: getPageChannel().toLowerCase(),
177    login_status: loginInfo.login_status,
178    searchTerm: searchTerm.toLowerCase(),
179    journey_name: getJourneyName().toLowerCase(),
180    customerID: loginInfo.customerID
181  };
182
183  console.log('📋 GTM search results:', eventData);
184  window.dataLayer.push(eventData);
185}
186
187function trackFileDownload(fileName) {
188
189
190  const eventData = {
191    event: 'file_download',
192    cta_name: 'file download',
193    page_name: pageName,
194    fileName: fileName.toLowerCase(),
195    login_status: loginInfo.login_status,
196    customerID: loginInfo.customerID
197  };
198
199  console.log('📁 GTM file download:', eventData);
200  window.dataLayer.push(eventData);
201}
202
203function trackLoanAmountChange(loanAmount, bannerName = 'home loan mini calculator') {
204
205  const eventData = {
206    event: 'loan_amount_change',
207    cta_name: 'loan amount cursor',
208    page_name: pageName,
209    banner_name: bannerName,
210    loan_amount: loanAmount,
211    login_status: loginInfo.login_status,
212    customerID: loginInfo.customerID
213  };
214
215  console.log('💰 GTM loan amount change:', eventData);
216  window.dataLayer.push(eventData);
217}
218
219function trackFDTenureChange(fdTenure, bannerName = 'fd mini calculator') {
220
221  const eventData = {
222    event: 'fd_tenure_change',
223    cta_name: 'fd tenure',
224    page_name: pageName,
225    banner_name: bannerName,
226    fd_tenure: fdTenure,
227    login_status: loginInfo.login_status,
228    customerID: loginInfo.customerID
229  };
230
231  console.log('📅 GTM FD tenure change:', eventData);
232  window.dataLayer.push(eventData);
233}
234
235// Form tracking analytics — Apply Now modal lead forms only
236// (home-loan, lead, lap-lead, bt, pmay, fd, property-auction, fill-details, careers, etc.)
237const FORM_FIELD_SELECTOR = 'input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]), select, textarea';
238const trackedFormStarts = new WeakSet();
239
240const FORM_ANALYTICS_IDS = {
241  'home-loan-form': 'hfc-hlf',
242  'lead-form': 'hfc-lf',
243  'lap-lead-form': 'hfc-laplf',
244  'bt-form': 'hfc-btf',
245  'pmay-form': 'hfc-pmayf',
246  'fd-cal-form': 'hfc-fdcf',
247  'property-auction-form': 'hfc-paf',
248  'careers-form': 'hfc-cf',
249  'fill-details': 'hfc-fdf',
250};
251
252function isLeadForm(form) {
253  if (!(form instanceof HTMLFormElement)) return false;
254  const action = form.dataset.action || form.getAttribute('action') || '';
255  if (!action.includes('/spreadsheet-apis/')) return false;
256  return Boolean(form.closest('.form-modal') || form.id === 'enquiry-form');
257}
258
259function shouldTrackForm(form) {
260  if (!isLeadForm(form)) return false;
261  if (form.dataset.trackForms === 'false') return false;
262  return true;
263}
264
265function getFormActionSlug(form) {
266  const action = form.dataset.action || form.getAttribute('action') || '';
267  if (!action) return '';
268  try {
269    const pathname = action.startsWith('http') ? new URL(action).pathname : action;
270    return pathname.split('/').filter(Boolean).pop()?.replace(/\.json$/, '') || '';
271  } catch {
272    return action.split('/').filter(Boolean).pop()?.replace(/\.json$/, '') || '';
273  }
274}
275
276function getFormMetadata(form) {
277  const actionSlug = getFormActionSlug(form);
278  const formId = form.dataset.formId || FORM_ANALYTICS_IDS[actionSlug] || form.id || null;
279  const formName = form.dataset.formName || form.getAttribute('name') || actionSlug || form.className.split(/\s+/).filter(Boolean)[0] || 'unknown';
280
281  return {
282    form_id: formId ? String(formId).toLowerCase() : null,
283    form_name: String(formName).toLowerCase(),
284  };
285}
286
287function buildFormEventData(event, eventName, form) {
288  const { form_id, form_name } = getFormMetadata(form);
289
290  return {
291    event,
292    event_name: eventName,
293    link_pagename: pageName,
294    login_status: loginInfo.login_status,
295    journey_name: getJourneyName().toLowerCase(),
296    customerID: loginInfo.customerID,
297    form_id,
298    form_name,
299  };
300}
301
302function trackFormStart(form) {
303  if (!shouldTrackForm(form) || trackedFormStarts.has(form)) return;
304  trackedFormStarts.add(form);
305
306  const eventData = buildFormEventData('form_start', 'form start', form);
307  console.log('📝 GTM form_start:', eventData);
308  window.dataLayer.push(eventData);
309}
310
311function trackFormSubmit(form) {
312  if (!shouldTrackForm(form)) return;
313
314  const eventData = buildFormEventData('form_submit', 'form submit', form);
315  console.log('✅ GTM form_submit:', eventData);
316  window.dataLayer.push(eventData);
317}
318
319function initFormTracking() {
320  document.addEventListener('focusin', (event) => {
321    const field = event.target;
322    if (!field.matches?.(FORM_FIELD_SELECTOR)) return;
323
324    const form = field.closest('form');
325    if (!form) return;
326
327    trackFormStart(form);
328  }, true);
329
330  document.addEventListener('change', (event) => {
331    const field = event.target;
332    if (!field.matches?.(FORM_FIELD_SELECTOR)) return;
333
334    const form = field.closest('form');
335    if (!form) return;
336
337    trackFormStart(form);
338  }, true);
339}
340
341export function initGtagTracking() {
342  if (isInitialized) return;
343  isInitialized = true;
344
345  // Track page view immediately
346  trackPageView();
347
348  // Check for search results to track
349  try {
350    const searchTerm = sessionStorage.getItem('search_term');
351    if (searchTerm) {
352      trackSearchResults(searchTerm);
353      sessionStorage.removeItem('search_term');
354    }
355  } catch (error) {
356    console.error('Error tracking search results page:', error);
357  }
358
359  // Track scroll events (25%, 50%, 75%, 100%) — throttled via rAF to reduce main-thread work
360  let scrollTracked = { 25: false, 50: false, 75: false, 100: false };
361  let scrollTicking = false;
362  const onScroll = () => {
363    if (scrollTicking) return;
364    scrollTicking = true;
365    requestAnimationFrame(() => {
366      const scrollPercent = Math.round((window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100);
367      [25, 50, 75, 100].forEach(threshold => {
368        if (scrollPercent >= threshold && !scrollTracked[threshold]) {
369          scrollTracked[threshold] = true;
370          trackScroll(`${threshold}%`);
371        }
372      });
373      // Remove listener once all thresholds are hit
374      if (scrollTracked[25] && scrollTracked[50] && scrollTracked[75] && scrollTracked[100]) {
375        window.removeEventListener('scroll', onScroll);
376      }
377      scrollTicking = false;
378    });
379  };
380  window.addEventListener('scroll', onScroll, { passive: true });
381
382  // Track clicks
383  document.addEventListener('click', (event) => {
384    const element = event.target.closest('a, button, [role="button"]');
385    if (element) {
386      const ctaName = element.textContent?.trim() ||
387        element.getAttribute('aria-label') ||
388        element.getAttribute('title') ||
389        'unknown';
390
391      trackClick(ctaName);
392
393      // Check if it's a PDF download
394      const href = element.href || element.getAttribute('href') || '';
395      if (href.toLowerCase().includes('.pdf')) {
396        const fileName = href.split('/').pop() || ctaName;
397        trackFileDownload(fileName);
398      }
399    }
400  });
401
402  // Intercept fetch for error responses — only process API errors, skip successful c
402alls
403  const originalFetch = window.fetch;
404  window.fetch = async (...args) => {
405    const response = await originalFetch(...args);
406    const url = (typeof args[0] === 'string' ? args[0] : args[0]?.url) || '';
407    const isApiCall = url.includes('icicihfc-micro-service');
408
409    // Only clone and parse JSON for API error responses to avoid unnecessary work
410    if (isApiCall && response.status >= 400) {
411      try {
412        const data = await response.clone().json();
413        const bodyStatusCode = data?.statusCode || data?.status;
414        trackError(
415          data?.message || data?.error || `http error ${response.status}`,
416          data?.errorCode || data?.code || `error${bodyStatusCode || response.status}`,
417          sessionStorage.getItem('customer-id') || localStorage.getItem('customer-id'),
418          getJourneyName()
419        );
420      } catch {
421        trackError(`http error ${response.status}`, `error${response.status}`, null, getJourneyName());
422      }
423    }
424    return response;
425  };
426
427  initFormTracking();
428}
429
430// FD journey analytics
431function trackFDJourneyEvent(eventType, customerID, customerType = 'existing customer', paymentDetails = {}){
432  
433  let event, eventName;
434  
435  if (eventType === 'book_new_fd') {
436    event = 'book_new_fd';
437    eventName = 'book new fd';
438  } else if (eventType === 'payment_initiated') {
439    event = 'fd_payment_initiated';
440    eventName = 'pay now';
441  } else if (eventType === 'success') {
442    event = 'applyfd_success';
443    eventName = 'applyfd-success';
444  } else if (eventType === 'failure') {
445    event = 'applyfd_failure';
446    eventName = 'applyfd-failure';
447  }
448  
449  const eventData = {
450    event: event,
451    event_name: eventName,
452    login_status: loginInfo.login_status,
453    journey_name: 'fixed deposit',
454    customer_type: customerType.toLowerCase(),
455    page_name: pageName,
456    customerID: loginInfo.customerID || hashCustomerID(customerID)
457  };
458
459   // ✅ NEW: Add payment details only for success/failure events
460  if (eventType === 'success' || eventType === 'failure') {
461    if (paymentDetails.transactionID) eventData.transactionID = paymentDetails.transactionID;
462    if (paymentDetails.fd_amount)     eventData.amount = paymentDetails.fd_amount;
463    if (paymentDetails.payment_mode)  eventData.payment_mode = paymentDetails.payment_mode;
464    if (paymentDetails.payment_method) eventData.payment_method = paymentDetails.payment_method;
465  }
466  
467  // Add link_pagename only for book_new_fd and payment_initiated
468  // if (eventType === 'book_new_fd' || eventType === 'payment_initiated') {
469  //   eventData.page_name = pageName;
470  // }
471  
472  console.log(`📊 GTM FD Journey (${event}):`, eventData);
473  window.dataLayer.push(eventData);
474}
475
476function trackApplyFDStep(stepNumber, stepName, customerID, customerType = 'existing customer') {
477  
478  const eventData = {
479    event: `applyfd_step${stepNumber}`,
480    event_name: `applyfd-step${stepNumber}`,
481    step_name: stepName.toLowerCase(),
482    page_name: pageName,
483    login_status: loginInfo.login_status,
484    journey_name: 'fixed deposit',
485    customer_type: customerType.toLowerCase(),
486    customerID: loginInfo.customerID || hashCustomerID(customerID)
487  };
488  
489  console.log(`📝 GTM apply FD step ${stepNumber}:`, eventData);
490  window.dataLayer.push(eventData);
491}
492
493function trackKYCVerifyStep(stepNumber, customerID, customerType = 'new customer') {
494  
495  const eventData = {
496    event: stepNumber === 1 ? 'kycverify_start' : 'kycverify_success',
497    event_name: stepNumber === 1 ? 'kyc verify start' : 'kyc verify success',
498    step_name: `kycverify-step${stepNumber}`,
499    login_status: 'logged-in',
500    journey_name: 'fixed deposit',
501    customer_type: customerType.toLowerCase(),
502    customerID: loginInfo.customerID || hashCustomerID(customerID)
503  };
504  
505  console.log(`📝 GTM KYC verify step ${stepNumber}:`, eventData);
506  window.dataLayer.push(eventData);
507}
508
509// VMA journey analytics
510function trackVMAEvent(event, eventName, customerID) {
511  
512  const eventData = {
513    event: event,
514    event_name: eventName,
515    page_name: pageName,
516    login_status: loginInfo.login_status,
517    journey_name: 'view my application',
518    customerID: loginInfo.customerID || hashCustomerID(customerID)
519  };
520  
521  console.log(`📊 GTM VMA Event (${event}):`, eventData);
522  window.dataLayer.push(eventData);
523}
524
525function trackVMAFieldEntry(fieldEvent) {
526  const eventData = {
527    event: fieldEvent,
528    event_name: fieldEvent,
529    link_pagename: pageName,
530    login_status: loginInfo.login_status,
531    journey_name: 'view my application',
532    customerID: loginInfo.customerID
533  };
534  console.log(`📊 GTM VMA Field (${fieldEvent}):`, JSON.stringify(eventData, null, 2));
535  window.dataLayer.push(eventData);
536}
537
538// Login Fees interaction tracking
539function trackLoginFees(customerID) {
540  const eventData = {
541    event: 'login_fees',
542    event_name: 'login_fees',
543    link_pagename: pageName,
544    login_status: loginInfo.login_status,
545    journey_name: getJourneyName().toLowerCase(),
546    customerID: loginInfo.customerID || hashCustomerID(customerID)
547  };
548
549  console.log('💳 GTM login fees:', JSON.stringify(eventData, null, 2));
550  window.dataLayer.push(eventData);
551}
552
553// Processing Fees interaction tracking
554function trackProcessingFees(customerID) {
555  const eventData = {
556    event: 'processing_fees',
557    event_name: 'processing_fees',
558    link_pagename: pageName,
559    login_status: loginInfo.login_status,
560    journey_name: getJourneyName().toLowerCase(),
561    customerID: loginInfo.customerID || hashCustomerID(customerID)
562  };
563
564  console.log('💳 GTM processing fees:', JSON.stringify(eventData, null, 2));
565  window.dataLayer.push(eventData);
566}
567
568// VMA Login Fees Payment Success/Failure tracking
569function trackVMALoginFeesPayment(eventType, customerID, paymentDetails = {}) {
570  const isSuccess = eventType === 'success';
571
572  const eventData = {
573    event: isSuccess ? 'vma_loginfees_success' : 'vma_loginfees_failure',
574    event_name: isSuccess ? 'vma_loginfees_success' : 'vma_loginfees_failure',
575    step_name: isSuccess ? 'success' : 'failure',
576    login_status: loginInfo.login_status,
577    journey_name: getJourneyName().toLowerCase(),
578    customerID: loginInfo.customerID || hashCustomerID(customerID)
579  };
580
581  if (paymentDetails.transactionID)  eventData.transactionID   = paymentDetails.transactionID;
582  if (paymentDetails.amount)         eventData.amount           = paymentDetails.amount;
583  if (paymentDetails.payment_mode)   eventData.payment_mode     = paymentDetails.payment_mode;
584  if (paymentDetails.payment_method) eventData.payment_method   = paymentDetails.payment_method;
585
586  console.log(`💳 GTM VMA Login Fees (${eventData.event}):`, JSON.stringify(eventData, null, 2));
587  window.dataLayer.push(eventData);
588}
589
590// VMA Processing Fees Payment Success/Failure tracking
591function trackVMAProcessingFeesPayment(eventType, customerID, paymentDetails = {}) {
592  const isSuccess = eventType === 'success';
593
594  const eventData = {
595    event: isSuccess ? 'vma_processingfees_success' : 'vma_processingfees_failure',
596    event_name: isSuccess ? 'vma_processingfees_success' : 'vma_processingfees_failure',
597    step_name: isSuccess ? 'success' : 'failure',
598    login_status: loginInfo.login_status,
599    journey_name: getJourneyName().toLowerCase(),
600    customerID: loginInfo.customerID || hashCustomerID(customerID)
601  };
602
603  if (paymentDetails.transactionID)  eventData.transactionID   = paymentDetails.transactionID;
604  if (paymentDetails.amount)         eventData.amount           = paymentDetails.amount;
605  if (paymentDetails.payment_mode)   eventData.payment_mode     = paymentDetails.payment_mode;
606  if (paymentDetails.payment_method) eventData.payment_method   = paymentDetails.payment_method;
607
608  console.log(`💳 GTM VMA Processing Fees (${eventData.event}):`, JSON.stringify(eventData, null, 2));
609  window.dataLayer.push(eventData);
610}
611
612// Loan Service (LSP) journey analytics
613function trackLSPEvent(eventType, customerID, requestType = '') {
614  
615  // Map event types to their configurations
616  const eventConfig = {
617    'ticket_submit_success': {
618      event: 'ticket_submit_success',
619      status: 'success'
620    },
621    'ticket_submit_failure': {
622      event: 'ticket_submit_failure',
623      status: 'failure'
624    },
625    'update_address_success': {
626      event: 'update_address_success',
627      eventName: 'update address success',
628      status: 'success'
629    },
630    'update_address_failure': {
631      event: 'update_address_failure',
632      eventName: 'update address failure',
633      status: 'failure'
634    }
635  };
636  
637  const config = eventConfig[eventType];
638  
639  if (!config) {
640    console.error(`Unknown LSP event type: ${eventType}`);
641    return;
642  }
643  
644  const eventData = {
645    event: config.event,
646    page_name: pageName,
647    request_status: config.status,
648    login_status: loginInfo.login_status,
649    journey_name: 'loan-service',
650    customerID: loginInfo.customerID || hashCustomerID(customerID)
651  };
652  
653  // Add event_name if defined
654  if (config.eventName) {
655    eventData.event_name = config.eventName;
656  }
657  
658  if (eventType === 'ticket_submit_success' && requestType) {
659    eventData.request_type = requestType;
660  }
661
662  console.log(`📊 GTM LSP Event (${config.event}):`, JSON.stringify(eventData, null, 2));
663  window.dataLayer.push(eventData);
664}
665
666// Payment Success/Failure tracking
667function trackLSPPaymentEvent(eventType, customerID, paymentDetails = {}) {
668  const isSuccess = eventType === 'success';
669
670  const eventData = {
671    event: isSuccess ? 'lsp_payment_success' : 'lsp_payment_failure',
672    event_name: isSuccess ? 'lsp_payment_success' : 'lsp_payment_failure',
673    step_name: isSuccess ? 'success' : 'failure',
674    request_status: isSuccess ? 'success' : 'failure',
675    login_status: loginInfo.login_status,
676    journey_name: getJourneyName().toLowerCase(),
677    customerID: loginInfo.customerID || hashCustomerID(customerID)
678  };
679
680  if (paymentDetails.transactionID) eventData.transactionID = String(paymentDetails.transactionID);
681  if (paymentDetails.amount)         eventData.amount           = paymentDetails.amount;
682  if (paymentDetails.payment_mode)   eventData.payment_mode     = paymentDetails.payment_mode;
683  if (paymentDetails.payment_method) eventData.payment_method   = paymentDetails.payment_method;
684  if (paymentDetails.request_type)   eventData.request_type     = paymentDetails.request_type;
685
686  console.log(`💳 GTM LSP Payment (${eventData.event}):`, JSON.stringify(eventData, null, 2));
687  window.dataLayer.push(eventData);
688}
689
690//Common otp succes,failure,resend analytics
691function trackOTPSuccess(customerID) {
692  const eventData = {
693    event: 'otp_success',
694    event_name: 'otp_sucess',
695    page_name: pageName,
696    journey_name: getJourneyName().toLowerCase(),
697    login_status: loginInfo.login_status,
698    customerID: loginInfo.customerID || hashCustomerID(customerID)
699  };
700  
701  console.log('🔐 GTM OTP success:', JSON.stringify(eventData, null, 2));
702  window.dataLayer.push(eventData);
703}
704
705function trackOTPFailure(customerID) {
706  const eventData = {
707    event: 'otp_failure',
708    event_name: 'otp failure',
709    page_name: pageName,
710    journey_name: getJourneyName().toLowerCase(),
711    login_status: loginInfo.login_status,
712    customerID: loginInfo.customerID || hashCustomerID(customerID)
713  };
714  
715  console.log('🔐 GTM OTP failure:', JSON.stringify(eventData, null, 2));
716  window.dataLayer.push(eventData);
717}
718
719function trackOTPResend(customerID) {
720  const eventData = {
721    event: 'otp_resend',
722    event_name: 'otp resend',
723    page_name: pageName,
724    journey_name: getJourneyName().toLowerCase(),
725    login_status: loginInfo.login_status,
726    customerID: loginInfo.customerID || hashCustomerID(customerID)
727  };
728  
729  console.log("🔐 GTM OTP resend:", JSON.stringify(eventData, null, 2));
730  window.dataLayer.push(eventData);
731}
732
733//Common error tracking analytics
734function trackError(errorName, errorCode, customerID, journeyName) {
735  const eventData = {
736    event: 'error',
737    error_name: errorCode,
738    error_code: errorName.toLowerCase(),
739    page_name: pageName,
740    login_status: loginInfo.login_status,
741    journey_name: getJourneyName().toLowerCase() || journeyName,
742    customerID: loginInfo.customerID || hashCustomerID(customerID)
743  };
744
745  console.log('❌ GTM error:', JSON.stringify(eventData, null, 2));
746  window.dataLayer.push(eventData);
747}
748
749//commomn logic for login success
750function trackLoginSuccess(customerID, journeyName) {
751  const eventData = {
752    event: 'login_success',
753    event_name: 'login success',
754    link_pagename: pageName,
755    login_status: loginInfo.login_status,
756    journey_name: getJourneyName().toLowerCase() || journeyName.toLowerCase(),
757    customerID: loginInfo.customerID || hashCustomerID(customerID)
758  };
759
760  console.log("🔑 GTM login success:", JSON.stringify(eventData, null, 2));
761  window.dataLayer.push(eventData);
762}
763
764// Calculator apply_now / request_callback. `calculations` holds the
765// calculator-specific payload: emi_calculations, fd_calculations or pmay_calculations.
766function trackCalculatorEvent(eventName, calculatorName, calculations = {}) {
767  const eventData = {
768    event: eventName,
769    event_name: eventName,
770    link_pagename: pageName,
771    login_status: loginInfo.login_status,
772    journey_name: getJourneyName().toLowerCase(),
773    customerID: loginInfo.customerID,
774    calculator_name: String(calculatorName || '').toLowerCase(),
775    ...calculations,
776  };
777
778  console.log(`🧮 GTM calculator (${eventName}):`, JSON.stringify(eventData, null, 2));
779  window.dataLayer.push(eventData);
780}
781
782function trackCalculatorCategoryClick(ctaName) {
783  const eventData = {
784    event: 'click',
785    cta_name: String(ctaName || '').trim().toLowerCase(),
786    link_pagename: pageName,
787    login_status: loginInfo.login_status,
788    journey_name: getJourneyName().toLowerCase(),
789    customerID: loginInfo.customerID,
790  };
791
792  console.log('🧮 GTM calculator category click:', JSON.stringify(eventData, null, 2));
793  window.dataLayer.push(eventData);
794}
795
796// Export individual tracking functions for manual use
797export { trackClick, trackBannerClick, trackSearch, trackSearchResults, trackFileDownload, trackScroll, trackLoanAmountChange, trackFDTenureChange, trackFDJourneyEvent, trackApplyFDStep, trackOTPSuccess, trackOTPFailure, trackOTPResend, trackKYCVerifyStep, trackVMAEvent, trackLSPEvent, trackLoginSuccess, trackError, trackVMAFieldEntry, trackLoginFees, trackProcessingFees, trackLSPPaymentEvent, trackVMALoginFeesPayment, trackVMAProcessingFeesPayment, trackFormStart, trackFormSubmit, trackCalculatorEvent, trackCalculatorCategoryClick };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.