@meetelise/chat 1.20.113 → 1.20.115

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.
@@ -39,6 +39,12 @@ object-assign
39
39
  * SPDX-License-Identifier: BSD-3-Clause
40
40
  */
41
41
 
42
+ /**
43
+ * @license
44
+ * Copyright 2019 Google LLC
45
+ * SPDX-License-Identifier: BSD-3-Clause
46
+ */
47
+
42
48
  /**
43
49
  * @license
44
50
  * Copyright 2020 Google LLC
package/src/MEChat.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import axios from "axios";
2
2
  import { StyleInfo } from "lit/directives/style-map";
3
- import { ThemeIdString } from "./themes";
4
- import { tintColor } from "./utils";
5
3
  import "./WebComponent/me-chat";
6
4
  import { MEChat as MEChatLitElement } from "./WebComponent/me-chat";
7
5
  import fetchBuildingInfo from "./fetchBuildingInfo";
8
6
  import { fetchFeatureFlagUseOverrideContactUsForm } from "./fetchFeatureFlag";
9
7
  import fetchCurrentParsedLeadSource from "./fetchCurrentParsedLeadSource";
8
+ import { tintColor } from "./themes";
10
9
 
11
10
  /**
12
11
  * The interface to MeetElise chat.
@@ -42,7 +41,6 @@ export default class MEChat {
42
41
  this.hasBuildingSlug = !!opts.building;
43
42
  }
44
43
  installFont();
45
- installTalkjsMobileStyleFix();
46
44
  const meChat = document.createElement("me-chat");
47
45
  meChat.setAttribute("orgSlug", opts.organization);
48
46
  this.handleBuildingslug(meChat, opts.building);
@@ -51,15 +49,6 @@ export default class MEChat {
51
49
  meChat.setAttribute("aria-label", "EliseAI Widget");
52
50
  meChat.setAttribute("aria-describedby", "aria-describe-info");
53
51
  meChat.setAttribute("aria-modal", "true");
54
- if (opts.themeId) {
55
- meChat.setAttribute("themeId", opts.themeId);
56
- }
57
- if (opts.avatarSrc) {
58
- meChat.setAttribute("avatarSrc", opts.avatarSrc);
59
- }
60
- if (opts.mini !== undefined) {
61
- meChat.useMiniWidget = opts.mini;
62
- }
63
52
  if (opts.launcherStyles) {
64
53
  meChat.launcherStyles = opts.launcherStyles;
65
54
  }
@@ -154,7 +143,6 @@ export default class MEChat {
154
143
  export interface Options {
155
144
  building: string;
156
145
  organization: string;
157
- themeId?: ThemeIdString;
158
146
  avatarSrc?: string;
159
147
  mini?: boolean;
160
148
  launcherStyles?: StyleInfo;
@@ -169,16 +157,6 @@ const installFont = () => {
169
157
  document.head.appendChild(link);
170
158
  };
171
159
 
172
- const installTalkjsMobileStyleFix = () => {
173
- const metaTag = document.createElement("meta");
174
- metaTag.setAttribute("name", "viewport");
175
- metaTag.setAttribute(
176
- "content",
177
- "width=device-width, initial-scale=1, maximum-scale=2"
178
- );
179
- document.head.appendChild(metaTag);
180
- };
181
-
182
160
  const findElementById = (priorityIds: string[]): HTMLElement | null => {
183
161
  for (const id of priorityIds) {
184
162
  const element = document.getElementById(id);
@@ -392,6 +370,7 @@ const logContactUsFormError = (
392
370
  orgSlug,
393
371
  buildingSlug,
394
372
  reason,
373
+ url: window.location.href,
395
374
  });
396
375
 
397
376
  fetch(url, {
package/src/MyPubnub.ts CHANGED
@@ -439,7 +439,10 @@ class MyPubnub {
439
439
  return false;
440
440
  }
441
441
 
442
- const expirationDate = addHours(new Date(), this.ttlHours);
442
+ const expirationDate = addHours(
443
+ storageValueDeconstructed.timestamp,
444
+ this.ttlHours
445
+ );
443
446
  return isBefore(storageValueDeconstructed.timestamp, expirationDate);
444
447
  };
445
448
  }
@@ -1,12 +1,22 @@
1
+ import axios from "axios";
1
2
  import { css, html, LitElement, TemplateResult } from "lit";
2
- import { customElement, property } from "lit/decorators.js";
3
+ import { customElement, property, state } from "lit/decorators.js";
4
+ import { createRef, ref, Ref } from "lit/directives/ref.js";
5
+ import { pushGtmEvent } from "../../gtm";
3
6
  import { PhoneBlackStrokeIcon } from "../../svgIcons";
4
7
 
5
8
  import { getOfficeHourText } from "../OfficeHours";
9
+ import {
10
+ formatToPhoneInput,
11
+ isModifierKey,
12
+ isNumericInput,
13
+ } from "./formatPhoneNumber";
14
+ import { InputStyles } from "./InputStyles";
6
15
 
7
16
  @customElement("call-us-window")
8
17
  export class CallUsWindow extends LitElement {
9
18
  static styles = [
19
+ InputStyles,
10
20
  css`
11
21
  #call-us-window__content-wrapper {
12
22
  font-family: "Helvetica Neue", Arial;
@@ -73,30 +83,88 @@ export class CallUsWindow extends LitElement {
73
83
  text-transform: lowercase;
74
84
  text-align: right;
75
85
  }
86
+
87
+ .text-us-wrapper {
88
+ margin-top: 33px;
89
+ color: #202020;
90
+ }
91
+ .text-us-window__header {
92
+ font-family: "Helvetica Neue", Arial;
93
+ font-style: normal;
94
+ font-weight: 700;
95
+ line-height: 140%;
96
+ font-size: 14px;
97
+ margin: 0;
98
+ }
99
+
100
+ .text-us-window__input-container {
101
+ margin-top: 8px;
102
+ margin-bottom: 8px;
103
+ width: -webkit-fill-available;
104
+ position: relative;
105
+ }
106
+ .text-us-window__submit-text {
107
+ position: absolute;
108
+ top: 14px;
109
+ right: 6px;
110
+ }
111
+ .text-us-window__vertical-spacer {
112
+ height: 20px;
113
+ }
114
+ .text-us-window__description,
115
+ .text-us-window__error {
116
+ font-family: "Helvetica Neue", Arial;
117
+ font-style: normal;
118
+ font-weight: 400;
119
+ font-size: 10px;
120
+ line-height: 14px;
121
+ }
122
+
123
+ .text-us-window__error {
124
+ color: rgb(255, 49, 3);
125
+ }
76
126
  `,
77
127
  ];
78
128
 
129
+ @property({ attribute: true })
130
+ buildingSlug = "";
131
+ @property({ attribute: true })
132
+ orgSlug = "";
133
+
79
134
  @property({ attribute: false })
80
135
  onCloseClicked?: (e: MouseEvent) => void;
81
136
 
82
137
  @property()
83
- phoneNumber = "1-855-531-5131";
138
+ phoneNumberToCall = "1-855-531-5131";
139
+
140
+ @property()
141
+ phoneNumberToText = "";
142
+
84
143
  @property({ attribute: false })
85
144
  buildingId = 0;
86
145
  @property({ attribute: true })
87
146
  chatCallUsHeader = "";
147
+
148
+ @property({ attribute: true })
149
+ hasCallUsEnabled = "";
150
+
88
151
  @property({ attribute: true })
89
152
  hasTextUsEnabled = "";
90
153
 
91
154
  @property({ attribute: true })
92
155
  currentLeadSource = "";
93
156
 
94
- firstUpdated = async (): Promise<void> => {
95
- if (!this.buildingId) {
96
- return;
97
- }
98
- this.officeHoursTextInfo = await getOfficeHourText(this.buildingId);
99
- };
157
+ phoneNumberInputRef: Ref<HTMLInputElement> = createRef();
158
+
159
+ @state()
160
+ hasSubmittedForm = false;
161
+ @state()
162
+ hasPhoneNumberError = false;
163
+ @state()
164
+ hasSubmissionError = false;
165
+
166
+ @state()
167
+ submittingTextUsOption = false;
100
168
 
101
169
  @property({ attribute: false })
102
170
  officeHoursTextInfo: (
@@ -104,6 +172,11 @@ export class CallUsWindow extends LitElement {
104
172
  | { dayOfWeek: string; startTime: string; endTime: string }
105
173
  )[] = [];
106
174
 
175
+ firstUpdated = async (): Promise<void> => {
176
+ if (!this.buildingId || !this.hasCallUsEnabled) return;
177
+ this.officeHoursTextInfo = await getOfficeHourText(this.buildingId);
178
+ };
179
+
107
180
  getCallLink = (phoneNumber: string): TemplateResult => {
108
181
  const rawNumber = (phoneNumber.match(/\d+/g) || []).join("");
109
182
  const rawInternationalNumber =
@@ -119,6 +192,126 @@ export class CallUsWindow extends LitElement {
119
192
  >`;
120
193
  };
121
194
 
195
+ enforcePhoneFormat = (e: KeyboardEvent): void => {
196
+ if (!isNumericInput(e) && !isModifierKey(e)) {
197
+ e.preventDefault();
198
+ }
199
+ };
200
+ onSubmitTextUs = async (): Promise<void> => {
201
+ this.hasPhoneNumberError = false;
202
+ this.hasSubmissionError = false;
203
+ if (!this.phoneNumberToText || this.phoneNumberToText.length !== 14) {
204
+ this.hasPhoneNumberError = true;
205
+ }
206
+ if (this.hasPhoneNumberError) {
207
+ return;
208
+ }
209
+ try {
210
+ this.submittingTextUsOption = true;
211
+ await createTextWithUs(
212
+ this.phoneNumberToText,
213
+ this.buildingId,
214
+ this.orgSlug,
215
+ this.buildingSlug,
216
+ [
217
+ ...new Set(
218
+ this.currentLeadSource
219
+ ? [this.currentLeadSource, "property-website"]
220
+ : ["property-website"]
221
+ ),
222
+ ]
223
+ );
224
+ this.hasSubmittedForm = true;
225
+ this.submittingTextUsOption = false;
226
+ } catch (e) {
227
+ this.submittingTextUsOption = false;
228
+ this.hasSubmissionError = true;
229
+ }
230
+ };
231
+
232
+ onChangePhoneNumber = (e: Event): void => {
233
+ if (!e.target || !this.phoneNumberInputRef.value) {
234
+ return;
235
+ }
236
+ if (isModifierKey(e as KeyboardEvent)) {
237
+ return;
238
+ }
239
+ const inputElement = e.target as HTMLInputElement;
240
+
241
+ this.phoneNumberToText = formatToPhoneInput(inputElement.value);
242
+
243
+ this.phoneNumberInputRef.value.value = this.phoneNumberToText;
244
+ };
245
+
246
+ private renderFormTextUsOption = (): TemplateResult => {
247
+ return html` <div class="text-us-wrapper">
248
+ <h1 class="text-us-window__header">Send Us a Text</h1>
249
+ <div class="text-us-window__input-container">
250
+ <input
251
+ ${ref(this.phoneNumberInputRef)}
252
+ maxlength="14"
253
+ type="text"
254
+ placeholder="Enter phone"
255
+ inputmode="tel"
256
+ class="webchat-input text-us-window__input-container"
257
+ .value=${this.phoneNumberToText}
258
+ @keydown=${this.enforcePhoneFormat}
259
+ @keyup=${this.onChangePhoneNumber}
260
+ />
261
+ <div class="text-us-window__submit-text">
262
+ <action-confirm-button
263
+ .onClick=${this.onSubmitTextUs}
264
+ .isLoading=${this.submittingTextUsOption}
265
+ text="SEND"
266
+ ></action-confirm-button>
267
+ </div>
268
+ ${this.hasPhoneNumberError
269
+ ? html`
270
+ <div class="text-us-window__error">
271
+ Enter a valid phone number
272
+ </div>
273
+ `
274
+ : ""}
275
+ </div>
276
+
277
+ <div class="text-us-window__description">
278
+ By entering your number and selecting Send, you consent to be contacted
279
+ by our AI Leasing Assistant. Your consent to this process is not a
280
+ requirement for leasing at our property.
281
+ <div class="text-us-window__description">
282
+ Message and data rates may apply.
283
+ </div>
284
+ </div>
285
+ </div>`;
286
+ };
287
+
288
+ private renderSubmittedTextUsOption = (): TemplateResult => {
289
+ return html`
290
+ <div class="text-us-wrapper">
291
+ <div class="text-us-window__vertical-spacer"></div>
292
+ <svg
293
+ width="20"
294
+ height="20"
295
+ viewBox="0 0 20 20"
296
+ fill="none"
297
+ xmlns="http://www.w3.org/2000/svg"
298
+ >
299
+ <path
300
+ d="M4.455 16L0 19.5V1C0 0.734784 0.105357 0.48043 0.292893 0.292893C0.48043 0.105357 0.734784 0 1 0H19C19.2652 0 19.5196 0.105357 19.7071 0.292893C19.8946 0.48043 20 0.734784 20 1V15C20 15.2652 19.8946 15.5196 19.7071 15.7071C19.5196 15.8946 19.2652 16 19 16H4.455ZM2 15.385L3.763 14H18V2H2V15.385ZM10 7V4L14 8L10 12V9H6V7H10Z"
301
+ fill="#202020"
302
+ />
303
+ </svg>
304
+ <div class="text-us-window__description">
305
+ Thank you!<br />Look for a text message from our team. We can answer
306
+ questions and help you book a tour through text.
307
+ </div>
308
+ <div class="text-us-window__description">
309
+ Opt out at anytime by texting “Stop”
310
+ </div>
311
+ </div>
312
+ `;
313
+ };
314
+
122
315
  render = (): TemplateResult => {
123
316
  return html`
124
317
  <details-window
@@ -126,43 +319,46 @@ export class CallUsWindow extends LitElement {
126
319
  headerText="${this.chatCallUsHeader}"
127
320
  .onCloseClick=${this.onCloseClicked}
128
321
  >
129
- <div id="call-us-window__content-wrapper">
130
- <h1 class="call-us-window__call-us-text call-us-window__header">
131
- Contact Our Leasing Team
132
- </h1>
133
- <div class="call-us-window__phone-redirect">
134
- ${PhoneBlackStrokeIcon} ${this.getCallLink(this.phoneNumber)}
135
- </div>
136
- <div class="call-us-window__office-hours-wrapper">
137
- <h1 class="call-us-window__oh-header call-us-window__header">
138
- Office Hours
139
- </h1>
140
- ${this.officeHoursTextInfo.map((oh) => {
141
- if (typeof oh === "string") {
142
- return html`
143
- <div class="call-us-window__day-of-week-wrapper">
144
- <div class="call-us-window__day-of-week">${oh}</div>
145
- <div class="call-us-window__closed">Closed</div>
146
- </div>
147
- `;
148
- }
149
- return html`
150
- <div class="call-us-window__day-of-week-wrapper">
151
- <div class="call-us-window__day-of-week">${oh.dayOfWeek}</div>
152
- <div class="call-us-window__open-times">
153
- ${oh.startTime}&nbsp;&ndash;&nbsp;${oh.endTime}
154
- </div>
155
- </div>
156
- `;
157
- })}
158
- </div>
159
- </div>
322
+ ${this.hasCallUsEnabled
323
+ ? html` <div id="call-us-window__content-wrapper">
324
+ <h1 class="call-us-window__call-us-text call-us-window__header">
325
+ Contact Our Leasing Team
326
+ </h1>
327
+ <div class="call-us-window__phone-redirect">
328
+ ${PhoneBlackStrokeIcon}
329
+ ${this.getCallLink(this.phoneNumberToCall)}
330
+ </div>
331
+ <div class="call-us-window__office-hours-wrapper">
332
+ <h1 class="call-us-window__oh-header call-us-window__header">
333
+ Office Hours
334
+ </h1>
335
+ ${this.officeHoursTextInfo.map((oh) => {
336
+ if (typeof oh === "string") {
337
+ return html`
338
+ <div class="call-us-window__day-of-week-wrapper">
339
+ <div class="call-us-window__day-of-week">${oh}</div>
340
+ <div class="call-us-window__closed">Closed</div>
341
+ </div>
342
+ `;
343
+ }
344
+ return html`
345
+ <div class="call-us-window__day-of-week-wrapper">
346
+ <div class="call-us-window__day-of-week">
347
+ ${oh.dayOfWeek}
348
+ </div>
349
+ <div class="call-us-window__open-times">
350
+ ${oh.startTime}&nbsp;&ndash;&nbsp;${oh.endTime}
351
+ </div>
352
+ </div>
353
+ `;
354
+ })}
355
+ </div>
356
+ </div>`
357
+ : ""}
160
358
  ${this.hasTextUsEnabled
161
- ? html` <text-us-window
162
- .buildingId=${this.buildingId}
163
- inPopup=${!this.hasTextUsEnabled}
164
- currentLeadSource="${this.currentLeadSource}"
165
- ></text-us-window>`
359
+ ? this.hasSubmittedForm
360
+ ? this.renderSubmittedTextUsOption()
361
+ : this.renderFormTextUsOption()
166
362
  : ""}
167
363
  </details-window>
168
364
  `;
@@ -174,3 +370,45 @@ export const installCallUsWindow = (): void => {
174
370
  window.customElements.define("call-us-window", CallUsWindow);
175
371
  }
176
372
  };
373
+
374
+ const createTextWithUs = async (
375
+ rawPhoneNumber: string,
376
+ buildingId: number,
377
+ orgSlug: string,
378
+ buildingSlug: string,
379
+ leadSources: string[]
380
+ ) => {
381
+ const formattedPhoneNumber =
382
+ "+1" +
383
+ rawPhoneNumber
384
+ .replace("(", "")
385
+ .replace(")", "")
386
+ .replace(" ", "")
387
+ .replace("-", "");
388
+ const queryParams = new URLSearchParams(window.location.search);
389
+ const requestBody = {
390
+ building_id: buildingId,
391
+ lead_sources: leadSources,
392
+ phone_number: formattedPhoneNumber,
393
+ referrer: document.referrer,
394
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
395
+ // @ts-ignore
396
+ query_params: Object.fromEntries(queryParams.entries()),
397
+ };
398
+ pushGtmEvent("textUsSubmitted", {
399
+ phone: formattedPhoneNumber,
400
+ originatingSource:
401
+ leadSources.find((i) => i !== "property-website") || null,
402
+ });
403
+ await axios.post(
404
+ "https://app.meetelise.com/platformApi/state/create/textMe",
405
+ requestBody,
406
+ {
407
+ headers: {
408
+ ["building-slug"]: buildingSlug,
409
+ ["org-slug"]: orgSlug,
410
+ ["X-SecurityKey"]: "JRL8jV4VcSCwOSir5gWkpgNLfKghmhBG",
411
+ },
412
+ }
413
+ );
414
+ };