@meetelise/chat 1.22.75 → 1.22.77

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.
@@ -0,0 +1,22 @@
1
+ import { svg, SVGTemplateResult } from "lit";
2
+
3
+ export const ApplyOutlineIcon = (
4
+ color = "white"
5
+ ): SVGTemplateResult => svg`<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
6
+ <g clip-path="url(#clip0_18382_27345)">
7
+ <g clip-path="url(#clip1_18382_27345)">
8
+ <path d="M9.91666 0.625V3.45833C9.91666 3.6462 9.99128 3.82636 10.1241 3.9592C10.257 4.09204 10.4371 4.16667 10.625 4.16667H13.4583" stroke="${color}" stroke-linecap="round" stroke-linejoin="round"/>
9
+ <path d="M12.0417 13.375H4.95832C4.5826 13.375 4.22227 13.2257 3.95659 12.9601C3.69091 12.6944 3.54166 12.3341 3.54166 11.9583V2.04167C3.54166 1.66594 3.69091 1.30561 3.95659 1.03993C4.22227 0.774255 4.5826 0.625 4.95832 0.625H9.91666L13.4583 4.16667V11.9583C13.4583 12.3341 13.3091 12.6944 13.0434 12.9601C12.7777 13.2257 12.4174 13.375 12.0417 13.375Z" stroke="${color}" stroke-linecap="round" stroke-linejoin="round"/>
10
+ <path d="M6.375 9.125L7.79167 10.5417L10.625 7.70833" stroke="${color}" stroke-linecap="round" stroke-linejoin="round"/>
11
+ </g>
12
+ </g>
13
+ <defs>
14
+ <clipPath id="clip0_18382_27345">
15
+ <rect width="16" height="14" fill="white"/>
16
+ </clipPath>
17
+ <clipPath id="clip1_18382_27345">
18
+ <rect width="17" height="17" fill="white" transform="translate(0 -1.5)"/>
19
+ </clipPath>
20
+ </defs>
21
+ </svg>
22
+ `;
@@ -37,6 +37,7 @@ import { EmailOutlineIcon } from "../icons/EmailOutlineIcon";
37
37
  import { PhoneOutlineIcon } from "../icons/PhoneOutlineIcon";
38
38
  import { HeyThereEmoji } from "../icons/HeyThereEmojiIcon";
39
39
  import { ContactResidentIcon } from "../icons/ContactResidentIcon";
40
+ import { ApplyOutlineIcon } from "../icons/ApplyOutlineIcon";
40
41
 
41
42
  @customElement("meetelise-launcher")
42
43
  export class Launcher extends LitElement {
@@ -100,6 +101,12 @@ export class Launcher extends LitElement {
100
101
  hasEmailEnabledDesktop = false;
101
102
  @property({ type: Boolean })
102
103
  hasEmailEnabledMobile = false;
104
+ @property({ attribute: true })
105
+ hasApplyNowEnabledDesktop = false;
106
+ @property({ attribute: true })
107
+ hasApplyNowEnabledMobile = false;
108
+ @property({ attribute: true })
109
+ applicationLink = "";
103
110
  @property({ type: Boolean })
104
111
  hasCallUsEnabledDesktop = false;
105
112
  @property({ type: Boolean })
@@ -191,12 +198,15 @@ export class Launcher extends LitElement {
191
198
  const isSSTEnabled = this.hasSSTEnabledDesktop || this.hasSSTEnabledMobile;
192
199
  const isTextUsEnabled =
193
200
  this.hasTextUsEnabledDesktop || this.hasTextUsEnabledMobile;
201
+ const isApplyNowEnabled =
202
+ this.hasApplyNowEnabledDesktop || this.hasApplyNowEnabledMobile;
194
203
 
195
204
  return [
196
205
  isEmailEnabled,
197
206
  isPhoneNumberEnabled,
198
207
  isSSTEnabled,
199
208
  isTextUsEnabled,
209
+ isApplyNowEnabled,
200
210
  ].filter((v) => v).length;
201
211
  };
202
212
 
@@ -251,6 +261,13 @@ export class Launcher extends LitElement {
251
261
  this.isCallUsWindowOpen = true;
252
262
  };
253
263
 
264
+ onClickApplyNowOption = (e: MouseEvent): void => {
265
+ e.preventDefault();
266
+ e.stopPropagation();
267
+ closeRengrataWidget();
268
+ window.open(this.applicationLink, "_blank");
269
+ };
270
+
254
271
  onClosePhoneWindow = (): void => {
255
272
  this.isCallUsWindowOpen = false;
256
273
  };
@@ -313,6 +330,7 @@ export class Launcher extends LitElement {
313
330
  .onClickPhoneOption=${this.onClickPhoneOption}
314
331
  .onClickSSTOption=${this.onClickSSTOption}
315
332
  .onChatTapped=${this.onChatTapped}
333
+ .onClickApplyNowOption=${this.onClickApplyNowOption}
316
334
  .overrideRentgrata=${this.overrideRentgrata}
317
335
  .isMobileDesign=${isMobile() ||
318
336
  this.designConcept === DesignConcepts.MINIMIZED}
@@ -341,6 +359,12 @@ export class Launcher extends LitElement {
341
359
  : this.hasHideMobileFeatures && !this.isMobileFeaturesExpanded
342
360
  ? false
343
361
  : this.hasSSTEnabledMobile}
362
+ .hasApplyNowEnabled=${!isMobile()
363
+ ? this.hasApplyNowEnabledDesktop
364
+ : this.hasHideMobileFeatures && !this.isMobileFeaturesExpanded
365
+ ? false
366
+ : this.hasApplyNowEnabledMobile}
367
+ .applicationLink=${this.applicationLink}
344
368
  ></mobile-launcher>`;
345
369
  }
346
370
 
@@ -758,6 +782,43 @@ export class Launcher extends LitElement {
758
782
  `
759
783
  : null,
760
784
  },
785
+ {
786
+ pillKey: "Apply",
787
+ pill:
788
+ this.applicationLink && this.hasApplyNowEnabledDesktop
789
+ ? html`
790
+ <div
791
+ @click=${this.onClickApplyNowOption}
792
+ class="inner-pill-wrapper"
793
+ style=${styleMap({
794
+ background: this.backgroundColor,
795
+ })}
796
+ >
797
+ <div class="vertical-pill-left">
798
+ <div class="vertical-pill-icon">
799
+ ${ApplyOutlineIcon(
800
+ this.foregroundColorOnSecondaryBackgroundColor
801
+ )}
802
+ </div>
803
+ <div
804
+ class="vertical-pill-title"
805
+ style=${styleMap({
806
+ color: this.foregroundColorOnSecondaryBackgroundColor,
807
+ })}
808
+ >
809
+ <span class="vertical-pill-bold">Apply</span>
810
+ now
811
+ </div>
812
+ </div>
813
+ <div class="chevron-right">
814
+ ${ChevronRightIcon(
815
+ this.foregroundColorOnSecondaryBackgroundColor
816
+ )}
817
+ </div>
818
+ </div>
819
+ `
820
+ : null,
821
+ },
761
822
  ];
762
823
  const pills = verticalPillListItems.filter((v) => !!v.pill);
763
824
  if (showTourAtTop) {
@@ -7,6 +7,7 @@ import { EmailOutlineIcon } from "../icons/EmailOutlineIcon";
7
7
  import { PhoneOutlineIcon } from "../icons/PhoneOutlineIcon";
8
8
  import { BookTourOutlineIcon } from "../icons/BookTourOutlineIcon";
9
9
  import { ChatOutlineIcon } from "../icons/ChatOutlineIcon";
10
+ import { ApplyOutlineIcon } from "../icons/ApplyOutlineIcon";
10
11
 
11
12
  @customElement("mobile-launcher")
12
13
  export class MobileLauncher extends LitElement {
@@ -30,6 +31,10 @@ export class MobileLauncher extends LitElement {
30
31
  return;
31
32
  };
32
33
  @property({ attribute: true })
34
+ onClickApplyNowOption: (e: MouseEvent) => void = () => {
35
+ return;
36
+ };
37
+ @property({ attribute: true })
33
38
  onChatTapped: (e: MouseEvent) => void = () => {
34
39
  return;
35
40
  };
@@ -61,6 +66,12 @@ export class MobileLauncher extends LitElement {
61
66
  @property({ attribute: true })
62
67
  hasSSTEnabled = false;
63
68
 
69
+ @property({ attribute: true })
70
+ hasApplyNowEnabled = false;
71
+
72
+ @property({ attribute: true })
73
+ applicationLink = "";
74
+
64
75
  @property({ attribute: true })
65
76
  overrideRentgrata = false;
66
77
 
@@ -93,6 +104,11 @@ export class MobileLauncher extends LitElement {
93
104
  render(): TemplateResult {
94
105
  return html`<div class="typeMobile-list">
95
106
  <ul>
107
+ ${this.renderListElement(
108
+ !!(this.hasApplyNowEnabled && this.applicationLink),
109
+ this.onClickApplyNowOption,
110
+ ApplyOutlineIcon("black")
111
+ )}
96
112
  ${this.renderListElement(
97
113
  this.hasEmailEnabled,
98
114
  this.onClickEmailOption,
@@ -15,6 +15,7 @@ import {
15
15
  fetchFeatureFlagMaintenanceMode,
16
16
  fetchFeatureFlagReplaceScheduleTourCtaWebsite,
17
17
  fetchFeatureFlagShowMarketingSourceDropdown,
18
+ fetchFeatureFlagUseApplicationsLinkReplacement,
18
19
  fetchFeatureFlagUseOverrideContactUsForm,
19
20
  fetchFeatureFlagUsePhoneNumberBySource,
20
21
  } from "../fetchFeatureFlag";
@@ -66,6 +67,7 @@ import LeadSourceClient, {
66
67
  import noop from "lodash/noop";
67
68
  import { updateRentgrataToken } from "../rentgrata";
68
69
  import { WidgetType } from "../main/MEChat";
70
+ import { replaceSelectButtonsWithNewLink } from "../replaceSelectButtonsWithNewLink";
69
71
 
70
72
  @customElement("me-chat")
71
73
  export class MEChat extends LitElement {
@@ -433,6 +435,7 @@ export class MEChat extends LitElement {
433
435
  featureFlagUseDNI,
434
436
  featureFlagInsertDNIWebsite,
435
437
  featureFlagReplaceScheduleTourCtaWebsite,
438
+ featureFlagUseApplicationsLinkReplacement,
436
439
  ] = await Promise.all([
437
440
  fetchBuildingABTestType(this.buildingSlug),
438
441
  fetchLeadSources(this.orgSlug, this.buildingSlug),
@@ -440,6 +443,7 @@ export class MEChat extends LitElement {
440
443
  fetchFeatureFlagUsePhoneNumberBySource(this.buildingSlug),
441
444
  fetchFeatureFlagInsertDNIWebsite(this.buildingSlug),
442
445
  fetchFeatureFlagReplaceScheduleTourCtaWebsite(this.buildingSlug),
446
+ fetchFeatureFlagUseApplicationsLinkReplacement(this.buildingSlug),
443
447
  ]);
444
448
 
445
449
  if (this.buildingWebchatView) {
@@ -594,6 +598,27 @@ export class MEChat extends LitElement {
594
598
  }
595
599
  }
596
600
  getRawAvailabilities(buildingDetails.id); // we're not using this here, just want to cache the result
601
+
602
+ if (featureFlagUseApplicationsLinkReplacement && this.buildingSlug) {
603
+ const totalReplacements = replaceSelectButtonsWithNewLink(
604
+ `https://applications.eliseai.com/building/${this.buildingSlug}`,
605
+ this.orgSlug,
606
+ this.buildingSlug
607
+ );
608
+ if (totalReplacements > 0) {
609
+ logSignal({
610
+ params: {
611
+ org_slug: this.orgSlug,
612
+ building_slug: this.buildingSlug,
613
+ is_application_link_replacement_success: true,
614
+ extra_data: {
615
+ total_replacements: totalReplacements,
616
+ },
617
+ },
618
+ sampleRate: this.sampleRateToUse,
619
+ });
620
+ }
621
+ }
597
622
  };
598
623
 
599
624
  private initializeChatVariables = async (): Promise<void> => {
@@ -989,6 +1014,11 @@ export class MEChat extends LitElement {
989
1014
  .hasTextUsEnabledMobile=${this.enabledChatWidgets.textMobile}
990
1015
  .hasSSTEnabledDesktop=${this.enabledChatWidgets.sstDesktop}
991
1016
  .hasSSTEnabledMobile=${this.enabledChatWidgets.sstMobile}
1017
+ .hasApplyNowEnabledDesktop=${this.buildingWebchatView
1018
+ .shouldShowApplyNowDesktop}
1019
+ .hasApplyNowEnabledMobile=${this.buildingWebchatView
1020
+ .shouldShowApplyNowMobile}
1021
+ .applicationLink=${this.buildingWebchatView.applicationLink}
992
1022
  .hasHideMobileFeatures=${this.hideMobileFeatures}
993
1023
  .top=${this.top}
994
1024
  .bottom=${this.bottom}
package/src/analytics.ts CHANGED
@@ -187,6 +187,7 @@ interface SignalLoggerProps {
187
187
  is_any_webchat_showing?: boolean | null;
188
188
  is_form_override_success?: boolean | null;
189
189
  is_dni_insert_override_success?: boolean | null;
190
+ is_application_link_replacement_success?: boolean | null;
190
191
  extra_data?: Record<string, unknown> | null;
191
192
  }
192
193
 
@@ -105,6 +105,10 @@ export interface BuildingWebchatView {
105
105
  shouldShowEmailMobile: boolean | null;
106
106
  shouldShowSstDesktop: boolean | null;
107
107
  shouldShowSstMobile: boolean | null;
108
+ shouldShowApplyNowDesktop: boolean | null;
109
+ shouldShowApplyNowMobile: boolean | null;
110
+ applicationLink: string | null;
111
+ isLiveOnCollect: boolean | null;
108
112
  requiresConsentForChat: boolean | null;
109
113
  privacyPolicyUrlForChat: string | null;
110
114
  collapseMobileFeatures: boolean | null;
@@ -223,3 +223,28 @@ export async function fetchFeatureFlagReplaceScheduleTourCtaWebsite(
223
223
  return false;
224
224
  }
225
225
  }
226
+
227
+ export async function fetchFeatureFlagUseApplicationsLinkReplacement(
228
+ buildingSlug: string
229
+ ): Promise<boolean> {
230
+ if (!buildingSlug) {
231
+ return false;
232
+ }
233
+ try {
234
+ const featureFlagResponse = await axios.get(
235
+ featureFlagEndpointV1(buildingSlug),
236
+ {
237
+ params: {
238
+ building_slug: buildingSlug,
239
+ flag_type: "bool",
240
+ feature_flag: "webchat-use-applications-link-replacement",
241
+ default_str: null,
242
+ default_bool: false,
243
+ },
244
+ }
245
+ );
246
+ return featureFlagResponse.data;
247
+ } catch (_) {
248
+ return false;
249
+ }
250
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Script to find all application links/buttons on a page and replace them with a
3
+ * new elise application link.
4
+ */
5
+ import { LogType, sendLoggingEvent } from "./analytics";
6
+
7
+ /**
8
+ * Script to find all "Select" buttons with specific URL patterns and replace them with a
9
+ * new application link.
10
+ */
11
+ export const replaceSelectButtonsWithNewLink = (
12
+ newApplicationLink: string,
13
+ orgSlug: string,
14
+ buildingSlug?: string
15
+ ): number => {
16
+ const urlPatterns = [
17
+ /thehavenatgreenhill\.securecafe\.com\/onlineleasing\/the-haven-at-greenhill\/oleapplication\.aspx(\?.*)?$/,
18
+ /securecafe\.com\/onlineleasing.*\/oleapplication\.aspx(\?.*)?$/,
19
+ /\/onlineleasing.*\/oleapplication\.aspx(\?.*)?$/,
20
+ ];
21
+
22
+ const allElements = document.body.getElementsByTagName("a");
23
+ let totalReplacements = 0;
24
+
25
+ for (let i = 0; i < allElements.length; i++) {
26
+ const element = allElements[i] as HTMLAnchorElement;
27
+ if (element.nodeType !== Node.ELEMENT_NODE) continue;
28
+
29
+ const matchesUrlPattern = urlPatterns.some((pattern) =>
30
+ pattern.test(element.href)
31
+ );
32
+ const isSelectButtonWithUrl = matchesUrlPattern;
33
+
34
+ if (isSelectButtonWithUrl) {
35
+ const oldHref = element.getAttribute("href");
36
+ element.setAttribute("href", newApplicationLink);
37
+ totalReplacements++;
38
+
39
+ sendLoggingEvent({
40
+ logTitle: "SELECT_BUTTON_LINK_REPLACEMENT",
41
+ logData: {
42
+ type: "link-replacement",
43
+ oldHref,
44
+ newHref: newApplicationLink,
45
+ website: window.location.href,
46
+ },
47
+ logType: LogType.info,
48
+ buildingSlug,
49
+ orgSlug,
50
+ });
51
+ }
52
+ }
53
+
54
+ if (totalReplacements === 0) {
55
+ sendLoggingEvent({
56
+ logTitle: "SELECT_BUTTON_LINK_REPLACEMENT",
57
+ logData: {
58
+ type: "no-select-buttons-found",
59
+ website: window.location.href,
60
+ },
61
+ logType: LogType.warn,
62
+ buildingSlug,
63
+ orgSlug,
64
+ });
65
+ }
66
+
67
+ return totalReplacements;
68
+ };