CRC_recomendation_tool
CrcPromotionresultstab ist temporär nicht erreichbar.
Fehler bei der Verarbeitung der Vorlage.
Java method "com.sun.proxy.$Proxy1454.findCrcUser(long)" threw an exception when invoked on com.sun.proxy.$Proxy1454 object "com.elan.crc.user.service.impl.CrcUserLocalServiceImpl@59fe88ad"; see cause exception in the Java stack trace.
----
FTL stack trace ("~" means nesting-related):
- Failed at: #assign user = crcuserLS.findCrcUser(... [in template "10154#10192#5255368" at line 12, column 1]
----
1<#assign utilServ = (serviceLocator.findService("com.elan.crc.utilites.service.CRCRemoteServiceService"))! />
2<#assign portalURL = themeDisplay.getPortalURL() />
3<#assign oadURL_TC = 'https://online1.elancard.com/oad/terms.controller' />
4
5<#if portalURL?contains("uat")>
6 <#assign oadURL_TC = 'https://uat-online1.elancard.com/oad/terms.controller' />
7</#if>
8
9<#-- New code to find location code via client profile rather than query string params -->
10<#assign userId = themeDisplay.getUserId() />
11<#assign crcuserLS = serviceLocator.findService('com.elan.crc.user.service.CrcUserLocalService')>
12<#assign user = crcuserLS.findCrcUser(userId?number)>
13<#assign lc = user.getLocationCode()>
14<#assign prefix = "00000" >
15<#assign lcWithPrefix = prefix + lc >
16<#assign withPrefixLength = lcWithPrefix?length >
17<#assign startHere = withPrefixLength - 5>
18<#assign lc = lcWithPrefix?substring(startHere)>
19
20<#-- using lc gets partner name, pbu, and subbu -->
21<#assign clientProfileService = serviceLocator.findService('com.elan.crc.user.service.CrcClientProfileLocalService') >
22<#assign clientProfileList = clientProfileService.findByLocationCode(lc)>
23<#if clientProfileList?first?has_content>
24 <#assign clientProfile = clientProfileList?first>
25 <#assign partnerName = clientProfile.getMarketingNameLong()>
26 <#assign pbu = clientProfile.getPbu()>
27 <#assign subbu = clientProfile.getSUBBRANDBUNBR()>
28 <#-- logoFileName path -->
29 <#assign logoFileName = clientProfile.getLogoBFormat1()?replace(".", "-")?replace("-([^-]*)$", ".$1", "r")?lower_case>
30
31 <#-- gets cards for finical institution(fi) -->
32 <#assign fiCardsData = (utilServ.makeCall('crcTier3GetPbuData','0&'+ pbu +'&'+subbu))!>
33</#if>
34
35
36<#assign cdnCardArtUrl = (propsUtil.get('rackspace.cdn.cardart.url'))!>
37<#-- Prod cdnLogoUrl-->
38<#assign cdnLogoUrl = (propsUtil.get('rackspace.cdn.web.url'))!>
39
40
41 <#assign crcRewardsCalculatorLocalService = serviceLocator.findService('com.elan.crc.quicklink.service.CrcRewardsCalculatorLocalService') >
42 <#assign rewardsList =
43 crcRewardsCalculatorLocalService.getCrcRewardsCalculators(-1,-1)>
44
45
46<#-- BUILD REWARDS LOOKUP MAP (offerType → reward) -->
47
48<#assign rewardsByOfferType = {}>
49
50<#if rewardsList?has_content>
51 <#list rewardsList as reward>
52 <#assign rewardsByOfferType =
53 rewardsByOfferType + {
54 reward.getOfferType()?string : reward
55 }
56 >
57 </#list>
58</#if>
59
60<#-- WEB CONTENT LOOKUP (offerType → child fields) -->
61<#-- Parent: Numberh6rv (offerType) -->
62
63<#assign wcByOfferType = {}>
64
65<#if Numberh6rv.getSiblings()?has_content>
66 <#list Numberh6rv.getSiblings() as wcItem>
67
68 <#assign offerTypeKey = wcItem.getData()?string />
69
70 <#assign textTitle = "" />
71 <#if wcItem.Text13zu?? && wcItem.Text13zu.getData()?has_content>
72 <#assign textTitle = wcItem.Text13zu.getData() />
73 </#if>
74
75 <#assign childTextHtml = "" />
76 <#if wcItem.HTMLbl7t?? && wcItem.HTMLbl7t.getData()?has_content>
77 <#assign childTextHtml = wcItem.HTMLbl7t.getData() />
78 </#if>
79
80 <#assign childText = "" />
81 <#if wcItem.annualFeeText?? && wcItem.annualFeeText.getData()?has_content>
82 <#assign childText = wcItem.annualFeeText.getData() />
83 </#if>
84
85 <#assign wcByOfferType = wcByOfferType + {
86 offerTypeKey : {
87 "text" : textTitle,
88 "textHtml" : childTextHtml,
89 "childText" : childText
90 }
91 }>
92
93
94 </#list>
95</#if>
96
97<#-- COMBINED LIST -->
98
99<#assign combinedList = []>
100<#if fiCardsData?has_content>
101 <#list fiCardsData as fib>
102 <#assign offerKey = fib.offerType?string />
103 <#assign offerId = fib.offerId?string />
104 <#assign reward = rewardsByOfferType[offerKey]! />
105 <#assign wcData = wcByOfferType[offerKey]! />
106 <#-- Include only when reward exists -->
107 <#if reward?has_content>
108 <#assign combinedList = combinedList + [{
109 "offerType" : offerKey,
110 "offerId" : offerId,
111 "card" : fib,
112 "reward" : reward,
113 "wc" : wcData
114 }]>
115 </#if>
116
117 </#list>
118</#if>
119
120<#-- STEP 3: OPTIONAL SORT (BY CARD ORDER) -->
121<#--
122<#if combinedList?has_content>
123 <#assign combinedList = combinedList?sort_by("card.cardOrder")>
124</#if>
125-->
126
127<div class="container re_header">
128 <!-- Logo Section -->
129 <div class="header-bar">
130 <div class="header-left">
131 <#if (backArrow.getData())?? && backArrow.getData() != "">
132 <img class="d-none" alt="${backArrow.getAttribute("alt")}" data-fileentryid="${backArrow.getAttribute("fileEntryId")}" src="${backArrow.getData()}" />
133 </#if>
134 <img
135 class="re_cdn_logo" src="${cdnLogoUrl!''}/${logoFileName!''}"
136 alt="elan-logo" loading="eager"/>
137 </div>
138
139 <!-- Right section -->
140 <div class="header-right d-none card_reset_btn" id="resettext"onclick="setCardType('ALL', this)">
141 <#if (resetIcon.getData())?? && resetIcon.getData() != "">
142 <img alt="${resetIcon.getAttribute("alt")}" data-fileentryid="${resetIcon.getAttribute("fileEntryId")}" src="${resetIcon.getData()}" />
143 </#if>
144 <div class="reset-text">Reset</div>
145 </div>
146 </div>
147
148 <h1 class="re_cdn_titl" id="title1">
149 <#if (title.getData())??>
150 ${title.getData()}
151 </#if>
152 </h1>
153 <p class="re_cdn_sub_titl" id="des1">
154 <#if (description.getData())??>
155 ${description.getData()}
156 </#if></p>
157 <h1 class="re_cdn_titl" id="title2">
158 <#if (consumerCardTitle.getData())??>
159 ${consumerCardTitle.getData()}
160 </#if></h1>
161 <p class="re_cdn_sub_titl" id="des2">
162 <#if (consumerCardDes.getData())??>
163 ${consumerCardDes.getData()}
164 </#if></p>
165 <h1 class="re_cdn_titl" id="title3">
166 <#if (consumerCategoryTitle.getData())??>
167 ${consumerCategoryTitle.getData()}
168 </#if></h1>
169 <p class="re_cdn_sub_titl" id="des3">
170 <#if (consumerEveryDayDes.getData())??>
171 ${consumerEveryDayDes.getData()}
172 </#if></p>
173 <p class="re_cdn_sub_titl" id="des4">
174 <#if (consumerTravelDes.getData())??>
175 ${consumerTravelDes.getData()}
176 </#if></p>
177
178 <!-- Cards Section -->
179
180 <div class="row re_card_sec" id="reCardSec">
181
182 <div class="re_card" id="re_card_cosumer" onclick="setCardType('CONSUMER', this)">
183 <img class="re_card_icon" src="${consumerCardImage.getData()}" />
184 <h2 class="re_card_title">Consumer Credit Cards</h2>
185 </div>
186
187 <div class="re_card" id="re_card_consumer_business" onclick="setCardType('BUSINESS', this)">
188 <img class="re_card_icon" src="${businessCardImage.getData()}" />
189 <h2 class="re_card_title">Business Credit Cards</h2>
190 </div>
191 <div class="plus-symbol d-none" id="plussymbol">
192 +
193 </div>
194 <div class="re_card d-none" id="re_card_everyday" onclick="setCardType('EVERYDAY', this)">
195 <#if (consumerEveryDayImg.getData())?? && consumerEveryDayImg.getData() != "">
196 <img class="re_card_icon" alt="${consumerEveryDayImg.getAttribute("alt")}" data-fileentryid="${consumerEveryDayImg.getAttribute("fileEntryId")}" src="${consumerEveryDayImg.getData()}" />
197 </#if>
198 <h2 class="re_card_title">Everyday Spend</h2>
199 </div>
200
201 <div class="re_card d-none" id="re_card_travel" onclick="setCardType('TRAVEL', this)">
202 <#if (consumerTravelImg.getData())?? && consumerTravelImg.getData() != "">
203 <img class="re_card_icon" alt="${consumerTravelImg.getAttribute("alt")}" data-fileentryid="${consumerTravelImg.getAttribute("fileEntryId")}" src="${consumerTravelImg.getData()}" />
204 </#if>
205 <h2 class="re_card_title">Travel</h2>
206 </div>
207
208 </div>
209
210
211</div>
212
213<div class="car_sec">
214 <div class="section-header">
215 <div class="section-title" id="cardCount">
216 All Cards (${combinedList?has_content?then(combinedList?size, 0)} Cards)
217 </div>
218 </div>
219
220 <section class="carousel-section" role="region" aria-label="Cards carousel">
221
222 <!-- ADD rcarousel-inner class -->
223 <div class="card-list rcarousel-inner" id="carouselInner"></div>
224 </section>
225</div>
226
227<div class="frame_sec d-none" id="framesec">
228 <div class="section-aerrow">
229 <div class="carousel-indicator">
230 <button class="indicator-arrow" id="iframePrev"
231 onclick="changeIframe(-1)" aria-label="Previous calculator">
232 ←
233 </button>
234
235 <div class="indicator-count" id="indicatorCount"></div>
236
237 <button class="indicator-arrow" id="iframeNext"
238 onclick="changeIframe(1)" aria-label="Next calculator">
239 →
240 </button>
241
242 </div>
243
244 </div>
245
246 <div class="iframe-wrapper d-none" id="iframeWrapper"></div>
247
248
249
250 <div class="section-footer">
251 <div class="section-footer-title" id="section-footer-title" onclick="scrollToTop()">
252 ↑ Back to top
253 </div>
254 </div>
255</div>
256
257<style>
258.indicator-arrow:disabled {
259 opacity: 0.4;
260 cursor: not-allowed;
261 pointer-events: none;
262}
263
264.indicator-count {
265 min-width: 60px;
266 text-align: center;
267 font-family: Arial, sans-serif;
268 font-size: 14px;
269 font-weight: 700;
270 color: #ffffff; /* arrow bar is black */
271}
272.iframe-wrapper {
273 width: 100%;
274 height: 100%;
275 padding: 0 8px;
276 background: #ffffff;
277
278 display: inline-flex;
279 justify-content: center;
280 align-items: center;
281 gap: 8px;
282 box-sizing: border-box;
283}
284
285.iframe-content {
286 flex: 1 1 0;
287 width: 100%;
288 max-width: 736px;
289 height: 558.9px;
290
291 border: 0;
292}
293
294/* ===== HEADER WRAPPER ===== */
295.header-bar {
296 display: flex;
297 flex-direction: row;
298 justify-content: space-between;
299 align-items: center;
300 max-width: 700px;
301 width: 100%;
302 height: auto;
303 min-height: 40px;
304 position: relative;
305}
306
307/* ===== LEFT SECTION ===== */
308.header-left {
309 width: 300px;
310 display: flex;
311 align-items: center;
312 gap: 16px;
313}
314/* ===== RIGHT SECTION ===== */
315.header-right {
316 display: flex;
317 align-items: center;
318 justify-content: center;
319 gap: 8px;
320}
321
322
323/* Reset text */
324.card_reset_btn{
325 cursor: pointer;
326}
327.reset-text {
328 color: var(--Core-Black, #000000);
329 font-family: Arial, sans-serif;
330 font-size: 14px;
331 font-weight: 700;
332 text-align: center;
333}
334
335.re_header {
336 max-width: 752px;
337 width: 100%;
338 background: #E6E6E6;
339 padding: 24px;
340 box-sizing: border-box;
341}
342.re_car_sec{
343 height: 62px;
344 background: #000000;
345 border-top: 1px solid #D7D7D7;
346 box-sizing: border-box;
347}
348
349.re_cdn_logo {
350 width: 162px;
351 height: 62px;
352}
353.re_cdn_logo.consumer-logo {
354 width: 104px;
355 height: 40px;
356}
357.reset-link {
358 margin-left: auto; /* pushes to right corner */
359 cursor: pointer;
360 font-weight: 700;
361 margin-right:50px;
362}
363
364.re_cdn_titl{
365 font: 400 34px/52px Arial, sans-serif;
366 color: #000;
367}
368
369.re_cdn_sub_titl {
370 max-width: 702px;
371 width: 100%;
372 font-family: Arial, sans-serif;
373 font-weight: 400;
374 font-size: 14px;
375 line-height: 22px;
376 letter-spacing: 0;
377 color: #000000;
378}
379
380
381.re_card_sec{
382 display: flex;
383 justify-content: center;
384 align-items: center;
385 gap: 16px;
386 height: 100px;
387}
388
389.re_card {
390 box-sizing: border-box;
391 display: flex;
392 flex-direction: column;
393 align-items: center;
394 width: 100%;
395 max-width: 200px;
396 height: 100px;
397 padding: 12px;
398 background: #FFFFFF;
399 border: 1px solid #969798;
400 cursor: pointer;
401}
402
403.plus-symbol {
404 color: var(--Core-Elan-Dark-Blue, #084BB7);
405 text-align: center;
406 font-family: Arial, sans-serif;
407 font-size: 20px;
408 font-weight: 700;
409 line-height: normal;
410}
411
412.re_card.active {
413 background: var(--Core-Elan-Dark-Blue, #084BB7);
414
415}
416.re_card.active .re_card_title {
417 color: #ffffff; /* or whatever color you want */
418}
419.re_card_sec.consumer-active {
420 gap: 8px;
421}
422.re_card.active .re_card_icon {
423 filter: brightness(0) invert(1);
424}
425
426.re_card_icon{
427 width: 32px;
428 height: 32px;
429 position: relative;
430 top: 2.08px;
431}
432.re_card_title{
433 width: 85px;
434 height: 32px;
435 font-family: Arial, sans-serif;
436 font-weight: 700;
437 font-size: 14px;
438 line-height: 1;
439 text-align: center;
440 color: #084BB7;
441}
442
443</style>
444
445<style>
446.car_sec{
447 max-width: 754px;
448 width: 100%;
449 /* min-height: 705px; */
450 box-sizing: border-box;
451}
452.section-header {
453 box-sizing: border-box;
454 width: 100%;
455 max-width: 754px;
456 height: 62px;
457 background: #000000;
458 border: 1px solid #D7D7D7;
459 display: flex;
460 align-items: center;
461 padding: 0 16px;
462}
463
464.section-title {
465 font-family: Arial, sans-serif;
466 font-weight: 700;
467 font-size: 18px;
468 line-height: 22px;
469 color: #FFFFFF;
470}
471.section-footer{
472 box-sizing: border-box;
473 width: 100%;
474 max-width: 754px;
475 height: 62px;
476 background: var(--Core-Dark-Gray, #969798);
477 border: 1px solid #D7D7D7;
478 display: flex;
479 align-items: center;
480 padding: 0 16px;
481}
482.carousel-section {
483 position: relative;
484 overflow: hidden;
485 max-width: 750px;
486 width: 100%;
487 /* min-height: 643px; */
488 display: flex;
489 flex-direction: column;
490 gap: 16px;
491 padding: 24px 0;
492 background: var(--Core-White, #FFFFFF);
493 box-sizing: border-box;
494}
495
496.card-list {
497 display: flex !important;
498 flex-wrap: wrap !important;
499 justify-content: center;
500 gap: 16px;
501 transform: none !important;
502 transition: none !important;
503}
504
505.rcard {
506 flex: 0 0 calc(33.333% - 16px);
507 max-width: calc(33.333% - 16px);
508 display: flex;
509 justify-content: center;
510}
511
512
513.cal-card {
514 max-width: 210px;
515 width: 100%;
516 border: 1px solid #FFFFFF;
517 box-sizing: border-box;
518 position: relative;
519 font-family: Arial, sans-serif;
520}
521
522/* ===== IMAGE ===== */
523.cal-card-image {
524 width: 100%;
525 display: flex;
526 justify-content: center;
527 position: absolute;
528 z-index: 2;
529}
530
531.cal-card-image img {
532 width: 171.875px;
533 height: 110px;
534 object-fit: contain;
535}
536
537
538/* ===== BODY ===== */
539
540.cal-card-body {
541 margin-top: 83px;
542 width: 100%;
543 background: #CECACA;
544 border-radius: 12px;
545 box-sizing: border-box;
546 padding: 16px;
547 color:#00000;
548}
549
550
551/* ===== TITLE ===== */
552.cal-card-title {
553 height: 44px;
554 font-family: Arial, sans-serif;
555 font-weight: 700;
556 font-size: 18px;
557 line-height: 21.6px;
558 letter-spacing: 0;
559 text-align: center;
560 display: flex;
561 align-items: center;
562 justify-content: center;
563}
564
565
566/* ===== DIVIDER ===== */
567
568.cal-divider, .cal-card-divider {
569 width: 162px;
570 margin: 16px auto;
571 border-bottom: 1px solid #000000;
572}
573
574/* ===== DETAILS ===== */
575 .cal-card-details {
576 width: 162px;
577 height: 75px;
578 display: flex;
579 flex-direction: column;
580 gap: 12px;
581}
582
583/* Frame */
584.cal-card-detail-row {
585 width: 100%;
586 min-height: 44px;
587 display: flex;
588 gap: 8px;
589 box-sizing: border-box;
590}
591
592.multiplier {
593 min-width: 21px;
594 font-family: Arial, sans-serif;
595 font-weight: 700;
596 font-size: 14px;
597 line-height: 22px;
598 white-space: nowrap;
599}
600.description {
601 font-family: Arial, sans-serif;
602 font-weight: 400;
603 font-size: 14px;
604 line-height: 22px;
605 text-align: center;
606}
607
608/* ===== FEE ===== */
609.cal-fee {
610 font-family: Arial, sans-serif;
611 font-weight: 400;
612 font-size: 14px;
613 line-height: 22px;
614 text-decoration: underline;
615 text-align: center;
616 color : #000 !important;
617}
618
619
620/* ===== ACTIONS ===== */
621.cal-actions {
622 display: flex;
623 justify-content: center;
624 margin-top: auto;
625}
626
627/* ===== BUTTON ===== */
628.cal-btn-primary {
629 color:var(--Core-Elan-Dark-Blue, #084BB7) !important;
630 width: 150px;
631 height: 44px;
632 color: #000;
633 cursor: pointer;
634 display: flex;
635 align-items: center;
636 justify-content: center;
637 border: 2px solid var(--Core-Elan-Dark-Blue, #084BB7) !important;
638 background: var(--Core-White, #FFF) !important;
639}
640
641.cal-btn-text {
642 text-shadow: none !important;
643 font-family: Arial, sans-serif;
644 font-size: 14px;
645 font-style: normal;
646 font-weight: 700;
647 line-height: 22.5px;
648 text-transform: capitalize;
649}
650.frame_sec{
651 max-width: 754px;
652 width: 100%;
653 box-sizing: border-box;
654}
655
656.frame-section-header {
657 box-sizing: border-box;
658 width: 100%;
659 max-width: 754px;
660 height: 62px;
661 background: #000000;
662 border: 1px solid #D7D7D7;
663 display: flex;
664 align-items: center;
665 justify-content: center;
666}
667
668.frame-title {
669 font-family: Arial, sans-serif;
670 font-weight: 700;
671 font-size: 18px;
672 line-height: 22px;
673 color: #FFFFFF;
674}
675.section-footer-title {
676 color: var(--Core-Black, #000000);
677 text-align: center;
678 font-family: Arial, sans-serif;
679 font-size: 18px;
680 font-style: normal;
681 font-weight: 700;
682 line-height: 22px;
683 max-width: 737.203px;
684 flex-shrink: 0;
685 margin: auto;
686 cursor: pointer;
687}
688
689/* Arrows */
690.section-aerrow {
691 box-sizing: border-box;
692 width: 100%;
693 max-width: 754px;
694 height: 62px;
695 background: #000000;
696 border: 1px solid #D7D7D7;
697 display: flex;
698 padding: 0 16px;
699 align-items: center;
700 justify-content: center;
701}
702.carousel-arrow {
703 position: absolute;
704 top: 50%;
705 transform: translateY(-50%);
706 background: #ffffff;
707 border: 1px solid #ccc;
708 width: 36px;
709 height: 36px;
710 border-radius: 50%;
711 cursor: pointer;
712 font-size: 22px;
713 line-height: 34px;
714 text-align: center;
715 z-index: 5;
716}
717
718.carousel-arrow.left {
719 left: 10px;
720}
721
722.carousel-arrow.right {
723 right: 10px;
724}
725
726.carousel-arrow:hover {
727 background: #084BB7;
728 color: #fff;
729}
730.carousel-indicator {
731 display: flex;
732 align-items: center;
733 gap: 14px;
734}
735
736.indicator-arrow {
737 border: none;
738 font-size: 22px;
739 cursor: pointer;
740 background: #000 !important;
741}
742
743.indicator-arrow:hover {
744 color: #084BB7;
745}
746
747.indicator-dots {
748 display: flex;
749 gap: 8px;
750}
751
752.indicator-dot {
753 width: 10px;
754 height: 10px;
755 border-radius: 50%;
756 background: #d0d0d0;
757 cursor: pointer;
758}
759
760.indicator-dot.active {
761 background: #084BB7;
762}
763
764.rcard.is-focused {
765 outline: none;
766 box-shadow: none;
767 border: none;
768}
769
770.rcard.is-focused .cal-card-body {
771 color:#fff;
772 background:var(--Core-Elan-Dark-Blue, #084BB7)
773}
774.rcard.is-focused .cal-fee {
775 color:#fff !important;
776}
777.rcard.is-focused .cal-divider, .rcard.is-focused .cal-card-divider {
778 border-bottom: 1px solid var(--Core-Elan-Dark-Blue, #fff)
779}
780</style>
781
782<script>
783const cardsData = [
784 <#if combinedList?has_content>
785 <#list combinedList as item>
786 {
787 offerType: "${item.offerType}",
788 offerId: "${item.offerId}",
789 title: "${item.reward.getCardName()?js_string!''}",
790 groupName:
791 "${item.reward.getGroupName()?upper_case?contains('BUSINESS')?then('BUSINESS','CONSUMER')}",
792 category:
793 "${item.reward.getCategory()?upper_case?contains('EVERYDAY')?then('EVERYDAY','TRAVEL')}",
794 imageUrl: "${cdnCardArtUrl}/${item.card.filename}.png",
795 wcTextHtml: "${item.wc?has_content?then(item.wc.textHtml?js_string,'')}",
796 wcChildText: "${item.wc?has_content?then(item.wc.childText?js_string,'')}"
797 }<#if item_has_next>,</#if>
798 </#list>
799 </#if>
800];
801
802
803/* =========================================================
804 INIT
805========================================================= */
806
807document.addEventListener('DOMContentLoaded', () => {
808 applyInitialCardMixUI();
809 renderCarousel();
810 autoSelectFirstCard();
811 updateIframeCounter();
812 isInitialLoad = false;
813});
814
815
816function setCardType(type, el) {
817
818 if (
819 (type === 'CONSUMER' || type === 'BUSINESS') &&
820 el.classList.contains('disabled')
821 ) {
822 return;
823 }
824
825 if (type === 'ALL') {
826 resetUIToDefault();
827 return;
828 }
829
830 // Consumer / Business
831 if (type === 'CONSUMER' || type === 'BUSINESS') {
832 activeCardType = type;
833 activeCategory = 'ALL';
834 }
835
836 // Category only under Consumer
837 if (
838 (type === 'EVERYDAY' || type === 'TRAVEL') &&
839 activeCardType === 'CONSUMER'
840 ) {
841 activeCategory = type;
842 }
843
844 // Active UI
845 // Remove active only when switching main types
846 if (type === 'CONSUMER' || type === 'BUSINESS') {
847 document.querySelectorAll('.re_card')
848 .forEach(card => card.classList.remove('active'));
849 }
850
851 // Always activate clicked card
852 el.classList.add('active');
853
854 // Keep CONSUMER active when selecting sub-categories
855 if (type === 'EVERYDAY' || type === 'TRAVEL') {
856 const consumerCard = document.getElementById('re_card_cosumer');
857 if (consumerCard) {
858 consumerCard.classList.add('active');
859 }
860 }
861
862
863 // Disable logic
864 if (type === 'CONSUMER') {
865 handleConsumerSubCard();
866 el.classList.add('disabled');
867 }
868
869 if (type === 'BUSINESS') {
870 document.getElementById('re_card_everyday')?.classList.remove('active');
871 document.getElementById('re_card_travel')?.classList.remove('active');
872 el.classList.add('disabled');
873 }
874
875 if (type === 'EVERYDAY') handleEveryDayCard();
876 if (type === 'TRAVEL') handleTravelCard();
877
878 currentIframeIndex = 0;
879 currentPage = 0;
880 renderCarousel();
881 autoSelectFirstCard();
882 updateIframeCounter();
883
884}
885
886
887function handleConsumerSubCard() {
888 $("#re_card_consumer_business").hide();
889 $("#re_card_everyday,#re_card_travel,#plussymbol").removeClass("d-none");
890
891 $("#title2,#des2").removeClass("d-none");
892 $("#title1,#des1").addClass("d-none");
893 const cardContainer = document.querySelector('.re_card_sec');
894 cardContainer?.classList.add('consumer-active');
895 $("#resettext").removeClass("d-none");
896 document.querySelector('.re_cdn_logo')?.classList.add('consumer-logo');
897
898}
899function handleEveryDayCard() {
900 document.getElementById('re_card_travel')?.classList.remove('active');
901 $("#resettext").removeClass("d-none");
902 $("#title3,#des3").removeClass("d-none");
903 $("#title2,#des2,#des4").addClass("d-none");
904}
905function handleTravelCard() {
906 document.getElementById('re_card_everyday')?.classList.remove('active');
907 $("#resettext").removeClass("d-none");
908 $("#title3,#des4").removeClass("d-none");
909 $("#title2,#des2,#des3").addClass("d-none");
910}
911
912
913function applyInitialCardMixUI() {
914
915 const groupSet = new Set(cardsData.map(card => card.groupName));
916 $("#reCardSec").removeClass("d-none");
917 // First hide everything (safe – already hidden, no flicker)
918 $("#title1,#title2,#title3,#des1,#des2,#des3,#des4,#framesec,#resettext")
919 .addClass("d-none");
920
921 // BOTH Consumer + Business
922 if (groupSet.size > 1) {
923 $("#title1,#des1").removeClass("d-none");
924 }
925
926 // ONLY Consumer
927 else if (groupSet.has('CONSUMER')) {
928 activeCardType = 'CONSUMER';
929 activeCategory = 'ALL';
930 $("#title2,#des2").removeClass("d-none");
931 $("#re_card_everyday,#re_card_travel").removeClass("d-none");
932 $("#re_card_cosumer,#re_card_consumer_business").addClass("d-none");
933 }
934
935 // ONLY Business
936 else if (groupSet.has('BUSINESS')) {
937 $("#title1,#des1").removeClass("d-none");
938 $("#reCardSec").addClass("d-none");
939 }
940}
941
942
943
944function resetUIToDefault() {
945
946 // Reset filter logic
947 activeCardType = 'ALL';
948 activeCategory = 'ALL';
949
950 // Reset card buttons
951 document.querySelectorAll('.re_card').forEach(card => {
952 card.classList.remove('active');
953 card.classList.remove('disabled');
954 });
955
956 // Restore card button visibility
957 $("#re_card_consumer_business").show();
958 $("#re_card_everyday,#re_card_travel,#plussymbol").addClass("d-none");
959 document.querySelector('.re_card_sec')?.classList.remove('consumer-active');
960 document.querySelector('.re_cdn_logo')?.classList.remove('consumer-logo');
961
962 document.querySelectorAll('.rcard')
963 .forEach(card => card.classList.remove('is-focused'));
964
965
966 currentIframeIndex = 0;
967 // Apply data-based hiding (ONLY what is needed)
968 applyInitialCardMixUI();
969
970 // Reset carousel state
971 currentPage = 0;
972
973
974 // Re-render
975 renderCarousel();
976 autoSelectFirstCard();
977}
978
979
980
981/* =========================================================
982 GLOBAL STATE
983========================================================= */
984const PAGE_SIZE = 3;
985let activeCardType = 'ALL';
986let activeCategory = 'ALL';
987let currentPage = 0;
988let isInitialLoad = true;
989let currentIframeIndex = 0;
990let allowAutoScroll = true;
991
992
993
994
995
996
997
998
999
1000/* =========================================================
1001 FILTER + COUNT
1002========================================================= */
1003
1004function updateCardCount(count, type) {
1005 const el = document.getElementById('cardCount');
1006 if (!el) return;
1007
1008 let label = 'All Cards';
1009 if (type === 'CONSUMER') label = 'Consumer Cards';
1010 if (type === 'BUSINESS') label = 'Business Cards';
1011
1012 // reeMarker-safe
1013 el.textContent = `${r"${label}"} (${r"${count}"} Cards)`;
1014}
1015
1016
1017
1018function getFilteredCards() {
1019 return cardsData.filter(card => {
1020
1021 // Filter by Consumer / Business
1022 if (activeCardType !== 'ALL' && card.groupName !== activeCardType) {
1023 return false;
1024 }
1025
1026 // Category filter ONLY when Consumer is selected
1027 if (activeCardType === 'CONSUMER' && activeCategory !== 'ALL') {
1028 return card.category === activeCategory;
1029 }
1030
1031 // Business ignores category
1032 return true;
1033 });
1034}
1035
1036
1037/* =========================================================
1038 CAROUSEL RENDER
1039========================================================= */
1040function renderCarousel() {
1041 const container = document.getElementById('carouselInner');
1042 if (!container) return;
1043
1044 const filteredCards = getFilteredCards();
1045
1046
1047
1048 if (!filteredCards.length) {
1049 container.innerHTML = '<p style="padding:20px">No cards available</p>';
1050 updateCardCount(0, activeCardType);
1051 return;
1052 }
1053
1054
1055 updateCardCount(filteredCards.length, activeCardType);
1056
1057 const start = currentPage * PAGE_SIZE;
1058 const end = start + PAGE_SIZE;
1059 const pageCards = filteredCards.slice(start, end);
1060
1061 container.innerHTML = pageCards.map(card => `
1062 <div class="rcard"
1063 tabindex="0"
1064 data-offer-type="${r"${card.offerType}"}"
1065 onclick="showCalculator(
1066 window.panoUrl + '/calculators/${r"${card.offerType}"}',
1067 '${r"${card.title}"}'
1068 )">
1069
1070 <div class="cal-card">
1071 <div class="cal-card-image">
1072 <img src="${r"${card.imageUrl}"}" alt="Card Image">
1073 </div>
1074
1075 <div class="cal-card-body">
1076 <h3 class="cal-card-title">${r"${card.title}"}</h3>
1077 <div class="cal-divider"></div>
1078
1079 <div class="cal-card-details">
1080 <a class="cal-fee"
1081 href="${oadURL_TC}?step=display&offerId=${r"${card.offerId}"}&locationCode=${lc}"
1082 target="_blank"
1083 onclick="event.stopPropagation();">
1084 ${r"${card.wcChildText}"}
1085 </a>
1086 </div>
1087 </div>
1088 </div>
1089 </div>
1090 `).join('');
1091
1092
1093}
1094
1095/* =========================================================
1096 FILTER BUTTON HANDLER
1097========================================================= */
1098
1099
1100
1101
1102/* =========================================================
1103 CAROUSEL CORE
1104========================================================= */
1105function getCards() {
1106 return document.querySelectorAll('.rcard');
1107}
1108
1109
1110
1111
1112function showCalculator(url, title) {
1113 console.log("[CAL URL]",url);
1114 const filteredCards = getFilteredCards();
1115
1116 const index = filteredCards.findIndex(
1117 card => url.includes(card.offerType)
1118 );
1119
1120 if (index !== -1) {
1121 currentIframeIndex = index;
1122 highlightCard(filteredCards[index].offerType);
1123 }
1124
1125 const wrapper = document.getElementById('iframeWrapper');
1126 const titleEl = document.getElementById('frametitle');
1127
1128 if (titleEl) {
1129 titleEl.textContent = title + ' Rewards Calculator';
1130 }
1131
1132 wrapper.innerHTML = '';
1133
1134 const iframe = document.createElement('iframe');
1135 iframe.src = url;
1136 iframe.className = 'iframe-content';
1137 iframe.loading = 'lazy';
1138
1139 wrapper.appendChild(iframe);
1140
1141 $("#framesec,#iframeWrapper").removeClass("d-none");
1142
1143 // scroll only when allowed
1144 if (allowAutoScroll) {
1145 wrapper.scrollIntoView({ behavior: 'smooth' });
1146 }
1147
1148 updateIframeCounter();
1149}
1150
1151
1152function scrollToTop() {
1153 $('html, body').animate({ scrollTop: 0 }, 'slow');
1154}
1155
1156
1157function changeIframe(direction) {
1158 const filteredCards = getFilteredCards();
1159 const total = filteredCards.length;
1160 if (!total) return;
1161
1162 const nextIndex = currentIframeIndex + direction;
1163
1164 // stop at bounds
1165 if (nextIndex < 0 || nextIndex >= total) return;
1166
1167 currentIframeIndex = nextIndex;
1168
1169 // derive page from index
1170 const requiredPage = Math.floor(currentIframeIndex / PAGE_SIZE);
1171
1172 // switch page ONLY if needed
1173 if (requiredPage !== currentPage) {
1174 currentPage = requiredPage;
1175
1176 // prevent auto-select + scroll during re-render
1177 allowAutoScroll = false;
1178 renderCarousel();
1179 allowAutoScroll = true;
1180 }
1181
1182 const card = filteredCards[currentIframeIndex];
1183
1184 // highlight selected card
1185 highlightCard(card.offerType);
1186
1187 // load iframe (user action → scroll allowed)
1188 const url = window.panoUrl+'/calculators/' + card.offerType;
1189
1190 showCalculator(url, card.title);
1191
1192 updateIframeCounter();
1193}
1194
1195
1196
1197
1198
1199function updateIframeCounter() {
1200 const counterEl = document.getElementById('indicatorCount');
1201 const prevBtn = document.getElementById('iframePrev');
1202 const nextBtn = document.getElementById('iframeNext');
1203
1204 if (!counterEl || !prevBtn || !nextBtn) return;
1205
1206 const filteredCards = getFilteredCards();
1207 const total = filteredCards.length;
1208
1209 if (!total) {
1210 counterEl.textContent = '';
1211 prevBtn.disabled = true;
1212 nextBtn.disabled = true;
1213 return;
1214 }
1215
1216 // ARD-BASED COUNT (this is what you want)
1217 counterEl.textContent =
1218 (currentIframeIndex + 1) + ' of ' + total;
1219
1220 // isable arrows at first / last card
1221 prevBtn.disabled = currentIframeIndex === 0;
1222 nextBtn.disabled = currentIframeIndex === total - 1;
1223}
1224
1225
1226
1227function highlightCard(offerType) {
1228 document.querySelectorAll('.rcard').forEach(card => {
1229 if (card.dataset.offerType === offerType) {
1230 card.classList.add('is-focused');
1231 } else {
1232 card.classList.remove('is-focused');
1233 }
1234 });
1235}
1236
1237function autoSelectFirstCard() {
1238 const filteredCards = getFilteredCards();
1239 if (!filteredCards.length) return;
1240
1241 const firstCard = filteredCards[0];
1242
1243 currentIframeIndex = 0;
1244
1245 // disable scroll for auto select
1246 allowAutoScroll = false;
1247
1248 highlightCard(firstCard.offerType);
1249
1250 const url = window.panoUrl+'/calculators/' + firstCard.offerType;
1251
1252 showCalculator(url, firstCard.title);
1253
1254 // re-enable scroll for user actions
1255 allowAutoScroll = true;
1256}
1257
1258</script>