@active-reach/web-sdk 1.9.0 → 1.10.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.
@@ -47,6 +47,9 @@ export interface InAppCampaign {
47
47
  config?: Record<string, unknown>;
48
48
  };
49
49
  assigned_variant_id?: string;
50
+ delivery_modes?: Array<'in_app_overlay' | 'embedded_card' | 'standalone_page'>;
51
+ widget_category?: 'feedback' | 'loyalty' | 'promo' | 'commerce' | 'support' | 'custom';
52
+ page_key?: 'bill' | 'rewards' | 'feedback' | 'reviews' | 'members';
50
53
  }
51
54
  /**
52
55
  * Payload for `campaign-click` lifecycle event. CANCELLABLE — return `false`
@@ -231,6 +234,72 @@ export declare class AegisInAppManager {
231
234
  private processABAssignments;
232
235
  private getVariantId;
233
236
  private tryDisplayNextCampaign;
237
+ private filledSlots;
238
+ /**
239
+ * Scan the page for `[data-aegis-slot]` anchors and render any
240
+ * campaign whose `widget_category` matches the slot key AND whose
241
+ * `delivery_modes` includes `'embedded_card'`.
242
+ *
243
+ * Runs after every refresh-campaigns success — the WeakSet guard
244
+ * keeps it idempotent across re-fetches. The slot key is the campaign's
245
+ * `widget_category` (the wide grouping discriminator from F3), so a
246
+ * `<div data-aegis-slot="feedback">` will accept the first active
247
+ * campaign with `widget_category='feedback'` + embedded_card mode.
248
+ */
249
+ private renderIntoSlots;
250
+ /**
251
+ * U7 — render server-injected token-bound campaigns.
252
+ *
253
+ * The standalone-page route at `apps/cashier-portal/src/app/s/[slug]/
254
+ * [workspace]/[page_key]/page.tsx` (and the `/c/[token]` Aegis-hosted
255
+ * fallback) verify the URL token server-side via the F16 internal
256
+ * endpoint, then embed the resolved campaign payload as JSON inside
257
+ * a `<div data-aegis-token-data="...">` anchor. This SDK method
258
+ * scans for those anchors, parses the payload, and dispatches to
259
+ * the SAME `renderCampaignIntoSlot` used by the [data-aegis-slot]
260
+ * path — so widget rendering code is single-sourced.
261
+ *
262
+ * Why server-injected (not client-fetched): keeps INTERNAL_API_SECRET
263
+ * server-side, avoids an extra browser round-trip for verify, gives
264
+ * Next.js SSR-friendly initial HTML for SEO/no-JS users (rendered
265
+ * via the page handler's React tree alongside the SDK anchor).
266
+ *
267
+ * Idempotency: filledSlots is the same WeakSet used by
268
+ * renderIntoSlots — re-running on SSE/poll refresh is a no-op for
269
+ * already-filled anchors.
270
+ */
271
+ private renderTokenAnchors;
272
+ /**
273
+ * Slot-mode counterpart to `displayCampaign`. Reuses the existing
274
+ * lifecycle hooks (`campaign-will-show`, `displayedCampaigns`,
275
+ * `campaign-shown`, impression tracking) so analytics + frequency
276
+ * caps work the same way as overlay renders.
277
+ *
278
+ * Only the sub_types that make UX sense embedded are handled; the
279
+ * rest (modal, full_screen, half_interstitial, alert, pip) silently
280
+ * skip — those types only make sense as fullscreen overlays.
281
+ */
282
+ private renderCampaignIntoSlot;
283
+ /**
284
+ * Slot-mode wrappers. They build a small card container (no overlay
285
+ * positioning, no close button) and append the SHARED body element
286
+ * the overlay path also uses. Body construction lives in the
287
+ * `_buildXxxBody` helpers below so visual output stays identical
288
+ * between overlay and slot modes — adding a renderer feature in
289
+ * one place updates both.
290
+ */
291
+ private renderStarRatingSlot;
292
+ private renderNPSSurveySlot;
293
+ /**
294
+ * Token-bound submission helper. POSTs the structured response to
295
+ * the per-token submit URL the page handler embedded. Best-effort
296
+ * — failure is logged but doesn't throw (the customer already
297
+ * clicked; we can't undo their interaction).
298
+ */
299
+ private _submitTokenResponse;
300
+ private _wrapInSlotCard;
301
+ private _buildStarRatingBody;
302
+ private _buildNPSSurveyBody;
234
303
  /**
235
304
  * Evaluate the currently armed campaigns against a client-side event
236
305
  * and render any that match their `client_trigger`.
@@ -1 +1 @@
1
- {"version":3,"file":"AegisInAppManager.d.ts","sourceRoot":"","sources":["../../src/inapp/AegisInAppManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AegisInAppManager.d.ts","sourceRoot":"","sources":["../../src/inapp/AegisInAppManager.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -755,6 +755,7 @@ const _AegisInAppManager = class _AegisInAppManager {
755
755
  this.ready = new Promise((resolve) => {
756
756
  this.readyResolve = resolve;
757
757
  });
758
+ this.filledSlots = /* @__PURE__ */ new WeakSet();
758
759
  this.writeKey = config.writeKey;
759
760
  this.apiHost = config.apiHost || "https://api.aegis.ai";
760
761
  this.userId = config.userId ?? readAnonIdFromStorage$1();
@@ -1099,6 +1100,8 @@ const _AegisInAppManager = class _AegisInAppManager {
1099
1100
  this.emit("campaigns-loaded", this.campaigns);
1100
1101
  this.readyResolve();
1101
1102
  this.tryDisplayNextCampaign();
1103
+ this.renderIntoSlots();
1104
+ this.renderTokenAnchors();
1102
1105
  } catch (error) {
1103
1106
  this.emitError(error, { stage: "refresh-campaigns" });
1104
1107
  this.log(`Error refreshing campaigns: ${error}`, "error");
@@ -1147,6 +1150,344 @@ const _AegisInAppManager = class _AegisInAppManager {
1147
1150
  this.displayCampaign(campaign);
1148
1151
  }
1149
1152
  }
1153
+ /**
1154
+ * Scan the page for `[data-aegis-slot]` anchors and render any
1155
+ * campaign whose `widget_category` matches the slot key AND whose
1156
+ * `delivery_modes` includes `'embedded_card'`.
1157
+ *
1158
+ * Runs after every refresh-campaigns success — the WeakSet guard
1159
+ * keeps it idempotent across re-fetches. The slot key is the campaign's
1160
+ * `widget_category` (the wide grouping discriminator from F3), so a
1161
+ * `<div data-aegis-slot="feedback">` will accept the first active
1162
+ * campaign with `widget_category='feedback'` + embedded_card mode.
1163
+ */
1164
+ renderIntoSlots() {
1165
+ if (typeof document === "undefined") return;
1166
+ const slots = document.querySelectorAll("[data-aegis-slot]");
1167
+ if (slots.length === 0) return;
1168
+ const eligibleByCategory = /* @__PURE__ */ new Map();
1169
+ for (const c of this.campaigns) {
1170
+ const modes = c.delivery_modes;
1171
+ const category = c.widget_category;
1172
+ if (!modes || !modes.includes("embedded_card")) continue;
1173
+ if (!category) continue;
1174
+ if (!eligibleByCategory.has(category)) {
1175
+ eligibleByCategory.set(category, c);
1176
+ }
1177
+ }
1178
+ if (eligibleByCategory.size === 0) return;
1179
+ slots.forEach((slot) => {
1180
+ if (this.filledSlots.has(slot)) return;
1181
+ const key = slot.getAttribute("data-aegis-slot");
1182
+ if (!key) return;
1183
+ const campaign = eligibleByCategory.get(key);
1184
+ if (!campaign) return;
1185
+ this.renderCampaignIntoSlot(campaign, slot);
1186
+ this.filledSlots.add(slot);
1187
+ });
1188
+ }
1189
+ /**
1190
+ * U7 — render server-injected token-bound campaigns.
1191
+ *
1192
+ * The standalone-page route at `apps/cashier-portal/src/app/s/[slug]/
1193
+ * [workspace]/[page_key]/page.tsx` (and the `/c/[token]` Aegis-hosted
1194
+ * fallback) verify the URL token server-side via the F16 internal
1195
+ * endpoint, then embed the resolved campaign payload as JSON inside
1196
+ * a `<div data-aegis-token-data="...">` anchor. This SDK method
1197
+ * scans for those anchors, parses the payload, and dispatches to
1198
+ * the SAME `renderCampaignIntoSlot` used by the [data-aegis-slot]
1199
+ * path — so widget rendering code is single-sourced.
1200
+ *
1201
+ * Why server-injected (not client-fetched): keeps INTERNAL_API_SECRET
1202
+ * server-side, avoids an extra browser round-trip for verify, gives
1203
+ * Next.js SSR-friendly initial HTML for SEO/no-JS users (rendered
1204
+ * via the page handler's React tree alongside the SDK anchor).
1205
+ *
1206
+ * Idempotency: filledSlots is the same WeakSet used by
1207
+ * renderIntoSlots — re-running on SSE/poll refresh is a no-op for
1208
+ * already-filled anchors.
1209
+ */
1210
+ renderTokenAnchors() {
1211
+ if (typeof document === "undefined") return;
1212
+ const anchors = document.querySelectorAll("[data-aegis-token-data]");
1213
+ if (anchors.length === 0) return;
1214
+ anchors.forEach((anchor) => {
1215
+ if (this.filledSlots.has(anchor)) return;
1216
+ const raw = anchor.getAttribute("data-aegis-token-data");
1217
+ if (!raw) return;
1218
+ let payload = null;
1219
+ try {
1220
+ payload = JSON.parse(raw);
1221
+ } catch (e) {
1222
+ this.log(
1223
+ `data-aegis-token-data: invalid JSON, skipping anchor (${e})`,
1224
+ "warn"
1225
+ );
1226
+ this.filledSlots.add(anchor);
1227
+ return;
1228
+ }
1229
+ if (!payload || !payload.campaign || !payload.campaign.id) {
1230
+ this.log(
1231
+ "data-aegis-token-data: missing `campaign` in payload, skipping",
1232
+ "warn"
1233
+ );
1234
+ this.filledSlots.add(anchor);
1235
+ return;
1236
+ }
1237
+ this.renderCampaignIntoSlot(
1238
+ payload.campaign,
1239
+ anchor,
1240
+ { submitUrl: payload.submit_url }
1241
+ );
1242
+ this.filledSlots.add(anchor);
1243
+ });
1244
+ }
1245
+ /**
1246
+ * Slot-mode counterpart to `displayCampaign`. Reuses the existing
1247
+ * lifecycle hooks (`campaign-will-show`, `displayedCampaigns`,
1248
+ * `campaign-shown`, impression tracking) so analytics + frequency
1249
+ * caps work the same way as overlay renders.
1250
+ *
1251
+ * Only the sub_types that make UX sense embedded are handled; the
1252
+ * rest (modal, full_screen, half_interstitial, alert, pip) silently
1253
+ * skip — those types only make sense as fullscreen overlays.
1254
+ */
1255
+ renderCampaignIntoSlot(campaign, target, options) {
1256
+ const proceed = this.emit("campaign-will-show", campaign);
1257
+ if (!proceed) {
1258
+ this.log(
1259
+ `slot campaign ${campaign.id} suppressed by campaign-will-show handler`
1260
+ );
1261
+ return;
1262
+ }
1263
+ this.displayedCampaigns.add(campaign.id);
1264
+ this.addAnimationStyles();
1265
+ const ic = campaign.interactive_config || {};
1266
+ const bg = this.sanitizeColor(campaign.background_color || "#4169e1");
1267
+ const text = this.sanitizeColor(campaign.text_color || "#ffffff");
1268
+ const submitUrl = options == null ? void 0 : options.submitUrl;
1269
+ let rendered = false;
1270
+ switch (campaign.sub_type) {
1271
+ case "star_rating":
1272
+ rendered = this.renderStarRatingSlot(
1273
+ campaign,
1274
+ ic,
1275
+ bg,
1276
+ text,
1277
+ target,
1278
+ submitUrl
1279
+ );
1280
+ break;
1281
+ case "nps_survey":
1282
+ rendered = this.renderNPSSurveySlot(
1283
+ campaign,
1284
+ ic,
1285
+ bg,
1286
+ text,
1287
+ target,
1288
+ submitUrl
1289
+ );
1290
+ break;
1291
+ // Future sub_types (quick_poll, countdown_offer, quiz, sticky_bar,
1292
+ // progress_bar, carousel_cards, product_recommendation) get added
1293
+ // here as merchants need them embedded. Until then, fall through
1294
+ // to the warn below — campaigns will still render as overlays via
1295
+ // the parallel `tryDisplayNextCampaign` path.
1296
+ default:
1297
+ this.log(
1298
+ `slot mode not yet supported for sub_type: ${campaign.sub_type ?? campaign.type}`,
1299
+ "warn"
1300
+ );
1301
+ this.displayedCampaigns.delete(campaign.id);
1302
+ return;
1303
+ }
1304
+ if (!rendered) return;
1305
+ this.trackEvent(campaign.id, "impression");
1306
+ this.emit("campaign-shown", campaign);
1307
+ }
1308
+ /**
1309
+ * Slot-mode wrappers. They build a small card container (no overlay
1310
+ * positioning, no close button) and append the SHARED body element
1311
+ * the overlay path also uses. Body construction lives in the
1312
+ * `_buildXxxBody` helpers below so visual output stays identical
1313
+ * between overlay and slot modes — adding a renderer feature in
1314
+ * one place updates both.
1315
+ */
1316
+ renderStarRatingSlot(campaign, ic, bg, text, target, submitUrl) {
1317
+ const card = this._wrapInSlotCard(
1318
+ "aegis-in-app-rating-card",
1319
+ campaign.id,
1320
+ bg,
1321
+ text
1322
+ );
1323
+ card.appendChild(
1324
+ this._buildStarRatingBody(campaign, ic, text, "slot", submitUrl)
1325
+ );
1326
+ target.appendChild(card);
1327
+ return true;
1328
+ }
1329
+ renderNPSSurveySlot(campaign, ic, bg, text, target, submitUrl) {
1330
+ const card = this._wrapInSlotCard(
1331
+ "aegis-in-app-nps-card",
1332
+ campaign.id,
1333
+ bg,
1334
+ text
1335
+ );
1336
+ card.appendChild(
1337
+ this._buildNPSSurveyBody(campaign, ic, text, "slot", submitUrl)
1338
+ );
1339
+ target.appendChild(card);
1340
+ return true;
1341
+ }
1342
+ /**
1343
+ * Token-bound submission helper. POSTs the structured response to
1344
+ * the per-token submit URL the page handler embedded. Best-effort
1345
+ * — failure is logged but doesn't throw (the customer already
1346
+ * clicked; we can't undo their interaction).
1347
+ */
1348
+ _submitTokenResponse(submitUrl, payload) {
1349
+ if (!submitUrl) return;
1350
+ fetch(submitUrl, {
1351
+ method: "POST",
1352
+ headers: { "Content-Type": "application/json" },
1353
+ body: JSON.stringify({ response: payload }),
1354
+ keepalive: true
1355
+ }).catch((err) => {
1356
+ this.log(`token submit failed: ${err}`, "warn");
1357
+ });
1358
+ }
1359
+ // --- Shared body builders (used by both overlay + slot paths) ----------
1360
+ //
1361
+ // Sizing: `'overlay'` matches the legacy fullscreen-modal scale (24px
1362
+ // padding, 18px title, 32px stars). `'slot'` is the embedded card
1363
+ // scale (20px padding, 16px title, 28px stars) — proportional, doesn't
1364
+ // dwarf merchant page chrome. Adding a new variant means a new tuple
1365
+ // here, not a new renderer.
1366
+ _wrapInSlotCard(className, campaignId, bg, text) {
1367
+ const card = document.createElement("div");
1368
+ card.className = className;
1369
+ card.setAttribute("data-campaign-id", campaignId);
1370
+ card.style.cssText = `
1371
+ width: 100%; border-radius: 12px; overflow: hidden;
1372
+ background: ${bg}; color: ${text};
1373
+ box-shadow: 0 4px 12px rgba(0,0,0,0.06);
1374
+ `;
1375
+ return card;
1376
+ }
1377
+ _buildStarRatingBody(campaign, ic, text, variant, submitUrl) {
1378
+ const isOverlay = variant === "overlay";
1379
+ const padding = isOverlay ? "24px" : "20px";
1380
+ const titleSize = isOverlay ? "18px" : "16px";
1381
+ const titleWeight = isOverlay ? "700" : "600";
1382
+ const titleMargin = isOverlay ? "16px" : "12px";
1383
+ const starSize = isOverlay ? "32px" : "28px";
1384
+ const starsMarginBottom = isOverlay ? "16px" : "0";
1385
+ const hoverScale = isOverlay ? "1.2" : "1.15";
1386
+ const body = document.createElement("div");
1387
+ body.style.cssText = `padding: ${padding}; text-align: center;`;
1388
+ const title = document.createElement("div");
1389
+ title.style.cssText = `font-size: ${titleSize}; font-weight: ${titleWeight}; margin-bottom: ${titleMargin};`;
1390
+ title.textContent = campaign.title || "Rate your experience";
1391
+ body.appendChild(title);
1392
+ const stars = document.createElement("div");
1393
+ stars.style.cssText = `display: flex; gap: 8px; justify-content: center;` + (starsMarginBottom !== "0" ? ` margin-bottom: ${starsMarginBottom};` : "");
1394
+ const maxStars = ic.rating_scale || 5;
1395
+ for (let i = 1; i <= maxStars; i++) {
1396
+ const star = document.createElement("span");
1397
+ star.style.cssText = `font-size: ${starSize}; cursor: pointer; transition: transform 0.1s; user-select: none;` + (text ? ` color: ${text};` : "");
1398
+ star.textContent = "☆";
1399
+ const value = i;
1400
+ star.addEventListener("click", () => {
1401
+ this.trackEvent(campaign.id, "clicked");
1402
+ if (submitUrl) {
1403
+ this._submitTokenResponse(submitUrl, {
1404
+ sub_type: "star_rating",
1405
+ value
1406
+ });
1407
+ }
1408
+ });
1409
+ star.addEventListener("mouseenter", () => {
1410
+ star.style.transform = `scale(${hoverScale})`;
1411
+ });
1412
+ star.addEventListener("mouseleave", () => {
1413
+ star.style.transform = "scale(1)";
1414
+ });
1415
+ stars.appendChild(star);
1416
+ }
1417
+ body.appendChild(stars);
1418
+ return body;
1419
+ }
1420
+ _buildNPSSurveyBody(campaign, ic, text, variant, submitUrl) {
1421
+ const isOverlay = variant === "overlay";
1422
+ const padding = isOverlay ? "24px" : "20px";
1423
+ const titleAlign = isOverlay ? "center" : "center";
1424
+ const body = document.createElement("div");
1425
+ body.style.cssText = `padding: ${padding};` + (isOverlay ? " text-align: center;" : "");
1426
+ const title = document.createElement("div");
1427
+ title.style.cssText = `font-size: 16px; font-weight: ${isOverlay ? "700" : "600"}; margin-bottom: ${isOverlay ? "16px" : "12px"}; text-align: ${titleAlign};`;
1428
+ title.textContent = ic.nps_question || campaign.title || "How likely are you to recommend us?";
1429
+ body.appendChild(title);
1430
+ if (isOverlay) {
1431
+ const scale = document.createElement("div");
1432
+ scale.style.cssText = "display: flex; gap: 4px; justify-content: center; flex-wrap: wrap; margin-bottom: 12px;";
1433
+ for (let i = 0; i <= 10; i++) {
1434
+ const btn = document.createElement("span");
1435
+ btn.style.cssText = `
1436
+ width: 28px; height: 28px; border-radius: 6px; display: flex;
1437
+ align-items: center; justify-content: center; font-size: 11px;
1438
+ font-weight: 600; cursor: pointer; background: ${text}33; color: ${text};
1439
+ transition: transform 0.1s;
1440
+ `;
1441
+ btn.textContent = String(i);
1442
+ const value = i;
1443
+ btn.addEventListener("click", () => {
1444
+ this.trackEvent(campaign.id, "clicked");
1445
+ if (submitUrl) {
1446
+ this._submitTokenResponse(submitUrl, {
1447
+ sub_type: "nps_survey",
1448
+ value
1449
+ });
1450
+ }
1451
+ });
1452
+ scale.appendChild(btn);
1453
+ }
1454
+ body.appendChild(scale);
1455
+ const labels = document.createElement("div");
1456
+ labels.style.cssText = "display: flex; justify-content: space-between; font-size: 11px; opacity: 0.6; margin-bottom: 16px;";
1457
+ const notLikely = document.createElement("span");
1458
+ notLikely.textContent = "Not likely";
1459
+ const veryLikely = document.createElement("span");
1460
+ veryLikely.textContent = "Very likely";
1461
+ labels.appendChild(notLikely);
1462
+ labels.appendChild(veryLikely);
1463
+ body.appendChild(labels);
1464
+ return body;
1465
+ }
1466
+ const grid = document.createElement("div");
1467
+ grid.style.cssText = "display: grid; grid-template-columns: repeat(11, 1fr); gap: 4px;";
1468
+ for (let n = 0; n <= 10; n++) {
1469
+ const btn = document.createElement("button");
1470
+ btn.style.cssText = `
1471
+ padding: 8px 0; border-radius: 6px; border: 1px solid ${text}33;
1472
+ background: transparent; color: ${text}; font-size: 13px; font-weight: 600;
1473
+ cursor: pointer; transition: background 0.15s;
1474
+ `;
1475
+ btn.textContent = String(n);
1476
+ const value = n;
1477
+ btn.addEventListener("click", () => {
1478
+ this.trackEvent(campaign.id, "clicked");
1479
+ if (submitUrl) {
1480
+ this._submitTokenResponse(submitUrl, {
1481
+ sub_type: "nps_survey",
1482
+ value
1483
+ });
1484
+ }
1485
+ });
1486
+ grid.appendChild(btn);
1487
+ }
1488
+ body.appendChild(grid);
1489
+ return body;
1490
+ }
1150
1491
  /**
1151
1492
  * Evaluate the currently armed campaigns against a client-side event
1152
1493
  * and render any that match their `client_trigger`.
@@ -1338,38 +1679,7 @@ const _AegisInAppManager = class _AegisInAppManager {
1338
1679
  background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;
1339
1680
  box-shadow: 0 20px 60px rgba(0,0,0,0.15);
1340
1681
  `;
1341
- const body = document.createElement("div");
1342
- body.style.cssText = "padding: 24px; text-align: center;";
1343
- const question = document.createElement("div");
1344
- question.style.cssText = "font-size: 16px; font-weight: 700; margin-bottom: 16px;";
1345
- question.textContent = ic.nps_question || "How likely are you to recommend us?";
1346
- body.appendChild(question);
1347
- const scale = document.createElement("div");
1348
- scale.style.cssText = "display: flex; gap: 4px; justify-content: center; flex-wrap: wrap; margin-bottom: 12px;";
1349
- for (let i = 0; i <= 10; i++) {
1350
- const btn = document.createElement("span");
1351
- btn.style.cssText = `
1352
- width: 28px; height: 28px; border-radius: 6px; display: flex;
1353
- align-items: center; justify-content: center; font-size: 11px;
1354
- font-weight: 600; cursor: pointer; background: ${text}33; color: ${text};
1355
- transition: transform 0.1s;
1356
- `;
1357
- btn.textContent = String(i);
1358
- btn.addEventListener("click", () => {
1359
- this.trackEvent(campaign.id, "clicked");
1360
- });
1361
- scale.appendChild(btn);
1362
- }
1363
- body.appendChild(scale);
1364
- const labels = document.createElement("div");
1365
- labels.style.cssText = "display: flex; justify-content: space-between; font-size: 11px; opacity: 0.6; margin-bottom: 16px;";
1366
- const notLikely = document.createElement("span");
1367
- notLikely.textContent = "Not likely";
1368
- const veryLikely = document.createElement("span");
1369
- veryLikely.textContent = "Very likely";
1370
- labels.appendChild(notLikely);
1371
- labels.appendChild(veryLikely);
1372
- body.appendChild(labels);
1682
+ const body = this._buildNPSSurveyBody(campaign, ic, text, "overlay");
1373
1683
  this.addCloseButton(body, overlay, campaign.id);
1374
1684
  modal.appendChild(body);
1375
1685
  overlay.appendChild(modal);
@@ -1450,31 +1760,7 @@ const _AegisInAppManager = class _AegisInAppManager {
1450
1760
  background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;
1451
1761
  box-shadow: 0 20px 60px rgba(0,0,0,0.15);
1452
1762
  `;
1453
- const body = document.createElement("div");
1454
- body.style.cssText = "padding: 24px; text-align: center;";
1455
- const title = document.createElement("div");
1456
- title.style.cssText = "font-size: 18px; font-weight: 700; margin-bottom: 16px;";
1457
- title.textContent = campaign.title || "Rate your experience";
1458
- body.appendChild(title);
1459
- const stars = document.createElement("div");
1460
- stars.style.cssText = "display: flex; gap: 8px; justify-content: center; margin-bottom: 16px;";
1461
- const maxStars = ic.rating_scale || 5;
1462
- for (let i = 1; i <= maxStars; i++) {
1463
- const star = document.createElement("span");
1464
- star.style.cssText = "font-size: 32px; cursor: pointer; transition: transform 0.1s;";
1465
- star.textContent = "☆";
1466
- star.addEventListener("click", () => {
1467
- this.trackEvent(campaign.id, "clicked");
1468
- });
1469
- star.addEventListener("mouseenter", () => {
1470
- star.style.transform = "scale(1.2)";
1471
- });
1472
- star.addEventListener("mouseleave", () => {
1473
- star.style.transform = "scale(1)";
1474
- });
1475
- stars.appendChild(star);
1476
- }
1477
- body.appendChild(stars);
1763
+ const body = this._buildStarRatingBody(campaign, ic, text, "overlay");
1478
1764
  this.addCloseButton(body, overlay, campaign.id);
1479
1765
  modal.appendChild(body);
1480
1766
  overlay.appendChild(modal);
@@ -2526,7 +2812,7 @@ const _AegisInAppManager = class _AegisInAppManager {
2526
2812
  this.disconnectSSE();
2527
2813
  if (typeof document !== "undefined") {
2528
2814
  document.querySelectorAll(
2529
- ".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"
2815
+ ".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"
2530
2816
  ).forEach((el) => {
2531
2817
  if (el.parentNode) {
2532
2818
  el.parentNode.removeChild(el);