@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.
@@ -0,0 +1,586 @@
1
+ import { html, LitElement, TemplateResult, css } from "lit";
2
+ import { customElement, property, state } from "lit/decorators.js";
3
+ import { createRef, ref, Ref } from "lit/directives/ref.js";
4
+ import { launcherStyles } from "./launcherStyles";
5
+ import { typeEmojiStyles } from "./typeEmojiStyles";
6
+ import { typeMiniStyles } from "./typeMiniStyles";
7
+ import {
8
+ EmailUsWindow,
9
+ installEmailUsWindow,
10
+ } from "../actions/email-us-window";
11
+ import { installTextUsWindow, TextUsWindow } from "../actions/text-us-window";
12
+ import { StyleInfo, styleMap } from "lit/directives/style-map.js";
13
+ import { classMap } from "lit/directives/class-map.js";
14
+ import { installCallUsWindow } from "../actions/call-us-window";
15
+ import { getRegisteredPhoneNumbers } from "../../getRegisteredPhoneNumbers";
16
+ import { TourScheduler } from "../Scheduler/tour-scheduler";
17
+ import { LabeledOption, UnitV2 } from "../../fetchBuildingInfo";
18
+ import { abTestTypes } from "../../fetchBuildingABTestType";
19
+ import {
20
+ EmailBlackOutlineIcon,
21
+ ChatWhiteOutlineIcon,
22
+ BookTourBlackOutlineIcon,
23
+ PhoneBlackOutlineIcon,
24
+ HeyThereEmoji,
25
+ ChevronIconBlack,
26
+ ChevronIconWhite,
27
+ } from "../../svgIcons";
28
+ import { defaultBrandColor } from "../../themes";
29
+ import { hexToAlmostWhite } from "../../utils";
30
+ import "./mobile-launcher";
31
+
32
+ @customElement("meetelise-launcher")
33
+ export class Launcher extends LitElement {
34
+ static styles = css`
35
+ ${launcherStyles}
36
+ ${typeEmojiStyles}
37
+ ${typeMiniStyles}
38
+ `;
39
+
40
+ @property({ type: Boolean })
41
+ isMobile = false;
42
+
43
+ @property({ attribute: true })
44
+ isMinimized = false;
45
+
46
+ @property({ type: Boolean })
47
+ isFirstMount = false;
48
+
49
+ @property()
50
+ backgroundColor = "rgba(255, 255, 255, 0.75)";
51
+
52
+ @property()
53
+ textColor = "#202020";
54
+
55
+ @property({ type: Boolean })
56
+ isMini = true;
57
+ @property({ attribute: false })
58
+ autoOpenChatWidget = false;
59
+
60
+ @property()
61
+ phoneNumber = "";
62
+ @property({ attribute: true })
63
+ chatId = "";
64
+ @property({ attribute: true })
65
+ chatCallUsHeader = "";
66
+ @property({ attribute: false })
67
+ buildingId = 0;
68
+ @property({ attribute: true })
69
+ buildingSlug = "";
70
+ @property({ attribute: true })
71
+ orgSlug = "";
72
+ @property({ attribute: true })
73
+ sgtUrl = "";
74
+ @property({ attribute: true })
75
+ buildingABTestType = "";
76
+ @property({ attribute: true })
77
+ featureFlagShowDropdown = "";
78
+ @property({ attribute: false })
79
+ leadSources: string[] = [];
80
+ @property({ attribute: true })
81
+ currentLeadSource = "";
82
+ @property({ attribute: true })
83
+ escortedToursLink = "";
84
+ @property({ attribute: true })
85
+ virtualToursLink = "";
86
+ @property({ type: Boolean })
87
+ hasCallUsEnabled = false;
88
+ @property({ type: Boolean })
89
+ hasChatEnabled = false;
90
+ @property({ type: Boolean })
91
+ hasEmailEnabled = false;
92
+ @property({ type: Boolean })
93
+ hasSSTEnabled = false;
94
+ @property({ type: Boolean })
95
+ hasTextUsEnabled = false;
96
+ @property({ attribute: false })
97
+ layoutOptions: string[] = [];
98
+ @property({ attribute: false })
99
+ unitOptions: UnitV2[] = [];
100
+ @property({ attribute: false })
101
+ tourTypeOptions: LabeledOption[] = [];
102
+ @property({ attribute: false })
103
+ onChatTapped: () => void = () => {
104
+ return;
105
+ };
106
+
107
+ @property({ attribute: true })
108
+ onClickMinimize: (e: MouseEvent) => void = () => {
109
+ return;
110
+ };
111
+
112
+ @property({ attribute: false })
113
+ launcherStyles: StyleInfo = {};
114
+
115
+ @property({ attribute: true })
116
+ brandColor: string = defaultBrandColor;
117
+
118
+ @state()
119
+ isEmailWindowOpen = false;
120
+ @state()
121
+ isCallUsWindowOpen = false;
122
+ @state()
123
+ isSSTWindowOpen = false;
124
+ @state()
125
+ isTextUsWindowOpen = false;
126
+
127
+ getNumCallToActions = (): number => {
128
+ return [
129
+ this.hasEmailEnabled,
130
+ !!this.phoneNumber && this.hasCallUsEnabled,
131
+ this.hasSSTEnabled,
132
+ this.hasTextUsEnabled,
133
+ ].filter((v) => v).length;
134
+ };
135
+
136
+ emailUsWindowRef: Ref<EmailUsWindow> = createRef();
137
+ textUsWindowRef: Ref<TextUsWindow> = createRef();
138
+ tourSchedulerRef: Ref<TourScheduler> = createRef();
139
+
140
+ updated = async (): Promise<void> => {
141
+ this.attachOnClickToEmailUsWindow();
142
+ this.attachOnClickToTextUsWindow();
143
+ this.attachOnClickToSSTWindow();
144
+ if (this.buildingId) {
145
+ const registeredPhoneNumbers = await getRegisteredPhoneNumbers(
146
+ this.buildingId
147
+ );
148
+ this.hasTextUsEnabled =
149
+ registeredPhoneNumbers.length > 0 &&
150
+ this.hasTextUsEnabled &&
151
+ this.buildingId !== 4895;
152
+ // TODO: replace this with the real API call once the endpoint exists
153
+ const schedulingIsEnabled = await (async function putApiCallHere(
154
+ buildingId: number
155
+ ) {
156
+ return !!buildingId;
157
+ })(this.buildingId);
158
+ if (schedulingIsEnabled && this.hasSSTEnabled) {
159
+ this.hasSSTEnabled = true;
160
+ }
161
+ }
162
+ };
163
+
164
+ attachOnClickToEmailUsWindow = (): void => {
165
+ const emailUsWindowRef = this.emailUsWindowRef.value;
166
+ if (!emailUsWindowRef) {
167
+ return;
168
+ }
169
+ emailUsWindowRef.onCloseClicked = this.onCloseEmailWindow;
170
+ };
171
+
172
+ attachOnClickToTextUsWindow = (): void => {
173
+ const textUsWindowRef = this.textUsWindowRef.value;
174
+ if (!textUsWindowRef) {
175
+ return;
176
+ }
177
+ textUsWindowRef.onCloseClicked = this.onCloseTextUsWindow;
178
+ };
179
+
180
+ attachOnClickToSSTWindow = (): void => {
181
+ const sstWindowRef = this.tourSchedulerRef.value;
182
+ if (!sstWindowRef) {
183
+ return;
184
+ }
185
+ sstWindowRef.onCloseClicked = this.onCloseSSTWindow;
186
+ };
187
+
188
+ onClickEmailOption = (e: MouseEvent): void => {
189
+ e.preventDefault();
190
+ e.stopPropagation();
191
+
192
+ this.isEmailWindowOpen = true;
193
+ };
194
+
195
+ onCloseEmailWindow = (): void => {
196
+ this.isEmailWindowOpen = false;
197
+ };
198
+
199
+ renderEmailOption = (): TemplateResult => {
200
+ const text = this.getNumCallToActions() > 2 ? "Email" : "Email us";
201
+ return html`
202
+ <div
203
+ @click=${this.onClickEmailOption}
204
+ class="launcher__call-to-action-option"
205
+ >
206
+ ${text}
207
+ </div>
208
+ `;
209
+ };
210
+
211
+ onClickPhoneOption = (e: MouseEvent): void => {
212
+ e.preventDefault();
213
+ e.stopPropagation();
214
+ this.isCallUsWindowOpen = true;
215
+ };
216
+
217
+ onClosePhoneWindow = (): void => {
218
+ this.isCallUsWindowOpen = false;
219
+ };
220
+
221
+ renderCallUsOption = (): TemplateResult => {
222
+ const text = this.getNumCallToActions() > 2 ? "Phone" : "Hours/Phone";
223
+ return html` <div class="launcher__call-to-action-option">${text}</div> `;
224
+ };
225
+
226
+ onClickSSTOption = (e: MouseEvent): void => {
227
+ e.preventDefault();
228
+ e.stopPropagation();
229
+ this.isSSTWindowOpen = true;
230
+ };
231
+
232
+ onCloseSSTWindow = (): void => {
233
+ this.isSSTWindowOpen = false;
234
+ };
235
+
236
+ renderSSTOption = (): TemplateResult => {
237
+ const text = this.getNumCallToActions() > 2 ? "Book" : "Book a tour";
238
+ return html`
239
+ <div
240
+ class="launcher__call-to-action-option"
241
+ @click=${this.onClickSSTOption}
242
+ >
243
+ ${text}
244
+ </div>
245
+ `;
246
+ };
247
+
248
+ onClickTextUsOption = (e: MouseEvent): void => {
249
+ e.preventDefault();
250
+ e.stopPropagation();
251
+ this.isTextUsWindowOpen = true;
252
+ };
253
+
254
+ onCloseTextUsWindow = (): void => {
255
+ this.isTextUsWindowOpen = false;
256
+ };
257
+
258
+ renderTextUsOption = (): TemplateResult => {
259
+ return html`
260
+ <div
261
+ @click=${this.onClickTextUsOption}
262
+ class="launcher__call-to-action-option"
263
+ >
264
+ Text us
265
+ </div>
266
+ `;
267
+ };
268
+
269
+ renderCallToActions = (): TemplateResult => {
270
+ if (this.getNumCallToActions() === 0) {
271
+ return html``;
272
+ }
273
+
274
+ return html`
275
+ <div class="launcher__call-to-actions-wrapper">
276
+ ${this.hasEmailEnabled ? this.renderEmailOption() : ""}
277
+ ${this.phoneNumber && this.hasCallUsEnabled
278
+ ? this.renderCallUsOption()
279
+ : ""}
280
+ ${this.hasTextUsEnabled ? this.renderTextUsOption() : ""}
281
+ ${this.hasSSTEnabled ? this.renderSSTOption() : ""}
282
+ </div>
283
+ `;
284
+ };
285
+
286
+ isCallToActionWindowOpen = (): boolean => {
287
+ return (
288
+ [
289
+ this.isEmailWindowOpen,
290
+ this.isCallUsWindowOpen,
291
+ this.isSSTWindowOpen,
292
+ this.isTextUsWindowOpen,
293
+ ].filter((v) => v).length === 1
294
+ );
295
+ };
296
+
297
+ renderMiniOption = (
298
+ content: TemplateResult,
299
+ onClick?: (e: MouseEvent) => void,
300
+ isSecondaryAction?: boolean
301
+ ): TemplateResult => {
302
+ return html`
303
+ <div class="launcher__mini-option-wrapper">
304
+ <div
305
+ @click=${onClick}
306
+ class=${classMap({
307
+ ["launcher__mini-option"]: true,
308
+ ["launcher__secondary-option"]: !!isSecondaryAction,
309
+ })}
310
+ >
311
+ ${content}
312
+ </div>
313
+ </div>
314
+ `;
315
+ };
316
+
317
+ renderActionPills = (): TemplateResult => {
318
+ if (this.isMobile || this.isMinimized) {
319
+ return html` <mobile-launcher
320
+ .onClickMinimize=${this.onClickMinimize}
321
+ .onClickEmailOption=${this.onClickEmailOption}
322
+ .onClickPhoneOption=${this.onClickPhoneOption}
323
+ .onClickSSTOption=${this.onClickSSTOption}
324
+ .onChatTapped=${this.onChatTapped}
325
+ .isMobile=${this.isMobile}
326
+ .brandColor=${this.brandColor}
327
+ ></mobile-launcher>`;
328
+ }
329
+
330
+ if (this.buildingABTestType === abTestTypes.ConceptEmoji) {
331
+ return html`
332
+ <div
333
+ class="type-hey__list"
334
+ style=${styleMap({
335
+ background: this.brandColor,
336
+ })}
337
+ >
338
+ <div class="type-hey__chat-container">
339
+ <minimize-expand-button
340
+ .brandColor=${this.brandColor}
341
+ .onClick=${this.onClickMinimize}
342
+ ></minimize-expand-button>
343
+ </div>
344
+
345
+ <div class="type-hey__top-section">
346
+ <div class="type-hey__ai-topic">
347
+ <p>AI Assistant</p>
348
+ <div class="type-hey__ai-topic-notch"></div>
349
+ </div>
350
+ <div class="type-hey__greeting">
351
+ <h1>Hey there!</h1>
352
+ <div>${HeyThereEmoji}</div>
353
+ </div>
354
+ ${this.hasChatEnabled
355
+ ? html`
356
+ <div
357
+ id="type-hey__ask-prompt"
358
+ class="type-hey__pill"
359
+ @click=${this.onChatTapped}
360
+ style=${styleMap({
361
+ background: hexToAlmostWhite(this.brandColor, 0.2),
362
+ })}
363
+ >
364
+ <div class="type-hey__pill-left">
365
+ <div class="type-hey__icon">${ChatWhiteOutlineIcon}</div>
366
+ <div><span class="title-bold">Chat</span> with us</div>
367
+ </div>
368
+ <div class="type-gradient__icon">${ChevronIconWhite}</div>
369
+ </div>
370
+ `
371
+ : ""}
372
+ </div>
373
+
374
+ <div class="type-hey__bottom-section">
375
+ <div class="type-hey__botton-section-pills">
376
+ ${this.hasEmailEnabled
377
+ ? html` <div
378
+ class="type-hey__pill"
379
+ @click=${this.onClickEmailOption}
380
+ >
381
+ <div class="type-hey__pill-left">
382
+ <div class="type-hey__icon">${EmailBlackOutlineIcon}</div>
383
+ <div><span class="title-bold">Email</span> an agent</div>
384
+ </div>
385
+ <div class="type-gradient__icon">${ChevronIconBlack}</div>
386
+ </div>
387
+ </div>`
388
+ : ""}
389
+ ${this.phoneNumber && this.hasCallUsEnabled
390
+ ? html`
391
+ <div class="type-hey__pill" @click=${
392
+ this.onClickPhoneOption
393
+ }>
394
+ <div class="type-hey__pill-left">
395
+ <div class="type-hey__icon">${PhoneBlackOutlineIcon}</div>
396
+ <div>
397
+ <span class="title-bold">Call</span>
398
+ ${
399
+ this.hasTextUsEnabled
400
+ ? html`or
401
+ <span class="title-bold">text</span> us`
402
+ : "us"
403
+ }
404
+ </div>
405
+ </div>
406
+ <div class="type-gradient__icon">${ChevronIconBlack}</div>
407
+ </div>
408
+ </div>`
409
+ : ""}
410
+ ${this.hasSSTEnabled
411
+ ? html`
412
+ <div class="type-hey__pill" @click=${this.onClickSSTOption}>
413
+ <div class="type-hey__pill-left">
414
+ <div class="type-hey__icon">${BookTourBlackOutlineIcon}</div>
415
+ <div>
416
+ <span class="title-bold">Book</span> a tour
417
+ </div>
418
+ </div>
419
+ <div class="type-gradient__icon">${ChevronIconBlack}</div>
420
+ </div>
421
+ </div>`
422
+ : ""}
423
+ </div>
424
+
425
+ <div class="type-hey__bottom-info">
426
+ <div class="type-hey__bottom-info-inner">
427
+ Online <span class="title-bold">24 hours</span> a day,
428
+ <span class="title-bold">7</span> days a week
429
+ </div>
430
+ </div>
431
+ </div>
432
+ </div>
433
+ `;
434
+ }
435
+
436
+ return html` <div class="vertical-pill-list">
437
+ <button @click=${this.onClickMinimize} class="minimize-bttn">
438
+ <div class="chevron-down">${ChevronIconBlack}</div>
439
+ </button>
440
+ ${this.hasChatEnabled
441
+ ? html`
442
+ <div id="overlay-inner-pill" class="inner-pill-wrapper"
443
+ style=${styleMap({
444
+ background: this.brandColor,
445
+ })} @click=${this.onChatTapped}>
446
+ <div class="vertical-pill-left">
447
+ <div class="vertical-pill-icon">${ChatWhiteOutlineIcon}</div>
448
+ <div class="vertical-pill-title">
449
+ <span class="vertical-pill-bold">Chat</span> with us
450
+ </div>
451
+ </div>
452
+ <div class="chevron-right">${ChevronIconWhite}</div>
453
+ </div>
454
+ </div>
455
+ `
456
+ : ""}
457
+ ${this.hasEmailEnabled
458
+ ? html`
459
+ <div @click=${this.onClickEmailOption} class="inner-pill-wrapper">
460
+ <div class="vertical-pill-left">
461
+ <div class="vertical-pill-icon">${EmailBlackOutlineIcon}</div>
462
+ <div class="vertical-pill-title">
463
+ <span class="vertical-pill-bold">Email</span> an agent
464
+ </div>
465
+ </div>
466
+ <div class="chevron-right">${ChevronIconBlack}</div>
467
+ </div>
468
+ `
469
+ : ""}
470
+ ${this.phoneNumber && this.hasCallUsEnabled
471
+ ? html`
472
+ <div @click=${this.onClickPhoneOption} class="inner-pill-wrapper">
473
+ <div class="vertical-pill-left">
474
+ <div class="vertical-pill-icon">${PhoneBlackOutlineIcon}</div>
475
+ <div class="vertical-pill-title">
476
+ <span class="vertical-pill-bold">Call</span>
477
+ ${this.hasTextUsEnabled
478
+ ? html`or <span class="vertical-pill-bold">text</span> us`
479
+ : "us"}
480
+ </div>
481
+ </div>
482
+ <div class="chevron-right">${ChevronIconBlack}</div>
483
+ </div>
484
+ `
485
+ : ""}
486
+ ${this.hasSSTEnabled
487
+ ? html`
488
+ <div @click=${this.onClickSSTOption} class="inner-pill-wrapper">
489
+ <div class="vertical-pill-left">
490
+ <div class="vertical-pill-icon">
491
+ ${BookTourBlackOutlineIcon}
492
+ </div>
493
+ <div class="vertical-pill-title">
494
+ <span class="vertical-pill-bold">Book</span> a tour
495
+ </div>
496
+ </div>
497
+ <div class="chevron-right">${ChevronIconBlack}</div>
498
+ </div>
499
+ `
500
+ : ""}
501
+ </div>`;
502
+ };
503
+
504
+ render(): TemplateResult {
505
+ installEmailUsWindow();
506
+ installTextUsWindow();
507
+ installCallUsWindow();
508
+
509
+ return html`
510
+ <div
511
+ class=${this.isMobile || this.isMinimized
512
+ ? "launcher__mobile-launcher-wrapper"
513
+ : "launcher__mini-launcher-wrapper"}
514
+ style=${styleMap(this.launcherStyles)}
515
+ >
516
+ ${this.isEmailWindowOpen
517
+ ? html`<div class="launcher__window-wrapper">
518
+ <email-us-window
519
+ chatId="${this.chatId}"
520
+ .leadSources="${this.leadSources}"
521
+ currentLeadSource="${this.currentLeadSource}"
522
+ orgSlug="${this.orgSlug}"
523
+ buildingSlug="${this.buildingSlug}"
524
+ featureFlagShowDropdown="${this.featureFlagShowDropdown}"
525
+ ${ref(this.emailUsWindowRef)}
526
+ .buildingId=${this.buildingId}
527
+ >
528
+ </email-us-window>
529
+ </div>`
530
+ : ""}
531
+ ${this.isTextUsWindowOpen
532
+ ? html`<div class="launcher__window-wrapper">
533
+ <text-us-window
534
+ orgSlug="${this.orgSlug}"
535
+ buildingSlug="${this.buildingSlug}"
536
+ ${ref(this.textUsWindowRef)}
537
+ .buildingId=${this.buildingId}
538
+ currentLeadSource="${this.currentLeadSource}"
539
+ ></text-us-window>
540
+ </div>`
541
+ : ""}
542
+ ${this.isSSTWindowOpen
543
+ ? html`<div class="launcher__window-wrapper">
544
+ <tour-scheduler
545
+ chatId="${this.chatId}"
546
+ orgSlug="${this.orgSlug}"
547
+ buildingSlug="${this.buildingSlug}"
548
+ sgtUrl="${this.sgtUrl}"
549
+ escortedToursLink="${this.escortedToursLink}"
550
+ virtualToursLink="${this.virtualToursLink}"
551
+ currentLeadSource="${this.currentLeadSource}"
552
+ .leadSources="${this.leadSources}"
553
+ .layoutOptions=${this.layoutOptions}
554
+ .unitOptions=${this.unitOptions}
555
+ .tourTypeOptions=${this.tourTypeOptions}
556
+ buildingId=${this.buildingId}
557
+ featureFlagShowDropdown="${this.featureFlagShowDropdown}"
558
+ ${ref(this.tourSchedulerRef)}
559
+ ></tour-scheduler>
560
+ </div>`
561
+ : ""}
562
+ ${this.isCallUsWindowOpen
563
+ ? html`
564
+ <div class="launcher__window-wrapper">
565
+ <call-us-window
566
+ .onCloseClicked=${this.onClosePhoneWindow}
567
+ phoneNumber="${this.phoneNumber}"
568
+ .buildingId=${this.buildingId}
569
+ chatCallUsHeader="${this.chatCallUsHeader}"
570
+ hasTextUsEnabled=${this.hasTextUsEnabled ? true : ""}
571
+ currentLeadSource="${this.currentLeadSource}"
572
+ ></call-us-window>
573
+ </div>
574
+ `
575
+ : ""}
576
+ ${!this.isCallToActionWindowOpen() ? this.renderActionPills() : ""}
577
+ </div>
578
+ `;
579
+ }
580
+ }
581
+
582
+ export const installLauncher = (): void => {
583
+ if (!window.customElements.get("meetelise-launcher")) {
584
+ window.customElements.define("meetelise-launcher", Launcher);
585
+ }
586
+ };