@active-reach/web-sdk 1.35.0 → 1.35.4
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.min.js +1 -1
- package/dist/aegis.min.js.map +1 -1
- package/dist/{analytics-CCDZCnut.mjs → analytics-ZwrwqCTJ.mjs} +2 -2
- package/dist/{analytics-CCDZCnut.mjs.map → analytics-ZwrwqCTJ.mjs.map} +1 -1
- package/dist/chat/AegisChat.d.ts +1 -0
- package/dist/chat/AegisChat.d.ts.map +1 -1
- package/dist/core/analytics.d.ts +1 -1
- package/dist/inapp/AegisInAppManager.d.ts +31 -20
- package/dist/inapp/AegisInAppManager.d.ts.map +1 -1
- package/dist/inapp/actions.d.ts +8 -0
- package/dist/inapp/actions.d.ts.map +1 -1
- package/dist/inapp/renderers/card-template.d.ts +22 -1
- package/dist/inapp/renderers/card-template.d.ts.map +1 -1
- package/dist/inapp/renderers/index.d.ts +0 -2
- package/dist/inapp/renderers/index.d.ts.map +1 -1
- package/dist/inapp/renderers/reviews.d.ts.map +1 -1
- package/dist/inapp/renderers/types.d.ts +15 -0
- package/dist/inapp/renderers/types.d.ts.map +1 -1
- package/dist/index.js +597 -293
- package/dist/index.js.map +1 -1
- package/dist/react.js +1 -1
- package/package.json +1 -1
- package/dist/inapp/renderers/product-rating.d.ts +0 -40
- package/dist/inapp/renderers/product-rating.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Storage, l as logger, A as Aegis } from "./analytics-
|
|
2
|
-
import { B, E, N, R, U, m } from "./analytics-
|
|
1
|
+
import { S as Storage, l as logger, A as Aegis } from "./analytics-ZwrwqCTJ.mjs";
|
|
2
|
+
import { B, E, N, R, U, m } from "./analytics-ZwrwqCTJ.mjs";
|
|
3
3
|
import { AegisWebPush } from "./push/AegisWebPush.js";
|
|
4
4
|
function debounce(func, wait) {
|
|
5
5
|
let timeoutId = null;
|
|
@@ -524,7 +524,8 @@ function applyPerCardOverride(tpl, look) {
|
|
|
524
524
|
if (look.hide_price) drop("price");
|
|
525
525
|
if (look.hide_cta) drop("cta");
|
|
526
526
|
if (look.hide_rating) drop("rating");
|
|
527
|
-
if (look.cta_text)
|
|
527
|
+
if (look.cta_text)
|
|
528
|
+
elements = elements.map((e) => e.type === "cta" ? { ...e, text: look.cta_text } : e);
|
|
528
529
|
if (look.cta_action || look.cta_url || look.cta_custom_name) {
|
|
529
530
|
elements = elements.map(
|
|
530
531
|
(e) => e.type === "cta" ? {
|
|
@@ -537,7 +538,9 @@ function applyPerCardOverride(tpl, look) {
|
|
|
537
538
|
}
|
|
538
539
|
if (look.badge) {
|
|
539
540
|
const badgeEl = { type: "badge", text: look.badge, color: look.badge_color };
|
|
540
|
-
elements = elements.some((e) => e.type === "badge") ? elements.map((e) => e.type === "badge" ? badgeEl : e) : [elements[0], badgeEl, ...elements.slice(1)].filter(
|
|
541
|
+
elements = elements.some((e) => e.type === "badge") ? elements.map((e) => e.type === "badge" ? badgeEl : e) : [elements[0], badgeEl, ...elements.slice(1)].filter(
|
|
542
|
+
Boolean
|
|
543
|
+
);
|
|
541
544
|
}
|
|
542
545
|
return { ...tpl, frame, elements };
|
|
543
546
|
}
|
|
@@ -585,13 +588,12 @@ function paintRating(card) {
|
|
|
585
588
|
const count = Number(meta.review_count ?? 0);
|
|
586
589
|
const rr = document.createElement("div");
|
|
587
590
|
rr.style.cssText = "display:flex;align-items:center;gap:3px;font-size:11px;line-height:1;";
|
|
588
|
-
const star2 =
|
|
589
|
-
star2.
|
|
590
|
-
star2.style.cssText = "color:#059669;";
|
|
591
|
+
const star2 = lucideSvgEl("star", { size: 12, color: "#059669" });
|
|
592
|
+
if (star2) star2.setAttribute("fill", "#059669");
|
|
591
593
|
const val = document.createElement("span");
|
|
592
594
|
val.textContent = avg.toFixed(1);
|
|
593
595
|
val.style.cssText = "color:#059669;font-weight:700;";
|
|
594
|
-
rr.appendChild(star2);
|
|
596
|
+
if (star2) rr.appendChild(star2);
|
|
595
597
|
rr.appendChild(val);
|
|
596
598
|
if (count > 0) {
|
|
597
599
|
const c = document.createElement("span");
|
|
@@ -650,7 +652,9 @@ function appendRatingThanks(wrap, el2, ctx, fg, accent, isHigh) {
|
|
|
650
652
|
cta.textContent = el2.rating_high_cta || "Leave a review";
|
|
651
653
|
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;`;
|
|
652
654
|
cta.addEventListener("click", () => {
|
|
653
|
-
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
655
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
656
|
+
meta: { element: "rating_input", cta: "high_review" }
|
|
657
|
+
});
|
|
654
658
|
if (ctx.navigate) ctx.navigate(url, "cta");
|
|
655
659
|
else window.location.href = url;
|
|
656
660
|
});
|
|
@@ -753,6 +757,315 @@ function buildRatingInputElement(el2, ctx, fg) {
|
|
|
753
757
|
wrap.appendChild(row);
|
|
754
758
|
return wrap;
|
|
755
759
|
}
|
|
760
|
+
function starRow(scale, onPick) {
|
|
761
|
+
const row = document.createElement("div");
|
|
762
|
+
row.style.cssText = "display:flex;gap:2px;flex-shrink:0;";
|
|
763
|
+
const stars2 = [];
|
|
764
|
+
let picked = 0;
|
|
765
|
+
const paint = (upto) => {
|
|
766
|
+
stars2.forEach((s, idx) => {
|
|
767
|
+
const on = idx < upto;
|
|
768
|
+
s.setAttribute("stroke", on ? "#f59e0b" : "#d1d5db");
|
|
769
|
+
s.setAttribute("fill", on ? "#f59e0b" : "none");
|
|
770
|
+
});
|
|
771
|
+
};
|
|
772
|
+
for (let i = 1; i <= scale; i++) {
|
|
773
|
+
const btn = document.createElement("button");
|
|
774
|
+
btn.type = "button";
|
|
775
|
+
btn.setAttribute("aria-label", `${i} star${i > 1 ? "s" : ""}`);
|
|
776
|
+
btn.style.cssText = "cursor:pointer;border:none;background:none;padding:1px;line-height:0;";
|
|
777
|
+
const svg = lucideSvgEl("star", { size: 22, color: "#d1d5db" });
|
|
778
|
+
if (svg) {
|
|
779
|
+
btn.appendChild(svg);
|
|
780
|
+
stars2.push(svg);
|
|
781
|
+
}
|
|
782
|
+
btn.addEventListener("mouseenter", () => paint(i));
|
|
783
|
+
btn.addEventListener("mouseleave", () => paint(picked));
|
|
784
|
+
btn.addEventListener("click", () => {
|
|
785
|
+
picked = i;
|
|
786
|
+
paint(i);
|
|
787
|
+
onPick(i);
|
|
788
|
+
});
|
|
789
|
+
row.appendChild(btn);
|
|
790
|
+
}
|
|
791
|
+
return row;
|
|
792
|
+
}
|
|
793
|
+
function ratingThumb(it, accent) {
|
|
794
|
+
const box = document.createElement("div");
|
|
795
|
+
box.style.cssText = `width:40px;height:40px;flex-shrink:0;border-radius:8px;overflow:hidden;background:${accent}1f;display:flex;align-items:center;justify-content:center;`;
|
|
796
|
+
if (it.image_url) {
|
|
797
|
+
const img = document.createElement("img");
|
|
798
|
+
img.src = it.image_url;
|
|
799
|
+
img.alt = it.name || "";
|
|
800
|
+
img.loading = "lazy";
|
|
801
|
+
img.style.cssText = "width:100%;height:100%;object-fit:cover;";
|
|
802
|
+
box.appendChild(img);
|
|
803
|
+
} else {
|
|
804
|
+
const mono = document.createElement("span");
|
|
805
|
+
mono.textContent = (it.name || "?").slice(0, 1).toUpperCase();
|
|
806
|
+
mono.style.cssText = `font-size:16px;font-weight:700;color:${accent};`;
|
|
807
|
+
box.appendChild(mono);
|
|
808
|
+
}
|
|
809
|
+
return box;
|
|
810
|
+
}
|
|
811
|
+
function buildProductRatingListElement(el2, ctx, fg) {
|
|
812
|
+
const wrap = document.createElement("div");
|
|
813
|
+
wrap.style.cssText = "display:flex;flex-direction:column;gap:10px;margin:2px 0;";
|
|
814
|
+
const slot = ctx.slotEl;
|
|
815
|
+
if (!slot) return wrap;
|
|
816
|
+
const accent = ctx.sanitizeColor(
|
|
817
|
+
slot.getAttribute("data-accent") || ctx.campaign.background_color || "#4169e1"
|
|
818
|
+
);
|
|
819
|
+
const orderId = slot.getAttribute("data-order-id");
|
|
820
|
+
const billCode = slot.getAttribute("data-bill-code");
|
|
821
|
+
const token = slot.getAttribute("data-token");
|
|
822
|
+
const reviewsUrl = slot.getAttribute("data-reviews-url") || el2.rating_high_url || null;
|
|
823
|
+
const scale = typeof el2.rating_scale === "number" && el2.rating_scale >= 2 && el2.rating_scale <= 10 ? Math.floor(el2.rating_scale) : 5;
|
|
824
|
+
const showThumb = el2.show_thumbnail !== false && el2.rating_layout !== "compact";
|
|
825
|
+
const scores = {};
|
|
826
|
+
const allowText = el2.allow_review_text === true;
|
|
827
|
+
const canUpload = !!(ctx.apiHost && ctx.writeKey && ctx.organizationId);
|
|
828
|
+
const allowMedia = el2.allow_review_media === true && canUpload;
|
|
829
|
+
let reviewText = "";
|
|
830
|
+
const mediaUrls = [];
|
|
831
|
+
const fileToB64 = (file) => new Promise((resolve, reject) => {
|
|
832
|
+
const r = new FileReader();
|
|
833
|
+
r.onload = () => {
|
|
834
|
+
const s = String(r.result || "");
|
|
835
|
+
resolve(s.includes(",") ? s.split(",")[1] : s);
|
|
836
|
+
};
|
|
837
|
+
r.onerror = () => reject(r.error);
|
|
838
|
+
r.readAsDataURL(file);
|
|
839
|
+
});
|
|
840
|
+
const uploadOne = async (file) => {
|
|
841
|
+
if (!ctx.apiHost || !ctx.writeKey || !ctx.organizationId) return null;
|
|
842
|
+
try {
|
|
843
|
+
const data = await fileToB64(file);
|
|
844
|
+
const res = await fetch(`${ctx.apiHost}/v1/in-app/review-media`, {
|
|
845
|
+
method: "POST",
|
|
846
|
+
headers: {
|
|
847
|
+
"Content-Type": "application/json",
|
|
848
|
+
"X-Aegis-Write-Key": ctx.writeKey,
|
|
849
|
+
"X-Organization-ID": ctx.organizationId
|
|
850
|
+
},
|
|
851
|
+
body: JSON.stringify({ data, content_type: file.type })
|
|
852
|
+
});
|
|
853
|
+
if (!res.ok) return null;
|
|
854
|
+
const j = await res.json();
|
|
855
|
+
return j.url ? { url: j.url, type: j.type === "video" ? "video" : "image" } : null;
|
|
856
|
+
} catch {
|
|
857
|
+
return null;
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
const buildComposer = () => {
|
|
861
|
+
const box = document.createElement("div");
|
|
862
|
+
box.style.cssText = "display:flex;flex-direction:column;gap:8px;margin-top:2px;";
|
|
863
|
+
const toggle = document.createElement("button");
|
|
864
|
+
toggle.type = "button";
|
|
865
|
+
toggle.textContent = allowMedia ? "+ Add a written review & photos" : "+ Add a written review";
|
|
866
|
+
toggle.style.cssText = `align-self:flex-start;border:none;background:none;cursor:pointer;color:${accent};font-size:13px;font-weight:700;padding:2px 0;`;
|
|
867
|
+
const panel = document.createElement("div");
|
|
868
|
+
panel.style.cssText = "display:none;flex-direction:column;gap:8px;";
|
|
869
|
+
toggle.addEventListener("click", () => {
|
|
870
|
+
panel.style.display = "flex";
|
|
871
|
+
toggle.style.display = "none";
|
|
872
|
+
});
|
|
873
|
+
const hint = document.createElement("div");
|
|
874
|
+
hint.style.cssText = `font-size:11px;color:${fg};opacity:.65;`;
|
|
875
|
+
if (typeof el2.review_earn_hint === "string" && el2.review_earn_hint.trim()) {
|
|
876
|
+
hint.textContent = el2.review_earn_hint.trim();
|
|
877
|
+
panel.appendChild(hint);
|
|
878
|
+
} else if (ctx.apiHost && ctx.writeKey && ctx.organizationId) {
|
|
879
|
+
panel.appendChild(hint);
|
|
880
|
+
const url = `${ctx.apiHost}/v1/in-app/review-earn-hint`;
|
|
881
|
+
void fetch(url, {
|
|
882
|
+
headers: { "X-Aegis-Write-Key": ctx.writeKey, "X-Organization-ID": ctx.organizationId }
|
|
883
|
+
}).then((r) => r.ok ? r.json() : null).then((d) => {
|
|
884
|
+
if (!d || d.enabled !== true) return;
|
|
885
|
+
const name = typeof d.points_name === "string" ? d.points_name : "points";
|
|
886
|
+
const parts = [`Earn ${Number(d.base)} ${name}`];
|
|
887
|
+
if (Number(d.photo_bonus) > 0) parts.push(`+${Number(d.photo_bonus)} photo`);
|
|
888
|
+
if (Number(d.video_bonus) > 0) parts.push(`+${Number(d.video_bonus)} video`);
|
|
889
|
+
hint.textContent = parts.join(" · ");
|
|
890
|
+
}).catch(() => {
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
if (allowText) {
|
|
894
|
+
const ta = document.createElement("textarea");
|
|
895
|
+
ta.placeholder = "Tell us more (optional)";
|
|
896
|
+
ta.rows = 3;
|
|
897
|
+
ta.style.cssText = `width:100%;box-sizing:border-box;border:1px solid #e5e7eb;border-radius:10px;padding:8px 10px;font-size:13px;color:${fg};font-family:inherit;resize:vertical;`;
|
|
898
|
+
ta.addEventListener("input", () => {
|
|
899
|
+
reviewText = ta.value;
|
|
900
|
+
});
|
|
901
|
+
panel.appendChild(ta);
|
|
902
|
+
}
|
|
903
|
+
if (allowMedia) {
|
|
904
|
+
const strip = document.createElement("div");
|
|
905
|
+
strip.style.cssText = "display:flex;gap:8px;flex-wrap:wrap;align-items:center;";
|
|
906
|
+
const input = document.createElement("input");
|
|
907
|
+
input.type = "file";
|
|
908
|
+
input.accept = "image/*,video/*";
|
|
909
|
+
input.multiple = true;
|
|
910
|
+
input.style.cssText = "display:none;";
|
|
911
|
+
const addTile = document.createElement("button");
|
|
912
|
+
addTile.type = "button";
|
|
913
|
+
addTile.setAttribute("aria-label", "Add photo or video");
|
|
914
|
+
addTile.style.cssText = `width:48px;height:48px;flex-shrink:0;border:1px dashed ${accent};border-radius:10px;background:${accent}14;color:${accent};cursor:pointer;display:flex;align-items:center;justify-content:center;`;
|
|
915
|
+
const plus = lucideSvgEl("plus", { size: 22, color: accent });
|
|
916
|
+
if (plus) addTile.appendChild(plus);
|
|
917
|
+
addTile.addEventListener("click", () => input.click());
|
|
918
|
+
input.addEventListener("change", () => {
|
|
919
|
+
const files = Array.from(input.files || []);
|
|
920
|
+
input.value = "";
|
|
921
|
+
files.forEach((f) => {
|
|
922
|
+
if (mediaUrls.length >= 6) return;
|
|
923
|
+
const thumb = document.createElement("div");
|
|
924
|
+
thumb.style.cssText = "width:48px;height:48px;flex-shrink:0;border-radius:10px;background:#f3f4f6;display:flex;align-items:center;justify-content:center;overflow:hidden;font-size:11px;color:#9ca3af;";
|
|
925
|
+
thumb.textContent = "…";
|
|
926
|
+
strip.insertBefore(thumb, addTile);
|
|
927
|
+
void uploadOne(f).then((up) => {
|
|
928
|
+
if (!up) {
|
|
929
|
+
thumb.textContent = "!";
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
mediaUrls.push(up);
|
|
933
|
+
thumb.textContent = "";
|
|
934
|
+
if (up.type === "video") {
|
|
935
|
+
const vid = lucideSvgEl("play", { size: 20, color: "#6b7280" });
|
|
936
|
+
if (vid) thumb.appendChild(vid);
|
|
937
|
+
} else {
|
|
938
|
+
const img = document.createElement("img");
|
|
939
|
+
img.src = up.url;
|
|
940
|
+
img.style.cssText = "width:100%;height:100%;object-fit:cover;";
|
|
941
|
+
thumb.appendChild(img);
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
});
|
|
945
|
+
});
|
|
946
|
+
strip.appendChild(addTile);
|
|
947
|
+
strip.appendChild(input);
|
|
948
|
+
panel.appendChild(strip);
|
|
949
|
+
}
|
|
950
|
+
box.appendChild(toggle);
|
|
951
|
+
box.appendChild(panel);
|
|
952
|
+
return box;
|
|
953
|
+
};
|
|
954
|
+
const submitBtn = document.createElement("button");
|
|
955
|
+
submitBtn.type = "button";
|
|
956
|
+
submitBtn.textContent = "Submit ratings";
|
|
957
|
+
submitBtn.style.cssText = `margin-top:2px;width:100%;border:0;border-radius:999px;background:${accent};color:#fff;font-size:14px;font-weight:700;padding:10px 16px;cursor:pointer;opacity:.5;pointer-events:none;`;
|
|
958
|
+
const refreshSubmit = () => {
|
|
959
|
+
const has = Object.keys(scores).length > 0;
|
|
960
|
+
submitBtn.style.opacity = has ? "1" : ".5";
|
|
961
|
+
submitBtn.style.pointerEvents = has ? "auto" : "none";
|
|
962
|
+
};
|
|
963
|
+
const done = (branch) => {
|
|
964
|
+
var _a;
|
|
965
|
+
wrap.innerHTML = "";
|
|
966
|
+
const ack = document.createElement("div");
|
|
967
|
+
ack.style.cssText = "text-align:center;padding:8px 0;";
|
|
968
|
+
const t = document.createElement("p");
|
|
969
|
+
t.textContent = typeof el2.thanks_text === "string" && el2.thanks_text.trim() ? el2.thanks_text : "Thanks for rating your items!";
|
|
970
|
+
t.style.cssText = `margin:0;font-size:14px;font-weight:700;color:${fg};`;
|
|
971
|
+
ack.appendChild(t);
|
|
972
|
+
if (branch === "positive" && reviewsUrl) {
|
|
973
|
+
const a = document.createElement("a");
|
|
974
|
+
a.href = ctx.sanitizeUrl(reviewsUrl) || "#";
|
|
975
|
+
a.textContent = el2.rating_high_cta || "Share a review";
|
|
976
|
+
a.style.cssText = `display:inline-block;margin-top:6px;font-size:12px;font-weight:700;color:${accent};text-decoration:none;`;
|
|
977
|
+
ack.appendChild(a);
|
|
978
|
+
}
|
|
979
|
+
wrap.appendChild(ack);
|
|
980
|
+
(_a = ctx.playReaction) == null ? void 0 : _a.call(ctx, wrap, branch === "positive" ? "affirm" : "acknowledge");
|
|
981
|
+
};
|
|
982
|
+
let submitting = false;
|
|
983
|
+
submitBtn.addEventListener("click", () => {
|
|
984
|
+
var _a;
|
|
985
|
+
const rated = Object.keys(scores);
|
|
986
|
+
if (submitting || rated.length === 0) return;
|
|
987
|
+
submitting = true;
|
|
988
|
+
submitBtn.disabled = true;
|
|
989
|
+
submitBtn.style.opacity = ".6";
|
|
990
|
+
submitBtn.textContent = "Saving…";
|
|
991
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
992
|
+
meta: { element: "product_rating_list", items: rated.length }
|
|
993
|
+
});
|
|
994
|
+
Promise.resolve(
|
|
995
|
+
(_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "rating", {
|
|
996
|
+
items: rated.map((pid) => ({ product_id: pid, score: scores[pid] })),
|
|
997
|
+
order_id: orderId,
|
|
998
|
+
bill_code: billCode,
|
|
999
|
+
token,
|
|
1000
|
+
...reviewText.trim() ? { review_text: reviewText.trim() } : {},
|
|
1001
|
+
...mediaUrls.length ? { media_urls: mediaUrls } : {}
|
|
1002
|
+
})
|
|
1003
|
+
).then((res) => {
|
|
1004
|
+
var _a2;
|
|
1005
|
+
const branch = ((_a2 = res == null ? void 0 : res.rating_result) == null ? void 0 : _a2.branch) || "positive";
|
|
1006
|
+
done(branch);
|
|
1007
|
+
}).catch(() => {
|
|
1008
|
+
submitting = false;
|
|
1009
|
+
submitBtn.disabled = false;
|
|
1010
|
+
submitBtn.style.opacity = "1";
|
|
1011
|
+
submitBtn.textContent = "Submit ratings";
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
const render = (items) => {
|
|
1015
|
+
wrap.innerHTML = "";
|
|
1016
|
+
const clean = (items || []).filter((it) => it && it.product_id);
|
|
1017
|
+
if (clean.length === 0) return;
|
|
1018
|
+
const list = document.createElement("div");
|
|
1019
|
+
list.style.cssText = "display:flex;flex-direction:column;gap:10px;";
|
|
1020
|
+
clean.forEach((it) => {
|
|
1021
|
+
const rowEl = document.createElement("div");
|
|
1022
|
+
rowEl.style.cssText = "display:flex;align-items:center;gap:10px;";
|
|
1023
|
+
if (showThumb) rowEl.appendChild(ratingThumb(it, accent));
|
|
1024
|
+
const nameEl = document.createElement("span");
|
|
1025
|
+
nameEl.textContent = it.name || it.product_id;
|
|
1026
|
+
nameEl.style.cssText = `flex:1;min-width:0;font-size:13px;color:${fg};overflow:hidden;text-overflow:ellipsis;white-space:nowrap;`;
|
|
1027
|
+
rowEl.appendChild(nameEl);
|
|
1028
|
+
rowEl.appendChild(
|
|
1029
|
+
starRow(scale, (n) => {
|
|
1030
|
+
scores[it.product_id] = n;
|
|
1031
|
+
refreshSubmit();
|
|
1032
|
+
})
|
|
1033
|
+
);
|
|
1034
|
+
list.appendChild(rowEl);
|
|
1035
|
+
});
|
|
1036
|
+
wrap.appendChild(list);
|
|
1037
|
+
if (allowText || allowMedia) wrap.appendChild(buildComposer());
|
|
1038
|
+
wrap.appendChild(submitBtn);
|
|
1039
|
+
};
|
|
1040
|
+
const rawAttr = slot.getAttribute("data-rateable-items");
|
|
1041
|
+
if (rawAttr) {
|
|
1042
|
+
try {
|
|
1043
|
+
const parsed = JSON.parse(rawAttr);
|
|
1044
|
+
render(Array.isArray(parsed) ? parsed : []);
|
|
1045
|
+
} catch {
|
|
1046
|
+
}
|
|
1047
|
+
} else if (token && ctx.apiHost && ctx.writeKey) {
|
|
1048
|
+
const loader = document.createElement("div");
|
|
1049
|
+
loader.textContent = "Loading your items…";
|
|
1050
|
+
loader.style.cssText = `font-size:13px;color:${fg};opacity:.6;padding:6px 0;`;
|
|
1051
|
+
wrap.appendChild(loader);
|
|
1052
|
+
const url = `${ctx.apiHost}/v1/in-app/rateable-items?token=${encodeURIComponent(token)}`;
|
|
1053
|
+
const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
1054
|
+
if (ac)
|
|
1055
|
+
setTimeout(() => {
|
|
1056
|
+
try {
|
|
1057
|
+
ac.abort();
|
|
1058
|
+
} catch {
|
|
1059
|
+
}
|
|
1060
|
+
}, 6e3);
|
|
1061
|
+
fetch(url, { headers: { "X-Aegis-Write-Key": ctx.writeKey }, signal: ac == null ? void 0 : ac.signal }).then((r) => r.ok ? r.json() : null).then((data) => {
|
|
1062
|
+
render(Array.isArray(data == null ? void 0 : data.items) ? data.items : []);
|
|
1063
|
+
}).catch(() => {
|
|
1064
|
+
wrap.innerHTML = "";
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
return wrap;
|
|
1068
|
+
}
|
|
756
1069
|
function appendThanks(wrap, el2, fg, fallback) {
|
|
757
1070
|
const msg = typeof el2.thanks_text === "string" && el2.thanks_text.trim() ? el2.thanks_text : fallback;
|
|
758
1071
|
if (!msg) return;
|
|
@@ -796,7 +1109,9 @@ function buildPollElement(el2, ctx, fg, accent) {
|
|
|
796
1109
|
btn.style.background = accent;
|
|
797
1110
|
btn.style.color = "#fff";
|
|
798
1111
|
btn.style.opacity = "1";
|
|
799
|
-
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1112
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1113
|
+
meta: { element: "poll", option_index: idx, option_label: label }
|
|
1114
|
+
});
|
|
800
1115
|
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "poll", { option_index: idx, option_label: label }));
|
|
801
1116
|
(_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, "affirm");
|
|
802
1117
|
appendThanks(wrap, el2, fg, "Thanks for your answer!");
|
|
@@ -819,7 +1134,9 @@ function appendNpsThanks(wrap, el2, ctx, fg, accent, isPromoter) {
|
|
|
819
1134
|
cta.textContent = el2.nps_promoter_cta || "Leave a review";
|
|
820
1135
|
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;`;
|
|
821
1136
|
cta.addEventListener("click", () => {
|
|
822
|
-
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1137
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1138
|
+
meta: { element: "nps", cta: "promoter_review" }
|
|
1139
|
+
});
|
|
823
1140
|
if (ctx.navigate) ctx.navigate(url, "cta");
|
|
824
1141
|
else window.location.href = url;
|
|
825
1142
|
});
|
|
@@ -864,7 +1181,11 @@ function renderNpsReasonStep(wrap, el2, ctx, score, fg, accent) {
|
|
|
864
1181
|
done = true;
|
|
865
1182
|
const comment = withComment ? ta.value.trim() : "";
|
|
866
1183
|
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "nps", comment ? { score, band, comment } : { score, band }));
|
|
867
|
-
(_b = ctx.playReaction) == null ? void 0 : _b.call(
|
|
1184
|
+
(_b = ctx.playReaction) == null ? void 0 : _b.call(
|
|
1185
|
+
ctx,
|
|
1186
|
+
wrap,
|
|
1187
|
+
score >= promoterThreshold ? "affirm" : score <= 6 ? "empathize" : "acknowledge"
|
|
1188
|
+
);
|
|
868
1189
|
appendNpsThanks(wrap, el2, ctx, fg, accent, score >= promoterThreshold);
|
|
869
1190
|
};
|
|
870
1191
|
const btnRow = document.createElement("div");
|
|
@@ -920,7 +1241,9 @@ function buildNpsElement(el2, ctx, fg, accent, muted) {
|
|
|
920
1241
|
btn.style.color = "#fff";
|
|
921
1242
|
btn.style.opacity = "1";
|
|
922
1243
|
const npsPayload = { score: n, band: npsBandFromScore(n) };
|
|
923
|
-
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1244
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1245
|
+
meta: { element: "nps", score: n, band: npsPayload.band }
|
|
1246
|
+
});
|
|
924
1247
|
if (el2.nps_followup_enabled === true) {
|
|
925
1248
|
renderNpsReasonStep(wrap, el2, ctx, n, fg, accent);
|
|
926
1249
|
} else {
|
|
@@ -1037,13 +1360,21 @@ function buildCtaAction(el2, card, meta, inline, sanitizeUrl) {
|
|
|
1037
1360
|
const sku = meta.sku;
|
|
1038
1361
|
const platformProductId = meta.platform_product_id;
|
|
1039
1362
|
const platformUrl = safe(meta.platform_url);
|
|
1363
|
+
const platformVariantId = meta.platform_variant_id;
|
|
1040
1364
|
switch (kind) {
|
|
1041
1365
|
case "open_cart":
|
|
1042
1366
|
return { type: "open_cart" };
|
|
1043
1367
|
case "open_login":
|
|
1044
1368
|
return { type: "open_login" };
|
|
1045
1369
|
case "open_product":
|
|
1046
|
-
return {
|
|
1370
|
+
return {
|
|
1371
|
+
type: "open_product",
|
|
1372
|
+
productId,
|
|
1373
|
+
sku,
|
|
1374
|
+
url: platformUrl || url,
|
|
1375
|
+
platformProductId,
|
|
1376
|
+
platformUrl
|
|
1377
|
+
};
|
|
1047
1378
|
case "navigate":
|
|
1048
1379
|
return { type: "navigate", url: url || "" };
|
|
1049
1380
|
case "custom":
|
|
@@ -1055,6 +1386,7 @@ function buildCtaAction(el2, card, meta, inline, sanitizeUrl) {
|
|
|
1055
1386
|
productId,
|
|
1056
1387
|
sku,
|
|
1057
1388
|
platformProductId,
|
|
1389
|
+
platformVariantId,
|
|
1058
1390
|
title: card.title,
|
|
1059
1391
|
price: meta.price ?? card.body,
|
|
1060
1392
|
imageUrl: card.image_url,
|
|
@@ -1233,6 +1565,10 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1233
1565
|
container.appendChild(buildNpsElement(el2, ctx, fg, brand, muted));
|
|
1234
1566
|
break;
|
|
1235
1567
|
}
|
|
1568
|
+
case "product_rating_list": {
|
|
1569
|
+
container.appendChild(buildProductRatingListElement(el2, ctx, fg));
|
|
1570
|
+
break;
|
|
1571
|
+
}
|
|
1236
1572
|
case "group": {
|
|
1237
1573
|
const g = document.createElement("div");
|
|
1238
1574
|
const gdir = el2.layout === "row" ? "row" : "column";
|
|
@@ -1256,7 +1592,10 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1256
1592
|
var _a;
|
|
1257
1593
|
e.stopPropagation();
|
|
1258
1594
|
trackEvent(campaign.id, "clicked", { stepId: el2.id || "add" });
|
|
1259
|
-
(_a = ctx.dispatchAction) == null ? void 0 : _a.call(
|
|
1595
|
+
(_a = ctx.dispatchAction) == null ? void 0 : _a.call(
|
|
1596
|
+
ctx,
|
|
1597
|
+
buildCtaAction(el2, card, meta, inline, sanitizeUrl)
|
|
1598
|
+
);
|
|
1260
1599
|
});
|
|
1261
1600
|
(overlayOnImage ? imageWrap : container).appendChild(cta);
|
|
1262
1601
|
break;
|
|
@@ -3724,6 +4063,7 @@ class AegisChat {
|
|
|
3724
4063
|
this.pendingEcho = [];
|
|
3725
4064
|
this.hasInteracted = false;
|
|
3726
4065
|
this.writeKey = config.writeKey;
|
|
4066
|
+
this.organizationId = config.organizationId;
|
|
3727
4067
|
this.apiHost = (config.apiHost ?? "").replace(/\/$/, "");
|
|
3728
4068
|
this.contactId = config.contactId;
|
|
3729
4069
|
this.channel = config.channel ?? "web";
|
|
@@ -3913,6 +4253,7 @@ class AegisChat {
|
|
|
3913
4253
|
const url = new URL("/v1/stream/realtime", this.apiHost || window.location.origin);
|
|
3914
4254
|
url.searchParams.set("ticket", ticket);
|
|
3915
4255
|
url.searchParams.set("mode", "ephemeral");
|
|
4256
|
+
if (this.organizationId) url.searchParams.set("org", this.organizationId);
|
|
3916
4257
|
try {
|
|
3917
4258
|
this.eventSource = new EventSource(url.toString());
|
|
3918
4259
|
this.eventSource.onopen = () => {
|
|
@@ -5027,7 +5368,7 @@ function disposeActiveWebChat() {
|
|
|
5027
5368
|
activeInstance = null;
|
|
5028
5369
|
}
|
|
5029
5370
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
5030
|
-
function el
|
|
5371
|
+
function el(tag, css, text) {
|
|
5031
5372
|
const e = document.createElement(tag);
|
|
5032
5373
|
if (css) e.style.cssText = css;
|
|
5033
5374
|
if (text != null) e.textContent = text;
|
|
@@ -5048,13 +5389,13 @@ function star(filled, size) {
|
|
|
5048
5389
|
return svg;
|
|
5049
5390
|
}
|
|
5050
5391
|
function stars(rating, size = 15) {
|
|
5051
|
-
const wrap = el
|
|
5392
|
+
const wrap = el("span", "display:inline-flex;gap:2px;vertical-align:middle;");
|
|
5052
5393
|
wrap.setAttribute("aria-label", `${rating} out of 5`);
|
|
5053
5394
|
for (let i = 1; i <= 5; i++) wrap.appendChild(star(i <= rating, size));
|
|
5054
5395
|
return wrap;
|
|
5055
5396
|
}
|
|
5056
5397
|
function verifiedBadge() {
|
|
5057
|
-
const b = el
|
|
5398
|
+
const b = el(
|
|
5058
5399
|
"span",
|
|
5059
5400
|
"display:inline-flex;align-items:center;gap:3px;font-size:12px;font-weight:600;color:#059669;"
|
|
5060
5401
|
);
|
|
@@ -5071,12 +5412,33 @@ function verifiedBadge() {
|
|
|
5071
5412
|
p.setAttribute("stroke-linejoin", "round");
|
|
5072
5413
|
svg.appendChild(p);
|
|
5073
5414
|
b.appendChild(svg);
|
|
5074
|
-
b.appendChild(el
|
|
5415
|
+
b.appendChild(el("span", "", "Verified purchase"));
|
|
5416
|
+
return b;
|
|
5417
|
+
}
|
|
5418
|
+
function incentivizedBadge() {
|
|
5419
|
+
const b = el(
|
|
5420
|
+
"span",
|
|
5421
|
+
"display:inline-flex;align-items:center;gap:3px;font-size:12px;font-weight:600;color:#6b7280;"
|
|
5422
|
+
);
|
|
5423
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
5424
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
5425
|
+
svg.setAttribute("width", "13");
|
|
5426
|
+
svg.setAttribute("height", "13");
|
|
5427
|
+
const p = document.createElementNS(SVG_NS, "path");
|
|
5428
|
+
p.setAttribute("d", "M20 12v9H4v-9M2 7h20v5H2zM12 7v14M12 7a2.5 2.5 0 0 0-2.5-2.5C8 4.5 7 6 7 7M12 7a2.5 2.5 0 0 1 2.5-2.5C16 4.5 17 6 17 7");
|
|
5429
|
+
p.setAttribute("fill", "none");
|
|
5430
|
+
p.setAttribute("stroke", "#6b7280");
|
|
5431
|
+
p.setAttribute("stroke-width", "2");
|
|
5432
|
+
p.setAttribute("stroke-linecap", "round");
|
|
5433
|
+
p.setAttribute("stroke-linejoin", "round");
|
|
5434
|
+
svg.appendChild(p);
|
|
5435
|
+
b.appendChild(svg);
|
|
5436
|
+
b.appendChild(el("span", "", "Incentivized"));
|
|
5075
5437
|
return b;
|
|
5076
5438
|
}
|
|
5077
5439
|
function avatar(name, accent) {
|
|
5078
5440
|
const initial = (name.trim()[0] || "?").toUpperCase();
|
|
5079
|
-
const a = el
|
|
5441
|
+
const a = el(
|
|
5080
5442
|
"span",
|
|
5081
5443
|
`flex:0 0 auto;width:34px;height:34px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:${accent};background:${accent}1a;`,
|
|
5082
5444
|
initial
|
|
@@ -5084,7 +5446,7 @@ function avatar(name, accent) {
|
|
|
5084
5446
|
return a;
|
|
5085
5447
|
}
|
|
5086
5448
|
function thumbBtn(up, count, reviewId, creds) {
|
|
5087
|
-
const btn = el
|
|
5449
|
+
const btn = el(
|
|
5088
5450
|
"button",
|
|
5089
5451
|
"display:inline-flex;align-items:center;gap:5px;font-size:13px;color:#6b7280;background:none;border:none;cursor:pointer;padding:4px 6px;border-radius:8px;font-family:inherit;"
|
|
5090
5452
|
);
|
|
@@ -5101,7 +5463,7 @@ function thumbBtn(up, count, reviewId, creds) {
|
|
|
5101
5463
|
p.setAttribute("fill", "currentColor");
|
|
5102
5464
|
svg.appendChild(p);
|
|
5103
5465
|
btn.appendChild(svg);
|
|
5104
|
-
const c = el
|
|
5466
|
+
const c = el("span", "", String(count));
|
|
5105
5467
|
btn.appendChild(c);
|
|
5106
5468
|
const key = `aegis_rv_voted_${reviewId}`;
|
|
5107
5469
|
let voted = false;
|
|
@@ -5179,7 +5541,7 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5179
5541
|
const productId = cfg.getAttribute("data-product-id") || ctx.campaign.product_id || null;
|
|
5180
5542
|
const state = { sort: "recent", withPhotos: false };
|
|
5181
5543
|
target.innerHTML = "";
|
|
5182
|
-
target.appendChild(el
|
|
5544
|
+
target.appendChild(el("div", `font-family:${IN_APP_FONT_STACK};font-size:13px;color:#9ca3af;padding:10px 0;`, "Loading reviews…"));
|
|
5183
5545
|
function buildUrl() {
|
|
5184
5546
|
const url = new URL("/v1/reviews/public", creds.apiHost);
|
|
5185
5547
|
if (productId) url.searchParams.set("product_id", productId);
|
|
@@ -5194,20 +5556,21 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5194
5556
|
let listContainer = null;
|
|
5195
5557
|
function reviewCard(r) {
|
|
5196
5558
|
var _a, _b, _c;
|
|
5197
|
-
const li = el
|
|
5198
|
-
const head = el
|
|
5559
|
+
const li = el("li", "padding:22px 0;border-top:1px solid #f0f1f3;");
|
|
5560
|
+
const head = el("div", "display:flex;align-items:center;gap:10px;");
|
|
5199
5561
|
head.appendChild(avatar(((_a = r.reviewer_snapshot) == null ? void 0 : _a.name) ?? "Customer", accent));
|
|
5200
|
-
const idn = el
|
|
5201
|
-
const nameRow = el
|
|
5202
|
-
nameRow.appendChild(el
|
|
5562
|
+
const idn = el("div", "display:flex;flex-direction:column;gap:2px;min-width:0;");
|
|
5563
|
+
const nameRow = el("div", "display:flex;flex-wrap:wrap;align-items:center;gap:8px;");
|
|
5564
|
+
nameRow.appendChild(el("span", "font-size:15px;font-weight:700;color:#111827;", ((_b = r.reviewer_snapshot) == null ? void 0 : _b.name) ?? "Customer"));
|
|
5203
5565
|
if (r.verified_purchase) nameRow.appendChild(verifiedBadge());
|
|
5566
|
+
if (r.is_incentivized) nameRow.appendChild(incentivizedBadge());
|
|
5204
5567
|
idn.appendChild(nameRow);
|
|
5205
5568
|
idn.appendChild(stars(r.rating ?? 0, 14));
|
|
5206
5569
|
head.appendChild(idn);
|
|
5207
5570
|
if (r.created_at) {
|
|
5208
5571
|
const d = new Date(r.created_at);
|
|
5209
5572
|
if (!Number.isNaN(d.getTime())) {
|
|
5210
|
-
head.appendChild(el
|
|
5573
|
+
head.appendChild(el(
|
|
5211
5574
|
"span",
|
|
5212
5575
|
"margin-left:auto;font-size:12px;color:#9ca3af;white-space:nowrap;",
|
|
5213
5576
|
d.toLocaleDateString(void 0, { day: "numeric", month: "short", year: "numeric" })
|
|
@@ -5216,10 +5579,10 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5216
5579
|
}
|
|
5217
5580
|
li.appendChild(head);
|
|
5218
5581
|
if (r.review_text) {
|
|
5219
|
-
li.appendChild(el
|
|
5582
|
+
li.appendChild(el("p", "margin:12px 0 0;font-size:15px;line-height:1.6;color:#374151;", r.review_text));
|
|
5220
5583
|
}
|
|
5221
5584
|
if (r.media && r.media.length > 0) {
|
|
5222
|
-
const gallery = el
|
|
5585
|
+
const gallery = el("div", "display:flex;flex-wrap:wrap;gap:8px;margin-top:12px;");
|
|
5223
5586
|
for (const m2 of r.media.slice(0, 6)) {
|
|
5224
5587
|
const safe = ctx.sanitizeUrl(m2.url);
|
|
5225
5588
|
if (!safe) continue;
|
|
@@ -5232,22 +5595,22 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5232
5595
|
}
|
|
5233
5596
|
if (gallery.childElementCount) li.appendChild(gallery);
|
|
5234
5597
|
}
|
|
5235
|
-
const foot = el
|
|
5236
|
-
foot.appendChild(el
|
|
5598
|
+
const foot = el("div", "display:flex;align-items:center;gap:6px;margin-top:12px;");
|
|
5599
|
+
foot.appendChild(el("span", "font-size:13px;color:#9ca3af;margin-right:2px;", "Was this helpful?"));
|
|
5237
5600
|
foot.appendChild(thumbBtn(true, r.helpful_count ?? 0, r.id, creds));
|
|
5238
5601
|
foot.appendChild(thumbBtn(false, r.not_helpful_count ?? 0, r.id, creds));
|
|
5239
5602
|
li.appendChild(foot);
|
|
5240
5603
|
if ((_c = r.operator_reply) == null ? void 0 : _c.text) {
|
|
5241
|
-
const reply = el
|
|
5604
|
+
const reply = el(
|
|
5242
5605
|
"div",
|
|
5243
5606
|
`margin-top:14px;margin-left:44px;border-left:3px solid ${accent};background:${accent}0d;border-radius:0 12px 12px 0;padding:12px 14px;`
|
|
5244
5607
|
);
|
|
5245
|
-
const rlabel = el
|
|
5246
|
-
rlabel.appendChild(el
|
|
5247
|
-
rlabel.appendChild(el
|
|
5248
|
-
rlabel.appendChild(el
|
|
5608
|
+
const rlabel = el("div", "display:flex;align-items:center;gap:6px;margin-bottom:5px;");
|
|
5609
|
+
rlabel.appendChild(el("span", `width:7px;height:7px;border-radius:999px;background:${accent};display:inline-block;`));
|
|
5610
|
+
rlabel.appendChild(el("span", `font-size:12px;font-weight:700;color:${accent};`, brand));
|
|
5611
|
+
rlabel.appendChild(el("span", "font-size:12px;color:#9ca3af;", "· replied"));
|
|
5249
5612
|
reply.appendChild(rlabel);
|
|
5250
|
-
reply.appendChild(el
|
|
5613
|
+
reply.appendChild(el("p", "margin:0;font-size:14px;line-height:1.55;color:#4b5563;", r.operator_reply.text));
|
|
5251
5614
|
li.appendChild(reply);
|
|
5252
5615
|
}
|
|
5253
5616
|
return li;
|
|
@@ -5256,15 +5619,15 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5256
5619
|
if (!listContainer) return;
|
|
5257
5620
|
listContainer.innerHTML = "";
|
|
5258
5621
|
if (items.length === 0) {
|
|
5259
|
-
listContainer.appendChild(el
|
|
5622
|
+
listContainer.appendChild(el("p", "padding:20px 0;font-size:14px;color:#9ca3af;text-align:center;", "No reviews match this filter."));
|
|
5260
5623
|
return;
|
|
5261
5624
|
}
|
|
5262
|
-
const ul = el
|
|
5625
|
+
const ul = el("ul", "list-style:none;margin:0;padding:0;");
|
|
5263
5626
|
for (const r of items) ul.appendChild(reviewCard(r));
|
|
5264
5627
|
listContainer.appendChild(ul);
|
|
5265
5628
|
}
|
|
5266
5629
|
function pill(label, active, onClick) {
|
|
5267
|
-
const b = el
|
|
5630
|
+
const b = el(
|
|
5268
5631
|
"button",
|
|
5269
5632
|
`font-size:13px;font-weight:600;padding:7px 16px;border-radius:999px;cursor:pointer;font-family:inherit;` + (active ? `background:${accent};color:#fff;border:1px solid ${accent};` : "background:#fff;color:#374151;border:1px solid #e5e7eb;"),
|
|
5270
5633
|
label
|
|
@@ -5273,7 +5636,7 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5273
5636
|
return b;
|
|
5274
5637
|
}
|
|
5275
5638
|
function controls(rerender) {
|
|
5276
|
-
const bar = el
|
|
5639
|
+
const bar = el("div", "display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:18px 0 4px;");
|
|
5277
5640
|
const sel = document.createElement("select");
|
|
5278
5641
|
sel.style.cssText = `font-size:13px;font-weight:600;padding:7px 30px 7px 14px;border:1px solid #e5e7eb;border-radius:999px;background:#fff;color:#374151;cursor:pointer;font-family:inherit;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 12px center;`;
|
|
5279
5642
|
for (const s of SORTS) {
|
|
@@ -5300,29 +5663,29 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5300
5663
|
return bar;
|
|
5301
5664
|
}
|
|
5302
5665
|
function distributionPanel(dist, total) {
|
|
5303
|
-
const box = el
|
|
5666
|
+
const box = el("div", "flex:0 1 300px;width:300px;max-width:100%;display:flex;flex-direction:column;gap:7px;");
|
|
5304
5667
|
for (let r = 5; r >= 1; r--) {
|
|
5305
5668
|
const n = dist[String(r)] ?? 0;
|
|
5306
5669
|
const pct = total > 0 ? Math.round(n / total * 100) : 0;
|
|
5307
|
-
const row = el
|
|
5308
|
-
const lbl = el
|
|
5670
|
+
const row = el("div", "display:flex;align-items:center;gap:10px;font-size:13px;color:#6b7280;");
|
|
5671
|
+
const lbl = el("span", "width:30px;display:inline-flex;align-items:center;gap:3px;color:#374151;font-weight:600;", String(r));
|
|
5309
5672
|
lbl.appendChild(star(true, 11));
|
|
5310
5673
|
row.appendChild(lbl);
|
|
5311
|
-
const track = el
|
|
5312
|
-
track.appendChild(el
|
|
5674
|
+
const track = el("div", "flex:1;height:8px;background:#f0f1f3;border-radius:999px;overflow:hidden;");
|
|
5675
|
+
track.appendChild(el("div", `width:${pct}%;height:100%;background:${accent};border-radius:999px;`));
|
|
5313
5676
|
row.appendChild(track);
|
|
5314
|
-
row.appendChild(el
|
|
5677
|
+
row.appendChild(el("span", "width:24px;text-align:right;font-variant-numeric:tabular-nums;", String(n)));
|
|
5315
5678
|
box.appendChild(row);
|
|
5316
5679
|
}
|
|
5317
5680
|
return box;
|
|
5318
5681
|
}
|
|
5319
5682
|
function aiSummaryCard(summary) {
|
|
5320
|
-
const card = el
|
|
5683
|
+
const card = el(
|
|
5321
5684
|
"div",
|
|
5322
5685
|
"position:relative;border-radius:18px;padding:18px 20px;margin-bottom:24px;background:linear-gradient(135deg,#faf5ff 0%,#f5f3ff 100%);border:1px solid #ede9fe;"
|
|
5323
5686
|
);
|
|
5324
|
-
const head = el
|
|
5325
|
-
const left = el
|
|
5687
|
+
const head = el("div", "display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;");
|
|
5688
|
+
const left = el("div", "display:flex;align-items:center;gap:7px;");
|
|
5326
5689
|
const sp = document.createElementNS(SVG_NS, "svg");
|
|
5327
5690
|
sp.setAttribute("viewBox", "0 0 24 24");
|
|
5328
5691
|
sp.setAttribute("width", "16");
|
|
@@ -5332,17 +5695,17 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5332
5695
|
spp.setAttribute("fill", "#7c3aed");
|
|
5333
5696
|
sp.appendChild(spp);
|
|
5334
5697
|
left.appendChild(sp);
|
|
5335
|
-
left.appendChild(el
|
|
5698
|
+
left.appendChild(el("span", "font-size:12px;font-weight:800;letter-spacing:.07em;color:#6d28d9;text-transform:uppercase;", "What customers say"));
|
|
5336
5699
|
head.appendChild(left);
|
|
5337
|
-
head.appendChild(el
|
|
5700
|
+
head.appendChild(el("span", "font-size:11px;font-weight:600;color:#a78bda;", "AI-generated"));
|
|
5338
5701
|
card.appendChild(head);
|
|
5339
|
-
card.appendChild(el
|
|
5340
|
-
card.appendChild(el
|
|
5702
|
+
card.appendChild(el("p", "margin:0;font-size:15px;line-height:1.6;color:#3b2a63;", summary.text));
|
|
5703
|
+
card.appendChild(el("p", "margin:8px 0 0;font-size:12px;color:#a78bda;text-align:right;", `from ${summary.review_count} reviews`));
|
|
5341
5704
|
return card;
|
|
5342
5705
|
}
|
|
5343
5706
|
function renderWall(data) {
|
|
5344
5707
|
target.innerHTML = "";
|
|
5345
|
-
const root = el
|
|
5708
|
+
const root = el(
|
|
5346
5709
|
"section",
|
|
5347
5710
|
`font-family:${IN_APP_FONT_STACK};color:#111827;max-width:860px;margin:0 auto;-webkit-font-smoothing:antialiased;`
|
|
5348
5711
|
);
|
|
@@ -5350,25 +5713,25 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5350
5713
|
const summary = data.aggregate.ai_summary;
|
|
5351
5714
|
if (summary && summary.text) root.appendChild(aiSummaryCard(summary));
|
|
5352
5715
|
if (data.aggregate.count === 0) {
|
|
5353
|
-
root.appendChild(el
|
|
5716
|
+
root.appendChild(el("p", "font-size:15px;color:#9ca3af;padding:6px 0;", "No reviews yet — be the first to share your experience."));
|
|
5354
5717
|
target.appendChild(root);
|
|
5355
5718
|
return;
|
|
5356
5719
|
}
|
|
5357
|
-
const header = el
|
|
5358
|
-
const ratingBlock = el
|
|
5359
|
-
const topRow = el
|
|
5720
|
+
const header = el("div", "display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:24px 40px;padding-bottom:4px;");
|
|
5721
|
+
const ratingBlock = el("div", "display:flex;flex-direction:column;gap:4px;");
|
|
5722
|
+
const topRow = el("div", "display:flex;align-items:center;gap:12px;");
|
|
5360
5723
|
const avg = data.aggregate.avg_rating ?? 0;
|
|
5361
5724
|
topRow.appendChild(stars(Math.round(avg), 20));
|
|
5362
|
-
topRow.appendChild(el
|
|
5725
|
+
topRow.appendChild(el("span", "font-size:32px;font-weight:800;line-height:1;letter-spacing:-0.02em;", avg ? avg.toFixed(1) : "—"));
|
|
5363
5726
|
ratingBlock.appendChild(topRow);
|
|
5364
|
-
ratingBlock.appendChild(el
|
|
5727
|
+
ratingBlock.appendChild(el("span", "font-size:13px;color:#6b7280;", `${data.aggregate.count} review${data.aggregate.count === 1 ? "" : "s"} · 100% verified`));
|
|
5365
5728
|
header.appendChild(ratingBlock);
|
|
5366
5729
|
header.appendChild(distributionPanel(data.aggregate.distribution, data.aggregate.count));
|
|
5367
5730
|
root.appendChild(header);
|
|
5368
5731
|
root.appendChild(controls(() => {
|
|
5369
5732
|
void fetchReviews().then((d) => renderList((d == null ? void 0 : d.items) ?? []));
|
|
5370
5733
|
}));
|
|
5371
|
-
listContainer = el
|
|
5734
|
+
listContainer = el("div", "margin-top:2px;");
|
|
5372
5735
|
root.appendChild(listContainer);
|
|
5373
5736
|
renderList(data.items);
|
|
5374
5737
|
target.appendChild(root);
|
|
@@ -5380,23 +5743,23 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5380
5743
|
var _a;
|
|
5381
5744
|
target.innerHTML = "";
|
|
5382
5745
|
if (data.aggregate.count === 0) return;
|
|
5383
|
-
const root = el
|
|
5746
|
+
const root = el(
|
|
5384
5747
|
"section",
|
|
5385
5748
|
`font-family:${IN_APP_FONT_STACK};color:#111827;max-width:860px;margin:0 auto;border:1px solid #f0f1f3;border-radius:16px;padding:16px 18px;`
|
|
5386
5749
|
);
|
|
5387
|
-
const line = el
|
|
5750
|
+
const line = el("div", "display:flex;align-items:center;gap:8px;");
|
|
5388
5751
|
const avg = data.aggregate.avg_rating ?? 0;
|
|
5389
5752
|
line.appendChild(stars(Math.round(avg), 16));
|
|
5390
|
-
line.appendChild(el
|
|
5391
|
-
line.appendChild(el
|
|
5753
|
+
line.appendChild(el("span", "font-size:16px;font-weight:700;", avg ? avg.toFixed(1) : "—"));
|
|
5754
|
+
line.appendChild(el("span", "font-size:13px;color:#6b7280;", `(${data.aggregate.count})`));
|
|
5392
5755
|
line.appendChild(verifiedBadge());
|
|
5393
5756
|
root.appendChild(line);
|
|
5394
5757
|
const top = data.items.find((i) => i.review_text);
|
|
5395
5758
|
if (top && top.review_text) {
|
|
5396
|
-
root.appendChild(el
|
|
5397
|
-
root.appendChild(el
|
|
5759
|
+
root.appendChild(el("p", "margin:10px 0 0;font-size:15px;color:#374151;line-height:1.5;", `“${top.review_text}”`));
|
|
5760
|
+
root.appendChild(el("span", "font-size:13px;color:#9ca3af;", `— ${((_a = top.reviewer_snapshot) == null ? void 0 : _a.name) ?? "Customer"}`));
|
|
5398
5761
|
}
|
|
5399
|
-
const seeAll = el
|
|
5762
|
+
const seeAll = el("button", `margin-top:12px;font-size:14px;font-weight:700;color:${accent};background:none;border:none;cursor:pointer;padding:0;font-family:inherit;`, "See all reviews →");
|
|
5400
5763
|
seeAll.addEventListener("click", () => renderWall(data));
|
|
5401
5764
|
root.appendChild(seeAll);
|
|
5402
5765
|
target.appendChild(root);
|
|
@@ -5420,10 +5783,10 @@ function renderReviewsOverlay(ctx, creds) {
|
|
|
5420
5783
|
overlay.style.cssText = "position:fixed;inset:0;background:rgba(17,24,39,0.5);display:flex;align-items:flex-end;justify-content:center;z-index:99999;";
|
|
5421
5784
|
const sheet = document.createElement("div");
|
|
5422
5785
|
sheet.style.cssText = `background:#fff;color:#111827;max-width:680px;width:100%;max-height:84vh;overflow-y:auto;border-radius:24px 24px 0 0;padding:18px 18px 28px;font-family:${IN_APP_FONT_STACK};box-shadow:0 -16px 40px rgba(0,0,0,0.14);`;
|
|
5423
|
-
sheet.appendChild(el
|
|
5424
|
-
const bar = el
|
|
5425
|
-
bar.appendChild(el
|
|
5426
|
-
const close = el
|
|
5786
|
+
sheet.appendChild(el("div", "width:38px;height:4px;border-radius:999px;background:#e5e7eb;margin:0 auto 14px;"));
|
|
5787
|
+
const bar = el("div", "display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;");
|
|
5788
|
+
bar.appendChild(el("div", "font-weight:800;font-size:17px;", ctx.campaign.title || "Customer reviews"));
|
|
5789
|
+
const close = el("button", "background:none;border:none;cursor:pointer;padding:4px 8px;color:#6b7280;");
|
|
5427
5790
|
const x = document.createElementNS(SVG_NS, "svg");
|
|
5428
5791
|
x.setAttribute("viewBox", "0 0 24 24");
|
|
5429
5792
|
x.setAttribute("width", "20");
|
|
@@ -5442,7 +5805,7 @@ function renderReviewsOverlay(ctx, creds) {
|
|
|
5442
5805
|
});
|
|
5443
5806
|
bar.appendChild(close);
|
|
5444
5807
|
sheet.appendChild(bar);
|
|
5445
|
-
const body = el
|
|
5808
|
+
const body = el("div");
|
|
5446
5809
|
const pid = ctx.campaign.product_id;
|
|
5447
5810
|
if (pid) body.setAttribute("data-product-id", pid);
|
|
5448
5811
|
sheet.appendChild(body);
|
|
@@ -5456,161 +5819,6 @@ function renderReviewsOverlay(ctx, creds) {
|
|
|
5456
5819
|
document.body.appendChild(overlay);
|
|
5457
5820
|
renderReviewsInline(ctx, body, creds);
|
|
5458
5821
|
}
|
|
5459
|
-
const STAR_PATH = "M12 2L15.09 8.26 22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z";
|
|
5460
|
-
function el(tag, css, txt) {
|
|
5461
|
-
const e = document.createElement(tag);
|
|
5462
|
-
e.style.cssText = css;
|
|
5463
|
-
if (txt != null) e.textContent = txt;
|
|
5464
|
-
return e;
|
|
5465
|
-
}
|
|
5466
|
-
function starSvg(filled, accent, size = 26) {
|
|
5467
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
5468
|
-
svg.setAttribute("viewBox", "0 0 24 24");
|
|
5469
|
-
svg.setAttribute("width", String(size));
|
|
5470
|
-
svg.setAttribute("height", String(size));
|
|
5471
|
-
svg.style.cssText = "display:block;transition:transform .1s ease;";
|
|
5472
|
-
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
5473
|
-
path.setAttribute("d", STAR_PATH);
|
|
5474
|
-
path.setAttribute("fill", "#d1d5db");
|
|
5475
|
-
svg.appendChild(path);
|
|
5476
|
-
return svg;
|
|
5477
|
-
}
|
|
5478
|
-
function readContext(target) {
|
|
5479
|
-
const cfg = target.closest("[data-aegis-slot]") ?? target;
|
|
5480
|
-
let items = [];
|
|
5481
|
-
const raw = cfg.getAttribute("data-rateable-items");
|
|
5482
|
-
if (raw) {
|
|
5483
|
-
try {
|
|
5484
|
-
const parsed = JSON.parse(raw);
|
|
5485
|
-
if (Array.isArray(parsed)) {
|
|
5486
|
-
items = parsed.filter((p) => !!p && typeof p === "object" && "product_id" in p).map((p) => ({ product_id: String(p.product_id), name: p.name ?? null }));
|
|
5487
|
-
}
|
|
5488
|
-
} catch {
|
|
5489
|
-
}
|
|
5490
|
-
}
|
|
5491
|
-
return {
|
|
5492
|
-
items,
|
|
5493
|
-
orderId: cfg.getAttribute("data-order-id"),
|
|
5494
|
-
billCode: cfg.getAttribute("data-bill-code"),
|
|
5495
|
-
token: cfg.getAttribute("data-token"),
|
|
5496
|
-
accent: cfg.getAttribute("data-accent"),
|
|
5497
|
-
reviewsUrl: cfg.getAttribute("data-reviews-url"),
|
|
5498
|
-
// The bill knows its customer server-side; the SDK may still be anonymous
|
|
5499
|
-
// on a fresh visit. Prefer the slot-stamped contact so the per-item write
|
|
5500
|
-
// (contact_id NOT NULL) succeeds. On untrusted origins the server ignores
|
|
5501
|
-
// this and uses the token-resolved contact instead.
|
|
5502
|
-
contactId: cfg.getAttribute("data-contact-id")
|
|
5503
|
-
};
|
|
5504
|
-
}
|
|
5505
|
-
function renderProductRatingInline(ctx, target, creds) {
|
|
5506
|
-
if (typeof document === "undefined" || !(creds == null ? void 0 : creds.apiHost) || !(creds == null ? void 0 : creds.writeKey)) return false;
|
|
5507
|
-
ensureInAppFont();
|
|
5508
|
-
const { items, orderId, billCode, token, accent: slotAccent, reviewsUrl, contactId: slotContact } = readContext(target);
|
|
5509
|
-
if (items.length === 0 || !orderId && !billCode && !token) return false;
|
|
5510
|
-
const ic = ctx.campaign.interactive_config || {};
|
|
5511
|
-
const accent = ctx.sanitizeColor(
|
|
5512
|
-
slotAccent || ctx.campaign.background_color || "#4169e1"
|
|
5513
|
-
);
|
|
5514
|
-
const title = ic.question || "Rate your items";
|
|
5515
|
-
const scores = {};
|
|
5516
|
-
target.innerHTML = "";
|
|
5517
|
-
const card = el(
|
|
5518
|
-
"div",
|
|
5519
|
-
`font-family:${IN_APP_FONT_STACK};border:1px solid #f1f1f3;border-radius:14px;background:#fff;padding:14px 16px;`
|
|
5520
|
-
);
|
|
5521
|
-
card.appendChild(el("p", "margin:0;font-size:14px;font-weight:700;color:#111827;", title));
|
|
5522
|
-
const list = el("ul", "list-style:none;margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:10px;");
|
|
5523
|
-
for (const it of items) {
|
|
5524
|
-
const li = el("li", "display:flex;align-items:center;justify-content:space-between;gap:12px;");
|
|
5525
|
-
li.appendChild(el("span", "flex:1;min-width:0;font-size:13px;color:#374151;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;", it.name || it.product_id));
|
|
5526
|
-
const row = el("div", "display:flex;gap:2px;flex-shrink:0;");
|
|
5527
|
-
const starEls = [];
|
|
5528
|
-
for (let n = 1; n <= 5; n += 1) {
|
|
5529
|
-
const btn = el("span", "cursor:pointer;padding:1px;line-height:0;");
|
|
5530
|
-
const sv = starSvg();
|
|
5531
|
-
btn.appendChild(sv);
|
|
5532
|
-
starEls.push(sv);
|
|
5533
|
-
btn.setAttribute("role", "button");
|
|
5534
|
-
btn.setAttribute("aria-label", `Rate ${it.name || it.product_id} ${n} star${n === 1 ? "" : "s"}`);
|
|
5535
|
-
btn.addEventListener("click", () => {
|
|
5536
|
-
scores[it.product_id] = n;
|
|
5537
|
-
starEls.forEach((s, i) => {
|
|
5538
|
-
const p = s.querySelector("path");
|
|
5539
|
-
if (p) p.setAttribute("fill", i < n ? accent : "#d1d5db");
|
|
5540
|
-
});
|
|
5541
|
-
submitBtn.style.opacity = "1";
|
|
5542
|
-
submitBtn.style.pointerEvents = "auto";
|
|
5543
|
-
});
|
|
5544
|
-
row.appendChild(btn);
|
|
5545
|
-
}
|
|
5546
|
-
li.appendChild(row);
|
|
5547
|
-
list.appendChild(li);
|
|
5548
|
-
}
|
|
5549
|
-
card.appendChild(list);
|
|
5550
|
-
const submitBtn = el(
|
|
5551
|
-
"button",
|
|
5552
|
-
`margin-top:14px;width:100%;border:0;border-radius:999px;background:${accent};color:#fff;font-size:14px;font-weight:700;padding:10px 16px;cursor:pointer;opacity:.5;pointer-events:none;font-family:inherit;`,
|
|
5553
|
-
"Submit ratings"
|
|
5554
|
-
);
|
|
5555
|
-
card.appendChild(submitBtn);
|
|
5556
|
-
const done = (branch) => {
|
|
5557
|
-
target.innerHTML = "";
|
|
5558
|
-
const ack = el(
|
|
5559
|
-
"div",
|
|
5560
|
-
`font-family:${IN_APP_FONT_STACK};border:1px solid #f1f1f3;border-radius:14px;background:#fff;padding:18px 16px;text-align:center;`
|
|
5561
|
-
);
|
|
5562
|
-
ack.appendChild(el("p", "margin:0;font-size:14px;font-weight:700;color:#111827;", "Thanks for rating your items!"));
|
|
5563
|
-
const positiveUrl = reviewsUrl || ic.rating_high_url || null;
|
|
5564
|
-
if (branch === "positive" && positiveUrl) {
|
|
5565
|
-
const a = document.createElement("a");
|
|
5566
|
-
a.href = positiveUrl;
|
|
5567
|
-
a.textContent = ic.rating_high_cta || "Share a review";
|
|
5568
|
-
a.style.cssText = `display:inline-block;margin-top:8px;font-size:12px;font-weight:700;color:${accent};text-decoration:none;`;
|
|
5569
|
-
ack.appendChild(a);
|
|
5570
|
-
} else {
|
|
5571
|
-
ack.appendChild(el("p", "margin:6px 0 0;font-size:12px;color:#9ca3af;", "We'll use your feedback to improve."));
|
|
5572
|
-
}
|
|
5573
|
-
target.appendChild(ack);
|
|
5574
|
-
};
|
|
5575
|
-
submitBtn.addEventListener("click", () => {
|
|
5576
|
-
const rated = Object.keys(scores);
|
|
5577
|
-
if (rated.length === 0) return;
|
|
5578
|
-
submitBtn.disabled = true;
|
|
5579
|
-
submitBtn.style.opacity = ".6";
|
|
5580
|
-
submitBtn.textContent = "Saving…";
|
|
5581
|
-
const payload = {
|
|
5582
|
-
items: rated.map((pid) => ({ product_id: pid, score: scores[pid] })),
|
|
5583
|
-
order_id: orderId,
|
|
5584
|
-
bill_code: billCode,
|
|
5585
|
-
token
|
|
5586
|
-
};
|
|
5587
|
-
fetch(new URL("/v1/in-app/responses", creds.apiHost).toString(), {
|
|
5588
|
-
method: "POST",
|
|
5589
|
-
headers: {
|
|
5590
|
-
"Content-Type": "application/json",
|
|
5591
|
-
"X-Aegis-Write-Key": creds.writeKey,
|
|
5592
|
-
"X-Organization-ID": creds.organizationId
|
|
5593
|
-
},
|
|
5594
|
-
body: JSON.stringify({
|
|
5595
|
-
campaign_id: ctx.campaign.id,
|
|
5596
|
-
response_type: "rating",
|
|
5597
|
-
contact_id: slotContact || creds.contactId,
|
|
5598
|
-
platform: "web",
|
|
5599
|
-
payload
|
|
5600
|
-
})
|
|
5601
|
-
}).then((r) => r.ok ? r.json() : null).then((data) => {
|
|
5602
|
-
var _a;
|
|
5603
|
-
ctx.trackEvent(ctx.campaign.id, "clicked");
|
|
5604
|
-
done(((_a = data == null ? void 0 : data.rating_result) == null ? void 0 : _a.branch) || "positive");
|
|
5605
|
-
}).catch(() => {
|
|
5606
|
-
submitBtn.disabled = false;
|
|
5607
|
-
submitBtn.style.opacity = "1";
|
|
5608
|
-
submitBtn.textContent = "Submit ratings";
|
|
5609
|
-
});
|
|
5610
|
-
});
|
|
5611
|
-
target.appendChild(card);
|
|
5612
|
-
return true;
|
|
5613
|
-
}
|
|
5614
5822
|
function readAnonIdFromStorage$1() {
|
|
5615
5823
|
if (typeof document === "undefined") return void 0;
|
|
5616
5824
|
try {
|
|
@@ -5987,6 +6195,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
5987
6195
|
return;
|
|
5988
6196
|
}
|
|
5989
6197
|
const url = new URL("/v1/stream/realtime", this.apiHost);
|
|
6198
|
+
url.searchParams.set("org", this.organizationId);
|
|
5990
6199
|
const headers = {
|
|
5991
6200
|
"X-Aegis-Write-Key": this.writeKey,
|
|
5992
6201
|
"X-Organization-ID": this.organizationId
|
|
@@ -5994,11 +6203,10 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
5994
6203
|
if (this.contactId) {
|
|
5995
6204
|
headers["X-Contact-ID"] = this.contactId;
|
|
5996
6205
|
}
|
|
5997
|
-
const queryParams = new URLSearchParams();
|
|
5998
6206
|
Object.entries(headers).forEach(([key, value]) => {
|
|
5999
|
-
|
|
6207
|
+
url.searchParams.append(key, value);
|
|
6000
6208
|
});
|
|
6001
|
-
this.eventSource = new EventSource(
|
|
6209
|
+
this.eventSource = new EventSource(url.toString());
|
|
6002
6210
|
this.eventSource.addEventListener("open", () => {
|
|
6003
6211
|
this.log("SSE connection established");
|
|
6004
6212
|
this.reconnectAttempts = 0;
|
|
@@ -6161,7 +6369,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6161
6369
|
}
|
|
6162
6370
|
tryDisplayNextCampaign() {
|
|
6163
6371
|
const campaign = this.campaigns.find(
|
|
6164
|
-
(c) => !this.displayedCampaigns.has(c.id) && !c.client_trigger && !this.isSuppressed(c.id) && c.type !== "active_web_chat"
|
|
6372
|
+
(c) => !this.displayedCampaigns.has(c.id) && !c.client_trigger && !this.isSuppressed(c.id) && c.type !== "active_web_chat" && !(Array.isArray(c.delivery_modes) && c.delivery_modes.length > 0 && !c.delivery_modes.includes("in_app_overlay"))
|
|
6165
6373
|
);
|
|
6166
6374
|
if (campaign) {
|
|
6167
6375
|
this.displayCampaign(campaign);
|
|
@@ -6194,34 +6402,38 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6194
6402
|
* `<div data-aegis-slot="feedback">` will accept the first active
|
|
6195
6403
|
* campaign with `widget_category='feedback'` + embedded_card mode.
|
|
6196
6404
|
*/
|
|
6197
|
-
/**
|
|
6198
|
-
*
|
|
6199
|
-
*
|
|
6200
|
-
*
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
matchesCurrentSurface(c) {
|
|
6206
|
-
if (!c.surface || c.surface.length === 0) return true;
|
|
6207
|
-
if (this.currentSurface === null) return true;
|
|
6208
|
-
return c.surface.includes(this.currentSurface);
|
|
6405
|
+
/** Normalize a declared free-form screen name to a canonical surface key
|
|
6406
|
+
* ('storefront.catalog' → 'storefront_catalog'), or null when it maps
|
|
6407
|
+
* outside the enum (e.g. 'shopify.product'). Mirrors the server's
|
|
6408
|
+
* `_canonical_surface_from_screen`. */
|
|
6409
|
+
canonicalSurfaceFromScreen() {
|
|
6410
|
+
if (!this.currentScreen) return null;
|
|
6411
|
+
const norm = this.currentScreen.trim().toLowerCase().replace(/[.\- ]/g, "_");
|
|
6412
|
+
return _AegisInAppManager.KNOWN_SURFACES.has(norm) ? norm : null;
|
|
6209
6413
|
}
|
|
6210
6414
|
/**
|
|
6211
|
-
* Dynamic Screens P2 — combined surface + free-form
|
|
6212
|
-
*
|
|
6213
|
-
*
|
|
6214
|
-
*
|
|
6215
|
-
*
|
|
6415
|
+
* Dynamic Screens P2 + substrate isolation — combined surface + free-form
|
|
6416
|
+
* screen predicate, mirroring the server's `_screen_or_surface_matches`:
|
|
6417
|
+
* - screen-only campaign matches strictly on `currentScreen`
|
|
6418
|
+
* - a declared screen NORMALIZES into the canonical tier
|
|
6419
|
+
* ('storefront.catalog' → storefront_catalog) so surface pins match
|
|
6420
|
+
* - a declared screen that maps OUTSIDE the enum (e.g. 'shopify.product')
|
|
6421
|
+
* does NOT fall through to the all-surfaces default for surface-pinned
|
|
6422
|
+
* campaigns — actii.me-pinned campaigns never render on Shopify
|
|
6423
|
+
* - hosts that declare nothing keep the legacy render-everything default
|
|
6216
6424
|
*/
|
|
6217
6425
|
matchesCurrentScreenOrSurface(c) {
|
|
6218
6426
|
const screens = c.target_screens;
|
|
6219
|
-
|
|
6220
|
-
|
|
6427
|
+
const screenOk = !!screens && screens.length > 0 && this.currentScreen !== null && screens.includes(this.currentScreen);
|
|
6428
|
+
if (screens && screens.length > 0 && (!c.surface || c.surface.length === 0)) {
|
|
6429
|
+
return screenOk;
|
|
6221
6430
|
}
|
|
6222
|
-
|
|
6223
|
-
if (!c.surface || c.surface.length === 0) return
|
|
6224
|
-
|
|
6431
|
+
if (screenOk) return true;
|
|
6432
|
+
if (!c.surface || c.surface.length === 0) return true;
|
|
6433
|
+
const effective = this.currentSurface ?? this.canonicalSurfaceFromScreen();
|
|
6434
|
+
if (effective !== null) return c.surface.includes(effective);
|
|
6435
|
+
if (this.currentScreen !== null) return false;
|
|
6436
|
+
return true;
|
|
6225
6437
|
}
|
|
6226
6438
|
// Re-fill slots that mount after a poll (SPA route changes). Observes the DOM
|
|
6227
6439
|
// once; when a new [data-aegis-slot] appears it re-runs renderIntoSlots from
|
|
@@ -6257,6 +6469,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6257
6469
|
this.slotObserver.observe(document.body, { childList: true, subtree: true });
|
|
6258
6470
|
}
|
|
6259
6471
|
renderIntoSlots() {
|
|
6472
|
+
var _a, _b;
|
|
6260
6473
|
if (typeof document === "undefined") return;
|
|
6261
6474
|
this.ensureSlotObserver();
|
|
6262
6475
|
const slots = document.querySelectorAll("[data-aegis-slot]");
|
|
@@ -6269,6 +6482,16 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6269
6482
|
const category = c.widget_category;
|
|
6270
6483
|
if (!modes || !modes.includes("embedded_card")) continue;
|
|
6271
6484
|
if (!category) continue;
|
|
6485
|
+
const anchorSel = (_b = (_a = c.interactive_config) == null ? void 0 : _a.anchor) == null ? void 0 : _b.element_selector;
|
|
6486
|
+
if (typeof anchorSel === "string" && anchorSel.trim()) {
|
|
6487
|
+
let anchorResolves = false;
|
|
6488
|
+
try {
|
|
6489
|
+
anchorResolves = !!document.querySelector(anchorSel);
|
|
6490
|
+
} catch {
|
|
6491
|
+
anchorResolves = false;
|
|
6492
|
+
}
|
|
6493
|
+
if (anchorResolves) continue;
|
|
6494
|
+
}
|
|
6272
6495
|
if (seenIds.has(c.id)) continue;
|
|
6273
6496
|
seenIds.add(c.id);
|
|
6274
6497
|
const arr2 = eligibleByCategory.get(category);
|
|
@@ -6295,8 +6518,8 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6295
6518
|
slot.querySelectorAll(":scope > [data-aegis-slot-default]").forEach((d) => d.remove());
|
|
6296
6519
|
const attrMs = parseInt(slot.getAttribute("data-aegis-slot-rotate") || "0", 10);
|
|
6297
6520
|
const declaredMs = list.reduce((m2, c) => {
|
|
6298
|
-
var
|
|
6299
|
-
const v = Number((
|
|
6521
|
+
var _a2;
|
|
6522
|
+
const v = Number((_a2 = c.interactive_config) == null ? void 0 : _a2.slot_rotate_ms);
|
|
6300
6523
|
return Number.isFinite(v) && v > m2 ? v : m2;
|
|
6301
6524
|
}, 0);
|
|
6302
6525
|
const rotateMs = attrMs > 0 ? attrMs : declaredMs;
|
|
@@ -6582,23 +6805,6 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6582
6805
|
}
|
|
6583
6806
|
return;
|
|
6584
6807
|
}
|
|
6585
|
-
if (campaign.sub_type === "star_rating" && this._isPerItemRating(campaign)) {
|
|
6586
|
-
const ok = renderProductRatingInline(this.buildRenderContext(campaign), target, {
|
|
6587
|
-
apiHost: this.apiHost,
|
|
6588
|
-
writeKey: this.writeKey,
|
|
6589
|
-
organizationId: this.organizationId ?? "",
|
|
6590
|
-
contactId: this.contactId ?? null
|
|
6591
|
-
});
|
|
6592
|
-
if (!ok) {
|
|
6593
|
-
this.displayedCampaigns.delete(campaign.id);
|
|
6594
|
-
return;
|
|
6595
|
-
}
|
|
6596
|
-
if (!(options == null ? void 0 : options.deferImpression)) {
|
|
6597
|
-
this.trackEvent(campaign.id, "impression");
|
|
6598
|
-
this.emit("campaign-shown", campaign);
|
|
6599
|
-
}
|
|
6600
|
-
return;
|
|
6601
|
-
}
|
|
6602
6808
|
this._ensureSurveyTemplate(campaign);
|
|
6603
6809
|
const _isMultiCard = campaign.sub_type === "product_recommendation" || campaign.sub_type === "carousel_cards";
|
|
6604
6810
|
if (this._hasComposedTemplate(ic) && !_isMultiCard) {
|
|
@@ -6679,14 +6885,6 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6679
6885
|
const tpl = ic.card_template;
|
|
6680
6886
|
return !!tpl && typeof tpl === "object" && Array.isArray(tpl.elements) && tpl.elements.length > 0;
|
|
6681
6887
|
}
|
|
6682
|
-
/** G5 — whether a `star_rating` campaign captures PER-SKU ratings (one row per
|
|
6683
|
-
* order line-item) vs a single order-level score. Opt-in via
|
|
6684
|
-
* `interactive_config.rating_mode='per_item'`, so the order-level star path is
|
|
6685
|
-
* untouched. The renderer reads the SKUs from the slot at mount. */
|
|
6686
|
-
_isPerItemRating(campaign) {
|
|
6687
|
-
const ic = campaign.interactive_config || {};
|
|
6688
|
-
return ic.rating_mode === "per_item";
|
|
6689
|
-
}
|
|
6690
6888
|
/**
|
|
6691
6889
|
* P6.full — flat survey → card_template adapter. A `nps_survey` / `star_rating`
|
|
6692
6890
|
* / `quick_poll` authored WITHOUT a card_template gets one synthesised in place
|
|
@@ -6756,9 +6954,11 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6756
6954
|
};
|
|
6757
6955
|
const template = resolveGlobalTemplate(ic);
|
|
6758
6956
|
const { accent } = this._surfacePalette(campaign);
|
|
6957
|
+
const ctx = this.buildRenderContext(campaign);
|
|
6958
|
+
ctx.slotEl = target.closest("[data-aegis-slot]") ?? target;
|
|
6759
6959
|
const tile = renderCardFromTemplate(card, {
|
|
6760
6960
|
template,
|
|
6761
|
-
ctx
|
|
6961
|
+
ctx,
|
|
6762
6962
|
action: "navigate",
|
|
6763
6963
|
layoutAccent: this.sanitizeColor(campaign.background_color || accent)
|
|
6764
6964
|
});
|
|
@@ -8604,7 +8804,10 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
8604
8804
|
},
|
|
8605
8805
|
sanitizeColor: (color) => this.sanitizeColor(color),
|
|
8606
8806
|
log: (msg, level) => this.log(msg, level),
|
|
8607
|
-
addAnimationStyles: () => this.addAnimationStyles()
|
|
8807
|
+
addAnimationStyles: () => this.addAnimationStyles(),
|
|
8808
|
+
apiHost: this.apiHost,
|
|
8809
|
+
writeKey: this.writeKey,
|
|
8810
|
+
organizationId: this.organizationId
|
|
8608
8811
|
};
|
|
8609
8812
|
}
|
|
8610
8813
|
/**
|
|
@@ -9009,6 +9212,12 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9009
9212
|
* so journeys can react. Degrades gracefully where Notification is absent.
|
|
9010
9213
|
*/
|
|
9011
9214
|
renderPushPrimer(campaign, ic, bg, text) {
|
|
9215
|
+
if (typeof Notification !== "undefined" && Notification.permission !== "default") {
|
|
9216
|
+
void this.trackEvent(campaign.id, "dismissed", {
|
|
9217
|
+
meta: { reason: `permission_${Notification.permission}` }
|
|
9218
|
+
});
|
|
9219
|
+
return;
|
|
9220
|
+
}
|
|
9012
9221
|
const overlay = this.createOverlay("aegis-in-app-push-primer-overlay");
|
|
9013
9222
|
const modal = document.createElement("div");
|
|
9014
9223
|
modal.style.cssText = `
|
|
@@ -9019,8 +9228,13 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9019
9228
|
const body = document.createElement("div");
|
|
9020
9229
|
body.style.cssText = "padding: 24px; text-align: center;";
|
|
9021
9230
|
const icon = document.createElement("div");
|
|
9022
|
-
icon.style.cssText = "
|
|
9023
|
-
|
|
9231
|
+
icon.style.cssText = "margin-bottom: 8px; display: flex; justify-content: center;";
|
|
9232
|
+
if (typeof ic.icon === "string" && ic.icon.length > 0) {
|
|
9233
|
+
icon.textContent = ic.icon;
|
|
9234
|
+
icon.style.fontSize = "36px";
|
|
9235
|
+
} else {
|
|
9236
|
+
icon.innerHTML = lucideSvg("bell", { size: 36, color: text });
|
|
9237
|
+
}
|
|
9024
9238
|
body.appendChild(icon);
|
|
9025
9239
|
const title = document.createElement("div");
|
|
9026
9240
|
title.style.cssText = "font-size: 18px; font-weight: 700; margin-bottom: 8px;";
|
|
@@ -9040,7 +9254,8 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9040
9254
|
if (overlay.parentNode) document.body.removeChild(overlay);
|
|
9041
9255
|
return;
|
|
9042
9256
|
}
|
|
9043
|
-
|
|
9257
|
+
enableBtn.disabled = true;
|
|
9258
|
+
void this._subscribeFromPushPrimer().then((perm) => {
|
|
9044
9259
|
void this.trackEvent(campaign.id, "clicked", { meta: { permission: perm } });
|
|
9045
9260
|
if (overlay.parentNode) document.body.removeChild(overlay);
|
|
9046
9261
|
});
|
|
@@ -9059,6 +9274,90 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9059
9274
|
this.addAnimationStyles();
|
|
9060
9275
|
document.body.appendChild(overlay);
|
|
9061
9276
|
}
|
|
9277
|
+
/**
|
|
9278
|
+
* Push-primer Enable handler — runs the SAME end-to-end subscribe flow the
|
|
9279
|
+
* (now-retired) ACS PushOptInBanner did, so the primer is a complete web-push
|
|
9280
|
+
* opt-in on every surface (ACS / Shopify / 3rd-party site), not just an OS
|
|
9281
|
+
* permission prompt. Steps: fetch VAPID public key → register SW → hard-ask →
|
|
9282
|
+
* subscribe → POST /v1/web_push/subscriptions → stitch known contact.
|
|
9283
|
+
*
|
|
9284
|
+
* Push runtime resolution prefers the already-loaded `window.AegisWebPush`
|
|
9285
|
+
* global (the Shopify embed lazy-loads it via /apps/active_reach/push.js, so
|
|
9286
|
+
* we reuse that single instance instead of registering the SW twice), and
|
|
9287
|
+
* only dynamic-imports the module as a fallback for hosts that didn't preload
|
|
9288
|
+
* it (ACS / 3rd-party sites). Returns the resolved permission string for
|
|
9289
|
+
* engagement reporting; falls back to a bare permission ask if push can't be
|
|
9290
|
+
* wired (no propertyId, no VAPID).
|
|
9291
|
+
*/
|
|
9292
|
+
async _subscribeFromPushPrimer() {
|
|
9293
|
+
var _a, _b, _c, _d;
|
|
9294
|
+
if (!this.propertyId) {
|
|
9295
|
+
return Notification.requestPermission();
|
|
9296
|
+
}
|
|
9297
|
+
try {
|
|
9298
|
+
const cfgRes = await fetch(`${this.apiHost}/v1/sdk/config`, {
|
|
9299
|
+
headers: { "X-Aegis-Write-Key": this.writeKey }
|
|
9300
|
+
});
|
|
9301
|
+
const cfg = await cfgRes.json();
|
|
9302
|
+
const vapidPublicKey = ((_a = cfg.push) == null ? void 0 : _a.vapid_public_key) || "";
|
|
9303
|
+
if (!((_b = cfg.push) == null ? void 0 : _b.enabled) || !vapidPublicKey) {
|
|
9304
|
+
return Notification.requestPermission();
|
|
9305
|
+
}
|
|
9306
|
+
const serviceWorkerPath = ((_c = cfg.push) == null ? void 0 : _c.service_worker_path) || "/aegis-sw.js";
|
|
9307
|
+
const serviceWorkerScope = ((_d = cfg.push) == null ? void 0 : _d.service_worker_scope) || "/";
|
|
9308
|
+
if (this.contactId && typeof window !== "undefined") {
|
|
9309
|
+
try {
|
|
9310
|
+
window.localStorage.setItem("aegis_contact_id", this.contactId);
|
|
9311
|
+
} catch {
|
|
9312
|
+
}
|
|
9313
|
+
}
|
|
9314
|
+
const writeKey = this.writeKey;
|
|
9315
|
+
const apiHost = this.apiHost;
|
|
9316
|
+
const organizationId = this.organizationId;
|
|
9317
|
+
const apiClient = {
|
|
9318
|
+
async post(endpoint, payload, headers) {
|
|
9319
|
+
const res = await fetch(`${apiHost}${endpoint}`, {
|
|
9320
|
+
method: "POST",
|
|
9321
|
+
headers: {
|
|
9322
|
+
"Content-Type": "application/json",
|
|
9323
|
+
"X-Aegis-Write-Key": writeKey,
|
|
9324
|
+
...organizationId ? { "X-Organization-ID": organizationId } : {},
|
|
9325
|
+
...headers ?? {}
|
|
9326
|
+
},
|
|
9327
|
+
body: JSON.stringify(payload)
|
|
9328
|
+
});
|
|
9329
|
+
if (!res.ok) throw new Error(`push api ${endpoint} → ${res.status}`);
|
|
9330
|
+
return res.json().catch(() => ({}));
|
|
9331
|
+
}
|
|
9332
|
+
};
|
|
9333
|
+
const AegisWebPush2 = typeof window !== "undefined" && window.AegisWebPush || (await import("./push/AegisWebPush.js")).AegisWebPush;
|
|
9334
|
+
const webPush = new AegisWebPush2(apiClient, {
|
|
9335
|
+
vapidPublicKey,
|
|
9336
|
+
propertyId: this.propertyId,
|
|
9337
|
+
// Anon id doubles as the device-fingerprint salt so a re-subscribe
|
|
9338
|
+
// after VAPID rotation upserts onto the same subscription row.
|
|
9339
|
+
firstPartyCookieId: readAnonIdFromStorage$1() || this.contactId || this.organizationId || "",
|
|
9340
|
+
serviceWorkerPath,
|
|
9341
|
+
serviceWorkerScope,
|
|
9342
|
+
autoPrompt: false,
|
|
9343
|
+
writeKey,
|
|
9344
|
+
organizationId
|
|
9345
|
+
});
|
|
9346
|
+
await webPush.initialize();
|
|
9347
|
+
const granted = await webPush.requestPermission();
|
|
9348
|
+
if (granted && this.contactId) {
|
|
9349
|
+
await webPush.identify({ contactId: this.contactId });
|
|
9350
|
+
}
|
|
9351
|
+
return Notification.permission;
|
|
9352
|
+
} catch (err) {
|
|
9353
|
+
console.warn("[Aegis in-app] push-primer subscribe failed:", err);
|
|
9354
|
+
try {
|
|
9355
|
+
return await Notification.requestPermission();
|
|
9356
|
+
} catch {
|
|
9357
|
+
return Notification.permission;
|
|
9358
|
+
}
|
|
9359
|
+
}
|
|
9360
|
+
}
|
|
9062
9361
|
renderQuiz(campaign, ic, bg, text) {
|
|
9063
9362
|
const overlay = this.createOverlay("aegis-in-app-quiz-overlay");
|
|
9064
9363
|
const modal = document.createElement("div");
|
|
@@ -11088,7 +11387,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
11088
11387
|
}
|
|
11089
11388
|
}
|
|
11090
11389
|
try {
|
|
11091
|
-
const url = `${this.apiHost}/v1/
|
|
11390
|
+
const url = `${this.apiHost}/v1/in-app/events`;
|
|
11092
11391
|
const externalEventId = `inapp_${campaignId}_${eventType}_${Date.now()}`;
|
|
11093
11392
|
const headers = {
|
|
11094
11393
|
"Content-Type": "application/json",
|
|
@@ -11122,12 +11421,16 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
11122
11421
|
// journey/template/outlet on the engagement delivery_event.
|
|
11123
11422
|
journey_execution_id: trackedCampaign == null ? void 0 : trackedCampaign.journey_execution_id,
|
|
11124
11423
|
lifecycle_fork_id: trackedCampaign == null ? void 0 : trackedCampaign.lifecycle_fork_id,
|
|
11125
|
-
|
|
11424
|
+
// Outlet the customer is ACTIVELY viewing (setLocation) wins — that's
|
|
11425
|
+
// where the impression/click actually happened; fall back to the served
|
|
11426
|
+
// campaign's outlet, else brand-tier. The gateway promotes this to the
|
|
11427
|
+
// canonical event's top-level for per-outlet in-app analytics.
|
|
11428
|
+
location_id: this.currentLocationId ?? (trackedCampaign == null ? void 0 : trackedCampaign.location_id),
|
|
11126
11429
|
metadata: {
|
|
11127
11430
|
property_id: this.propertyId,
|
|
11128
11431
|
variant_id: this.getVariantId(campaignId) ?? void 0,
|
|
11129
11432
|
lifecycle_fork_id: trackedCampaign == null ? void 0 : trackedCampaign.lifecycle_fork_id,
|
|
11130
|
-
location_id: trackedCampaign == null ? void 0 : trackedCampaign.location_id,
|
|
11433
|
+
location_id: this.currentLocationId ?? (trackedCampaign == null ? void 0 : trackedCampaign.location_id),
|
|
11131
11434
|
// Renderer-supplied metadata (e.g. anchor_resolved/anchor_selector on
|
|
11132
11435
|
// an anchor_unresolved event). The gateway spreads payload.metadata
|
|
11133
11436
|
// into the canonical event so these land in in_app_events.metadata.
|
|
@@ -11190,6 +11493,7 @@ _AegisInAppManager.KNOWN_SURFACES = /* @__PURE__ */ new Set([
|
|
|
11190
11493
|
"storefront_feedback",
|
|
11191
11494
|
"storefront_reviews",
|
|
11192
11495
|
"storefront_members",
|
|
11496
|
+
"storefront_orders",
|
|
11193
11497
|
"external_website",
|
|
11194
11498
|
"external_mobile_screen"
|
|
11195
11499
|
]);
|
|
@@ -14417,7 +14721,7 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
14417
14721
|
var _a;
|
|
14418
14722
|
this.initialized = false;
|
|
14419
14723
|
{
|
|
14420
|
-
const v = "1.35.
|
|
14724
|
+
const v = "1.35.4";
|
|
14421
14725
|
try {
|
|
14422
14726
|
globalThis.__AEGIS_SDK_VERSION__ = v;
|
|
14423
14727
|
} catch {
|