@meetelise/chat 1.43.41 → 1.44.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.
@@ -29,6 +29,7 @@ import { DatePicker } from "./date-picker";
29
29
  import { MESelect } from "../me-select";
30
30
  import { TimePicker } from "./time-picker";
31
31
  import { LabeledOption } from "../../fetchBuildingInfo";
32
+ import { LayoutOption } from "../../fetchBuildingWebchatView";
32
33
  import {
33
34
  isMobile,
34
35
  isValidEmail,
@@ -79,6 +80,8 @@ export class TourScheduler extends LitElement {
79
80
 
80
81
  @property({ attribute: false })
81
82
  tourTypeOptions: LabeledOption[] = [];
83
+ @property({ attribute: false })
84
+ layoutOptions: LayoutOption[] = [];
82
85
  @property({ attribute: true })
83
86
  chatId = "";
84
87
  @property({ type: Number })
@@ -191,6 +194,8 @@ export class TourScheduler extends LitElement {
191
194
  phoneInput!: HTMLInputElement;
192
195
  @query("me-select#leadSource")
193
196
  selectedLeadSource!: MESelect;
197
+ @query("me-select#layout")
198
+ selectedLayoutEl?: MESelect;
194
199
 
195
200
  @state()
196
201
  firstNameInputValue = "";
@@ -198,6 +203,8 @@ export class TourScheduler extends LitElement {
198
203
  lastNameInputValue = "";
199
204
  @state()
200
205
  leadSourceInputValue = "";
206
+ @state()
207
+ selectedLayoutValue = "";
201
208
 
202
209
  @state()
203
210
  errorGettingAvailabilities = false;
@@ -643,6 +650,8 @@ export class TourScheduler extends LitElement {
643
650
  this.lastNameInputValue = this.lastNameInput.value;
644
651
  }
645
652
  this.leadSourceInputValue = parsedLeadSource;
653
+ const selectedLayout =
654
+ this.selectedLayoutEl?.value || this.selectedLayoutValue || null;
646
655
  pushGtmEvent("scheduleTourSubmitted", {
647
656
  email: this.email,
648
657
  phone: `+1${this.phoneNumber.match(/\d/g)?.join("")}`,
@@ -652,6 +661,7 @@ export class TourScheduler extends LitElement {
652
661
  tourTime: `${this.selectedTime.datetime}${this.selectedTime.offset}`,
653
662
  originatingSource:
654
663
  leadSources.find((i) => i !== "property-website") || null,
664
+ layout: selectedLayout,
655
665
  });
656
666
  const data = {
657
667
  referrer: document.referrer,
@@ -672,6 +682,7 @@ export class TourScheduler extends LitElement {
672
682
  ],
673
683
  query_params: Object.fromEntries(queryParams.entries()),
674
684
  conversation_tracking_id: this.leadSourceClient?.chatId,
685
+ layouts: selectedLayout ? [selectedLayout] : undefined,
675
686
  lead_sources_with_timestamps: (
676
687
  this.leadSourceMultitouchClient?.getSafeLeadSourceTouchpointsWithDefault(
677
688
  {
@@ -1225,6 +1236,27 @@ export class TourScheduler extends LitElement {
1225
1236
  ? html`<p class="error-message">Invalid phone number</p>`
1226
1237
  : ""}
1227
1238
  </div>
1239
+ ${this.layoutOptions.length > 0
1240
+ ? html` <me-select
1241
+ id="layout"
1242
+ placeholder="Bedroom preference (optional)"
1243
+ .options="${this.layoutOptions.map((o) => ({
1244
+ label: o.label,
1245
+ value: layoutValueToCanonical(o.value),
1246
+ }))}"
1247
+ @change=${() => {
1248
+ const v = this.selectedLayoutEl?.value || "";
1249
+ this.selectedLayoutValue = v;
1250
+ if (v) {
1251
+ pushGtmEvent("tourLayoutSelected", {
1252
+ layout: v,
1253
+ buildingId: this.buildingId,
1254
+ });
1255
+ }
1256
+ }}
1257
+ >
1258
+ </me-select>`
1259
+ : ""}
1228
1260
  ${this.leadSources.length > 0 &&
1229
1261
  (this.featureFlagShowDropdown === FeatureFlagsShowDropdown.always ||
1230
1262
  (this.featureFlagShowDropdown ===
@@ -1678,3 +1710,9 @@ const tourTypeForSubmission = {
1678
1710
  [TourType.Self]: "self-guided-tour",
1679
1711
  [TourType.Virtual]: "live-virtual-tour",
1680
1712
  };
1713
+
1714
+ // LayoutOption.value is bedrooms * 10; EliseAI classifier expects `studio` / `Nbr`.
1715
+ function layoutValueToCanonical(value: number): string {
1716
+ const bedrooms = Math.round(value / 10);
1717
+ return bedrooms === 0 ? "studio" : `${bedrooms}br`;
1718
+ }
@@ -21,7 +21,7 @@ import {
21
21
  } from "../../themes";
22
22
  import "./mobile-launcher";
23
23
  import "../actions/collapse-expand-button";
24
- import { DesignConcepts } from "../../fetchBuildingWebchatView";
24
+ import { DesignConcepts, LayoutOption } from "../../fetchBuildingWebchatView";
25
25
  import { isMobile, onMobileChange } from "../../utils";
26
26
  import classNames from "classnames";
27
27
  import LeadSourceClient from "../LeadSourceClient";
@@ -141,6 +141,9 @@ export class Launcher extends LitElement {
141
141
 
142
142
  @property({ attribute: false })
143
143
  tourTypeOptions: LabeledOption[] = [];
144
+
145
+ @property({ attribute: false })
146
+ layoutOptions: LayoutOption[] = [];
144
147
  @property({ attribute: true })
145
148
  hasDynamicSchedulingEnabled = false;
146
149
 
@@ -1299,6 +1302,7 @@ export class Launcher extends LitElement {
1299
1302
  .hasDynamicSchedulingEnabled=${this.hasDynamicSchedulingEnabled}
1300
1303
  .leadSources="${this.leadSources}"
1301
1304
  .tourTypeOptions=${this.tourTypeOptions}
1305
+ .layoutOptions=${this.layoutOptions}
1302
1306
  .orgLegalName=${this.orgLegalName}
1303
1307
  buildingId=${this.buildingId}
1304
1308
  featureFlagShowDropdown="${this.featureFlagShowDropdown}"
@@ -59,6 +59,7 @@ import { updateRentgrataToken } from "../rentgrata";
59
59
  import { WidgetType } from "../main/MEChat";
60
60
  import { logCdnVersion } from "../logCdnVersion";
61
61
  import { replaceSelectButtonsWithNewLink } from "../replaceSelectButtonsWithNewLink";
62
+ import { replaceOutdatedApplicationLinks } from "../replaceOutdatedApplicationLinks";
62
63
  import {
63
64
  overwriteCheckAvail,
64
65
  shouldOverwriteCheckAvail,
@@ -524,7 +525,7 @@ export class MEChat extends LitElement {
524
525
  this.buildingWebchatView?.isLiveOnApplications
525
526
  ) {
526
527
  const totalReplacements = replaceSelectButtonsWithNewLink(
527
- `https://applications.eliseai.com/building/${this.buildingSlug}`,
528
+ `https://applications.eliseai.com/building/${this.buildingSlug}/units`,
528
529
  this.orgSlug,
529
530
  this.buildingSlug
530
531
  );
@@ -543,6 +544,15 @@ export class MEChat extends LitElement {
543
544
  }
544
545
  }
545
546
 
547
+ if (this.buildingSlug && this.orgSlug) {
548
+ try {
549
+ replaceOutdatedApplicationLinks(this.buildingSlug);
550
+ } catch {
551
+ // eslint-disable-next-line no-console
552
+ console.warn("Error replacing outdated application links");
553
+ }
554
+ }
555
+
546
556
  if (
547
557
  this.buildingSlug &&
548
558
  !!this.buildingWebchatView?.isLiveOnApplications &&
@@ -995,6 +1005,8 @@ export class MEChat extends LitElement {
995
1005
  .orgLegalName=${this.buildingWebchatView?.orgLegalName ?? ""}
996
1006
  .tourTypeOptions=${this.buildingWebchatView
997
1007
  ?.tourTypeOptions ?? []}
1008
+ .layoutOptions=${this.buildingWebchatView?.layoutOptions ??
1009
+ []}
998
1010
  .launcherStyles=${this.launcherStyles}
999
1011
  .primaryColor=${this.primaryColor}
1000
1012
  .backgroundColor=${this.backgroundColor}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Some property websites embed hard-coded `applications.eliseai.com/building/<slug>/units`
3
+ * links whose slug has since been re-keyed in the backend. When the widget loads for the
4
+ * new slug, find those links on the host page and rewrite the slug to the current one,
5
+ * preserving query strings/hashes.
6
+ */
7
+
8
+ // Keys: current/correct building slug.
9
+ // Values: outdated slug that may still appear in URLs on the host page.
10
+ export const OUTDATED_APPLICATION_BUILDING_SLUGS: Record<string, string> = {
11
+ "95ae958e-95db-11ed-9862-6f4db5533d3f":
12
+ "8008db60-45b7-11f1-88ac-635f686365da",
13
+ "95ae96b0-95db-11ed-9863-a7aa664fd985":
14
+ "72ed27c4-45b7-11f1-82d9-37b7db547687",
15
+ };
16
+
17
+ const APPLICATIONS_HOST = "https://applications.eliseai.com";
18
+
19
+ export const replaceOutdatedApplicationLinks = (
20
+ currentBuildingSlug: string
21
+ ): number => {
22
+ const outdatedSlug = OUTDATED_APPLICATION_BUILDING_SLUGS[currentBuildingSlug];
23
+ if (!outdatedSlug) return 0;
24
+
25
+ const outdatedBase = `${APPLICATIONS_HOST}/building/${outdatedSlug}/units`;
26
+ const newBase = `${APPLICATIONS_HOST}/building/${currentBuildingSlug}/units`;
27
+ const pattern = new RegExp(
28
+ `https://applications\\.eliseai\\.com/building/${outdatedSlug}/units`,
29
+ "g"
30
+ );
31
+
32
+ let totalReplacements = 0;
33
+
34
+ const anchors = document.body.getElementsByTagName("a");
35
+ for (let i = 0; i < anchors.length; i++) {
36
+ const a = anchors[i];
37
+ const href = a.getAttribute("href");
38
+ if (!href || !href.includes(outdatedBase)) continue;
39
+ const newHref = href.replace(pattern, newBase);
40
+ if (newHref === href) continue;
41
+ a.setAttribute("href", newHref);
42
+ totalReplacements++;
43
+ }
44
+
45
+ const onclickElements =
46
+ document.body.querySelectorAll<HTMLElement>("[onclick]");
47
+ onclickElements.forEach((el) => {
48
+ const onclick = el.getAttribute("onclick");
49
+ if (!onclick || !onclick.includes(outdatedBase)) return;
50
+ const newOnclick = onclick.replace(pattern, newBase);
51
+ if (newOnclick === onclick) return;
52
+ el.setAttribute("onclick", newOnclick);
53
+ totalReplacements++;
54
+ });
55
+
56
+ return totalReplacements;
57
+ };