@meetelise/chat 1.20.63 → 1.20.65

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.
@@ -3,7 +3,7 @@ import { customElement, property, state } from "lit/decorators.js";
3
3
  import { classMap } from "lit/directives/class-map.js";
4
4
  import { createRef, ref, Ref } from "lit/directives/ref.js";
5
5
  import Talk from "talkjs";
6
- import { Launcher } from "./Launcher";
6
+ import { Launcher } from "./launcher/Launcher";
7
7
  import "./Scheduler/tour-scheduler";
8
8
  import Analytics from "../analytics";
9
9
  import { createChatID, getChatID } from "../chatID";
@@ -14,16 +14,15 @@ import {
14
14
  fetchFeatureFlagShowMarketingSourceDropdown,
15
15
  fetchFeatureFlagUsePhoneNumberBySource,
16
16
  } from "../fetchFeatureFlag";
17
- import fetchBuildingABTestType, {
18
- abTestTypes,
19
- } from "../fetchBuildingABTestType";
17
+ import fetchWebchatPreferences from "../fetchWebchatPreferences";
18
+ import fetchBuildingABTestType from "../fetchBuildingABTestType";
20
19
  import fetchCurrentParsedLeadSource from "../fetchCurrentParsedLeadSource";
21
20
  import fetchPhoneNumberFromSource, {
22
21
  NumberForSelectedSource,
23
22
  } from "../fetchPhoneNumberFromSource";
24
- import { getTheme, Theme, ThemeIdString } from "../themes";
23
+ import { defaultBrandColor, getTheme, Theme } from "../themes";
25
24
  import { isMobile } from "../utils";
26
- import { installLauncher } from "./Launcher";
25
+ import { installLauncher } from "./launcher/Launcher";
27
26
  import parseISO from "date-fns/parseISO";
28
27
  import isPast from "date-fns/isPast";
29
28
 
@@ -33,20 +32,10 @@ import { StyleInfo } from "lit/directives/style-map";
33
32
  import addMinutes from "date-fns/addMinutes";
34
33
  import formatISO from "date-fns/formatISO";
35
34
  import fetchLeadSources from "../fetchLeadSources";
36
- import {
37
- ChevronIconWhite,
38
- TabletWhiteStrokeIcon,
39
- WhiteStrokeTourIcon,
40
- } from "../svgIcons";
41
35
  import { formatPhoneNumber } from "./actions/formatPhoneNumber";
42
36
 
43
- export interface Options {
44
- building: string;
45
- organization: string;
46
- themeId?: ThemeIdString;
47
- avatarSrc?: string;
48
- mini?: boolean;
49
- }
37
+ import "./actions/minimize-expand-button";
38
+ import "./launcher/mobile-launcher";
50
39
 
51
40
  @customElement("me-chat")
52
41
  export class MEChat extends LitElement {
@@ -70,41 +59,30 @@ export class MEChat extends LitElement {
70
59
  .showTab {
71
60
  display: flex;
72
61
  align-items: center;
73
- justify-content: flex-start;
62
+ justify-content: space-between;
74
63
  gap: 32px;
75
64
  }
76
- #contactTabPopup {
65
+ #chatAdditionalActions {
77
66
  position: fixed;
78
- width: 300px;
79
- padding-left: 20px;
80
- padding-right: 20px;
81
- padding-bottom: 24px;
67
+ box-sizing: border-box;
68
+
69
+ box-sizing: border-box;
70
+ width: 340px;
71
+
72
+ padding-top: 0px;
73
+ padding-right: 12px;
74
+
82
75
  z-index: 1000000000;
83
- background: #000000;
84
- border-radius: 10px 0px 0px 0px;
85
- border-width: 1px 0px 0px 1px;
86
- border-style: solid;
87
- border-color: #ffffff;
88
76
  }
89
- .actionTabBttn {
90
- font-family: "Helvetica Neue", Arial;
91
- font-style: normal;
92
- font-size: 14px;
93
- line-height: 18px;
94
- color: #ffffff;
95
- border: none;
96
- background: none;
97
- padding: 0;
98
- display: flex;
99
- align-items: center;
100
- gap: 8px;
101
- }
102
- .actionTabBttn:hover {
103
- cursor: pointer;
104
- color: #f0f0f0;
105
- }
106
- .heavyLabel {
107
- font-weight: bold;
77
+ #chatAdditionalActions::after {
78
+ content: "";
79
+ position: absolute;
80
+ top: -6px;
81
+ right: 0;
82
+ width: 0;
83
+ height: 0;
84
+ border-bottom: 22px solid transparent;
85
+ border-right: 30px solid black;
108
86
  }
109
87
  `;
110
88
  static session: Promise<Talk.Session> = Talk.ready.then(() => {
@@ -132,10 +110,19 @@ export class MEChat extends LitElement {
132
110
  @property({ type: Object })
133
111
  launcherStyles: StyleInfo = {};
134
112
 
113
+ @property({ type: Boolean })
114
+ isMinimized = false;
115
+
116
+ @property({ type: String })
117
+ private brandColor: string | null = null;
118
+
135
119
  @state()
136
120
  private popup: Talk.Popup | null = null;
137
121
  @state()
138
- private theme: Theme = getTheme();
122
+ private theme: Theme = getTheme({
123
+ themeId: this.themeId,
124
+ brandColor: this.brandColor,
125
+ });
139
126
  @state()
140
127
  private chatId = "";
141
128
  @state()
@@ -187,6 +174,7 @@ export class MEChat extends LitElement {
187
174
  currentLeadSource,
188
175
  featureFlagShowDropdown,
189
176
  featureFlagUseDNI,
177
+ webchatPreferences,
190
178
  ] = await Promise.all([
191
179
  fetchBuildingInfo(this.orgSlug, this.buildingSlug),
192
180
  fetchBuildingABTestType(this.buildingSlug),
@@ -194,6 +182,7 @@ export class MEChat extends LitElement {
194
182
  fetchCurrentParsedLeadSource(this.buildingSlug, document.referrer),
195
183
  fetchFeatureFlagShowMarketingSourceDropdown(this.buildingSlug),
196
184
  fetchFeatureFlagUsePhoneNumberBySource(this.buildingSlug),
185
+ fetchWebchatPreferences(this.buildingSlug),
197
186
  ]);
198
187
 
199
188
  building.phoneNumber = formatPhoneNumber(building.phoneNumber);
@@ -203,6 +192,19 @@ export class MEChat extends LitElement {
203
192
  this.currentLeadSource = currentLeadSource;
204
193
  this.featureFlagShowDropdown = featureFlagShowDropdown;
205
194
 
195
+ if (webchatPreferences) {
196
+ if (this.brandColor === null) {
197
+ this.brandColor = webchatPreferences.primaryColor ?? null;
198
+ }
199
+ if (this.buildingABTestType === null) {
200
+ this.buildingABTestType = webchatPreferences.designConcept ?? null;
201
+ }
202
+ }
203
+
204
+ if (this.brandColor === null) {
205
+ this.brandColor = defaultBrandColor;
206
+ }
207
+
206
208
  let phoneNumberForSource = null;
207
209
  if (featureFlagUseDNI) {
208
210
  phoneNumberForSource = await fetchPhoneNumberFromSource(
@@ -230,7 +232,10 @@ export class MEChat extends LitElement {
230
232
  getRawAvailabilities(this.building.id); // we're not using this here, just want to cache the result
231
233
  this.chatId = getChatID(this.orgSlug, this.buildingSlug);
232
234
  this.avatarSrc = this.avatarSrc || this.building.avatarSrc;
233
- this.theme = getTheme(this.themeId ?? this.building.themeId);
235
+ this.theme = getTheme({
236
+ themeId: this.themeId ?? this.building.themeId,
237
+ brandColor: this.brandColor,
238
+ });
234
239
  this.analytics = new Analytics(
235
240
  this.orgSlug,
236
241
  this.buildingSlug,
@@ -352,17 +357,11 @@ export class MEChat extends LitElement {
352
357
  (talkjsPopupElement as HTMLElement).style.zIndex = "99999999999";
353
358
  this.popup = popup;
354
359
 
355
- const forceClose = true; // if we want to always keep the widget in the closed state
356
360
  const autoOpenedTimestamp = sessionStorage.getItem("autoOpenedTimestamp");
357
361
  const shouldAutoOpen =
358
362
  !autoOpenedTimestamp ||
359
363
  (autoOpenedTimestamp && isPast(parseISO(autoOpenedTimestamp)));
360
- if (
361
- !forceClose &&
362
- building.autoOpenChatWidget &&
363
- shouldAutoOpen &&
364
- !isMobile()
365
- ) {
364
+ if (building.autoOpenChatWidget && shouldAutoOpen && !isMobile()) {
366
365
  this.popup.show();
367
366
  this.hideLauncher = true;
368
367
  this.hasMounted = true;
@@ -442,11 +441,13 @@ export class MEChat extends LitElement {
442
441
  // of the talkjs popup - so we must adjust its coords on resize and mount
443
442
  adjustTopHeaderContactCoords = (): void => {
444
443
  if (this.talkjsPopupElement) {
445
- const currentHeaderCoords = getOffset(this.talkjsPopupElement);
446
- const headerRef = this.shadowRoot?.getElementById("contactTabPopup");
444
+ const talkjsPopupCoords = this.talkjsPopupElement.getBoundingClientRect();
445
+ const headerRef = this.shadowRoot?.getElementById(
446
+ "chatAdditionalActions"
447
+ );
447
448
  if (!headerRef) return;
448
- headerRef.style.left = `${currentHeaderCoords.left + 20}px`;
449
- headerRef.style.top = `${currentHeaderCoords.top - 50}px`;
449
+ headerRef.style.left = `${talkjsPopupCoords.left + 20}px`;
450
+ headerRef.style.top = `${talkjsPopupCoords.bottom - 24}px`;
450
451
  }
451
452
  };
452
453
  connectedCallback(): void {
@@ -461,14 +462,17 @@ export class MEChat extends LitElement {
461
462
  this.adjustTopHeaderContactCoords();
462
463
  }
463
464
 
465
+ onClickMinimize = (e: MouseEvent): void => {
466
+ e.preventDefault();
467
+ e.stopPropagation();
468
+ this.isMinimized = !this.isMinimized;
469
+ };
470
+
464
471
  render(): TemplateResult {
465
472
  installLauncher();
466
- const hideTopContactTab =
467
- !this.hideLauncher ||
468
- this.isLoading ||
469
- (this.buildingABTestType !== abTestTypes.ConceptBlueBlack &&
470
- this.buildingABTestType !== abTestTypes.ConceptBlueWhite) ||
471
- this.isMobile;
473
+
474
+ const showChatAdditionalActions =
475
+ this.hideLauncher && !this.isLoading && !this.isMobile;
472
476
 
473
477
  return html`
474
478
  <meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=1">
@@ -476,24 +480,6 @@ export class MEChat extends LitElement {
476
480
  EliseAI widget that allows you to chat with a virtual assistant, book
477
481
  a tour, contact the leasing office, and more.
478
482
  </div>
479
- <div
480
- id="contactTabPopup"
481
- class=${classMap({
482
- ["showTab"]: !hideTopContactTab,
483
- ["hideTab"]: hideTopContactTab,
484
- })}
485
- >
486
- <button class="actionTabBttn" @click=${this.handleTourClicked}>
487
- ${WhiteStrokeTourIcon}
488
- <p><span class="heavyLabel">Book</span> a tour</p>
489
- ${ChevronIconWhite}
490
- </button>
491
- <button class="actionTabBttn" @click=${this.handleContactTabClicked}>
492
- ${TabletWhiteStrokeIcon}
493
- <p><span class="heavyLabel">Contact</span> us</p>
494
- ${ChevronIconWhite}
495
- </button>
496
- </div>
497
483
  <div
498
484
  class=${classMap({
499
485
  launcherContainer: true,
@@ -517,6 +503,9 @@ export class MEChat extends LitElement {
517
503
  .unitOptions=${this.building?.unitOptionsV2 ?? []}
518
504
  .tourTypeOptions=${this.building?.tourTypeOptions ?? []}
519
505
  .launcherStyles=${this.launcherStyles}
506
+ .brandColor=${this.brandColor}
507
+ .isMinimized=${this.isMinimized}
508
+ .onClickMinimize=${this.onClickMinimize}
520
509
  .autoOpenChatWidget=${this.building.autoOpenChatWidget ??
521
510
  false}
522
511
  chatCallUsHeader=${this.building?.chatCallUsHeader ?? ""}
@@ -555,6 +544,31 @@ export class MEChat extends LitElement {
555
544
  </meetelise-launcher>`
556
545
  : ""
557
546
  }
547
+
548
+ </div>
549
+ <div
550
+ id="chatAdditionalActions"
551
+ class=${classMap({
552
+ ["showTab"]: showChatAdditionalActions,
553
+ ["hideTab"]: !showChatAdditionalActions,
554
+ })}
555
+ >
556
+ <mobile-launcher
557
+ .onClickMinimize=${this.onClickMinimize}
558
+ .onClickEmailOption=${this.handleContactClicked}
559
+ .onClickPhoneOption=${this.handleContactTabClicked}
560
+ .onClickSSTOption=${this.handleTourClicked}
561
+ .isMobile=${this.isMobile}
562
+ .brandColor=${this.brandColor}
563
+ .hideChat=${true}
564
+ ></mobile-launcher>
565
+ <minimize-expand-button
566
+ .brandColor=${this.brandColor}
567
+ .onClick=${() => {
568
+ this.popup?.hide();
569
+ this.hideLauncher = false;
570
+ }}
571
+ ></minimize-expand-button>
558
572
  </div>
559
573
  </meta>
560
574
  `;
@@ -588,11 +602,3 @@ declare global {
588
602
  RCTPCampaign?: { CampaignDetails: { Source: string } };
589
603
  }
590
604
  }
591
-
592
- function getOffset(el: Element) {
593
- const rect = el.getBoundingClientRect();
594
- return {
595
- left: rect.left,
596
- top: rect.top,
597
- };
598
- }
@@ -119,6 +119,10 @@ export class MESelect extends LitElement {
119
119
  align-items: center;
120
120
  justify-content: space-between;
121
121
  color: rgba(32, 32, 32, 0.5);
122
+
123
+ white-space: nowrap;
124
+ overflow: hidden;
125
+ text-overflow: ellipsis;
122
126
  }
123
127
 
124
128
  ::-webkit-scrollbar {
@@ -3,8 +3,7 @@ interface BuildingABType {
3
3
  abTestType: string;
4
4
  }
5
5
  export enum abTestTypes {
6
- "ConceptBlueBlack" = "Concept 1 (Blue/Black)",
7
- "ConceptBlueWhite" = "Concept 2 (Blue/White)",
6
+ "ConceptEmoji" = "Concept 2 (Blue/White)",
8
7
  }
9
8
  export default async function fetchBuildingABTestType(
10
9
  buildingSlug: string
@@ -14,6 +13,7 @@ export default async function fetchBuildingABTestType(
14
13
  const abTestTypeResponse = await axios.get(
15
14
  `${host}/platformApi/webchat/${buildingSlug}/ab-test-type`
16
15
  );
16
+ //return { abTestType: "Concept 2 (Blue/White)" };
17
17
  return abTestTypeResponse.data;
18
18
  } catch (_) {
19
19
  return null;
@@ -0,0 +1,28 @@
1
+ import axios from "axios";
2
+
3
+ export interface WebchatPreferences {
4
+ primaryColor: string;
5
+ designConcept: string;
6
+ delayOpen: number;
7
+ }
8
+
9
+ export default async function fetchWebchatPreferences(
10
+ buildingSlug: string
11
+ ): Promise<WebchatPreferences | null> {
12
+ const host = "https://app.meetelise.com";
13
+ try {
14
+ const webchatPreferencesResponse = await axios.get(
15
+ `${host}/platformApi/webchat/${buildingSlug}/customization`
16
+ );
17
+ if (webchatPreferencesResponse.data) {
18
+ return {
19
+ primaryColor: webchatPreferencesResponse.data["primaryColor"],
20
+ designConcept: webchatPreferencesResponse.data["designConcept"],
21
+ delayOpen: +webchatPreferencesResponse.data["delayOpen"],
22
+ };
23
+ }
24
+ return null;
25
+ } catch (_) {
26
+ return null;
27
+ }
28
+ }