@active-reach/web-sdk 1.29.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aegis-push.min.js +2 -0
- package/dist/aegis-push.min.js.map +1 -0
- package/dist/aegis.min.js +1 -1
- package/dist/aegis.min.js.map +1 -1
- package/dist/inapp/AegisInAppManager.d.ts +4 -7
- package/dist/inapp/AegisInAppManager.d.ts.map +1 -1
- package/dist/inapp/actions.d.ts +1 -1
- package/dist/inapp/renderers/card-style.d.ts +3 -0
- package/dist/inapp/renderers/card-style.d.ts.map +1 -1
- package/dist/inapp/renderers/card-template.d.ts +27 -6
- package/dist/inapp/renderers/card-template.d.ts.map +1 -1
- package/dist/inapp/renderers/carousel-cards.d.ts.map +1 -1
- package/dist/inapp/renderers/index.d.ts +2 -0
- package/dist/inapp/renderers/index.d.ts.map +1 -1
- package/dist/inapp/renderers/product-recommendation.d.ts.map +1 -1
- package/dist/inapp/renderers/reviews.d.ts +38 -0
- package/dist/inapp/renderers/reviews.d.ts.map +1 -0
- package/dist/index.js +750 -312
- package/dist/index.js.map +1 -1
- package/dist/push/cdn-push.d.ts +21 -0
- package/dist/push/cdn-push.d.ts.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -311,12 +311,12 @@ const ICONS = {
|
|
|
311
311
|
function escapeAttr(value) {
|
|
312
312
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
313
313
|
}
|
|
314
|
-
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
314
|
+
const SVG_NS$1 = "http://www.w3.org/2000/svg";
|
|
315
315
|
function lucideSvgEl(name, opts = {}) {
|
|
316
316
|
const node = ICONS[name];
|
|
317
317
|
if (!node || typeof document === "undefined") return null;
|
|
318
318
|
const size = opts.size ?? 24;
|
|
319
|
-
const svg = document.createElementNS(SVG_NS, "svg");
|
|
319
|
+
const svg = document.createElementNS(SVG_NS$1, "svg");
|
|
320
320
|
svg.setAttribute("width", String(size));
|
|
321
321
|
svg.setAttribute("height", String(size));
|
|
322
322
|
svg.setAttribute("viewBox", "0 0 24 24");
|
|
@@ -333,7 +333,7 @@ function lucideSvgEl(name, opts = {}) {
|
|
|
333
333
|
svg.setAttribute("aria-hidden", "true");
|
|
334
334
|
}
|
|
335
335
|
for (const [tag, attrs] of node) {
|
|
336
|
-
const child = document.createElementNS(SVG_NS, tag);
|
|
336
|
+
const child = document.createElementNS(SVG_NS$1, tag);
|
|
337
337
|
for (const [k, v] of Object.entries(attrs)) child.setAttribute(k, String(v));
|
|
338
338
|
svg.appendChild(child);
|
|
339
339
|
}
|
|
@@ -525,6 +525,16 @@ function applyPerCardOverride(tpl, look) {
|
|
|
525
525
|
if (look.hide_cta) drop("cta");
|
|
526
526
|
if (look.hide_rating) drop("rating");
|
|
527
527
|
if (look.cta_text) elements = elements.map((e) => e.type === "cta" ? { ...e, text: look.cta_text } : e);
|
|
528
|
+
if (look.cta_action || look.cta_url || look.cta_custom_name) {
|
|
529
|
+
elements = elements.map(
|
|
530
|
+
(e) => e.type === "cta" ? {
|
|
531
|
+
...e,
|
|
532
|
+
...look.cta_action ? { cta_action: look.cta_action } : {},
|
|
533
|
+
...look.cta_url ? { cta_url: look.cta_url } : {},
|
|
534
|
+
...look.cta_custom_name ? { cta_custom_name: look.cta_custom_name } : {}
|
|
535
|
+
} : e
|
|
536
|
+
);
|
|
537
|
+
}
|
|
528
538
|
if (look.badge) {
|
|
529
539
|
const badgeEl = { type: "badge", text: look.badge, color: look.badge_color };
|
|
530
540
|
elements = elements.some((e) => e.type === "badge") ? elements.map((e) => e.type === "badge" ? badgeEl : e) : [elements[0], badgeEl, ...elements.slice(1)].filter(Boolean);
|
|
@@ -538,7 +548,7 @@ function priceText(card) {
|
|
|
538
548
|
if (card.body && /\d/.test(card.body)) return card.body;
|
|
539
549
|
return void 0;
|
|
540
550
|
}
|
|
541
|
-
function paintPrice(card,
|
|
551
|
+
function paintPrice(card, el2, accent, ctx) {
|
|
542
552
|
const price = priceText(card);
|
|
543
553
|
if (price === void 0) return null;
|
|
544
554
|
const meta = card.metadata || {};
|
|
@@ -547,10 +557,10 @@ function paintPrice(card, el, accent, ctx) {
|
|
|
547
557
|
row.style.cssText = "display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;";
|
|
548
558
|
const p = document.createElement("span");
|
|
549
559
|
p.textContent = price;
|
|
550
|
-
if (
|
|
560
|
+
if (el2.variant === "plain") {
|
|
551
561
|
p.style.cssText = `color:${accent};font-weight:700;font-size:13px;`;
|
|
552
562
|
} else {
|
|
553
|
-
const pillBg =
|
|
563
|
+
const pillBg = el2.color ? ctx.sanitizeColor(el2.color) : "#059669";
|
|
554
564
|
p.style.cssText = `background:${pillBg};color:#fff;font-weight:800;font-size:12px;padding:2px 7px;border-radius:6px;box-shadow:0 2px 6px rgba(0,0,0,0.30);`;
|
|
555
565
|
}
|
|
556
566
|
row.appendChild(p);
|
|
@@ -602,14 +612,14 @@ function contrastFg(bg) {
|
|
|
602
612
|
const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
603
613
|
return lum < 0.5 ? "#ffffff" : "#1f2937";
|
|
604
614
|
}
|
|
605
|
-
function buildCountdownElement(
|
|
606
|
-
const targetStr = typeof
|
|
615
|
+
function buildCountdownElement(el2, fg, muted) {
|
|
616
|
+
const targetStr = typeof el2.countdown_target === "string" ? el2.countdown_target : "";
|
|
607
617
|
const target = targetStr ? Date.parse(targetStr) : NaN;
|
|
608
618
|
if (!targetStr || isNaN(target)) return null;
|
|
609
619
|
const wrap = document.createElement("div");
|
|
610
620
|
wrap.style.cssText = "display:flex;align-items:baseline;gap:8px;font-variant-numeric:tabular-nums;margin:2px 0;";
|
|
611
621
|
const label = document.createElement("span");
|
|
612
|
-
label.textContent = typeof
|
|
622
|
+
label.textContent = typeof el2.countdown_label === "string" && el2.countdown_label || "Ends in";
|
|
613
623
|
label.style.cssText = `font-size:12px;color:${muted};`;
|
|
614
624
|
const time = document.createElement("span");
|
|
615
625
|
time.style.cssText = `font-size:15px;font-weight:700;color:${fg};letter-spacing:0.2px;`;
|
|
@@ -631,13 +641,13 @@ function buildCountdownElement(el, fg, muted) {
|
|
|
631
641
|
timer = setInterval(tick, 1e3);
|
|
632
642
|
return wrap;
|
|
633
643
|
}
|
|
634
|
-
function appendRatingThanks(wrap,
|
|
635
|
-
appendThanks(wrap,
|
|
636
|
-
const url = isHigh && typeof
|
|
644
|
+
function appendRatingThanks(wrap, el2, ctx, fg, accent, isHigh) {
|
|
645
|
+
appendThanks(wrap, el2, fg, "Thanks for rating us!");
|
|
646
|
+
const url = isHigh && typeof el2.rating_high_url === "string" ? ctx.sanitizeUrl(el2.rating_high_url) : "";
|
|
637
647
|
if (url) {
|
|
638
648
|
const cta = document.createElement("button");
|
|
639
649
|
cta.type = "button";
|
|
640
|
-
cta.textContent =
|
|
650
|
+
cta.textContent = el2.rating_high_cta || "Leave a review";
|
|
641
651
|
cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;
|
|
642
652
|
cta.addEventListener("click", () => {
|
|
643
653
|
ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "rating_input", cta: "high_review" } });
|
|
@@ -647,10 +657,10 @@ function appendRatingThanks(wrap, el, ctx, fg, accent, isHigh) {
|
|
|
647
657
|
wrap.appendChild(cta);
|
|
648
658
|
}
|
|
649
659
|
}
|
|
650
|
-
function renderRatingFollowupStep(wrap,
|
|
660
|
+
function renderRatingFollowupStep(wrap, el2, ctx, stars2, fg, accent) {
|
|
651
661
|
while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
|
|
652
662
|
const heading = document.createElement("div");
|
|
653
|
-
heading.textContent =
|
|
663
|
+
heading.textContent = el2.rating_followup_prompt || "Sorry to hear that — what went wrong?";
|
|
654
664
|
heading.style.cssText = `font-size:13px;font-weight:700;color:${fg};text-align:center;margin-bottom:8px;`;
|
|
655
665
|
wrap.appendChild(heading);
|
|
656
666
|
const ta = document.createElement("textarea");
|
|
@@ -664,9 +674,9 @@ function renderRatingFollowupStep(wrap, el, ctx, stars, fg, accent) {
|
|
|
664
674
|
if (done) return;
|
|
665
675
|
done = true;
|
|
666
676
|
const comment = withComment ? ta.value.trim() : "";
|
|
667
|
-
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "rating", comment ? { stars, comment } : { stars }));
|
|
677
|
+
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "rating", comment ? { stars: stars2, comment } : { stars: stars2 }));
|
|
668
678
|
(_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, "acknowledge");
|
|
669
|
-
appendThanks(wrap,
|
|
679
|
+
appendThanks(wrap, el2, fg, "Thanks — we’ll do better.");
|
|
670
680
|
};
|
|
671
681
|
const btnRow = document.createElement("div");
|
|
672
682
|
btnRow.style.cssText = "display:flex;gap:8px;justify-content:center;";
|
|
@@ -685,23 +695,23 @@ function renderRatingFollowupStep(wrap, el, ctx, stars, fg, accent) {
|
|
|
685
695
|
wrap.appendChild(btnRow);
|
|
686
696
|
ta.focus();
|
|
687
697
|
}
|
|
688
|
-
function buildRatingInputElement(
|
|
698
|
+
function buildRatingInputElement(el2, ctx, fg) {
|
|
689
699
|
const accent = ctx.sanitizeColor(ctx.campaign.background_color || "#4169e1");
|
|
690
700
|
const wrap = document.createElement("div");
|
|
691
701
|
wrap.style.cssText = "display:flex;flex-direction:column;gap:6px;margin:2px 0;";
|
|
692
|
-
if (
|
|
702
|
+
if (el2.content) {
|
|
693
703
|
const q = document.createElement("div");
|
|
694
|
-
q.textContent =
|
|
704
|
+
q.textContent = el2.content;
|
|
695
705
|
q.style.cssText = `font-size:13px;font-weight:600;color:${fg};`;
|
|
696
706
|
wrap.appendChild(q);
|
|
697
707
|
}
|
|
698
|
-
const scale = typeof
|
|
708
|
+
const scale = typeof el2.rating_scale === "number" && el2.rating_scale >= 2 && el2.rating_scale <= 10 ? Math.floor(el2.rating_scale) : 5;
|
|
699
709
|
const row = document.createElement("div");
|
|
700
710
|
row.style.cssText = "display:flex;gap:6px;";
|
|
701
|
-
const
|
|
711
|
+
const stars2 = [];
|
|
702
712
|
let submitted = false;
|
|
703
713
|
const paint = (upto) => {
|
|
704
|
-
|
|
714
|
+
stars2.forEach((s, idx) => {
|
|
705
715
|
const on = idx < upto;
|
|
706
716
|
s.setAttribute("stroke", on ? "#f59e0b" : "#d1d5db");
|
|
707
717
|
s.setAttribute("fill", on ? "#f59e0b" : "none");
|
|
@@ -715,7 +725,7 @@ function buildRatingInputElement(el, ctx, fg) {
|
|
|
715
725
|
const svg = lucideSvgEl("star", { size: 26, color: "#d1d5db" });
|
|
716
726
|
if (svg) {
|
|
717
727
|
btn.appendChild(svg);
|
|
718
|
-
|
|
728
|
+
stars2.push(svg);
|
|
719
729
|
}
|
|
720
730
|
btn.addEventListener("mouseenter", () => {
|
|
721
731
|
if (!submitted) paint(i);
|
|
@@ -729,13 +739,13 @@ function buildRatingInputElement(el, ctx, fg) {
|
|
|
729
739
|
submitted = true;
|
|
730
740
|
paint(i);
|
|
731
741
|
ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "rating_input", stars: i } });
|
|
732
|
-
const lowThreshold = typeof
|
|
733
|
-
if (
|
|
734
|
-
renderRatingFollowupStep(wrap,
|
|
742
|
+
const lowThreshold = typeof el2.rating_followup_threshold === "number" ? el2.rating_followup_threshold : Math.ceil(scale * 0.8);
|
|
743
|
+
if (el2.rating_followup_enabled === true && i <= lowThreshold) {
|
|
744
|
+
renderRatingFollowupStep(wrap, el2, ctx, i, fg, accent);
|
|
735
745
|
} else {
|
|
736
746
|
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "rating", { stars: i }));
|
|
737
747
|
(_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, i >= Math.ceil(scale * 0.6) ? "affirm" : "acknowledge");
|
|
738
|
-
appendRatingThanks(wrap,
|
|
748
|
+
appendRatingThanks(wrap, el2, ctx, fg, accent, i >= Math.ceil(scale * 0.6));
|
|
739
749
|
}
|
|
740
750
|
});
|
|
741
751
|
row.appendChild(btn);
|
|
@@ -743,8 +753,8 @@ function buildRatingInputElement(el, ctx, fg) {
|
|
|
743
753
|
wrap.appendChild(row);
|
|
744
754
|
return wrap;
|
|
745
755
|
}
|
|
746
|
-
function appendThanks(wrap,
|
|
747
|
-
const msg = typeof
|
|
756
|
+
function appendThanks(wrap, el2, fg, fallback) {
|
|
757
|
+
const msg = typeof el2.thanks_text === "string" && el2.thanks_text.trim() ? el2.thanks_text : fallback;
|
|
748
758
|
if (!msg) return;
|
|
749
759
|
const note = document.createElement("div");
|
|
750
760
|
note.textContent = msg;
|
|
@@ -752,14 +762,14 @@ function appendThanks(wrap, el, fg, fallback) {
|
|
|
752
762
|
note.style.cssText = `font-size:12px;font-weight:600;color:${fg};opacity:0.85;margin-top:4px;`;
|
|
753
763
|
wrap.appendChild(note);
|
|
754
764
|
}
|
|
755
|
-
function buildPollElement(
|
|
756
|
-
const opts = Array.isArray(
|
|
765
|
+
function buildPollElement(el2, ctx, fg, accent) {
|
|
766
|
+
const opts = Array.isArray(el2.poll_options) ? el2.poll_options.filter((o) => typeof o === "string" && o.trim().length > 0) : [];
|
|
757
767
|
if (opts.length < 2) return null;
|
|
758
768
|
const wrap = document.createElement("div");
|
|
759
769
|
wrap.style.cssText = "display:flex;flex-direction:column;gap:6px;margin:2px 0;";
|
|
760
|
-
if (
|
|
770
|
+
if (el2.content) {
|
|
761
771
|
const q = document.createElement("div");
|
|
762
|
-
q.textContent =
|
|
772
|
+
q.textContent = el2.content;
|
|
763
773
|
q.style.cssText = `font-size:13px;font-weight:600;color:${fg};`;
|
|
764
774
|
wrap.appendChild(q);
|
|
765
775
|
}
|
|
@@ -789,24 +799,24 @@ function buildPollElement(el, ctx, fg, accent) {
|
|
|
789
799
|
ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "poll", option_index: idx, option_label: label } });
|
|
790
800
|
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "poll", { option_index: idx, option_label: label }));
|
|
791
801
|
(_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, "affirm");
|
|
792
|
-
appendThanks(wrap,
|
|
802
|
+
appendThanks(wrap, el2, fg, "Thanks for your answer!");
|
|
793
803
|
});
|
|
794
804
|
wrap.appendChild(btn);
|
|
795
805
|
});
|
|
796
806
|
return wrap;
|
|
797
807
|
}
|
|
798
|
-
function appendNpsThanks(wrap,
|
|
808
|
+
function appendNpsThanks(wrap, el2, ctx, fg, accent, isPromoter) {
|
|
799
809
|
while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
|
|
800
810
|
const note = document.createElement("div");
|
|
801
811
|
note.setAttribute("role", "status");
|
|
802
|
-
note.textContent = (typeof
|
|
812
|
+
note.textContent = (typeof el2.thanks_text === "string" && el2.thanks_text.trim() ? el2.thanks_text : "") || (isPromoter ? "Thank you! We’d love a public review." : "Thanks — your feedback helps us improve.");
|
|
803
813
|
note.style.cssText = `font-size:13px;font-weight:700;color:${fg};text-align:center;margin:6px 0;`;
|
|
804
814
|
wrap.appendChild(note);
|
|
805
|
-
const url = isPromoter && typeof
|
|
815
|
+
const url = isPromoter && typeof el2.nps_promoter_url === "string" ? ctx.sanitizeUrl(el2.nps_promoter_url) : "";
|
|
806
816
|
if (url) {
|
|
807
817
|
const cta = document.createElement("button");
|
|
808
818
|
cta.type = "button";
|
|
809
|
-
cta.textContent =
|
|
819
|
+
cta.textContent = el2.nps_promoter_cta || "Leave a review";
|
|
810
820
|
cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;
|
|
811
821
|
cta.addEventListener("click", () => {
|
|
812
822
|
ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "nps", cta: "promoter_review" } });
|
|
@@ -816,30 +826,30 @@ function appendNpsThanks(wrap, el, ctx, fg, accent, isPromoter) {
|
|
|
816
826
|
wrap.appendChild(cta);
|
|
817
827
|
}
|
|
818
828
|
}
|
|
819
|
-
function renderNpsReasonStep(wrap,
|
|
820
|
-
const promoterThreshold = typeof
|
|
829
|
+
function renderNpsReasonStep(wrap, el2, ctx, score, fg, accent) {
|
|
830
|
+
const promoterThreshold = typeof el2.nps_promoter_threshold === "number" ? el2.nps_promoter_threshold : 9;
|
|
821
831
|
const band = npsBandFromScore(score);
|
|
822
|
-
const prompt = score >= promoterThreshold ?
|
|
832
|
+
const prompt = score >= promoterThreshold ? el2.nps_follow_up_promoter || "What do you love most about us?" : score <= 6 ? el2.nps_follow_up_detractor || "How can we improve?" : el2.nps_follow_up_passive || "What would make us a 10?";
|
|
823
833
|
while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
|
|
824
834
|
const heading = document.createElement("div");
|
|
825
835
|
heading.textContent = prompt;
|
|
826
836
|
heading.style.cssText = `font-size:13px;font-weight:700;color:${fg};text-align:center;margin-bottom:8px;`;
|
|
827
837
|
wrap.appendChild(heading);
|
|
828
838
|
const ta = document.createElement("textarea");
|
|
829
|
-
const chips = Array.isArray(
|
|
839
|
+
const chips = Array.isArray(el2.nps_reason_chips) ? el2.nps_reason_chips : [];
|
|
830
840
|
if (chips.length) {
|
|
831
841
|
const chipRow = document.createElement("div");
|
|
832
842
|
chipRow.style.cssText = "display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin-bottom:8px;";
|
|
833
843
|
chips.slice(0, 8).forEach((c) => {
|
|
834
|
-
const
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
844
|
+
const chip2 = document.createElement("button");
|
|
845
|
+
chip2.type = "button";
|
|
846
|
+
chip2.textContent = c;
|
|
847
|
+
chip2.style.cssText = `padding:5px 11px;border-radius:999px;border:1px solid color-mix(in srgb, ${accent} 35%, transparent);background:transparent;color:${fg};font-size:12px;cursor:pointer;`;
|
|
848
|
+
chip2.addEventListener("click", () => {
|
|
839
849
|
ta.value = ta.value ? `${ta.value}, ${c}` : c;
|
|
840
850
|
ta.focus();
|
|
841
851
|
});
|
|
842
|
-
chipRow.appendChild(
|
|
852
|
+
chipRow.appendChild(chip2);
|
|
843
853
|
});
|
|
844
854
|
wrap.appendChild(chipRow);
|
|
845
855
|
}
|
|
@@ -855,7 +865,7 @@ function renderNpsReasonStep(wrap, el, ctx, score, fg, accent) {
|
|
|
855
865
|
const comment = withComment ? ta.value.trim() : "";
|
|
856
866
|
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "nps", comment ? { score, band, comment } : { score, band }));
|
|
857
867
|
(_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, score >= promoterThreshold ? "affirm" : score <= 6 ? "empathize" : "acknowledge");
|
|
858
|
-
appendNpsThanks(wrap,
|
|
868
|
+
appendNpsThanks(wrap, el2, ctx, fg, accent, score >= promoterThreshold);
|
|
859
869
|
};
|
|
860
870
|
const btnRow = document.createElement("div");
|
|
861
871
|
btnRow.style.cssText = "display:flex;gap:8px;justify-content:center;";
|
|
@@ -874,12 +884,12 @@ function renderNpsReasonStep(wrap, el, ctx, score, fg, accent) {
|
|
|
874
884
|
wrap.appendChild(btnRow);
|
|
875
885
|
ta.focus();
|
|
876
886
|
}
|
|
877
|
-
function buildNpsElement(
|
|
887
|
+
function buildNpsElement(el2, ctx, fg, accent, muted) {
|
|
878
888
|
const wrap = document.createElement("div");
|
|
879
889
|
wrap.style.cssText = "display:flex;flex-direction:column;gap:6px;margin:2px 0;";
|
|
880
|
-
if (
|
|
890
|
+
if (el2.content) {
|
|
881
891
|
const q = document.createElement("div");
|
|
882
|
-
q.textContent =
|
|
892
|
+
q.textContent = el2.content;
|
|
883
893
|
q.style.cssText = `font-size:13px;font-weight:600;color:${fg};`;
|
|
884
894
|
wrap.appendChild(q);
|
|
885
895
|
}
|
|
@@ -911,12 +921,12 @@ function buildNpsElement(el, ctx, fg, accent, muted) {
|
|
|
911
921
|
btn.style.opacity = "1";
|
|
912
922
|
const npsPayload = { score: n, band: npsBandFromScore(n) };
|
|
913
923
|
ctx.trackEvent(ctx.campaign.id, "clicked", { meta: { element: "nps", score: n, band: npsPayload.band } });
|
|
914
|
-
if (
|
|
915
|
-
renderNpsReasonStep(wrap,
|
|
924
|
+
if (el2.nps_followup_enabled === true) {
|
|
925
|
+
renderNpsReasonStep(wrap, el2, ctx, n, fg, accent);
|
|
916
926
|
} else {
|
|
917
927
|
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "nps", { ...npsPayload }));
|
|
918
928
|
(_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, n >= 9 ? "affirm" : n >= 7 ? "acknowledge" : "empathize");
|
|
919
|
-
appendThanks(wrap,
|
|
929
|
+
appendThanks(wrap, el2, fg, "Thanks for your feedback!");
|
|
920
930
|
}
|
|
921
931
|
});
|
|
922
932
|
row.appendChild(btn);
|
|
@@ -933,22 +943,22 @@ function buildNpsElement(el, ctx, fg, accent, muted) {
|
|
|
933
943
|
wrap.appendChild(scale);
|
|
934
944
|
return wrap;
|
|
935
945
|
}
|
|
936
|
-
function buildProgressElement(
|
|
946
|
+
function buildProgressElement(el2, ctx, accent, muted) {
|
|
937
947
|
const { trackEvent, campaign, sanitizeColor } = ctx;
|
|
938
|
-
const goal =
|
|
948
|
+
const goal = el2.goal_type || "cart_total";
|
|
939
949
|
const moneyGoal = goal === "cart_total";
|
|
940
|
-
const fill =
|
|
950
|
+
const fill = el2.fill ? sanitizeColor(el2.fill) : accent;
|
|
941
951
|
const props = () => ctx.properties && ctx.properties() || {};
|
|
942
952
|
const sym = () => {
|
|
943
953
|
var _a;
|
|
944
|
-
if (
|
|
954
|
+
if (el2.currency_symbol) return el2.currency_symbol;
|
|
945
955
|
const code = ctx.getCartState && ((_a = ctx.getCartState()) == null ? void 0 : _a.currency);
|
|
946
956
|
const M = { INR: "₹", USD: "$", EUR: "€", GBP: "£" };
|
|
947
957
|
return code && M[code] || "";
|
|
948
958
|
};
|
|
949
959
|
const threshold = () => {
|
|
950
|
-
if (typeof
|
|
951
|
-
if (
|
|
960
|
+
if (typeof el2.threshold === "number" && el2.threshold > 0) return el2.threshold;
|
|
961
|
+
if (el2.threshold_source === "free_delivery_setting") {
|
|
952
962
|
const v = Number(props()["cart.free_delivery_above"]);
|
|
953
963
|
if (v > 0) return v;
|
|
954
964
|
}
|
|
@@ -989,9 +999,9 @@ function buildProgressElement(el, ctx, accent, muted) {
|
|
|
989
999
|
goalType: goal,
|
|
990
1000
|
threshold: th,
|
|
991
1001
|
current: cur,
|
|
992
|
-
rewardText:
|
|
1002
|
+
rewardText: el2.reward_text,
|
|
993
1003
|
currencySymbol: moneyGoal ? sym() : "",
|
|
994
|
-
showPct:
|
|
1004
|
+
showPct: el2.show_pct
|
|
995
1005
|
});
|
|
996
1006
|
sub.textContent = labeled.text;
|
|
997
1007
|
if (complete) {
|
|
@@ -1000,8 +1010,8 @@ function buildProgressElement(el, ctx, accent, muted) {
|
|
|
1000
1010
|
if (!firedUnlock) {
|
|
1001
1011
|
firedUnlock = true;
|
|
1002
1012
|
if (seenIncomplete) {
|
|
1003
|
-
trackEvent(campaign.id, "clicked", { stepId:
|
|
1004
|
-
if (
|
|
1013
|
+
trackEvent(campaign.id, "clicked", { stepId: el2.id || "unlocked" });
|
|
1014
|
+
if (el2.celebrate_on_complete !== false) (_a = ctx.playReaction) == null ? void 0 : _a.call(ctx, wrap, "celebrate");
|
|
1005
1015
|
}
|
|
1006
1016
|
}
|
|
1007
1017
|
} else {
|
|
@@ -1019,6 +1029,40 @@ function buildProgressElement(el, ctx, accent, muted) {
|
|
|
1019
1029
|
}
|
|
1020
1030
|
return wrap;
|
|
1021
1031
|
}
|
|
1032
|
+
function buildCtaAction(el2, card, meta, inline, sanitizeUrl) {
|
|
1033
|
+
const kind = el2.cta_action ?? (inline ? "add_to_cart" : "navigate");
|
|
1034
|
+
const safe = (u) => u ? sanitizeUrl(u) || void 0 : void 0;
|
|
1035
|
+
const url = safe(el2.cta_url || card.cta_url);
|
|
1036
|
+
const productId = meta.product_id;
|
|
1037
|
+
const sku = meta.sku;
|
|
1038
|
+
const platformProductId = meta.platform_product_id;
|
|
1039
|
+
const platformUrl = safe(meta.platform_url);
|
|
1040
|
+
switch (kind) {
|
|
1041
|
+
case "open_cart":
|
|
1042
|
+
return { type: "open_cart" };
|
|
1043
|
+
case "open_login":
|
|
1044
|
+
return { type: "open_login" };
|
|
1045
|
+
case "open_product":
|
|
1046
|
+
return { type: "open_product", productId, sku, url: platformUrl || url, platformProductId, platformUrl };
|
|
1047
|
+
case "navigate":
|
|
1048
|
+
return { type: "navigate", url: url || "" };
|
|
1049
|
+
case "custom":
|
|
1050
|
+
return { type: "custom", name: el2.cta_custom_name || "cta", payload: { ...meta, url } };
|
|
1051
|
+
case "add_to_cart":
|
|
1052
|
+
default:
|
|
1053
|
+
return {
|
|
1054
|
+
type: "add_to_cart",
|
|
1055
|
+
productId,
|
|
1056
|
+
sku,
|
|
1057
|
+
platformProductId,
|
|
1058
|
+
title: card.title,
|
|
1059
|
+
price: meta.price ?? card.body,
|
|
1060
|
+
imageUrl: card.image_url,
|
|
1061
|
+
url: platformUrl || url,
|
|
1062
|
+
qty: 1
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1022
1066
|
function renderCardFromTemplate(card, opts) {
|
|
1023
1067
|
const { ctx, action, layout, layoutAccent } = opts;
|
|
1024
1068
|
const inline = action === "cart";
|
|
@@ -1038,7 +1082,7 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1038
1082
|
const fLayout = frame.layout;
|
|
1039
1083
|
const dir = fLayout === "row" ? "row" : "column";
|
|
1040
1084
|
const tileGap = frame.gap != null ? frame.gap : 6;
|
|
1041
|
-
const sizing = fLayout ? frame.width != null ? `width:${typeof frame.width === "number" ? `${frame.width}px` : frame.width};` : "flex:1 1 auto; width:100%;" : layout === "grid" ? "" : "flex:0 0 150px; scroll-snap-align:start;";
|
|
1085
|
+
const sizing = fLayout ? frame.width != null ? `width:${typeof frame.width === "number" ? `${frame.width}px` : frame.width};` : "flex:1 1 auto; width:100%;" : layout === "grid" || layout === "list" ? "" : "flex:0 0 150px; scroll-snap-align:start;";
|
|
1042
1086
|
tile.style.cssText = `
|
|
1043
1087
|
box-sizing:border-box; display:flex; flex-direction:${dir}; ${fLayout === "row" ? "align-items:center;" : ""} gap:${tileGap}px; position:relative;
|
|
1044
1088
|
color:${fg};
|
|
@@ -1055,11 +1099,11 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1055
1099
|
const elements = tpl.elements && tpl.elements.length ? tpl.elements : DEFAULT_CARD_TEMPLATE.elements;
|
|
1056
1100
|
let imageWrap = null;
|
|
1057
1101
|
const paintInto = (container, els) => {
|
|
1058
|
-
for (const
|
|
1059
|
-
switch (
|
|
1102
|
+
for (const el2 of els) {
|
|
1103
|
+
switch (el2.type) {
|
|
1060
1104
|
case "image": {
|
|
1061
1105
|
const wrap = document.createElement("div");
|
|
1062
|
-
wrap.style.cssText = `position:relative;width:100%;aspect-ratio:${ASPECT$2[
|
|
1106
|
+
wrap.style.cssText = `position:relative;width:100%;aspect-ratio:${ASPECT$2[el2.aspect || "1/1"] || "1 / 1"};border-radius:${el2.radius ?? 10}px;overflow:hidden;background:#f7f8fa;`;
|
|
1063
1107
|
const safeVideo = card.video_url ? sanitizeUrl(card.video_url) : "";
|
|
1064
1108
|
if (safeVideo) {
|
|
1065
1109
|
const v = document.createElement("video");
|
|
@@ -1087,17 +1131,17 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1087
1131
|
break;
|
|
1088
1132
|
}
|
|
1089
1133
|
case "badge": {
|
|
1090
|
-
const text =
|
|
1134
|
+
const text = el2.text;
|
|
1091
1135
|
if (!text) break;
|
|
1092
1136
|
const badge = document.createElement("span");
|
|
1093
1137
|
badge.textContent = text;
|
|
1094
|
-
const bg =
|
|
1138
|
+
const bg = el2.color ? sanitizeColor(el2.color) : brand;
|
|
1095
1139
|
badge.style.cssText = `position:absolute;top:6px;left:6px;z-index:1;background:${bg};color:#fff;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:0.03em;padding:3px 6px;border-radius:5px;`;
|
|
1096
1140
|
(imageWrap || container).appendChild(badge);
|
|
1097
1141
|
break;
|
|
1098
1142
|
}
|
|
1099
1143
|
case "price": {
|
|
1100
|
-
const node = paintPrice(card,
|
|
1144
|
+
const node = paintPrice(card, el2, accent, ctx);
|
|
1101
1145
|
if (node) container.appendChild(node);
|
|
1102
1146
|
break;
|
|
1103
1147
|
}
|
|
@@ -1105,8 +1149,8 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1105
1149
|
if (!card.title) break;
|
|
1106
1150
|
const t = document.createElement("div");
|
|
1107
1151
|
t.textContent = card.title;
|
|
1108
|
-
const lines =
|
|
1109
|
-
t.style.cssText = `font-size:12.5px;font-weight:600;line-height:1.3;text-align:${
|
|
1152
|
+
const lines = el2.lines && el2.lines > 0 ? el2.lines : 2;
|
|
1153
|
+
t.style.cssText = `font-size:12.5px;font-weight:600;line-height:1.3;text-align:${el2.align ?? "left"};color:${el2.color ? sanitizeColor(el2.color) : fg};display:-webkit-box;-webkit-line-clamp:${lines};-webkit-box-orient:vertical;overflow:hidden;`;
|
|
1110
1154
|
container.appendChild(t);
|
|
1111
1155
|
break;
|
|
1112
1156
|
}
|
|
@@ -1114,8 +1158,8 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1114
1158
|
if (!card.body) break;
|
|
1115
1159
|
const s = document.createElement("div");
|
|
1116
1160
|
s.textContent = card.body;
|
|
1117
|
-
const lines =
|
|
1118
|
-
s.style.cssText = `font-size:11px;color:${
|
|
1161
|
+
const lines = el2.lines && el2.lines > 0 ? el2.lines : 2;
|
|
1162
|
+
s.style.cssText = `font-size:11px;color:${el2.color ? sanitizeColor(el2.color) : muted};line-height:1.3;text-align:${el2.align ?? "left"};display:-webkit-box;-webkit-line-clamp:${lines};-webkit-box-orient:vertical;overflow:hidden;`;
|
|
1119
1163
|
container.appendChild(s);
|
|
1120
1164
|
break;
|
|
1121
1165
|
}
|
|
@@ -1127,25 +1171,25 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1127
1171
|
case "category": {
|
|
1128
1172
|
const cat = meta.category;
|
|
1129
1173
|
if (typeof cat !== "string" || !cat) break;
|
|
1130
|
-
const
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
container.appendChild(
|
|
1174
|
+
const chip2 = document.createElement("span");
|
|
1175
|
+
chip2.textContent = cat;
|
|
1176
|
+
chip2.style.cssText = "align-self:flex-start;background:#f3f4f6;color:#6b7280;font-size:10px;font-weight:500;padding:2px 7px;border-radius:6px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";
|
|
1177
|
+
container.appendChild(chip2);
|
|
1134
1178
|
break;
|
|
1135
1179
|
}
|
|
1136
1180
|
// ── rich-card-editor elements (P0) ──────────────────────────────────
|
|
1137
1181
|
case "icon": {
|
|
1138
|
-
const px = ICON_PX[
|
|
1182
|
+
const px = ICON_PX[el2.size || "md"] || 30;
|
|
1139
1183
|
const node = document.createElement("div");
|
|
1140
|
-
const iconColor =
|
|
1141
|
-
if (
|
|
1142
|
-
const bubbleTint =
|
|
1184
|
+
const iconColor = el2.color ? sanitizeColor(el2.color) : fg;
|
|
1185
|
+
if (el2.bubble) {
|
|
1186
|
+
const bubbleTint = el2.color ? sanitizeColor(el2.color) : brand;
|
|
1143
1187
|
node.style.cssText = `flex:0 0 auto;width:${px}px;height:${px}px;border-radius:999px;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb, ${bubbleTint} 16%, transparent);`;
|
|
1144
1188
|
} else {
|
|
1145
1189
|
node.style.cssText = "flex:0 0 auto;display:flex;align-items:center;";
|
|
1146
1190
|
}
|
|
1147
|
-
const svg = lucideSvgEl(
|
|
1148
|
-
size:
|
|
1191
|
+
const svg = lucideSvgEl(el2.icon_name || "circle-check", {
|
|
1192
|
+
size: el2.bubble ? Math.round(px * 0.55) : Math.round(px * 0.7),
|
|
1149
1193
|
color: iconColor,
|
|
1150
1194
|
strokeWidth: 2
|
|
1151
1195
|
});
|
|
@@ -1155,99 +1199,64 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1155
1199
|
}
|
|
1156
1200
|
case "text": {
|
|
1157
1201
|
const t = document.createElement("div");
|
|
1158
|
-
const fs = TEXT_PX[
|
|
1159
|
-
t.style.cssText = `font-size:${fs}px;font-weight:${WEIGHT[
|
|
1160
|
-
t.textContent = interpolateTokens(
|
|
1202
|
+
const fs = TEXT_PX[el2.size || "md"] || 13;
|
|
1203
|
+
t.style.cssText = `font-size:${fs}px;font-weight:${WEIGHT[el2.weight ?? "medium"]};text-align:${el2.align ?? "left"};color:${el2.color ? sanitizeColor(el2.color) : fg};line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;`;
|
|
1204
|
+
t.textContent = interpolateTokens(el2.content ?? "", ctx);
|
|
1161
1205
|
container.appendChild(t);
|
|
1162
1206
|
break;
|
|
1163
1207
|
}
|
|
1164
1208
|
case "spacer": {
|
|
1165
1209
|
const sp = document.createElement("div");
|
|
1166
|
-
sp.style.height = `${
|
|
1210
|
+
sp.style.height = `${el2.height ?? 8}px`;
|
|
1167
1211
|
container.appendChild(sp);
|
|
1168
1212
|
break;
|
|
1169
1213
|
}
|
|
1170
1214
|
case "progress": {
|
|
1171
|
-
container.appendChild(buildProgressElement(
|
|
1215
|
+
container.appendChild(buildProgressElement(el2, ctx, brand, muted));
|
|
1172
1216
|
break;
|
|
1173
1217
|
}
|
|
1174
1218
|
case "countdown": {
|
|
1175
|
-
const cd = buildCountdownElement(
|
|
1219
|
+
const cd = buildCountdownElement(el2, fg, muted);
|
|
1176
1220
|
if (cd) container.appendChild(cd);
|
|
1177
1221
|
break;
|
|
1178
1222
|
}
|
|
1179
1223
|
case "rating_input": {
|
|
1180
|
-
container.appendChild(buildRatingInputElement(
|
|
1224
|
+
container.appendChild(buildRatingInputElement(el2, ctx, fg));
|
|
1181
1225
|
break;
|
|
1182
1226
|
}
|
|
1183
1227
|
case "poll": {
|
|
1184
|
-
const p = buildPollElement(
|
|
1228
|
+
const p = buildPollElement(el2, ctx, fg, brand);
|
|
1185
1229
|
if (p) container.appendChild(p);
|
|
1186
1230
|
break;
|
|
1187
1231
|
}
|
|
1188
1232
|
case "nps": {
|
|
1189
|
-
container.appendChild(buildNpsElement(
|
|
1233
|
+
container.appendChild(buildNpsElement(el2, ctx, fg, brand, muted));
|
|
1190
1234
|
break;
|
|
1191
1235
|
}
|
|
1192
1236
|
case "group": {
|
|
1193
1237
|
const g = document.createElement("div");
|
|
1194
|
-
const gdir =
|
|
1195
|
-
g.style.cssText = `display:flex;flex-direction:${gdir};${
|
|
1196
|
-
paintInto(g,
|
|
1238
|
+
const gdir = el2.layout === "row" ? "row" : "column";
|
|
1239
|
+
g.style.cssText = `display:flex;flex-direction:${gdir};${el2.layout === "row" ? "align-items:center;" : ""}gap:${el2.gap ?? 4}px;min-width:0;${el2.layout === "row" ? "" : "flex:1 1 auto;"}`;
|
|
1240
|
+
paintInto(g, el2.elements || []);
|
|
1197
1241
|
container.appendChild(g);
|
|
1198
1242
|
break;
|
|
1199
1243
|
}
|
|
1200
1244
|
case "cta": {
|
|
1201
|
-
const label =
|
|
1245
|
+
const label = el2.text || card.cta_text || (inline ? "Add" : "Shop");
|
|
1202
1246
|
const cta = document.createElement("button");
|
|
1203
1247
|
cta.className = "aegis-cta";
|
|
1204
1248
|
cta.textContent = label;
|
|
1205
|
-
const overlayOnImage = !!
|
|
1249
|
+
const overlayOnImage = !!el2.overlay && !!imageWrap;
|
|
1206
1250
|
if (overlayOnImage) {
|
|
1207
|
-
cta.style.cssText =
|
|
1251
|
+
cta.style.cssText = el2.variant === "outline" ? `position:absolute;right:6px;bottom:6px;background:#fff;color:${brand};border:1px solid ${brand};border-radius:9px;padding:5px 18px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:0.04em;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.14);` : `position:absolute;right:6px;bottom:6px;background:${brand};color:#fff;border:none;border-radius:9px;padding:5px 18px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:0.04em;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.14);`;
|
|
1208
1252
|
} else {
|
|
1209
|
-
cta.style.cssText =
|
|
1253
|
+
cta.style.cssText = el2.variant === "outline" ? `margin-top:${fLayout === "row" ? "0" : "auto"};background:transparent;color:${brand};border:1px solid ${brand};border-radius:999px;padding:6px 10px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;` : `margin-top:${fLayout === "row" ? "0" : "auto"};background:${brand};color:#fff;border:none;border-radius:999px;padding:7px 10px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;`;
|
|
1210
1254
|
}
|
|
1211
1255
|
cta.addEventListener("click", (e) => {
|
|
1256
|
+
var _a;
|
|
1212
1257
|
e.stopPropagation();
|
|
1213
|
-
trackEvent(campaign.id, "clicked", { stepId:
|
|
1214
|
-
|
|
1215
|
-
const m2 = meta;
|
|
1216
|
-
if (ctx.dispatchAction) {
|
|
1217
|
-
ctx.dispatchAction({
|
|
1218
|
-
type: "add_to_cart",
|
|
1219
|
-
productId: m2.product_id,
|
|
1220
|
-
sku: m2.sku,
|
|
1221
|
-
// Platform-native id (catalog-sync C.2) — plugins use this for a
|
|
1222
|
-
// native add; undefined today → graceful product-search fallback.
|
|
1223
|
-
platformProductId: m2.platform_product_id,
|
|
1224
|
-
title: card.title,
|
|
1225
|
-
price: m2.price ?? card.body,
|
|
1226
|
-
imageUrl: card.image_url,
|
|
1227
|
-
url: m2.platform_url || card.cta_url,
|
|
1228
|
-
qty: 1
|
|
1229
|
-
});
|
|
1230
|
-
} else if (typeof window !== "undefined") {
|
|
1231
|
-
window.dispatchEvent(
|
|
1232
|
-
new CustomEvent("aegis:add-to-cart", {
|
|
1233
|
-
detail: {
|
|
1234
|
-
campaign_id: campaign.id,
|
|
1235
|
-
product_id: m2.product_id,
|
|
1236
|
-
sku: m2.sku,
|
|
1237
|
-
title: card.title,
|
|
1238
|
-
price: m2.price ?? card.body,
|
|
1239
|
-
image_url: card.image_url,
|
|
1240
|
-
cta_url: card.cta_url
|
|
1241
|
-
}
|
|
1242
|
-
})
|
|
1243
|
-
);
|
|
1244
|
-
}
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
1247
|
-
if (card.cta_url) {
|
|
1248
|
-
const safe = sanitizeUrl(card.cta_url);
|
|
1249
|
-
if (safe) ctx.navigate ? ctx.navigate(safe) : window.location.href = safe;
|
|
1250
|
-
}
|
|
1258
|
+
trackEvent(campaign.id, "clicked", { stepId: el2.id || "add" });
|
|
1259
|
+
(_a = ctx.dispatchAction) == null ? void 0 : _a.call(ctx, buildCtaAction(el2, card, meta, inline, sanitizeUrl));
|
|
1251
1260
|
});
|
|
1252
1261
|
(overlayOnImage ? imageWrap : container).appendChild(cta);
|
|
1253
1262
|
break;
|
|
@@ -1257,37 +1266,20 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1257
1266
|
};
|
|
1258
1267
|
paintInto(tile, elements);
|
|
1259
1268
|
const open = (e) => {
|
|
1269
|
+
var _a;
|
|
1260
1270
|
e.stopPropagation();
|
|
1261
1271
|
trackEvent(campaign.id, "clicked");
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
} else if (typeof window !== "undefined") {
|
|
1274
|
-
window.dispatchEvent(
|
|
1275
|
-
new CustomEvent("aegis:open-product", {
|
|
1276
|
-
detail: {
|
|
1277
|
-
campaign_id: campaign.id,
|
|
1278
|
-
product_id: bm.product_id,
|
|
1279
|
-
sku: bm.sku,
|
|
1280
|
-
cta_url: card.cta_url
|
|
1281
|
-
}
|
|
1282
|
-
})
|
|
1283
|
-
);
|
|
1284
|
-
}
|
|
1285
|
-
return;
|
|
1286
|
-
}
|
|
1287
|
-
if (card.cta_url) {
|
|
1288
|
-
const safe = sanitizeUrl(card.cta_url);
|
|
1289
|
-
if (safe) ctx.navigate ? ctx.navigate(safe) : window.location.href = safe;
|
|
1290
|
-
}
|
|
1272
|
+
const bm = meta;
|
|
1273
|
+
const safe = (u) => u ? sanitizeUrl(u) || void 0 : void 0;
|
|
1274
|
+
const act = inline ? {
|
|
1275
|
+
type: "open_product",
|
|
1276
|
+
productId: bm.product_id,
|
|
1277
|
+
sku: bm.sku,
|
|
1278
|
+
url: safe(bm.platform_url) || safe(card.cta_url),
|
|
1279
|
+
platformProductId: bm.platform_product_id,
|
|
1280
|
+
platformUrl: safe(bm.platform_url)
|
|
1281
|
+
} : { type: "navigate", url: safe(card.cta_url) || "" };
|
|
1282
|
+
(_a = ctx.dispatchAction) == null ? void 0 : _a.call(ctx, act);
|
|
1291
1283
|
};
|
|
1292
1284
|
if (inline || card.cta_url) tile.addEventListener("click", open);
|
|
1293
1285
|
if (frame.celebrate_on_show === true && ctx.playReaction) {
|
|
@@ -1320,6 +1312,7 @@ function renderCarouselCards(ctx) {
|
|
|
1320
1312
|
}
|
|
1321
1313
|
const autoplay = Number(ic.autoplay_ms) || 0;
|
|
1322
1314
|
const loop = ic.loop !== false;
|
|
1315
|
+
const layout = ic.rec_layout || "row";
|
|
1323
1316
|
addAnimationStyles();
|
|
1324
1317
|
ensureInAppFont();
|
|
1325
1318
|
const bg = "#ffffff";
|
|
@@ -1367,20 +1360,26 @@ function renderCarouselCards(ctx) {
|
|
|
1367
1360
|
header.appendChild(closeBtn);
|
|
1368
1361
|
card.appendChild(header);
|
|
1369
1362
|
const track = document.createElement("div");
|
|
1370
|
-
|
|
1371
|
-
display:
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1363
|
+
if (layout === "grid") {
|
|
1364
|
+
track.style.cssText = "display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; padding: 4px 2px;";
|
|
1365
|
+
} else if (layout === "list") {
|
|
1366
|
+
track.style.cssText = "display: flex; flex-direction: column; gap: 10px; padding: 4px 2px;";
|
|
1367
|
+
} else {
|
|
1368
|
+
track.style.cssText = `
|
|
1369
|
+
display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x proximity;
|
|
1370
|
+
scroll-padding-left: 0; scrollbar-width: none; -ms-overflow-style: none;
|
|
1371
|
+
/* room so overflow-x: auto doesn't clip the card drop-shadow */
|
|
1372
|
+
padding: 8px 6px 16px; margin: -8px -6px -6px; -webkit-overflow-scrolling: touch;
|
|
1373
|
+
`;
|
|
1374
|
+
track.style.msOverflowStyle = "none";
|
|
1375
|
+
}
|
|
1377
1376
|
const template = resolveGlobalTemplate(ic);
|
|
1378
1377
|
cards.forEach((c, i) => {
|
|
1379
1378
|
const tile = renderCardFromTemplate(c, {
|
|
1380
1379
|
template,
|
|
1381
1380
|
ctx,
|
|
1382
1381
|
action: "navigate",
|
|
1383
|
-
layout
|
|
1382
|
+
layout,
|
|
1384
1383
|
layoutAccent: accent
|
|
1385
1384
|
});
|
|
1386
1385
|
tile.setAttribute("data-card-index", String(i));
|
|
@@ -1390,6 +1389,12 @@ function renderCarouselCards(ctx) {
|
|
|
1390
1389
|
track.appendChild(tile);
|
|
1391
1390
|
});
|
|
1392
1391
|
card.appendChild(track);
|
|
1392
|
+
if (layout !== "row") {
|
|
1393
|
+
cards.forEach((_, i) => trackEvent(campaign.id, "impression", { stepId: `card_${i}` }));
|
|
1394
|
+
overlay.appendChild(card);
|
|
1395
|
+
document.body.appendChild(overlay);
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1393
1398
|
const dots = document.createElement("div");
|
|
1394
1399
|
dots.style.cssText = "display: flex; justify-content: center; gap: 6px; margin-top: 10px;";
|
|
1395
1400
|
const dotEls = [];
|
|
@@ -1419,9 +1424,9 @@ function renderCarouselCards(ctx) {
|
|
|
1419
1424
|
const tiles = track.querySelectorAll("[data-card-index]");
|
|
1420
1425
|
const goto = (idx) => {
|
|
1421
1426
|
activeIdx = (idx % cards.length + cards.length) % cards.length;
|
|
1422
|
-
const
|
|
1423
|
-
if (
|
|
1424
|
-
const delta =
|
|
1427
|
+
const el2 = tiles[activeIdx];
|
|
1428
|
+
if (el2) {
|
|
1429
|
+
const delta = el2.getBoundingClientRect().left - track.getBoundingClientRect().left;
|
|
1425
1430
|
track.scrollTo({ left: track.scrollLeft + delta, behavior: "smooth" });
|
|
1426
1431
|
setActive(activeIdx);
|
|
1427
1432
|
}
|
|
@@ -2038,6 +2043,10 @@ function renderProductRecommendation(ctx) {
|
|
|
2038
2043
|
display: flex; gap: 10px; overflow-x: auto;
|
|
2039
2044
|
scroll-snap-type: x mandatory; padding: 14px 2px 18px;
|
|
2040
2045
|
`;
|
|
2046
|
+
} else if (layout === "list") {
|
|
2047
|
+
grid.style.cssText = `
|
|
2048
|
+
display: flex; flex-direction: column; gap: 10px; padding: 14px 2px 6px;
|
|
2049
|
+
`;
|
|
2041
2050
|
} else {
|
|
2042
2051
|
grid.style.cssText = `
|
|
2043
2052
|
display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;
|
|
@@ -2094,9 +2103,11 @@ function renderProductRecommendationInline(ctx, target) {
|
|
|
2094
2103
|
section.appendChild(body);
|
|
2095
2104
|
}
|
|
2096
2105
|
const grid = document.createElement("div");
|
|
2097
|
-
const isRow = layout !== "grid";
|
|
2106
|
+
const isRow = layout === "row" || layout !== "grid" && layout !== "list";
|
|
2098
2107
|
if (layout === "grid") {
|
|
2099
2108
|
grid.style.cssText = "display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;";
|
|
2109
|
+
} else if (layout === "list") {
|
|
2110
|
+
grid.style.cssText = "display: flex; flex-direction: column; gap: 10px;";
|
|
2100
2111
|
} else {
|
|
2101
2112
|
grid.style.cssText = `
|
|
2102
2113
|
display: flex; gap: 10px; overflow-x: auto;
|
|
@@ -2639,10 +2650,10 @@ function brandAccent(ctx) {
|
|
|
2639
2650
|
function injectGameStyles() {
|
|
2640
2651
|
if (typeof document === "undefined" || document.getElementById("aegis-game-styles")) return;
|
|
2641
2652
|
if (typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
2642
|
-
const
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
document.head.appendChild(
|
|
2653
|
+
const el2 = document.createElement("style");
|
|
2654
|
+
el2.id = "aegis-game-styles";
|
|
2655
|
+
el2.textContent = "@keyframes aegisBoxRattle{0%,100%{transform:scale(1) rotate(0)}20%{transform:scale(1.05) rotate(-4deg)}40%{transform:scale(1.05) rotate(4deg)}60%{transform:scale(1.06) rotate(-3deg)}80%{transform:scale(1.06) rotate(3deg)}}@keyframes aegisStampIn{0%{transform:scale(2.4) rotate(-20deg);opacity:0}55%{transform:scale(0.82) rotate(7deg);opacity:1}100%{transform:scale(1) rotate(-6deg);opacity:1}}@keyframes aegisBadgePop{0%{transform:scale(0) rotate(-12deg);opacity:0}70%{transform:scale(1.18) rotate(4deg);opacity:1}100%{transform:scale(1) rotate(0);opacity:1}}";
|
|
2656
|
+
document.head.appendChild(el2);
|
|
2646
2657
|
}
|
|
2647
2658
|
function openGameModal(ctx, className, maxWidth = 360) {
|
|
2648
2659
|
const { campaign, trackEvent, addAnimationStyles } = ctx;
|
|
@@ -2737,10 +2748,10 @@ function renderStreak(ctx) {
|
|
|
2737
2748
|
s.content.appendChild(gEl("div", "font-size:22px;font-weight:800;margin:4px 0 2px", `${count}-day streak!`));
|
|
2738
2749
|
const chips = gEl("div", "display:flex;justify-content:center;gap:8px;margin:12px 0");
|
|
2739
2750
|
for (let i = 0; i < 7; i++) {
|
|
2740
|
-
const
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
chips.appendChild(
|
|
2751
|
+
const chip2 = gEl("div", "display:inline-flex;flex-direction:column;align-items:center;gap:3px;font-size:11px;opacity:0.8");
|
|
2752
|
+
chip2.appendChild(gEl("span", "", days[i]));
|
|
2753
|
+
chip2.appendChild(gEl("span", "font-size:16px", i < count % 7 || count >= 7 && i < 7 ? "✅" : i === count % 7 ? "🔥" : "⚪"));
|
|
2754
|
+
chips.appendChild(chip2);
|
|
2744
2755
|
}
|
|
2745
2756
|
s.content.appendChild(chips);
|
|
2746
2757
|
const note = gEl("div", "font-size:13px;opacity:0.8");
|
|
@@ -4105,15 +4116,15 @@ class AegisChat {
|
|
|
4105
4116
|
row.className = "aegis-chat-quickreplies";
|
|
4106
4117
|
for (const label of this.quickReplies) {
|
|
4107
4118
|
if (typeof label !== "string" || !label.trim()) continue;
|
|
4108
|
-
const
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4119
|
+
const chip2 = document.createElement("button");
|
|
4120
|
+
chip2.type = "button";
|
|
4121
|
+
chip2.className = "aegis-chat-chip";
|
|
4122
|
+
chip2.textContent = label;
|
|
4123
|
+
chip2.addEventListener("click", () => {
|
|
4113
4124
|
row.remove();
|
|
4114
4125
|
void this.send(label);
|
|
4115
4126
|
});
|
|
4116
|
-
row.appendChild(
|
|
4127
|
+
row.appendChild(chip2);
|
|
4117
4128
|
}
|
|
4118
4129
|
if (row.childElementCount > 0) this.thread.appendChild(row);
|
|
4119
4130
|
}
|
|
@@ -4150,8 +4161,8 @@ class AegisChat {
|
|
|
4150
4161
|
}
|
|
4151
4162
|
if (msg.interactive && Array.isArray(msg.interactive)) {
|
|
4152
4163
|
for (const inter of msg.interactive) {
|
|
4153
|
-
const
|
|
4154
|
-
if (
|
|
4164
|
+
const el2 = this.renderInteractive(inter);
|
|
4165
|
+
if (el2) row.appendChild(el2);
|
|
4155
4166
|
}
|
|
4156
4167
|
}
|
|
4157
4168
|
this.thread.appendChild(row);
|
|
@@ -5015,6 +5026,408 @@ function disposeActiveWebChat() {
|
|
|
5015
5026
|
activeInstance == null ? void 0 : activeInstance.destroy();
|
|
5016
5027
|
activeInstance = null;
|
|
5017
5028
|
}
|
|
5029
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
5030
|
+
function el(tag, css, text) {
|
|
5031
|
+
const e = document.createElement(tag);
|
|
5032
|
+
if (css) e.style.cssText = css;
|
|
5033
|
+
if (text != null) e.textContent = text;
|
|
5034
|
+
return e;
|
|
5035
|
+
}
|
|
5036
|
+
function stars(rating, size = 14) {
|
|
5037
|
+
const wrap = el("span", "display:inline-flex;gap:1px;vertical-align:middle;");
|
|
5038
|
+
wrap.setAttribute("aria-label", `${rating} out of 5`);
|
|
5039
|
+
for (let i = 1; i <= 5; i++) {
|
|
5040
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
5041
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
5042
|
+
svg.setAttribute("width", String(size));
|
|
5043
|
+
svg.setAttribute("height", String(size));
|
|
5044
|
+
const path = document.createElementNS(SVG_NS, "path");
|
|
5045
|
+
path.setAttribute(
|
|
5046
|
+
"d",
|
|
5047
|
+
"M12 2l2.9 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.1-1.01L12 2z"
|
|
5048
|
+
);
|
|
5049
|
+
path.setAttribute("fill", i <= rating ? "#f59e0b" : "#e2e8f0");
|
|
5050
|
+
svg.appendChild(path);
|
|
5051
|
+
wrap.appendChild(svg);
|
|
5052
|
+
}
|
|
5053
|
+
return wrap;
|
|
5054
|
+
}
|
|
5055
|
+
function chip(text, kind) {
|
|
5056
|
+
const palette = kind === "verified" ? "background:#ecfdf5;color:#047857;" : "background:#fffbeb;color:#b45309;";
|
|
5057
|
+
return el(
|
|
5058
|
+
"span",
|
|
5059
|
+
`display:inline-flex;align-items:center;font-size:11px;font-weight:600;padding:2px 8px;border-radius:999px;${palette}`,
|
|
5060
|
+
text
|
|
5061
|
+
);
|
|
5062
|
+
}
|
|
5063
|
+
function sparkles() {
|
|
5064
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
5065
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
5066
|
+
svg.setAttribute("width", "15");
|
|
5067
|
+
svg.setAttribute("height", "15");
|
|
5068
|
+
const p = document.createElementNS(SVG_NS, "path");
|
|
5069
|
+
p.setAttribute("d", "M12 3l1.6 4.4L18 9l-4.4 1.6L12 15l-1.6-4.4L6 9l4.4-1.6L12 3z");
|
|
5070
|
+
p.setAttribute("fill", "#7c3aed");
|
|
5071
|
+
svg.appendChild(p);
|
|
5072
|
+
return svg;
|
|
5073
|
+
}
|
|
5074
|
+
function miniStar() {
|
|
5075
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
5076
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
5077
|
+
svg.setAttribute("width", "11");
|
|
5078
|
+
svg.setAttribute("height", "11");
|
|
5079
|
+
const p = document.createElementNS(SVG_NS, "path");
|
|
5080
|
+
p.setAttribute(
|
|
5081
|
+
"d",
|
|
5082
|
+
"M12 2l2.9 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.1-1.01L12 2z"
|
|
5083
|
+
);
|
|
5084
|
+
p.setAttribute("fill", "#f59e0b");
|
|
5085
|
+
svg.appendChild(p);
|
|
5086
|
+
return svg;
|
|
5087
|
+
}
|
|
5088
|
+
function distributionRows(dist, total) {
|
|
5089
|
+
const box = el("div", "display:flex;flex-direction:column;gap:3px;margin:10px 0 4px;");
|
|
5090
|
+
for (let r = 5; r >= 1; r--) {
|
|
5091
|
+
const n = dist[String(r)] ?? 0;
|
|
5092
|
+
const pct = total > 0 ? Math.round(n / total * 100) : 0;
|
|
5093
|
+
const row = el("div", "display:flex;align-items:center;gap:8px;font-size:12px;color:#64748b;");
|
|
5094
|
+
const label = el("span", "width:26px;display:inline-flex;align-items:center;gap:2px;color:#334155;", String(r));
|
|
5095
|
+
label.appendChild(miniStar());
|
|
5096
|
+
row.appendChild(label);
|
|
5097
|
+
const track = el("div", "flex:1;height:6px;background:#f1f5f9;border-radius:999px;overflow:hidden;");
|
|
5098
|
+
track.appendChild(el("div", `width:${pct}%;height:100%;background:#f59e0b;`));
|
|
5099
|
+
row.appendChild(track);
|
|
5100
|
+
row.appendChild(el("span", "width:28px;text-align:right;", String(n)));
|
|
5101
|
+
box.appendChild(row);
|
|
5102
|
+
}
|
|
5103
|
+
return box;
|
|
5104
|
+
}
|
|
5105
|
+
function injectJsonLd(target, data, productId) {
|
|
5106
|
+
if (!data.aggregate.count || data.aggregate.avg_rating == null) return;
|
|
5107
|
+
const ld = {
|
|
5108
|
+
"@context": "https://schema.org",
|
|
5109
|
+
"@type": "Product",
|
|
5110
|
+
...productId ? { sku: productId } : {},
|
|
5111
|
+
aggregateRating: {
|
|
5112
|
+
"@type": "AggregateRating",
|
|
5113
|
+
ratingValue: data.aggregate.avg_rating,
|
|
5114
|
+
reviewCount: data.aggregate.count
|
|
5115
|
+
},
|
|
5116
|
+
review: data.items.slice(0, 10).map((r) => {
|
|
5117
|
+
var _a;
|
|
5118
|
+
return {
|
|
5119
|
+
"@type": "Review",
|
|
5120
|
+
reviewRating: { "@type": "Rating", ratingValue: r.rating ?? 0 },
|
|
5121
|
+
author: { "@type": "Person", name: ((_a = r.reviewer_snapshot) == null ? void 0 : _a.name) ?? "Customer" },
|
|
5122
|
+
...r.review_text ? { reviewBody: r.review_text } : {}
|
|
5123
|
+
};
|
|
5124
|
+
})
|
|
5125
|
+
};
|
|
5126
|
+
const script = document.createElement("script");
|
|
5127
|
+
script.type = "application/ld+json";
|
|
5128
|
+
script.textContent = JSON.stringify(ld);
|
|
5129
|
+
target.appendChild(script);
|
|
5130
|
+
}
|
|
5131
|
+
function thumbSvg(up) {
|
|
5132
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
5133
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
5134
|
+
svg.setAttribute("width", "14");
|
|
5135
|
+
svg.setAttribute("height", "14");
|
|
5136
|
+
if (!up) svg.style.cssText = "transform:rotate(180deg);";
|
|
5137
|
+
const p = document.createElementNS(SVG_NS, "path");
|
|
5138
|
+
p.setAttribute(
|
|
5139
|
+
"d",
|
|
5140
|
+
"M2 21h4V9H2v12zM23 10a2 2 0 0 0-2-2h-6.31l.95-4.57.03-.32a1.5 1.5 0 0 0-.44-1.06L14.17 1 7.59 7.59A2 2 0 0 0 7 9v10a2 2 0 0 0 2 2h9a2 2 0 0 0 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-1z"
|
|
5141
|
+
);
|
|
5142
|
+
p.setAttribute("fill", "currentColor");
|
|
5143
|
+
svg.appendChild(p);
|
|
5144
|
+
return svg;
|
|
5145
|
+
}
|
|
5146
|
+
function helpfulButton(reviewId, up, count, creds) {
|
|
5147
|
+
const btn = el(
|
|
5148
|
+
"button",
|
|
5149
|
+
"display:inline-flex;align-items:center;gap:4px;font-size:12px;color:#64748b;background:none;border:none;cursor:pointer;padding:2px 4px;"
|
|
5150
|
+
);
|
|
5151
|
+
btn.appendChild(thumbSvg(up));
|
|
5152
|
+
const countEl = el("span", "", String(count));
|
|
5153
|
+
btn.appendChild(countEl);
|
|
5154
|
+
const voteKey = `aegis_rv_voted_${reviewId}`;
|
|
5155
|
+
const already = (() => {
|
|
5156
|
+
try {
|
|
5157
|
+
return typeof localStorage !== "undefined" && localStorage.getItem(voteKey) != null;
|
|
5158
|
+
} catch {
|
|
5159
|
+
return false;
|
|
5160
|
+
}
|
|
5161
|
+
})();
|
|
5162
|
+
if (already) btn.style.opacity = "0.5";
|
|
5163
|
+
btn.addEventListener("click", () => {
|
|
5164
|
+
try {
|
|
5165
|
+
if (localStorage.getItem(voteKey) != null) return;
|
|
5166
|
+
localStorage.setItem(voteKey, up ? "up" : "down");
|
|
5167
|
+
} catch {
|
|
5168
|
+
}
|
|
5169
|
+
countEl.textContent = String(count + 1);
|
|
5170
|
+
btn.style.opacity = "0.5";
|
|
5171
|
+
const url = new URL(`/v1/reviews/${reviewId}/helpful`, creds.apiHost);
|
|
5172
|
+
void fetch(url.toString(), {
|
|
5173
|
+
method: "POST",
|
|
5174
|
+
headers: { "X-Aegis-Write-Key": creds.writeKey, "Content-Type": "application/json" },
|
|
5175
|
+
body: JSON.stringify({ direction: up ? "up" : "down" })
|
|
5176
|
+
}).catch(() => {
|
|
5177
|
+
});
|
|
5178
|
+
});
|
|
5179
|
+
return btn;
|
|
5180
|
+
}
|
|
5181
|
+
function reviewCard(ctx, r, creds) {
|
|
5182
|
+
var _a, _b;
|
|
5183
|
+
const li = el("li", "padding:14px 0;border-top:1px solid #e2e8f0;");
|
|
5184
|
+
const meta = el("div", "display:flex;flex-wrap:wrap;align-items:center;gap:8px;");
|
|
5185
|
+
meta.appendChild(stars(r.rating ?? 0));
|
|
5186
|
+
meta.appendChild(el("span", "font-size:14px;font-weight:600;", ((_a = r.reviewer_snapshot) == null ? void 0 : _a.name) ?? "Customer"));
|
|
5187
|
+
if (r.verified_purchase) meta.appendChild(chip("Verified purchase", "verified"));
|
|
5188
|
+
if (r.is_incentivized) meta.appendChild(chip("Reward included", "reward"));
|
|
5189
|
+
if (r.created_at) {
|
|
5190
|
+
const d = new Date(r.created_at);
|
|
5191
|
+
if (!Number.isNaN(d.getTime())) {
|
|
5192
|
+
meta.appendChild(el("span", "margin-left:auto;font-size:12px;color:#94a3b8;", d.toLocaleDateString()));
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
li.appendChild(meta);
|
|
5196
|
+
if (r.review_text) {
|
|
5197
|
+
li.appendChild(el("p", "margin:6px 0 0;font-size:14px;line-height:1.5;color:#334155;", r.review_text));
|
|
5198
|
+
}
|
|
5199
|
+
if (r.media && r.media.length > 0) {
|
|
5200
|
+
const gallery = el("div", "display:flex;gap:6px;margin-top:8px;");
|
|
5201
|
+
for (const m2 of r.media.slice(0, 6)) {
|
|
5202
|
+
const safe = ctx.sanitizeUrl(m2.url);
|
|
5203
|
+
if (!safe) continue;
|
|
5204
|
+
const img = document.createElement("img");
|
|
5205
|
+
img.src = safe;
|
|
5206
|
+
img.loading = "lazy";
|
|
5207
|
+
img.alt = "";
|
|
5208
|
+
img.style.cssText = "width:56px;height:56px;object-fit:cover;border-radius:8px;";
|
|
5209
|
+
gallery.appendChild(img);
|
|
5210
|
+
}
|
|
5211
|
+
if (gallery.childElementCount) li.appendChild(gallery);
|
|
5212
|
+
}
|
|
5213
|
+
if ((_b = r.operator_reply) == null ? void 0 : _b.text) {
|
|
5214
|
+
const reply = el(
|
|
5215
|
+
"div",
|
|
5216
|
+
"margin-top:8px;padding:8px 10px;background:#f8fafc;border-radius:8px;font-size:13px;color:#475569;"
|
|
5217
|
+
);
|
|
5218
|
+
reply.appendChild(el("span", "font-weight:600;margin-right:6px;", "Reply"));
|
|
5219
|
+
reply.appendChild(el("span", "", r.operator_reply.text));
|
|
5220
|
+
li.appendChild(reply);
|
|
5221
|
+
}
|
|
5222
|
+
const votes = el("div", "display:flex;align-items:center;gap:10px;margin-top:8px;");
|
|
5223
|
+
votes.appendChild(el("span", "font-size:12px;color:#94a3b8;", "Helpful?"));
|
|
5224
|
+
votes.appendChild(helpfulButton(r.id, true, r.helpful_count ?? 0, creds));
|
|
5225
|
+
votes.appendChild(helpfulButton(r.id, false, r.not_helpful_count ?? 0, creds));
|
|
5226
|
+
li.appendChild(votes);
|
|
5227
|
+
return li;
|
|
5228
|
+
}
|
|
5229
|
+
const SORTS = [
|
|
5230
|
+
{ key: "recent", label: "Most recent" },
|
|
5231
|
+
{ key: "helpful", label: "Most helpful" },
|
|
5232
|
+
{ key: "rating", label: "Highest rated" }
|
|
5233
|
+
];
|
|
5234
|
+
function renderReviewsInline(ctx, target, creds) {
|
|
5235
|
+
var _a;
|
|
5236
|
+
if (typeof document === "undefined" || !(creds == null ? void 0 : creds.apiHost) || !(creds == null ? void 0 : creds.writeKey)) return false;
|
|
5237
|
+
const productId = target.getAttribute("data-product-id") || ctx.campaign.product_id || null;
|
|
5238
|
+
const state = { sort: "recent", withPhotos: false };
|
|
5239
|
+
target.innerHTML = "";
|
|
5240
|
+
target.appendChild(el("div", "font-size:13px;color:#94a3b8;padding:8px 0;", "Loading reviews…"));
|
|
5241
|
+
function buildUrl() {
|
|
5242
|
+
const url = new URL("/v1/reviews/public", creds.apiHost);
|
|
5243
|
+
if (productId) url.searchParams.set("product_id", productId);
|
|
5244
|
+
url.searchParams.set("sort", state.sort);
|
|
5245
|
+
if (state.withPhotos) url.searchParams.set("with_photos", "true");
|
|
5246
|
+
url.searchParams.set("limit", "20");
|
|
5247
|
+
return url.toString();
|
|
5248
|
+
}
|
|
5249
|
+
function fetchReviews() {
|
|
5250
|
+
return fetch(buildUrl(), { method: "GET", headers: { "X-Aegis-Write-Key": creds.writeKey } }).then((r) => r.ok ? r.json() : null).catch(() => null);
|
|
5251
|
+
}
|
|
5252
|
+
let listContainer = null;
|
|
5253
|
+
function renderList(items) {
|
|
5254
|
+
if (!listContainer) return;
|
|
5255
|
+
listContainer.innerHTML = "";
|
|
5256
|
+
if (items.length === 0) {
|
|
5257
|
+
listContainer.appendChild(el("p", "padding:14px 0;font-size:13px;color:#94a3b8;", "No reviews match this filter."));
|
|
5258
|
+
return;
|
|
5259
|
+
}
|
|
5260
|
+
const ul = el("ul", "list-style:none;margin:0;padding:0;");
|
|
5261
|
+
for (const r of items) ul.appendChild(reviewCard(ctx, r, creds));
|
|
5262
|
+
listContainer.appendChild(ul);
|
|
5263
|
+
}
|
|
5264
|
+
function controls(onChange) {
|
|
5265
|
+
const bar = el("div", "display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:10px 0;");
|
|
5266
|
+
const sel = document.createElement("select");
|
|
5267
|
+
sel.style.cssText = "font-size:13px;padding:4px 8px;border:1px solid #e2e8f0;border-radius:999px;background:#fff;color:#334155;";
|
|
5268
|
+
for (const s of SORTS) {
|
|
5269
|
+
const opt = document.createElement("option");
|
|
5270
|
+
opt.value = s.key;
|
|
5271
|
+
opt.textContent = s.label;
|
|
5272
|
+
sel.appendChild(opt);
|
|
5273
|
+
}
|
|
5274
|
+
sel.value = state.sort;
|
|
5275
|
+
sel.addEventListener("change", () => {
|
|
5276
|
+
state.sort = sel.value;
|
|
5277
|
+
onChange();
|
|
5278
|
+
});
|
|
5279
|
+
bar.appendChild(sel);
|
|
5280
|
+
const photos = el(
|
|
5281
|
+
"button",
|
|
5282
|
+
"font-size:13px;padding:4px 12px;border:1px solid #e2e8f0;border-radius:999px;background:#fff;color:#334155;cursor:pointer;",
|
|
5283
|
+
"With photos"
|
|
5284
|
+
);
|
|
5285
|
+
photos.addEventListener("click", () => {
|
|
5286
|
+
state.withPhotos = !state.withPhotos;
|
|
5287
|
+
photos.style.cssText = "font-size:13px;padding:4px 12px;border-radius:999px;cursor:pointer;" + (state.withPhotos ? "background:#4169e1;color:#fff;border:1px solid #4169e1;" : "background:#fff;color:#334155;border:1px solid #e2e8f0;");
|
|
5288
|
+
onChange();
|
|
5289
|
+
});
|
|
5290
|
+
bar.appendChild(photos);
|
|
5291
|
+
return bar;
|
|
5292
|
+
}
|
|
5293
|
+
function renderWall(data) {
|
|
5294
|
+
target.innerHTML = "";
|
|
5295
|
+
const root = el(
|
|
5296
|
+
"section",
|
|
5297
|
+
"font-family:Inter,system-ui,-apple-system,sans-serif;color:#0f172a;max-width:760px;"
|
|
5298
|
+
);
|
|
5299
|
+
root.setAttribute("data-aegis-reviews", "true");
|
|
5300
|
+
const summary = data.aggregate.ai_summary;
|
|
5301
|
+
if (summary && summary.text) {
|
|
5302
|
+
const card = el(
|
|
5303
|
+
"div",
|
|
5304
|
+
"border:1px solid #ede9fe;background:#faf5ff;border-radius:12px;padding:12px 14px;margin-bottom:14px;"
|
|
5305
|
+
);
|
|
5306
|
+
const h = el("div", "display:flex;align-items:center;gap:6px;margin-bottom:6px;");
|
|
5307
|
+
h.appendChild(sparkles());
|
|
5308
|
+
h.appendChild(el("span", "font-size:12px;font-weight:700;color:#7c3aed;text-transform:uppercase;letter-spacing:.04em;", "AI summary"));
|
|
5309
|
+
card.appendChild(h);
|
|
5310
|
+
card.appendChild(el("p", "margin:0;font-size:14px;line-height:1.5;color:#334155;", summary.text));
|
|
5311
|
+
card.appendChild(
|
|
5312
|
+
el("p", "margin:6px 0 0;font-size:11px;color:#a78bda;text-align:right;", `summarized from ${summary.review_count} reviews`)
|
|
5313
|
+
);
|
|
5314
|
+
root.appendChild(card);
|
|
5315
|
+
}
|
|
5316
|
+
const head = el("div", "display:flex;align-items:center;gap:8px;");
|
|
5317
|
+
const avg = data.aggregate.avg_rating ?? 0;
|
|
5318
|
+
head.appendChild(stars(Math.round(avg), 18));
|
|
5319
|
+
head.appendChild(el("span", "font-size:20px;font-weight:700;", avg ? avg.toFixed(1) : "—"));
|
|
5320
|
+
head.appendChild(
|
|
5321
|
+
el("span", "font-size:13px;color:#64748b;", `${data.aggregate.count} review${data.aggregate.count === 1 ? "" : "s"}`)
|
|
5322
|
+
);
|
|
5323
|
+
root.appendChild(head);
|
|
5324
|
+
if (data.aggregate.count === 0) {
|
|
5325
|
+
root.appendChild(el("p", "margin-top:8px;font-size:14px;color:#94a3b8;", "No reviews yet."));
|
|
5326
|
+
target.appendChild(root);
|
|
5327
|
+
return;
|
|
5328
|
+
}
|
|
5329
|
+
root.appendChild(distributionRows(data.aggregate.distribution, data.aggregate.count));
|
|
5330
|
+
root.appendChild(
|
|
5331
|
+
controls(() => {
|
|
5332
|
+
void fetchReviews().then((d) => renderList((d == null ? void 0 : d.items) ?? []));
|
|
5333
|
+
})
|
|
5334
|
+
);
|
|
5335
|
+
listContainer = el("div");
|
|
5336
|
+
root.appendChild(listContainer);
|
|
5337
|
+
renderList(data.items);
|
|
5338
|
+
target.appendChild(root);
|
|
5339
|
+
injectJsonLd(target, data, productId);
|
|
5340
|
+
ctx.trackEvent(ctx.campaign.id, "impression");
|
|
5341
|
+
}
|
|
5342
|
+
const displayStyle = (_a = ctx.campaign.interactive_config) == null ? void 0 : _a["display_style"];
|
|
5343
|
+
const compact = displayStyle === "compact";
|
|
5344
|
+
function renderCompact(data) {
|
|
5345
|
+
var _a2;
|
|
5346
|
+
target.innerHTML = "";
|
|
5347
|
+
if (data.aggregate.count === 0) return;
|
|
5348
|
+
const root = el(
|
|
5349
|
+
"section",
|
|
5350
|
+
"font-family:Inter,system-ui,-apple-system,sans-serif;color:#0f172a;max-width:760px;border:1px solid #e2e8f0;border-radius:12px;padding:12px 14px;"
|
|
5351
|
+
);
|
|
5352
|
+
const line = el("div", "display:flex;align-items:center;gap:8px;");
|
|
5353
|
+
const avg = data.aggregate.avg_rating ?? 0;
|
|
5354
|
+
line.appendChild(stars(Math.round(avg), 16));
|
|
5355
|
+
line.appendChild(el("span", "font-size:15px;font-weight:700;", avg ? avg.toFixed(1) : "—"));
|
|
5356
|
+
line.appendChild(el("span", "font-size:13px;color:#64748b;", `(${data.aggregate.count})`));
|
|
5357
|
+
line.appendChild(chip("Verified reviews", "verified"));
|
|
5358
|
+
root.appendChild(line);
|
|
5359
|
+
const top = data.items.find((i) => i.review_text);
|
|
5360
|
+
if (top && top.review_text) {
|
|
5361
|
+
const q = el("p", "margin:8px 0 0;font-size:14px;color:#334155;", `“${top.review_text}”`);
|
|
5362
|
+
root.appendChild(q);
|
|
5363
|
+
root.appendChild(
|
|
5364
|
+
el("span", "font-size:12px;color:#94a3b8;", `— ${((_a2 = top.reviewer_snapshot) == null ? void 0 : _a2.name) ?? "Customer"}`)
|
|
5365
|
+
);
|
|
5366
|
+
}
|
|
5367
|
+
const seeAll = el(
|
|
5368
|
+
"button",
|
|
5369
|
+
"margin-top:10px;font-size:13px;font-weight:600;color:#4169e1;background:none;border:none;cursor:pointer;padding:0;",
|
|
5370
|
+
"See all reviews"
|
|
5371
|
+
);
|
|
5372
|
+
seeAll.addEventListener("click", () => renderWall(data));
|
|
5373
|
+
root.appendChild(seeAll);
|
|
5374
|
+
target.appendChild(root);
|
|
5375
|
+
ctx.trackEvent(ctx.campaign.id, "impression");
|
|
5376
|
+
}
|
|
5377
|
+
void fetchReviews().then((data) => {
|
|
5378
|
+
if (!data || !Array.isArray(data.items)) {
|
|
5379
|
+
target.innerHTML = "";
|
|
5380
|
+
return;
|
|
5381
|
+
}
|
|
5382
|
+
if (compact) renderCompact(data);
|
|
5383
|
+
else renderWall(data);
|
|
5384
|
+
});
|
|
5385
|
+
return true;
|
|
5386
|
+
}
|
|
5387
|
+
function renderReviewsOverlay(ctx, creds) {
|
|
5388
|
+
if (typeof document === "undefined" || !(creds == null ? void 0 : creds.apiHost) || !(creds == null ? void 0 : creds.writeKey)) return;
|
|
5389
|
+
const overlay = document.createElement("div");
|
|
5390
|
+
overlay.setAttribute("data-campaign-id", ctx.campaign.id);
|
|
5391
|
+
overlay.style.cssText = "position:fixed;inset:0;background:rgba(15,23,42,0.55);display:flex;align-items:flex-end;justify-content:center;z-index:99999;";
|
|
5392
|
+
const sheet = document.createElement("div");
|
|
5393
|
+
sheet.style.cssText = "background:#fff;color:#0f172a;max-width:620px;width:100%;max-height:82vh;overflow-y:auto;border-radius:20px 20px 0 0;padding:16px 16px 24px;font-family:Inter,system-ui,-apple-system,sans-serif;box-shadow:0 -12px 30px rgba(0,0,0,0.12);";
|
|
5394
|
+
const handle = el("div", "width:36px;height:4px;border-radius:999px;background:#e2e8f0;margin:0 auto 12px;");
|
|
5395
|
+
sheet.appendChild(handle);
|
|
5396
|
+
const bar = el("div", "display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;");
|
|
5397
|
+
bar.appendChild(el("div", "font-weight:700;font-size:16px;", ctx.campaign.title || "Customer reviews"));
|
|
5398
|
+
const close = el("button", "background:none;border:none;cursor:pointer;padding:4px 8px;color:#64748b;");
|
|
5399
|
+
const xsvg = document.createElementNS(SVG_NS, "svg");
|
|
5400
|
+
xsvg.setAttribute("viewBox", "0 0 24 24");
|
|
5401
|
+
xsvg.setAttribute("width", "18");
|
|
5402
|
+
xsvg.setAttribute("height", "18");
|
|
5403
|
+
const xp = document.createElementNS(SVG_NS, "path");
|
|
5404
|
+
xp.setAttribute("d", "M18 6L6 18M6 6l12 12");
|
|
5405
|
+
xp.setAttribute("stroke", "currentColor");
|
|
5406
|
+
xp.setAttribute("stroke-width", "2");
|
|
5407
|
+
xp.setAttribute("stroke-linecap", "round");
|
|
5408
|
+
xsvg.appendChild(xp);
|
|
5409
|
+
close.appendChild(xsvg);
|
|
5410
|
+
close.setAttribute("aria-label", "Close");
|
|
5411
|
+
close.addEventListener("click", () => {
|
|
5412
|
+
ctx.trackEvent(ctx.campaign.id, "dismissed");
|
|
5413
|
+
overlay.remove();
|
|
5414
|
+
});
|
|
5415
|
+
bar.appendChild(close);
|
|
5416
|
+
sheet.appendChild(bar);
|
|
5417
|
+
const body = el("div");
|
|
5418
|
+
const pid = ctx.campaign.product_id;
|
|
5419
|
+
if (pid) body.setAttribute("data-product-id", pid);
|
|
5420
|
+
sheet.appendChild(body);
|
|
5421
|
+
overlay.appendChild(sheet);
|
|
5422
|
+
overlay.addEventListener("click", (e) => {
|
|
5423
|
+
if (e.target === overlay) {
|
|
5424
|
+
ctx.trackEvent(ctx.campaign.id, "dismissed");
|
|
5425
|
+
overlay.remove();
|
|
5426
|
+
}
|
|
5427
|
+
});
|
|
5428
|
+
document.body.appendChild(overlay);
|
|
5429
|
+
renderReviewsInline(ctx, body, creds);
|
|
5430
|
+
}
|
|
5018
5431
|
function readAnonIdFromStorage$1() {
|
|
5019
5432
|
if (typeof document === "undefined") return void 0;
|
|
5020
5433
|
try {
|
|
@@ -5642,8 +6055,8 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
5642
6055
|
m2.addedNodes.forEach((n) => {
|
|
5643
6056
|
var _a, _b;
|
|
5644
6057
|
if (sawSlot || n.nodeType !== 1) return;
|
|
5645
|
-
const
|
|
5646
|
-
if (((_a =
|
|
6058
|
+
const el2 = n;
|
|
6059
|
+
if (((_a = el2.matches) == null ? void 0 : _a.call(el2, "[data-aegis-slot]")) || ((_b = el2.querySelector) == null ? void 0 : _b.call(el2, "[data-aegis-slot]"))) {
|
|
5647
6060
|
sawSlot = true;
|
|
5648
6061
|
}
|
|
5649
6062
|
});
|
|
@@ -5850,13 +6263,13 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
5850
6263
|
const anchor = ic == null ? void 0 : ic.anchor;
|
|
5851
6264
|
const selector = anchor == null ? void 0 : anchor.element_selector;
|
|
5852
6265
|
if (!selector) continue;
|
|
5853
|
-
let
|
|
6266
|
+
let el2 = null;
|
|
5854
6267
|
try {
|
|
5855
|
-
|
|
6268
|
+
el2 = document.querySelector(selector);
|
|
5856
6269
|
} catch {
|
|
5857
|
-
|
|
6270
|
+
el2 = null;
|
|
5858
6271
|
}
|
|
5859
|
-
if (!
|
|
6272
|
+
if (!el2) {
|
|
5860
6273
|
void this.trackEvent(c.id, "anchor_unresolved", {
|
|
5861
6274
|
meta: { anchor_resolved: false, anchor_selector: selector, widget_type: c.sub_type }
|
|
5862
6275
|
});
|
|
@@ -5867,9 +6280,9 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
5867
6280
|
if (position === "before" || position === "after") {
|
|
5868
6281
|
target = document.createElement("div");
|
|
5869
6282
|
target.setAttribute("data-aegis-anchor-wrapper", c.id);
|
|
5870
|
-
(_a =
|
|
6283
|
+
(_a = el2.parentNode) == null ? void 0 : _a.insertBefore(target, position === "before" ? el2 : el2.nextSibling);
|
|
5871
6284
|
} else {
|
|
5872
|
-
target =
|
|
6285
|
+
target = el2;
|
|
5873
6286
|
if (position === "replace") {
|
|
5874
6287
|
target.querySelectorAll(":scope > [data-aegis-slot-default]").forEach((d) => d.remove());
|
|
5875
6288
|
}
|
|
@@ -6026,6 +6439,12 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6026
6439
|
case "product_recommendation":
|
|
6027
6440
|
rendered = renderProductRecommendationInline(this.buildRenderContext(campaign), target);
|
|
6028
6441
|
break;
|
|
6442
|
+
case "reviews":
|
|
6443
|
+
rendered = renderReviewsInline(this.buildRenderContext(campaign), target, {
|
|
6444
|
+
apiHost: this.apiHost,
|
|
6445
|
+
writeKey: this.writeKey
|
|
6446
|
+
});
|
|
6447
|
+
break;
|
|
6029
6448
|
case "quiz":
|
|
6030
6449
|
rendered = this.renderQuizSlot(campaign, ic, bg, text, target);
|
|
6031
6450
|
if (!rendered) {
|
|
@@ -6258,10 +6677,10 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6258
6677
|
cbody.appendChild(b);
|
|
6259
6678
|
}
|
|
6260
6679
|
if (couponCode) {
|
|
6261
|
-
const
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
cbody.appendChild(
|
|
6680
|
+
const chip2 = document.createElement("span");
|
|
6681
|
+
chip2.style.cssText = `align-self: flex-start; border: 1px dashed ${text}66; border-radius: 6px; padding: 2px 8px; font-family: monospace; font-size: 11px; letter-spacing: 0.04em;`;
|
|
6682
|
+
chip2.textContent = couponCode;
|
|
6683
|
+
cbody.appendChild(chip2);
|
|
6265
6684
|
}
|
|
6266
6685
|
if (buttonText) {
|
|
6267
6686
|
const btn = document.createElement("button");
|
|
@@ -6844,19 +7263,19 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6844
7263
|
ov.appendChild(eyebrow);
|
|
6845
7264
|
ov.appendChild(big);
|
|
6846
7265
|
if (r.code) {
|
|
6847
|
-
const
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
7266
|
+
const chip2 = document.createElement("button");
|
|
7267
|
+
chip2.type = "button";
|
|
7268
|
+
chip2.textContent = `${r.code} ⧉`;
|
|
7269
|
+
chip2.style.cssText = `margin-top: 4px; padding: 9px 16px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 14px; letter-spacing: 1px; cursor: pointer;`;
|
|
7270
|
+
chip2.addEventListener("click", () => {
|
|
6852
7271
|
var _a;
|
|
6853
7272
|
try {
|
|
6854
7273
|
void ((_a = navigator.clipboard) == null ? void 0 : _a.writeText(r.code));
|
|
6855
|
-
|
|
7274
|
+
chip2.textContent = "Copied";
|
|
6856
7275
|
} catch {
|
|
6857
7276
|
}
|
|
6858
7277
|
});
|
|
6859
|
-
ov.appendChild(
|
|
7278
|
+
ov.appendChild(chip2);
|
|
6860
7279
|
} else if (r.kind === "freebie") {
|
|
6861
7280
|
const hint = document.createElement("div");
|
|
6862
7281
|
hint.textContent = "🎟️ Show this screen to claim";
|
|
@@ -6890,20 +7309,20 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6890
7309
|
result.textContent = "";
|
|
6891
7310
|
const tag = document.createElement("span");
|
|
6892
7311
|
tag.textContent = `${r.label} — `;
|
|
6893
|
-
const
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
7312
|
+
const chip2 = document.createElement("button");
|
|
7313
|
+
chip2.type = "button";
|
|
7314
|
+
chip2.textContent = `${r.code} ⧉`;
|
|
7315
|
+
chip2.style.cssText = `padding: 2px 8px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 12px; letter-spacing: 0.5px; cursor: pointer;`;
|
|
7316
|
+
chip2.addEventListener("click", () => {
|
|
6898
7317
|
var _a;
|
|
6899
7318
|
try {
|
|
6900
7319
|
void ((_a = navigator.clipboard) == null ? void 0 : _a.writeText(r.code));
|
|
6901
|
-
|
|
7320
|
+
chip2.textContent = "Copied";
|
|
6902
7321
|
} catch {
|
|
6903
7322
|
}
|
|
6904
7323
|
});
|
|
6905
7324
|
result.appendChild(tag);
|
|
6906
|
-
result.appendChild(
|
|
7325
|
+
result.appendChild(chip2);
|
|
6907
7326
|
} else result.textContent = r.label || "Thanks for playing!";
|
|
6908
7327
|
retireCta();
|
|
6909
7328
|
};
|
|
@@ -7078,17 +7497,27 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
7078
7497
|
h.textContent = campaign.title;
|
|
7079
7498
|
card.appendChild(h);
|
|
7080
7499
|
}
|
|
7500
|
+
const layout = ic.rec_layout || "row";
|
|
7081
7501
|
const strip = document.createElement("div");
|
|
7082
|
-
|
|
7502
|
+
const pad = `${st.padY ?? 12}px ${st.padX ?? 12}px`;
|
|
7503
|
+
if (layout === "grid") {
|
|
7504
|
+
strip.style.cssText = `display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; padding: ${pad};`;
|
|
7505
|
+
} else if (layout === "list") {
|
|
7506
|
+
strip.style.cssText = `display: flex; flex-direction: column; gap: 8px; padding: ${pad};`;
|
|
7507
|
+
} else {
|
|
7508
|
+
strip.style.cssText = `display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; scrollbar-width: none; padding: ${pad};`;
|
|
7509
|
+
}
|
|
7083
7510
|
const template = resolveGlobalTemplate(ic);
|
|
7084
7511
|
const ctx = this.buildRenderContext(campaign);
|
|
7085
7512
|
const accent = this.sanitizeColor(campaign.background_color || text);
|
|
7086
7513
|
cards.forEach((c, i) => {
|
|
7514
|
+
const meta = c.metadata || {};
|
|
7515
|
+
const linked = !!(meta.product_id || meta.sku);
|
|
7087
7516
|
const tile = renderCardFromTemplate(c, {
|
|
7088
7517
|
template,
|
|
7089
7518
|
ctx,
|
|
7090
|
-
action: "navigate",
|
|
7091
|
-
layout
|
|
7519
|
+
action: linked ? "cart" : "navigate",
|
|
7520
|
+
layout,
|
|
7092
7521
|
layoutAccent: accent
|
|
7093
7522
|
});
|
|
7094
7523
|
tile.setAttribute("data-card-index", String(i));
|
|
@@ -7096,6 +7525,43 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
7096
7525
|
});
|
|
7097
7526
|
card.appendChild(strip);
|
|
7098
7527
|
target.appendChild(card);
|
|
7528
|
+
const autoplayMs = Number(ic.autoplay_ms) || 0;
|
|
7529
|
+
if (layout === "row" && autoplayMs > 0 && cards.length > 1) {
|
|
7530
|
+
const loop = ic.loop !== false;
|
|
7531
|
+
const interval = Math.max(autoplayMs, 600);
|
|
7532
|
+
let idx = 0;
|
|
7533
|
+
let timer = null;
|
|
7534
|
+
const stop = () => {
|
|
7535
|
+
if (timer) {
|
|
7536
|
+
clearInterval(timer);
|
|
7537
|
+
timer = null;
|
|
7538
|
+
}
|
|
7539
|
+
};
|
|
7540
|
+
timer = setInterval(() => {
|
|
7541
|
+
if (!document.body.contains(strip)) {
|
|
7542
|
+
stop();
|
|
7543
|
+
return;
|
|
7544
|
+
}
|
|
7545
|
+
if (strip.scrollWidth <= strip.clientWidth + 4) return;
|
|
7546
|
+
const tiles = Array.from(strip.children);
|
|
7547
|
+
idx += 1;
|
|
7548
|
+
if (idx >= tiles.length) {
|
|
7549
|
+
if (!loop) {
|
|
7550
|
+
stop();
|
|
7551
|
+
return;
|
|
7552
|
+
}
|
|
7553
|
+
idx = 0;
|
|
7554
|
+
}
|
|
7555
|
+
const el2 = tiles[idx];
|
|
7556
|
+
if (el2) {
|
|
7557
|
+
const delta = el2.getBoundingClientRect().left - strip.getBoundingClientRect().left;
|
|
7558
|
+
strip.scrollTo({ left: strip.scrollLeft + delta, behavior: "smooth" });
|
|
7559
|
+
}
|
|
7560
|
+
}, interval);
|
|
7561
|
+
["pointerdown", "wheel", "touchstart"].forEach(
|
|
7562
|
+
(evt) => strip.addEventListener(evt, stop, { passive: true })
|
|
7563
|
+
);
|
|
7564
|
+
}
|
|
7099
7565
|
return true;
|
|
7100
7566
|
}
|
|
7101
7567
|
/** Built-in embedded COUNTDOWN — live H:M:S digits that pulse red in the
|
|
@@ -7118,10 +7584,10 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
7118
7584
|
const digits = document.createElement("div");
|
|
7119
7585
|
digits.style.cssText = "display: flex; gap: 6px; justify-content: center;";
|
|
7120
7586
|
const mkDigit = () => {
|
|
7121
|
-
const
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
return
|
|
7587
|
+
const el2 = document.createElement("span");
|
|
7588
|
+
el2.style.cssText = `padding: 6px 9px; border-radius: 8px; font-size: 18px; font-weight: 700; font-family: ui-monospace, monospace; background: ${text}1a; transition: background 0.3s ease, color 0.3s ease;`;
|
|
7589
|
+
el2.textContent = "00";
|
|
7590
|
+
return el2;
|
|
7125
7591
|
};
|
|
7126
7592
|
const hEl = mkDigit();
|
|
7127
7593
|
const mEl = mkDigit();
|
|
@@ -7151,9 +7617,9 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
7151
7617
|
sEl.textContent = String(s).padStart(2, "0");
|
|
7152
7618
|
if (diff > 0 && diff <= 6e4 && !urgent) {
|
|
7153
7619
|
urgent = true;
|
|
7154
|
-
[hEl, mEl, sEl].forEach((
|
|
7155
|
-
|
|
7156
|
-
|
|
7620
|
+
[hEl, mEl, sEl].forEach((el2) => {
|
|
7621
|
+
el2.style.boxShadow = "0 0 0 2px #ef4444";
|
|
7622
|
+
el2.style.animation = "aegisUrgencyPulse 1s ease-in-out infinite";
|
|
7157
7623
|
});
|
|
7158
7624
|
}
|
|
7159
7625
|
if (diff > 0) requestAnimationFrame(tick);
|
|
@@ -7508,19 +7974,19 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
7508
7974
|
ov.appendChild(eb);
|
|
7509
7975
|
ov.appendChild(big);
|
|
7510
7976
|
if (code) {
|
|
7511
|
-
const
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7977
|
+
const chip2 = document.createElement("button");
|
|
7978
|
+
chip2.type = "button";
|
|
7979
|
+
chip2.textContent = `${code} ⧉`;
|
|
7980
|
+
chip2.style.cssText = `margin-top: 4px; padding: 8px 14px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 13px; letter-spacing: 1px; cursor: pointer;`;
|
|
7981
|
+
chip2.addEventListener("click", () => {
|
|
7516
7982
|
var _a;
|
|
7517
7983
|
try {
|
|
7518
7984
|
void ((_a = navigator.clipboard) == null ? void 0 : _a.writeText(code));
|
|
7519
|
-
|
|
7985
|
+
chip2.textContent = "Copied";
|
|
7520
7986
|
} catch {
|
|
7521
7987
|
}
|
|
7522
7988
|
});
|
|
7523
|
-
ov.appendChild(
|
|
7989
|
+
ov.appendChild(chip2);
|
|
7524
7990
|
}
|
|
7525
7991
|
card.appendChild(ov);
|
|
7526
7992
|
try {
|
|
@@ -7530,10 +7996,10 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
7530
7996
|
} else {
|
|
7531
7997
|
prizeText.textContent = icoVal && !isUrl(icoVal) ? `${icoVal} ${label}` : label;
|
|
7532
7998
|
if (code) {
|
|
7533
|
-
const
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
body.appendChild(
|
|
7999
|
+
const chip2 = document.createElement("div");
|
|
8000
|
+
chip2.style.cssText = `margin-top: 6px; font-family: ui-monospace, monospace; font-size: 12px; font-weight: 700; padding: 4px 8px; border-radius: 6px; background: ${text}14; color: ${text};`;
|
|
8001
|
+
chip2.textContent = code;
|
|
8002
|
+
body.appendChild(chip2);
|
|
7537
8003
|
}
|
|
7538
8004
|
this._playReaction(body, "celebrate", "gift.json", ic);
|
|
7539
8005
|
}
|
|
@@ -7695,9 +8161,9 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
7695
8161
|
*/
|
|
7696
8162
|
_flattenSlotCard(container) {
|
|
7697
8163
|
const cards = container.matches("[data-campaign-id]") ? [container] : Array.from(container.querySelectorAll("[data-campaign-id]"));
|
|
7698
|
-
for (const
|
|
7699
|
-
|
|
7700
|
-
|
|
8164
|
+
for (const el2 of cards) {
|
|
8165
|
+
el2.style.borderRadius = "0";
|
|
8166
|
+
el2.style.boxShadow = "none";
|
|
7701
8167
|
}
|
|
7702
8168
|
}
|
|
7703
8169
|
/**
|
|
@@ -7783,6 +8249,15 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
7783
8249
|
this.emit("campaign-shown", campaign);
|
|
7784
8250
|
return;
|
|
7785
8251
|
}
|
|
8252
|
+
if (campaign.sub_type === "reviews") {
|
|
8253
|
+
renderReviewsOverlay(this.buildRenderContext(campaign), {
|
|
8254
|
+
apiHost: this.apiHost,
|
|
8255
|
+
writeKey: this.writeKey
|
|
8256
|
+
});
|
|
8257
|
+
this.trackEvent(campaign.id, "impression");
|
|
8258
|
+
this.emit("campaign-shown", campaign);
|
|
8259
|
+
return;
|
|
8260
|
+
}
|
|
7786
8261
|
{
|
|
7787
8262
|
const ic2 = campaign.interactive_config || {};
|
|
7788
8263
|
const multiCard2 = campaign.sub_type === "product_recommendation" || campaign.sub_type === "carousel_cards";
|
|
@@ -7910,7 +8385,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
7910
8385
|
navigate: (url, buttonId) => this.navigateToCampaignAction(campaign, url, buttonId),
|
|
7911
8386
|
dispatchAction: (action) => this.dispatchAction(action, campaign.id),
|
|
7912
8387
|
submitResponse: (responseType, payload) => this.submitWidgetResponse(campaign, responseType, payload),
|
|
7913
|
-
playReaction: (
|
|
8388
|
+
playReaction: (el2, intent, lottieFile, scope) => this._playReaction(el2, intent, lottieFile, campaign.interactive_config, scope),
|
|
7914
8389
|
haptic: (pattern) => {
|
|
7915
8390
|
try {
|
|
7916
8391
|
if (typeof navigator !== "undefined" && typeof navigator.vibrate === "function") {
|
|
@@ -8167,9 +8642,9 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
8167
8642
|
if (cells.s) cells.s.textContent = pad(s);
|
|
8168
8643
|
if (!urgent && diff > 0 && diff <= 6e4) {
|
|
8169
8644
|
urgent = true;
|
|
8170
|
-
Object.values(cells).forEach((
|
|
8171
|
-
if (
|
|
8172
|
-
|
|
8645
|
+
Object.values(cells).forEach((el2) => {
|
|
8646
|
+
if (el2) {
|
|
8647
|
+
el2.style.boxShadow = "0 0 0 2px #ef4444";
|
|
8173
8648
|
}
|
|
8174
8649
|
});
|
|
8175
8650
|
digits.style.animation = "aegisCountdownPulse 1.1s ease-in-out infinite";
|
|
@@ -8698,58 +9173,21 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
8698
9173
|
* 3. if STILL unhandled → canonical fallback (navigate to the product/cart
|
|
8699
9174
|
* page) so a CTA is never a dead no-op (the rail-"Add" 3rd-party gap).
|
|
8700
9175
|
* Both add-to-cart paths (card "Add" + the `aegis://cart-add` verb) funnel here,
|
|
8701
|
-
* so they're one behaviour
|
|
9176
|
+
* so they're one behaviour delivered to the host's single `onAction` handler.
|
|
9177
|
+
* (Phase E.2 hard cutover — the legacy `aegis:*` DOM events were removed.)
|
|
8702
9178
|
*/
|
|
8703
|
-
dispatchAction(action,
|
|
9179
|
+
dispatchAction(action, _campaignId) {
|
|
8704
9180
|
var _a;
|
|
8705
9181
|
let handled = false;
|
|
8706
9182
|
try {
|
|
8707
9183
|
handled = ((_a = this.onActionHandler) == null ? void 0 : _a.call(this, action)) === true;
|
|
8708
9184
|
} catch {
|
|
8709
9185
|
}
|
|
8710
|
-
if (typeof window !== "undefined") {
|
|
8711
|
-
const legacy = this._legacyEventFor(action, campaignId);
|
|
8712
|
-
if (legacy) {
|
|
8713
|
-
const evt = new CustomEvent(legacy.name, { detail: legacy.detail, cancelable: true });
|
|
8714
|
-
const proceed = window.dispatchEvent(evt);
|
|
8715
|
-
if (!proceed || evt.defaultPrevented) handled = true;
|
|
8716
|
-
}
|
|
8717
|
-
}
|
|
8718
9186
|
if (!handled) {
|
|
8719
9187
|
const url = fallbackUrlForAction(action, this.productUrl);
|
|
8720
9188
|
if (url && typeof window !== "undefined") window.location.href = url;
|
|
8721
9189
|
}
|
|
8722
9190
|
}
|
|
8723
|
-
/** Back-compat mapping: canonical action → the legacy DOM event hosts listen
|
|
8724
|
-
* for today. `add_to_cart` always emits `aegis:add-to-cart` (the one ACS
|
|
8725
|
-
* handles), collapsing the old `aegis:cart-add` divergence. */
|
|
8726
|
-
_legacyEventFor(a, campaignId) {
|
|
8727
|
-
const base = { campaign_id: campaignId };
|
|
8728
|
-
switch (a.type) {
|
|
8729
|
-
case "add_to_cart":
|
|
8730
|
-
return {
|
|
8731
|
-
name: "aegis:add-to-cart",
|
|
8732
|
-
detail: {
|
|
8733
|
-
...base,
|
|
8734
|
-
product_id: a.productId,
|
|
8735
|
-
sku: a.sku,
|
|
8736
|
-
title: a.title,
|
|
8737
|
-
price: a.price,
|
|
8738
|
-
image_url: a.imageUrl,
|
|
8739
|
-
cta_url: a.url,
|
|
8740
|
-
qty: a.qty ?? 1
|
|
8741
|
-
}
|
|
8742
|
-
};
|
|
8743
|
-
case "open_product":
|
|
8744
|
-
return { name: "aegis:open-product", detail: { ...base, sku: a.sku, product_id: a.productId, url: a.url } };
|
|
8745
|
-
case "open_cart":
|
|
8746
|
-
return { name: "aegis:open-cart", detail: base };
|
|
8747
|
-
case "open_login":
|
|
8748
|
-
return { name: "aegis:open-login", detail: base };
|
|
8749
|
-
default:
|
|
8750
|
-
return null;
|
|
8751
|
-
}
|
|
8752
|
-
}
|
|
8753
9191
|
/**
|
|
8754
9192
|
* Phase-2 CTA action verbs (`aegis://<verb>?<params>`). Performs an in-app
|
|
8755
9193
|
* behaviour instead of navigating. Verbs:
|
|
@@ -8789,9 +9227,9 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
8789
9227
|
/** Remove a rendered campaign's element(s) from the DOM by campaign id. */
|
|
8790
9228
|
removeCampaignEl(campaignId) {
|
|
8791
9229
|
if (typeof document === "undefined") return;
|
|
8792
|
-
document.querySelectorAll(`[data-campaign-id="${campaignId}"]`).forEach((
|
|
9230
|
+
document.querySelectorAll(`[data-campaign-id="${campaignId}"]`).forEach((el2) => {
|
|
8793
9231
|
var _a;
|
|
8794
|
-
return (_a =
|
|
9232
|
+
return (_a = el2.parentNode) == null ? void 0 : _a.removeChild(el2);
|
|
8795
9233
|
});
|
|
8796
9234
|
}
|
|
8797
9235
|
/**
|
|
@@ -10518,9 +10956,9 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
10518
10956
|
if (typeof document !== "undefined") {
|
|
10519
10957
|
document.querySelectorAll(
|
|
10520
10958
|
".aegis-in-app-banner, .aegis-in-app-modal-overlay, .aegis-in-app-fullscreen-overlay, .aegis-in-app-half-interstitial-overlay, .aegis-in-app-alert-overlay, .aegis-in-app-pip, .aegis-in-app-nps-overlay, .aegis-in-app-countdown-overlay, .aegis-in-app-rating-overlay, .aegis-in-app-poll-overlay, .aegis-in-app-quiz-overlay, .aegis-in-app-rating-card, .aegis-in-app-nps-card"
|
|
10521
|
-
).forEach((
|
|
10522
|
-
if (
|
|
10523
|
-
|
|
10959
|
+
).forEach((el2) => {
|
|
10960
|
+
if (el2.parentNode) {
|
|
10961
|
+
el2.parentNode.removeChild(el2);
|
|
10524
10962
|
}
|
|
10525
10963
|
});
|
|
10526
10964
|
const styles = document.getElementById("aegis-in-app-styles");
|
|
@@ -10552,9 +10990,9 @@ let AegisInAppManager = _AegisInAppManager;
|
|
|
10552
10990
|
function renderPreview(config, opts) {
|
|
10553
10991
|
document.querySelectorAll(
|
|
10554
10992
|
'[class^="aegis-in-app-"]'
|
|
10555
|
-
).forEach((
|
|
10556
|
-
if (
|
|
10557
|
-
|
|
10993
|
+
).forEach((el2) => {
|
|
10994
|
+
if (el2.parentNode) {
|
|
10995
|
+
el2.parentNode.removeChild(el2);
|
|
10558
10996
|
}
|
|
10559
10997
|
});
|
|
10560
10998
|
try {
|
|
@@ -10590,7 +11028,7 @@ function renderPreview(config, opts) {
|
|
|
10590
11028
|
if (anchors.length > 0) {
|
|
10591
11029
|
try {
|
|
10592
11030
|
anchors.forEach((slot) => {
|
|
10593
|
-
slot.querySelectorAll(":scope > :not([data-aegis-slot-default])").forEach((
|
|
11031
|
+
slot.querySelectorAll(":scope > :not([data-aegis-slot-default])").forEach((el2) => el2.remove());
|
|
10594
11032
|
});
|
|
10595
11033
|
const mm = manager;
|
|
10596
11034
|
mm.campaigns = (inline ? [config, ...coGroup] : coGroup).map((c) => ({ ...c, surface: void 0, target_screens: void 0 })).sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
@@ -10611,17 +11049,17 @@ function renderPreview(config, opts) {
|
|
|
10611
11049
|
m2.displayCampaign(config);
|
|
10612
11050
|
}
|
|
10613
11051
|
const MAX_NODES = 400;
|
|
10614
|
-
function selectorFor(
|
|
10615
|
-
if (
|
|
11052
|
+
function selectorFor(el2) {
|
|
11053
|
+
if (el2.id) return `#${CSS.escape(el2.id)}`;
|
|
10616
11054
|
for (const attr of ["data-testid", "data-test", "data-aegis-anchor", "data-qa", "name"]) {
|
|
10617
|
-
const v =
|
|
11055
|
+
const v = el2.getAttribute(attr);
|
|
10618
11056
|
if (v) {
|
|
10619
|
-
const sel = `${
|
|
11057
|
+
const sel = `${el2.tagName.toLowerCase()}[${attr}="${CSS.escape(v)}"]`;
|
|
10620
11058
|
if (document.querySelectorAll(sel).length === 1) return sel;
|
|
10621
11059
|
}
|
|
10622
11060
|
}
|
|
10623
11061
|
const parts = [];
|
|
10624
|
-
let node =
|
|
11062
|
+
let node = el2;
|
|
10625
11063
|
while (node && node !== document.body && parts.length < 6) {
|
|
10626
11064
|
if (node.id) {
|
|
10627
11065
|
parts.unshift(`#${CSS.escape(node.id)}`);
|
|
@@ -10645,15 +11083,15 @@ function captureWebScreen(screenName) {
|
|
|
10645
11083
|
const candidates = document.querySelectorAll(
|
|
10646
11084
|
"a,button,input,select,textarea,[role],[data-aegis-anchor],[data-testid],h1,h2,h3,img,[data-aegis-slot]"
|
|
10647
11085
|
);
|
|
10648
|
-
for (const
|
|
11086
|
+
for (const el2 of Array.from(candidates)) {
|
|
10649
11087
|
if (nodes.length >= MAX_NODES) break;
|
|
10650
|
-
const rect =
|
|
11088
|
+
const rect = el2.getBoundingClientRect();
|
|
10651
11089
|
if (rect.width <= 0 || rect.height <= 0) continue;
|
|
10652
|
-
const slotKey =
|
|
10653
|
-
const role = slotKey ? `slot:${slotKey}` :
|
|
10654
|
-
const text = (
|
|
11090
|
+
const slotKey = el2.getAttribute("data-aegis-slot");
|
|
11091
|
+
const role = slotKey ? `slot:${slotKey}` : el2.getAttribute("role") || el2.tagName.toLowerCase();
|
|
11092
|
+
const text = (el2.textContent || el2.getAttribute("aria-label") || "").trim().slice(0, 80);
|
|
10655
11093
|
nodes.push({
|
|
10656
|
-
selector: selectorFor(
|
|
11094
|
+
selector: selectorFor(el2),
|
|
10657
11095
|
role,
|
|
10658
11096
|
...text ? { text } : {},
|
|
10659
11097
|
bounds: {
|