@codebards/ik-embeddable-form 0.0.2736450101-qa → 0.0.2736851469-qa

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/embed.js CHANGED
@@ -1574,41 +1574,143 @@ var IKEmbeddableFormBundle = function(exports) {
1574
1574
  "December"
1575
1575
  ];
1576
1576
  const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
1577
- function createFutureStaticSlots(count = 3) {
1578
- const pad = (n2) => String(n2).padStart(2, "0");
1579
- const slots = [];
1577
+ const FALLBACK_SCHEDULES = {
1578
+ IND: {
1579
+ skipDay: null,
1580
+ timeSlots: {
1581
+ 0: "12:00:00",
1582
+ 1: "18:00:00",
1583
+ 2: "18:00:00",
1584
+ 3: "18:00:00",
1585
+ 4: "18:00:00",
1586
+ 5: "18:00:00",
1587
+ 6: "12:00:00"
1588
+ },
1589
+ timeZone: "Asia/Kolkata",
1590
+ utcOffsetHours: 5.5
1591
+ },
1592
+ USA: {
1593
+ skipDay: 6,
1594
+ timeSlots: {
1595
+ 0: "20:30:00",
1596
+ 1: "19:30:00",
1597
+ 2: "20:30:00",
1598
+ 3: "19:30:00",
1599
+ 4: "20:30:00",
1600
+ 5: "19:30:00",
1601
+ 6: null
1602
+ },
1603
+ timeZone: "America/New_York",
1604
+ utcOffsetHours: -5
1605
+ }
1606
+ };
1607
+ function pad2(n2) {
1608
+ return String(n2).padStart(2, "0");
1609
+ }
1610
+ function formatUtcOffset(offsetHours) {
1611
+ const sign = offsetHours >= 0 ? "+" : "-";
1612
+ const abs = Math.abs(offsetHours);
1613
+ const hours = Math.floor(abs);
1614
+ const minutes = Math.round((abs - hours) * 60);
1615
+ return `${sign}${pad2(hours)}:${pad2(minutes)}`;
1616
+ }
1617
+ function todayInTimeZone(timeZone) {
1618
+ const parts = new Intl.DateTimeFormat("en-US", {
1619
+ timeZone,
1620
+ year: "numeric",
1621
+ month: "2-digit",
1622
+ day: "2-digit"
1623
+ }).formatToParts(/* @__PURE__ */ new Date());
1624
+ const get2 = (type) => {
1625
+ var _a;
1626
+ return Number((_a = parts.find((p2) => p2.type === type)) == null ? void 0 : _a.value);
1627
+ };
1628
+ return { year: get2("year"), month: get2("month"), day: get2("day") };
1629
+ }
1630
+ function wallClockParts(date, timeZone) {
1631
+ const parts = new Intl.DateTimeFormat("en-US", {
1632
+ timeZone,
1633
+ year: "numeric",
1634
+ month: "2-digit",
1635
+ day: "2-digit",
1636
+ hour: "2-digit",
1637
+ minute: "2-digit",
1638
+ second: "2-digit",
1639
+ hourCycle: "h23"
1640
+ }).formatToParts(date);
1641
+ const get2 = (type) => {
1642
+ var _a;
1643
+ return Number((_a = parts.find((p2) => p2.type === type)) == null ? void 0 : _a.value);
1644
+ };
1645
+ return {
1646
+ year: get2("year"),
1647
+ month: get2("month"),
1648
+ day: get2("day"),
1649
+ hour: get2("hour"),
1650
+ minute: get2("minute"),
1651
+ second: get2("second")
1652
+ };
1653
+ }
1654
+ function timeZoneOffsetString(timeZone, at) {
1655
+ var _a;
1656
+ try {
1657
+ const raw = ((_a = new Intl.DateTimeFormat("en-US", { timeZone, timeZoneName: "longOffset" }).formatToParts(at).find((p2) => p2.type === "timeZoneName")) == null ? void 0 : _a.value) ?? "GMT+00:00";
1658
+ let offset = raw.replace("GMT", "") || "+00:00";
1659
+ if (/^[+-]\d:/.test(offset)) offset = offset.replace(/^([+-])(\d):/, "$10$2:");
1660
+ return offset || "+00:00";
1661
+ } catch {
1662
+ return "+00:00";
1663
+ }
1664
+ }
1665
+ function generateFallbackSlots(country, targetTimezone, webinarType, count = 13) {
1666
+ const schedule = FALLBACK_SCHEDULES[country === "IND" ? "IND" : "USA"];
1667
+ const scheduleOffset = formatUtcOffset(schedule.utcOffsetHours);
1668
+ const today = todayInTimeZone(schedule.timeZone);
1580
1669
  const now = /* @__PURE__ */ new Date();
1670
+ const slots = [];
1581
1671
  for (let i2 = 0; i2 < count; i2++) {
1582
- const start = new Date(now);
1583
- start.setDate(start.getDate() + 7 * (i2 + 1));
1584
- start.setHours(19, 30, 0, 0);
1585
- const end = new Date(start);
1586
- end.setHours(20, 30, 0, 0);
1587
- const hour12 = start.getHours() % 12 || 12;
1588
- const ampm = start.getHours() >= 12 ? "PM" : "AM";
1589
- const offsetMin = -start.getTimezoneOffset();
1590
- const sign = offsetMin >= 0 ? "+" : "-";
1591
- const tzOffset = `${sign}${pad(Math.floor(Math.abs(offsetMin) / 60))}:${pad(Math.abs(offsetMin) % 60)}`;
1592
- const toIso = (d2) => `${d2.getFullYear()}-${pad(d2.getMonth() + 1)}-${pad(d2.getDate())}T${pad(d2.getHours())}:${pad(d2.getMinutes())}:00${tzOffset}`;
1593
- const weekday = WEEKDAYS[start.getDay()] ?? "Tuesday";
1594
- const monthLabel = MONTHS[start.getMonth()] ?? "June";
1595
- const day = pad(start.getDate());
1596
- const year = String(start.getFullYear());
1597
- const minute = pad(start.getMinutes());
1598
- const endHour12 = end.getHours() % 12 || 12;
1599
- const endAmpm = end.getHours() >= 12 ? "PM" : "AM";
1672
+ const candidate = new Date(today.year, today.month - 1, today.day);
1673
+ candidate.setDate(candidate.getDate() + i2);
1674
+ const weekday = candidate.getDay();
1675
+ if (weekday === schedule.skipDay) continue;
1676
+ const time = schedule.timeSlots[weekday];
1677
+ if (!time) continue;
1678
+ const [hour, minute, second] = time.split(":").map(Number);
1679
+ candidate.setHours(hour, minute, second, 0);
1680
+ const isoInSchedule = `${candidate.getFullYear()}-${pad2(candidate.getMonth() + 1)}-${pad2(candidate.getDate())}T${pad2(candidate.getHours())}:${pad2(candidate.getMinutes())}:${pad2(candidate.getSeconds())}${scheduleOffset}`;
1681
+ const instant = new Date(isoInSchedule);
1682
+ if (Number.isNaN(instant.getTime()) || instant < now) continue;
1683
+ const end = new Date(instant.getTime() + 60 * 60 * 1e3);
1684
+ const startWall = wallClockParts(instant, targetTimezone);
1685
+ const endWall = wallClockParts(end, targetTimezone);
1686
+ const targetOffset = timeZoneOffsetString(targetTimezone, instant);
1687
+ const toIso = (w2) => `${w2.year}-${pad2(w2.month)}-${pad2(w2.day)}T${pad2(w2.hour)}:${pad2(w2.minute)}:${pad2(w2.second)}${targetOffset}`;
1688
+ const formatClock = (hour24, min) => {
1689
+ const hour12 = hour24 % 12 || 12;
1690
+ const ampm = hour24 >= 12 ? "PM" : "AM";
1691
+ return { label: `${hour12}:${pad2(min)}${ampm}`, hour12, ampm };
1692
+ };
1693
+ const weekdayLabel = WEEKDAYS[new Date(startWall.year, startWall.month - 1, startWall.day).getDay()] ?? "Tuesday";
1694
+ const monthLabel = MONTHS[startWall.month - 1] ?? "June";
1695
+ const dateLabel = `${weekdayLabel}, ${monthLabel} ${pad2(startWall.day)}, ${startWall.year}`;
1696
+ const start = formatClock(startWall.hour, startWall.minute);
1697
+ const slotEnd = formatClock(endWall.hour, endWall.minute);
1600
1698
  slots.push({
1601
- start_time: toIso(start),
1602
- end_time: toIso(end),
1603
- day,
1604
- month: pad(start.getMonth() + 1),
1605
- year,
1606
- hour: String(hour12),
1607
- minute,
1608
- am_or_pm: ampm,
1609
- weekday,
1610
- invitee_start_time: `${hour12}:${minute}${ampm} - ${weekday}, ${monthLabel} ${day}, ${year}`,
1611
- invitee_end_time: `${endHour12}:${pad(end.getMinutes())}${endAmpm} - ${weekday}, ${monthLabel} ${day}, ${year}`
1699
+ start_time: toIso(startWall),
1700
+ end_time: toIso(endWall),
1701
+ utc_start_time: instant.toISOString(),
1702
+ utc_end_time: end.toISOString(),
1703
+ day: pad2(startWall.day),
1704
+ month: pad2(startWall.month),
1705
+ year: String(startWall.year),
1706
+ hour: String(start.hour12),
1707
+ minute: pad2(startWall.minute),
1708
+ second: pad2(startWall.second),
1709
+ am_or_pm: start.ampm,
1710
+ weekday: weekdayLabel,
1711
+ invitee_start_time: `${start.label} - ${dateLabel}`,
1712
+ invitee_end_time: `${slotEnd.label} - ${dateLabel}`,
1713
+ webinar_lead_type: webinarType
1612
1714
  });
1613
1715
  }
1614
1716
  return slots;
@@ -1675,12 +1777,12 @@ var IKEmbeddableFormBundle = function(exports) {
1675
1777
  const slots = Array.isArray(data) ? data : [];
1676
1778
  return slots;
1677
1779
  }
1678
- function getStaticSlots(openConfig) {
1780
+ function getStaticSlots(openConfig, country, timezone, webinarType) {
1679
1781
  const custom = openConfig == null ? void 0 : openConfig.staticSlots;
1680
1782
  if (custom && custom.length > 0) {
1681
1783
  return custom;
1682
1784
  }
1683
- return createFutureStaticSlots();
1785
+ return generateFallbackSlots(country, timezone, webinarType);
1684
1786
  }
1685
1787
  function resolveSlotApiCountry(openConfig, slotApiConfig) {
1686
1788
  if (openConfig == null ? void 0 : openConfig.slotApiCountry) {
@@ -1723,20 +1825,18 @@ var IKEmbeddableFormBundle = function(exports) {
1723
1825
  return {
1724
1826
  slots: l2.map(apiSlotToWebinarSlot),
1725
1827
  rawSlots: l2,
1726
- assignedWebinarType: "REGULAR",
1828
+ assignedWebinarType: l2Type,
1727
1829
  source: "api-l2"
1728
1830
  };
1729
1831
  }
1730
1832
  } catch {
1731
1833
  }
1732
- const staticSlots = filterFutureSlots(getStaticSlots(openConfig));
1733
- const fallback = limitDisplaySlots(
1734
- staticSlots.length > 0 ? staticSlots : filterFutureSlots(createFutureStaticSlots())
1735
- );
1834
+ const staticSlots = filterFutureSlots(getStaticSlots(openConfig, country, timezone, l2Type));
1835
+ const fallback = limitDisplaySlots(staticSlots);
1736
1836
  return {
1737
1837
  slots: fallback.map(apiSlotToWebinarSlot),
1738
1838
  rawSlots: fallback,
1739
- assignedWebinarType: webinarType,
1839
+ assignedWebinarType: l2Type,
1740
1840
  source: "static"
1741
1841
  };
1742
1842
  }
@@ -1950,7 +2050,8 @@ var IKEmbeddableFormBundle = function(exports) {
1950
2050
  Region: formData.region ?? (ctx == null ? void 0 : ctx.region) ?? "",
1951
2051
  step,
1952
2052
  data_click_id: resolveClickId(formData),
1953
- ab_bucket_json: JSON.stringify({ gql_yoe_dropdown_ab: "" })
2053
+ ab_bucket_json: JSON.stringify({ gql_yoe_dropdown_ab: "" }),
2054
+ fingerprint_id: getFingerprintId()
1954
2055
  };
1955
2056
  }
1956
2057
  async function postClickstream(url, apiKey, datasetId, tableId, row) {
@@ -2363,7 +2464,7 @@ var IKEmbeddableFormBundle = function(exports) {
2363
2464
  }
2364
2465
  startSlotsPrefetch() {
2365
2466
  if (!this.needsSlotsPrefetch() || this.slotsPrefetchPromise) return;
2366
- this.mergeFormData({ slotsLoading: true });
2467
+ this.mergeFormData({ slotsLoading: true }, true);
2367
2468
  const earlyPrefetch = getSlotsPrefetchPromise();
2368
2469
  const prefetch = earlyPrefetch ? earlyPrefetch.then((result) => {
2369
2470
  this.applySlotFetchResult(result);
@@ -2736,11 +2837,12 @@ var IKEmbeddableFormBundle = function(exports) {
2736
2837
  });
2737
2838
  return data;
2738
2839
  }
2739
- mergeFormData(incoming) {
2840
+ mergeFormData(incoming, notify = false) {
2740
2841
  this.state = {
2741
2842
  ...this.state,
2742
2843
  formData: { ...this.state.formData, ...incoming }
2743
2844
  };
2845
+ if (notify) this.notifyListeners();
2744
2846
  }
2745
2847
  /**
2746
2848
  * Like `mergeFormData`, but notifies subscribers so the UI re-renders.
@@ -2753,6 +2855,9 @@ var IKEmbeddableFormBundle = function(exports) {
2753
2855
  }
2754
2856
  setState(partial) {
2755
2857
  this.state = { ...this.state, ...partial };
2858
+ this.notifyListeners();
2859
+ }
2860
+ notifyListeners() {
2756
2861
  this.listeners.forEach((l2) => l2(this.getState()));
2757
2862
  }
2758
2863
  }
@@ -2888,6 +2993,10 @@ var IKEmbeddableFormBundle = function(exports) {
2888
2993
  lastName: parts.slice(1).join(" ") || (parts[0] ?? "")
2889
2994
  };
2890
2995
  }
2996
+ function customerIdFields(context) {
2997
+ const customerId = str(context.openConfig.customerId);
2998
+ return customerId ? { customer_id: customerId } : {};
2999
+ }
2891
3000
  function resolveUtmMedium(context) {
2892
3001
  return resolveWordPressUtmMedium(
2893
3002
  context.visitorId,
@@ -2915,6 +3024,7 @@ var IKEmbeddableFormBundle = function(exports) {
2915
3024
  const { firstName, lastName } = resolveName(formData);
2916
3025
  const webinarType = resolveEffectiveWebinarType(formData, context.webinarType);
2917
3026
  return {
3027
+ ...customerIdFields(context),
2918
3028
  "First Name": firstName,
2919
3029
  "Last Name": lastName,
2920
3030
  "Email Address": email(formData.email),
@@ -2994,6 +3104,7 @@ var IKEmbeddableFormBundle = function(exports) {
2994
3104
  const gaEventId = Date.now();
2995
3105
  const { firstName, lastName } = resolveName(formData);
2996
3106
  return {
3107
+ ...customerIdFields(context),
2997
3108
  "First Name": firstName,
2998
3109
  "Last Name": lastName,
2999
3110
  "Email Address": email(formData.email),
@@ -3065,6 +3176,7 @@ var IKEmbeddableFormBundle = function(exports) {
3065
3176
  const linkedIn = str(formData.linkedIn).trim();
3066
3177
  const resumeBase64 = str(formData.resumeFileBase64).trim();
3067
3178
  return {
3179
+ ...customerIdFields(context),
3068
3180
  Lead_Created_Time: formatLeadCreatedTime(formData.leadCreatedTime),
3069
3181
  Page_URL: encodeURIComponent(context.pageUrl),
3070
3182
  "First Name": firstName,
@@ -3187,6 +3299,10 @@ var IKEmbeddableFormBundle = function(exports) {
3187
3299
  return url.toString();
3188
3300
  }
3189
3301
  buildBody(contract, formData) {
3302
+ const body = this.buildRawBody(contract, formData);
3303
+ return { ...body, fingerprint_id: getFingerprintId() };
3304
+ }
3305
+ buildRawBody(contract, formData) {
3190
3306
  if (contract.payloadBuilder) {
3191
3307
  return buildPayload(contract.payloadBuilder, formData);
3192
3308
  }
@@ -3211,7 +3327,13 @@ var IKEmbeddableFormBundle = function(exports) {
3211
3327
  }
3212
3328
  async fetchWithRetry(url, init, retriesLeft, timeoutMs) {
3213
3329
  const controller = new AbortController();
3214
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
3330
+ let timedOut = false;
3331
+ const timeoutId = setTimeout(() => {
3332
+ timedOut = true;
3333
+ controller.abort(
3334
+ new DOMException(`Request timed out after ${timeoutMs}ms`, "TimeoutError")
3335
+ );
3336
+ }, timeoutMs);
3215
3337
  try {
3216
3338
  const res = await fetch(url, { ...init, signal: controller.signal });
3217
3339
  clearTimeout(timeoutId);
@@ -3228,13 +3350,21 @@ var IKEmbeddableFormBundle = function(exports) {
3228
3350
  return { data, status: res.status, ok: true };
3229
3351
  } catch (err) {
3230
3352
  clearTimeout(timeoutId);
3231
- if (retriesLeft > 1 && this.isRetryable(err)) {
3353
+ const normalizedErr = timedOut ? this.buildTimeoutError(timeoutMs) : err;
3354
+ if (retriesLeft > 1 && this.isRetryable(normalizedErr)) {
3232
3355
  await this.delay(500 * (2 - retriesLeft));
3233
3356
  return this.fetchWithRetry(url, init, retriesLeft - 1, timeoutMs);
3234
3357
  }
3235
- throw err;
3358
+ throw normalizedErr;
3236
3359
  }
3237
3360
  }
3361
+ buildTimeoutError(timeoutMs) {
3362
+ return {
3363
+ code: "TIMEOUT",
3364
+ message: "This is taking longer than expected. Please try again.",
3365
+ details: { timeoutMs }
3366
+ };
3367
+ }
3238
3368
  applyResponseMapping(mapping, response) {
3239
3369
  if (!mapping) return {};
3240
3370
  const result = {};
@@ -3250,7 +3380,7 @@ var IKEmbeddableFormBundle = function(exports) {
3250
3380
  if (err instanceof TypeError) return true;
3251
3381
  if (err && typeof err === "object" && "code" in err) {
3252
3382
  const code = err.code;
3253
- return code === "HTTP_429" || code === "HTTP_503" || code === "HTTP_502";
3383
+ return code === "TIMEOUT" || code === "HTTP_429" || code === "HTTP_503" || code === "HTTP_502";
3254
3384
  }
3255
3385
  return false;
3256
3386
  }
@@ -21595,10 +21725,49 @@ var IKEmbeddableFormBundle = function(exports) {
21595
21725
  }
21596
21726
  }
21597
21727
  };
21728
+ const emailRegistrationVariant = {
21729
+ id: "email-registration",
21730
+ overrides: {
21731
+ layout: {
21732
+ leftPanel: {
21733
+ progressGroups: [
21734
+ {
21735
+ number: 1,
21736
+ title: "Webinar Registration",
21737
+ stepIds: ["slot-selection"]
21738
+ },
21739
+ {
21740
+ number: 2,
21741
+ title: "Profile Details",
21742
+ stepIds: ["profile-details", "goals-details"]
21743
+ }
21744
+ ]
21745
+ },
21746
+ rightPanel: {
21747
+ progressGroups: [
21748
+ { stepIds: ["slot-selection"] },
21749
+ { stepIds: ["profile-details"] },
21750
+ { stepIds: ["goals-details"] },
21751
+ { stepIds: ["consultation-prefs"] }
21752
+ ]
21753
+ }
21754
+ },
21755
+ steps: {
21756
+ // Lead is resolved from customer_id on the backend — skip contact collection.
21757
+ "contact-details": { show: false },
21758
+ "profile-details": {
21759
+ fields: {
21760
+ interviewTimeline: { show: false }
21761
+ }
21762
+ }
21763
+ }
21764
+ }
21765
+ };
21598
21766
  const GQL_WEBINAR_VARIANTS = [
21599
21767
  defaultVariant,
21600
21768
  indiaVariant,
21601
- eventVariant
21769
+ eventVariant,
21770
+ emailRegistrationVariant
21602
21771
  ];
21603
21772
  const gqlWebinarConfig = {
21604
21773
  ...gqlWebinarBaseConfig,