@meetelise/chat 1.29.0 → 1.30.1

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.
Files changed (228) hide show
  1. package/dist/src/MyPubnub.d.ts +116 -0
  2. package/dist/src/WebComponent/FeeCalculator/components/addon-item/addon-item-styles.d.ts +2 -0
  3. package/dist/src/WebComponent/FeeCalculator/components/addon-item/addon-item.d.ts +22 -0
  4. package/dist/src/WebComponent/FeeCalculator/components/addon-item/index.d.ts +1 -0
  5. package/dist/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout-styles.d.ts +2 -0
  6. package/dist/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout.d.ts +34 -0
  7. package/dist/src/WebComponent/FeeCalculator/components/fee-card/fee-card-styles.d.ts +2 -0
  8. package/dist/src/WebComponent/FeeCalculator/components/fee-card/fee-card.d.ts +21 -0
  9. package/dist/src/WebComponent/FeeCalculator/components/fee-item/fee-item-styles.d.ts +2 -0
  10. package/dist/src/WebComponent/FeeCalculator/components/fee-item/fee-item.d.ts +13 -0
  11. package/dist/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector-styles.d.ts +2 -0
  12. package/dist/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector.d.ts +33 -0
  13. package/dist/src/WebComponent/FeeCalculator/components/floorplan-image-card/floorplan-image-card-styles.d.ts +2 -0
  14. package/dist/src/WebComponent/FeeCalculator/components/floorplan-image-card/floorplan-image-card.d.ts +18 -0
  15. package/dist/src/WebComponent/FeeCalculator/components/index.d.ts +5 -0
  16. package/dist/src/WebComponent/FeeCalculator/components/promo-card/promo-card-styles.d.ts +2 -0
  17. package/dist/src/WebComponent/FeeCalculator/components/promo-card/promo-card.d.ts +13 -0
  18. package/dist/src/WebComponent/FeeCalculator/constants.d.ts +2 -0
  19. package/dist/src/WebComponent/FeeCalculator/fee-calculator-styles.d.ts +1 -0
  20. package/dist/src/WebComponent/FeeCalculator/fee-calculator.d.ts +59 -0
  21. package/dist/src/WebComponent/FeeCalculator/index.d.ts +2 -0
  22. package/dist/src/WebComponent/FeeCalculator/model/building-fee.d.ts +80 -0
  23. package/dist/src/WebComponent/FeeCalculator/model/transaction-category.d.ts +23 -0
  24. package/dist/src/WebComponent/LeadSourceClient.d.ts +46 -0
  25. package/dist/src/WebComponent/OfficeHours.d.ts +21 -0
  26. package/dist/src/WebComponent/Scheduler/date-picker.d.ts +28 -0
  27. package/dist/src/WebComponent/Scheduler/time-picker.d.ts +14 -0
  28. package/dist/src/WebComponent/Scheduler/tour-scheduler.d.ts +100 -0
  29. package/dist/src/WebComponent/Scheduler/tour-type-option.d.ts +13 -0
  30. package/dist/src/WebComponent/Scheduler/tourSchedulerStyles.d.ts +1 -0
  31. package/dist/src/WebComponent/actions/InputStyles.d.ts +1 -0
  32. package/dist/src/WebComponent/actions/action-confirm-button.d.ts +13 -0
  33. package/dist/src/WebComponent/actions/call-us-window.d.ts +37 -0
  34. package/dist/src/WebComponent/actions/collapse-expand-button.d.ts +8 -0
  35. package/dist/src/WebComponent/actions/details-window.d.ts +14 -0
  36. package/dist/src/WebComponent/actions/email-us-window.d.ts +46 -0
  37. package/dist/src/WebComponent/actions/formatPhoneNumber.d.ts +17 -0
  38. package/dist/src/WebComponent/actions/minimize-expand-button.d.ts +8 -0
  39. package/dist/src/WebComponent/chat-additional-actions.d.ts +28 -0
  40. package/dist/src/WebComponent/health-chat.d.ts +47 -0
  41. package/dist/src/WebComponent/healthchat-styles.d.ts +1 -0
  42. package/dist/src/WebComponent/icons/ApplyOutlineIcon.d.ts +2 -0
  43. package/dist/src/WebComponent/icons/BookTourOutlineIcon.d.ts +2 -0
  44. package/dist/src/WebComponent/icons/CalculatorOutlineIcon.d.ts +2 -0
  45. package/dist/src/WebComponent/icons/ChatOutlineIcon.d.ts +2 -0
  46. package/dist/src/WebComponent/icons/ChevronLeftIcon.d.ts +2 -0
  47. package/dist/src/WebComponent/icons/ChevronRightIcon.d.ts +2 -0
  48. package/dist/src/WebComponent/icons/ContactResidentIcon.d.ts +2 -0
  49. package/dist/src/WebComponent/icons/DollarOutlineIcon.d.ts +3 -0
  50. package/dist/src/WebComponent/icons/EmailOutlineIcon.d.ts +2 -0
  51. package/dist/src/WebComponent/icons/HeyThereEmojiIcon.d.ts +2 -0
  52. package/dist/src/WebComponent/icons/PhoneOutlineIcon.d.ts +2 -0
  53. package/dist/src/WebComponent/icons/SendMessageIcon.d.ts +3 -0
  54. package/dist/src/WebComponent/icons/TourSelfGuidedIcon.d.ts +2 -0
  55. package/dist/src/WebComponent/icons/TourVirtuallyIcon.d.ts +2 -0
  56. package/dist/src/WebComponent/icons/TourWithAgentIcon.d.ts +2 -0
  57. package/dist/src/WebComponent/icons/XOutlineIcon.d.ts +2 -0
  58. package/dist/src/WebComponent/index.d.ts +2 -0
  59. package/dist/src/WebComponent/launcher/Launcher.d.ts +98 -0
  60. package/dist/src/WebComponent/launcher/launcherStyles.d.ts +1 -0
  61. package/dist/src/WebComponent/launcher/mobile-launcher.d.ts +27 -0
  62. package/dist/src/WebComponent/launcher/typeEmojiStyles.d.ts +1 -0
  63. package/dist/src/WebComponent/launcher/typeMiniStyles.d.ts +1 -0
  64. package/dist/src/WebComponent/launcher/typeMobileStyles.d.ts +1 -0
  65. package/dist/src/WebComponent/leasing-chat-styles.d.ts +1 -0
  66. package/dist/src/WebComponent/me-chat.d.ts +91 -0
  67. package/dist/src/WebComponent/me-select.d.ts +24 -0
  68. package/dist/src/WebComponent/mega-loader.d.ts +7 -0
  69. package/dist/src/WebComponent/mini-loader.d.ts +5 -0
  70. package/dist/src/WebComponent/pubnub-chat-styles.d.ts +1 -0
  71. package/dist/src/WebComponent/pubnub-chat.d.ts +49 -0
  72. package/dist/src/WebComponent/pubnub-media.d.ts +14 -0
  73. package/dist/src/WebComponent/pubnub-message-styles.d.ts +1 -0
  74. package/dist/src/WebComponent/pubnub-message.d.ts +39 -0
  75. package/dist/src/WebComponent/simple-launcher/simple-launcher-styles.d.ts +1 -0
  76. package/dist/src/WebComponent/simple-launcher/simple-launcher.d.ts +12 -0
  77. package/dist/src/WebComponent/utilities-chat.d.ts +47 -0
  78. package/dist/src/WebComponent/utilities-styles.d.ts +1 -0
  79. package/dist/src/WebComponent/utils.d.ts +31 -0
  80. package/dist/src/analytics.d.ts +64 -0
  81. package/dist/src/disclaimers.d.ts +8 -0
  82. package/dist/src/fetchBuildingABTestType.d.ts +8 -0
  83. package/dist/src/fetchBuildingInfo.d.ts +57 -0
  84. package/dist/src/fetchBuildingWebchatView.d.ts +123 -0
  85. package/dist/src/fetchFeatureFlag.d.ts +14 -0
  86. package/dist/src/fetchLeadSources.d.ts +4 -0
  87. package/dist/src/fetchPhoneNumberFromSource.d.ts +6 -0
  88. package/dist/src/getAvailabilities.d.ts +45 -0
  89. package/dist/src/getBuildingPhoneNumber.d.ts +1 -0
  90. package/dist/src/getShouldAllowScheduling.d.ts +1 -0
  91. package/dist/src/getShouldShowWebchat.d.ts +3 -0
  92. package/dist/src/getTimezoneString.d.ts +1 -0
  93. package/dist/src/globals.d.ts +1 -0
  94. package/dist/src/gtm.d.ts +6 -0
  95. package/dist/src/handleChatId.d.ts +11 -0
  96. package/dist/src/insertDNIIntoWebsite.d.ts +5 -0
  97. package/dist/src/insertLeadSourceIntoSchedulerLinks.d.ts +4 -0
  98. package/dist/src/main/MEChat.d.ts +74 -0
  99. package/dist/src/main/utils.d.ts +2 -0
  100. package/dist/src/postLeadSources.d.ts +3 -0
  101. package/dist/src/rentgrata.d.ts +4 -0
  102. package/dist/src/replaceSelectButtonsWithNewLink.d.ts +5 -0
  103. package/dist/src/services/fees/calculateQuote.d.ts +52 -0
  104. package/dist/src/services/fees/fetchBuildingFees.d.ts +24 -0
  105. package/dist/src/services/fees/fetchBuildingFloorplans.d.ts +21 -0
  106. package/dist/src/services/fees/utils.d.ts +1 -0
  107. package/dist/src/svgIcons.d.ts +5 -0
  108. package/dist/src/themes.d.ts +5 -0
  109. package/dist/src/types/rest-sdk.types.d.ts +11 -0
  110. package/dist/src/types/webchat-no-show-reason.d.ts +1 -0
  111. package/dist/src/utils.d.ts +13 -0
  112. package/package.json +1 -1
  113. package/public/dist/index.js +384 -179
  114. package/src/MyPubnub.ts +792 -0
  115. package/src/WebComponent/FeeCalculator/components/addon-item/addon-item-styles.ts +79 -0
  116. package/src/WebComponent/FeeCalculator/components/addon-item/addon-item.ts +121 -0
  117. package/src/WebComponent/FeeCalculator/components/addon-item/index.ts +1 -0
  118. package/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout-styles.ts +127 -0
  119. package/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout.ts +191 -0
  120. package/src/WebComponent/FeeCalculator/components/fee-card/fee-card-styles.ts +65 -0
  121. package/src/WebComponent/FeeCalculator/components/fee-card/fee-card.ts +91 -0
  122. package/src/WebComponent/FeeCalculator/components/fee-item/fee-item-styles.ts +44 -0
  123. package/src/WebComponent/FeeCalculator/components/fee-item/fee-item.ts +38 -0
  124. package/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector-styles.ts +144 -0
  125. package/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector.ts +241 -0
  126. package/src/WebComponent/FeeCalculator/components/floorplan-image-card/floorplan-image-card-styles.ts +74 -0
  127. package/src/WebComponent/FeeCalculator/components/floorplan-image-card/floorplan-image-card.ts +72 -0
  128. package/src/WebComponent/FeeCalculator/components/index.ts +5 -0
  129. package/src/WebComponent/FeeCalculator/components/promo-card/promo-card-styles.ts +39 -0
  130. package/src/WebComponent/FeeCalculator/components/promo-card/promo-card.ts +39 -0
  131. package/src/WebComponent/FeeCalculator/constants.ts +3 -0
  132. package/src/WebComponent/FeeCalculator/fee-calculator-styles.ts +334 -0
  133. package/src/WebComponent/FeeCalculator/fee-calculator.ts +369 -0
  134. package/src/WebComponent/FeeCalculator/index.ts +4 -0
  135. package/src/WebComponent/FeeCalculator/model/building-fee.ts +120 -0
  136. package/src/WebComponent/FeeCalculator/model/transaction-category.ts +23 -0
  137. package/src/WebComponent/LeadSourceClient.ts +332 -0
  138. package/src/WebComponent/MEChat.css +5 -0
  139. package/src/WebComponent/OfficeHours.ts +73 -0
  140. package/src/WebComponent/Scheduler/date-picker.ts +405 -0
  141. package/src/WebComponent/Scheduler/time-picker.ts +190 -0
  142. package/src/WebComponent/Scheduler/tour-scheduler.ts +1373 -0
  143. package/src/WebComponent/Scheduler/tour-type-option.ts +112 -0
  144. package/src/WebComponent/Scheduler/tourSchedulerStyles.ts +418 -0
  145. package/src/WebComponent/actions/InputStyles.ts +57 -0
  146. package/src/WebComponent/actions/action-confirm-button.ts +125 -0
  147. package/src/WebComponent/actions/call-us-window.ts +445 -0
  148. package/src/WebComponent/actions/collapse-expand-button.ts +65 -0
  149. package/src/WebComponent/actions/details-window.ts +150 -0
  150. package/src/WebComponent/actions/email-us-window.ts +555 -0
  151. package/src/WebComponent/actions/formatPhoneNumber.ts +72 -0
  152. package/src/WebComponent/actions/minimize-expand-button.ts +93 -0
  153. package/src/WebComponent/chat-additional-actions.ts +135 -0
  154. package/src/WebComponent/health-chat.ts +270 -0
  155. package/src/WebComponent/healthchat-styles.ts +119 -0
  156. package/src/WebComponent/icons/ApplyOutlineIcon.ts +22 -0
  157. package/src/WebComponent/icons/BookTourOutlineIcon.ts +13 -0
  158. package/src/WebComponent/icons/CalculatorOutlineIcon.ts +22 -0
  159. package/src/WebComponent/icons/ChatOutlineIcon.ts +10 -0
  160. package/src/WebComponent/icons/ChevronLeftIcon.ts +7 -0
  161. package/src/WebComponent/icons/ChevronRightIcon.ts +7 -0
  162. package/src/WebComponent/icons/ContactResidentIcon.ts +9 -0
  163. package/src/WebComponent/icons/DollarOutlineIcon.ts +18 -0
  164. package/src/WebComponent/icons/EmailOutlineIcon.ts +7 -0
  165. package/src/WebComponent/icons/HeyThereEmojiIcon.ts +12 -0
  166. package/src/WebComponent/icons/PhoneOutlineIcon.ts +7 -0
  167. package/src/WebComponent/icons/SendMessageIcon.ts +17 -0
  168. package/src/WebComponent/icons/TourSelfGuidedIcon.ts +17 -0
  169. package/src/WebComponent/icons/TourVirtuallyIcon.ts +17 -0
  170. package/src/WebComponent/icons/TourWithAgentIcon.ts +17 -0
  171. package/src/WebComponent/icons/XOutlineIcon.ts +8 -0
  172. package/src/WebComponent/index.ts +2 -0
  173. package/src/WebComponent/launcher/Launcher.ts +1282 -0
  174. package/src/WebComponent/launcher/launcherStyles.ts +500 -0
  175. package/src/WebComponent/launcher/mobile-launcher.ts +162 -0
  176. package/src/WebComponent/launcher/typeEmojiStyles.ts +161 -0
  177. package/src/WebComponent/launcher/typeMiniStyles.ts +60 -0
  178. package/src/WebComponent/launcher/typeMobileStyles.ts +50 -0
  179. package/src/WebComponent/leasing-chat-styles.ts +114 -0
  180. package/src/WebComponent/me-chat.ts +1262 -0
  181. package/src/WebComponent/me-select.ts +322 -0
  182. package/src/WebComponent/mega-loader.ts +36 -0
  183. package/src/WebComponent/mini-loader.ts +28 -0
  184. package/src/WebComponent/pubnub-chat-styles.ts +204 -0
  185. package/src/WebComponent/pubnub-chat.ts +928 -0
  186. package/src/WebComponent/pubnub-media.ts +208 -0
  187. package/src/WebComponent/pubnub-message-styles.ts +54 -0
  188. package/src/WebComponent/pubnub-message.ts +431 -0
  189. package/src/WebComponent/simple-launcher/simple-launcher-styles.ts +34 -0
  190. package/src/WebComponent/simple-launcher/simple-launcher.ts +100 -0
  191. package/src/WebComponent/utilities-chat.ts +270 -0
  192. package/src/WebComponent/utilities-styles.ts +110 -0
  193. package/src/WebComponent/utils.ts +82 -0
  194. package/src/analytics.ts +217 -0
  195. package/src/assetUrls.ts +6 -0
  196. package/src/disclaimers.ts +58 -0
  197. package/src/fetchBuildingABTestType.ts +21 -0
  198. package/src/fetchBuildingInfo.ts +87 -0
  199. package/src/fetchBuildingWebchatView.ts +156 -0
  200. package/src/fetchFeatureFlag.ts +250 -0
  201. package/src/fetchLeadSources.ts +98 -0
  202. package/src/fetchPhoneNumberFromSource.ts +31 -0
  203. package/src/fetchWebchatPreferences.ts +54 -0
  204. package/src/getAvailabilities.ts +179 -0
  205. package/src/getBuildingPhoneNumber.ts +26 -0
  206. package/src/getShouldAllowScheduling.ts +16 -0
  207. package/src/getShouldShowWebchat.ts +114 -0
  208. package/src/getTimezoneString.ts +39 -0
  209. package/src/globals.ts +1 -0
  210. package/src/gtm.ts +17 -0
  211. package/src/handleChatId.ts +101 -0
  212. package/src/insertDNIIntoWebsite.ts +146 -0
  213. package/src/insertLeadSourceIntoSchedulerLinks.ts +71 -0
  214. package/src/main/MEChat.test.ts +110 -0
  215. package/src/main/MEChat.ts +404 -0
  216. package/src/main/utils.ts +70 -0
  217. package/src/postLeadSources.ts +44 -0
  218. package/src/rentgrata.ts +74 -0
  219. package/src/replaceSelectButtonsWithNewLink.ts +69 -0
  220. package/src/services/fees/calculateQuote.ts +135 -0
  221. package/src/services/fees/fetchBuildingFees.ts +63 -0
  222. package/src/services/fees/fetchBuildingFloorplans.ts +74 -0
  223. package/src/services/fees/utils.ts +4 -0
  224. package/src/svgIcons.ts +14 -0
  225. package/src/themes.ts +65 -0
  226. package/src/types/rest-sdk.types.ts +13 -0
  227. package/src/types/webchat-no-show-reason.ts +6 -0
  228. package/src/utils.ts +121 -0
@@ -0,0 +1,1262 @@
1
+ import { css, html, LitElement, TemplateResult } from "lit";
2
+ import { customElement, property, state } from "lit/decorators.js";
3
+ import { classMap } from "lit/directives/class-map.js";
4
+ import { createRef, ref, Ref } from "lit/directives/ref.js";
5
+ import { Launcher } from "./launcher/Launcher";
6
+ import "./Scheduler/tour-scheduler";
7
+ import fetchBuildingWebchatView, {
8
+ BuildingWebchatView,
9
+ DesignConcepts,
10
+ } from "../fetchBuildingWebchatView";
11
+ import Analytics, { logSignal, LogType, sendLoggingEvent } from "../analytics";
12
+ import {
13
+ FeatureFlagsShowDropdown,
14
+ fetchFeatureFlagInsertDNIWebsite,
15
+ fetchFeatureFlagReplaceScheduleTourCtaWebsite,
16
+ fetchFeatureFlagShowMarketingSourceDropdown,
17
+ fetchFeatureFlagUseApplicationsLinkReplacement,
18
+ fetchFeatureFlagUseOverrideContactUsForm,
19
+ fetchFeatureFlagUsePhoneNumberBySource,
20
+ } from "../fetchFeatureFlag";
21
+ import fetchBuildingABTestType from "../fetchBuildingABTestType";
22
+ import fetchPhoneNumberFromSource, {
23
+ NumberForSelectedSource,
24
+ } from "../fetchPhoneNumberFromSource";
25
+ import { defaultPrimaryColor, defaultBackgroundColor } from "../themes";
26
+ import getShouldShowWebchat from "../getShouldShowWebchat";
27
+ import { isMobile, isTestEnv } from "../utils";
28
+ import { installLauncher } from "./launcher/Launcher";
29
+ import parseISO from "date-fns/parseISO";
30
+ import isPast from "date-fns/isPast";
31
+
32
+ import "./MEChat.css";
33
+ import { getRawAvailabilities } from "../getAvailabilities";
34
+ import { StyleInfo } from "lit/directives/style-map";
35
+ import addMinutes from "date-fns/addMinutes";
36
+ import formatISO from "date-fns/formatISO";
37
+ import fetchLeadSources from "../fetchLeadSources";
38
+ import { formatPhoneNumber } from "./actions/formatPhoneNumber";
39
+ import MyPubnub from "../MyPubnub";
40
+ import { insertDNIIntoWebsite } from "../insertDNIIntoWebsite";
41
+ import {
42
+ insertLeadSourceIntoSchedulerLinks,
43
+ replacePageRedirectElementWithTourWidgetTrigger,
44
+ replaceSSTLinkWithTourWidgetTrigger,
45
+ replaceTourTriggerElementWithTourWidgetTrigger,
46
+ } from "../insertLeadSourceIntoSchedulerLinks";
47
+
48
+ import "./actions/minimize-expand-button";
49
+ import "./launcher/mobile-launcher";
50
+ import "./pubnub-chat";
51
+ import { getBuildingPhoneNumber } from "../getBuildingPhoneNumber";
52
+ import {
53
+ clearChatStorageKey,
54
+ createChatStorageKey,
55
+ getChatStorageKey,
56
+ isChatKeyValid,
57
+ } from "../handleChatId";
58
+ import LeadSourceClient, {
59
+ getDefaultLeadSourceAttribution,
60
+ } from "./LeadSourceClient";
61
+ import noop from "lodash/noop";
62
+ import { updateRentgrataToken } from "../rentgrata";
63
+ import { WidgetType } from "../main/MEChat";
64
+ import { replaceSelectButtonsWithNewLink } from "../replaceSelectButtonsWithNewLink";
65
+
66
+ import "./chat-additional-actions";
67
+
68
+ @customElement("me-chat")
69
+ export class MEChat extends LitElement {
70
+ static styles = css`
71
+ :host {
72
+ all: initial;
73
+ }
74
+
75
+ #__talkjs_launcher:not(.shouldBeVisible) {
76
+ display: none;
77
+ }
78
+
79
+ .launcherContainer.launcher__mobile {
80
+ width: 100%;
81
+ height: 100px;
82
+ }
83
+
84
+ .hideTab {
85
+ display: none;
86
+ }
87
+ .showTab {
88
+ display: flex;
89
+ align-items: center;
90
+ justify-content: space-between;
91
+ gap: 32px;
92
+ }
93
+ #chatAdditionalActionsPubnub {
94
+ position: fixed;
95
+ box-sizing: border-box;
96
+
97
+ box-sizing: border-box;
98
+ width: 340px;
99
+
100
+ padding-top: 0px;
101
+ padding-right: 12px;
102
+
103
+ z-index: 1000000000;
104
+ }
105
+ #chatAdditionalActionsPubnub::after {
106
+ content: "";
107
+ position: absolute;
108
+ top: -6px;
109
+ right: 0;
110
+ width: 0;
111
+ height: 0;
112
+ border-bottom: 22px solid transparent;
113
+ border-right: 30px solid black;
114
+ }
115
+ #pubnub-bottom {
116
+ position: fixed;
117
+ bottom: 0;
118
+ }
119
+ `;
120
+ @property({ type: String })
121
+ private buildingSlug = "";
122
+ @property({ type: String })
123
+ private orgSlug = "";
124
+ @property({ type: Object })
125
+ launcherStyles: StyleInfo = {};
126
+ @property()
127
+ mobileStyles: StyleInfo = {};
128
+
129
+ @property({ type: Boolean })
130
+ isMinimized = false;
131
+
132
+ @property({ type: String })
133
+ private primaryColor: string | null = null;
134
+
135
+ @property({ type: String })
136
+ private backgroundColor: string | null = null;
137
+
138
+ @property({ type: String })
139
+ private foregroundColorOnPrimaryBackgroundColor: string | null = null;
140
+
141
+ @property({ type: String })
142
+ private foregroundColorOnSecondaryBackgroundColor: string | null = null;
143
+
144
+ @state()
145
+ private chatId = "";
146
+ @state()
147
+ private analytics: Analytics | null = null;
148
+ @state()
149
+ private launcher: HTMLElement | null = null;
150
+ @state()
151
+ private buildingWebchatView: BuildingWebchatView | null = null;
152
+ @state()
153
+ private designConcept: string | null = null;
154
+ @state()
155
+ private leadSources: string[] | null = null;
156
+ @state()
157
+ private currentLeadSource: string | null = null;
158
+ @state()
159
+ private featureFlagShowDropdown: FeatureFlagsShowDropdown =
160
+ FeatureFlagsShowDropdown.always;
161
+ @state()
162
+ private phoneNumberForSource: NumberForSelectedSource | null = null;
163
+ @state()
164
+ private hasMounted = false;
165
+ @state()
166
+ private hideLauncher = false;
167
+ @state()
168
+ private isLoading = true;
169
+
170
+ @state()
171
+ private showTourNextToChat = false;
172
+
173
+ @state()
174
+ private displayPubnubChat = false;
175
+
176
+ @state()
177
+ private myPubnub: MyPubnub | null = null;
178
+
179
+ @state()
180
+ private LeadSourceClient: LeadSourceClient | null = null;
181
+
182
+ @state()
183
+ private sampleRateToUse = 0.05;
184
+
185
+ @property({ type: Number })
186
+ private right = undefined;
187
+
188
+ @property({ type: Number })
189
+ private bottom = undefined;
190
+
191
+ @property({ type: Number })
192
+ private top = undefined;
193
+
194
+ @property({ type: Number })
195
+ private left = undefined;
196
+
197
+ @property({ attribute: true })
198
+ overrideRentgrata = false;
199
+
200
+ @property({ attribute: true })
201
+ widgetType: WidgetType = WidgetType.Default;
202
+
203
+ @property()
204
+ onWidgetLoaded: () => void = noop;
205
+
206
+ @property({ attribute: true })
207
+ onSstClose: () => void = noop;
208
+
209
+ @state()
210
+ private enabledChatWidgets: {
211
+ callDesktop: boolean;
212
+ callMobile: boolean;
213
+ chatMobile: boolean;
214
+ chatDesktop: boolean;
215
+ emailDesktop: boolean;
216
+ emailMobile: boolean;
217
+ textDesktop: boolean;
218
+ textMobile: boolean;
219
+ sstDesktop: boolean;
220
+ sstMobile: boolean;
221
+ calcDesktop: boolean;
222
+ calcMobile: boolean;
223
+ } = {
224
+ callDesktop: false,
225
+ callMobile: false,
226
+ chatMobile: false,
227
+ chatDesktop: false,
228
+ emailDesktop: false,
229
+ emailMobile: false,
230
+ textDesktop: false,
231
+ textMobile: false,
232
+ sstDesktop: false,
233
+ sstMobile: false,
234
+ calcDesktop: false,
235
+ calcMobile: false,
236
+ };
237
+
238
+ @state()
239
+ private webchatConfigHasAutoOpenChat = false;
240
+
241
+ @state()
242
+ private requiresConsent = false;
243
+
244
+ @state()
245
+ private hideMobileFeatures = false;
246
+
247
+ @state()
248
+ private privacyPolicyUrl = "http://bit.ly/me_privacy_policy";
249
+
250
+ launcherRef: Ref<Launcher> = createRef();
251
+
252
+ firstUpdated = async (): Promise<void> => this.setupWebchat();
253
+
254
+ private setupWebchat = async (): Promise<void> => {
255
+ if (!this.buildingSlug || !this.orgSlug) return;
256
+ const buildingWebchatView = await fetchBuildingWebchatView(
257
+ this.orgSlug,
258
+ this.buildingSlug
259
+ );
260
+
261
+ if (Math.random() < 0.2) {
262
+ try {
263
+ updateRentgrataToken(this.buildingSlug, this.orgSlug);
264
+ } catch (e) {
265
+ // eslint-disable-next-line no-console
266
+ console.warn("Error updating rentgrata token", e);
267
+ }
268
+ }
269
+
270
+ const shouldShowWebchat = await getShouldShowWebchat(
271
+ buildingWebchatView,
272
+ this.buildingSlug,
273
+ this.orgSlug
274
+ );
275
+ if (!shouldShowWebchat) {
276
+ return;
277
+ } else {
278
+ logSignal({
279
+ params: {
280
+ org_slug: this.orgSlug,
281
+ building_slug: this.buildingSlug,
282
+ is_pixel_on_site: true,
283
+ is_any_webchat_showing: true,
284
+ },
285
+ // We use a lower sample rate if not launched yet because likely still in onboarding process
286
+ sampleRate: buildingWebchatView?.launchedDateTime ? 0.01 : 0.05,
287
+ });
288
+ }
289
+ this.buildingWebchatView = buildingWebchatView;
290
+
291
+ await this.initializeChatVariables();
292
+ await this.handleChatInitializeAnalytics();
293
+ await this.setBuildingDerivedInfo();
294
+
295
+ this.attachOnClickToLauncher();
296
+
297
+ if (this.shouldAutoOpenChat(this.webchatConfigHasAutoOpenChat)) {
298
+ this.displayPubnubChat = true;
299
+ this.hideLauncher = true;
300
+ this.hasMounted = true;
301
+ this.updateAlreadyAutoOpenedTimestamp();
302
+ }
303
+
304
+ this.isLoading = false;
305
+
306
+ if (localStorage.getItem("isChatOpen") === "true") {
307
+ this.hideLauncher = true;
308
+ this.displayPubnubChat = true;
309
+ }
310
+ };
311
+
312
+ // The general info for the buildingg
313
+ setBuildingDerivedInfo = async (): Promise<void> => {
314
+ const buildingDetails = this.buildingWebchatView;
315
+ if (!buildingDetails) return;
316
+ if (buildingDetails && this.orgSlug) {
317
+ overrideContactUsForm(
318
+ this.orgSlug,
319
+ this.LeadSourceClient?.chatId ?? null,
320
+ this.sampleRateToUse,
321
+ this.buildingSlug,
322
+ this.LeadSourceClient?.leadSource ?? null
323
+ );
324
+ }
325
+
326
+ const [
327
+ buildingABTest,
328
+ leadSources,
329
+ featureFlagShowDropdown,
330
+ featureFlagUseDNI,
331
+ featureFlagInsertDNIWebsite,
332
+ featureFlagReplaceScheduleTourCtaWebsite,
333
+ featureFlagUseApplicationsLinkReplacement,
334
+ ] = await Promise.all([
335
+ fetchBuildingABTestType(this.buildingSlug),
336
+ fetchLeadSources(this.orgSlug, this.buildingSlug),
337
+ fetchFeatureFlagShowMarketingSourceDropdown(this.buildingSlug),
338
+ fetchFeatureFlagUsePhoneNumberBySource(this.buildingSlug),
339
+ fetchFeatureFlagInsertDNIWebsite(this.buildingSlug),
340
+ fetchFeatureFlagReplaceScheduleTourCtaWebsite(this.buildingSlug),
341
+ fetchFeatureFlagUseApplicationsLinkReplacement(this.buildingSlug),
342
+ ]);
343
+
344
+ if (this.buildingWebchatView) {
345
+ this.buildingWebchatView.phoneNumber = formatPhoneNumber(
346
+ buildingDetails.phoneNumber
347
+ );
348
+ }
349
+ this.designConcept = buildingABTest?.abTestType ?? "";
350
+ this.leadSources = leadSources;
351
+ this.featureFlagShowDropdown = featureFlagShowDropdown;
352
+
353
+ // The backend is cached for ~4 hours, so falling back to the existing api request until the cache propagates all the changes
354
+ const buildingPhoneNumber = buildingDetails.textWithUsPhoneNumber
355
+ ? buildingDetails.textWithUsPhoneNumber
356
+ : await getBuildingPhoneNumber(this.buildingSlug);
357
+
358
+ const shouldShowCalcDesktop =
359
+ (this.buildingWebchatView?.shouldShowPricingCalculatorDesktop &&
360
+ isTestEnv()) ??
361
+ false;
362
+ const shouldShowCalcMobile =
363
+ (this.buildingWebchatView?.shouldShowPricingCalculatorMobile &&
364
+ isTestEnv()) ??
365
+ false;
366
+
367
+ this.enabledChatWidgets = {
368
+ callDesktop: this.buildingWebchatView?.shouldShowPhoneDesktop ?? false,
369
+ callMobile: this.buildingWebchatView?.shouldShowPhoneMobile ?? false,
370
+ chatDesktop: this.buildingWebchatView?.shouldShowChatDesktop ?? false,
371
+ chatMobile: this.buildingWebchatView?.shouldShowChatMobile ?? false,
372
+ emailDesktop: this.buildingWebchatView?.shouldShowEmailDesktop ?? false,
373
+ emailMobile: this.buildingWebchatView?.shouldShowEmailMobile ?? false,
374
+ textDesktop:
375
+ (this.buildingWebchatView?.shouldShowTextDesktop &&
376
+ !!buildingPhoneNumber) ??
377
+ false,
378
+ textMobile:
379
+ (this.buildingWebchatView?.shouldShowTextMobile &&
380
+ !!buildingPhoneNumber) ??
381
+ false,
382
+ sstDesktop: this.buildingWebchatView?.shouldShowSstDesktop ?? false,
383
+ sstMobile: this.buildingWebchatView?.shouldShowSstMobile ?? false,
384
+ calcDesktop: shouldShowCalcDesktop,
385
+ calcMobile: shouldShowCalcMobile,
386
+ };
387
+
388
+ if (
389
+ this.enabledChatWidgets.sstDesktop ||
390
+ this.enabledChatWidgets.sstMobile ||
391
+ this.orgSlug === "radco"
392
+ ) {
393
+ replaceSSTLinkWithTourWidgetTrigger(this.handleTourClicked);
394
+ if (featureFlagReplaceScheduleTourCtaWebsite) {
395
+ replacePageRedirectElementWithTourWidgetTrigger(this.handleTourClicked);
396
+ }
397
+ if (this.orgSlug === "radco") {
398
+ replaceTourTriggerElementWithTourWidgetTrigger(this.handleTourClicked);
399
+ }
400
+ } else {
401
+ if (this.LeadSourceClient?.leadSource) {
402
+ insertLeadSourceIntoSchedulerLinks(
403
+ this.LeadSourceClient?.leadSource ?? null
404
+ );
405
+ }
406
+ }
407
+
408
+ if (this.buildingWebchatView) {
409
+ this.primaryColor = this.buildingWebchatView.primaryColor ?? null;
410
+ this.backgroundColor = this.buildingWebchatView.secondaryColor ?? null;
411
+ this.foregroundColorOnPrimaryBackgroundColor =
412
+ this.buildingWebchatView.foregroundColorOnPrimaryBackgroundColor ??
413
+ null;
414
+ this.foregroundColorOnSecondaryBackgroundColor =
415
+ this.buildingWebchatView.foregroundColorOnSecondaryBackgroundColor ??
416
+ null;
417
+ this.webchatConfigHasAutoOpenChat =
418
+ this.buildingWebchatView.autoOpenChat ?? false;
419
+ this.requiresConsent =
420
+ this.buildingWebchatView.requiresConsentForChat ?? false;
421
+ this.hideMobileFeatures =
422
+ this.buildingWebchatView.collapseMobileFeatures ?? false;
423
+ this.privacyPolicyUrl =
424
+ this.buildingWebchatView.privacyPolicyUrlForChat ??
425
+ this.privacyPolicyUrl;
426
+
427
+ if (this.buildingWebchatView.displayStyle === DesignConcepts.MINIMIZED) {
428
+ // this.designConcept = DesignConcepts.MINIMIZED; // uncomment this if we want to remove the minimize/expand option
429
+ this.isMinimized = true;
430
+ }
431
+ if (this.buildingWebchatView.displayStyle === DesignConcepts.PILLS) {
432
+ this.designConcept = null; // default design concept is PILLS, no need to specify here
433
+ }
434
+ if (this.buildingWebchatView.displayStyle === DesignConcepts.EMOJI) {
435
+ this.designConcept = DesignConcepts.EMOJI;
436
+ }
437
+
438
+ this.onWidgetLoaded();
439
+ }
440
+
441
+ if (this.primaryColor === null) this.primaryColor = defaultPrimaryColor;
442
+ if (this.backgroundColor === null)
443
+ this.backgroundColor = defaultBackgroundColor;
444
+
445
+ const buildingsPhoneNumber = this.buildingWebchatView?.phoneNumber ?? "";
446
+ let phoneNumberForSource = null;
447
+ if (featureFlagUseDNI) {
448
+ phoneNumberForSource = await fetchPhoneNumberFromSource(
449
+ this.buildingSlug,
450
+ this.LeadSourceClient?.leadSource ?? null
451
+ );
452
+
453
+ // By default, we want to use the building's phone number, not the default catchall number
454
+ // https://meetelise.slack.com/archives/C01BT9GB9LZ/p1696437860448009
455
+ if (phoneNumberForSource && !phoneNumberForSource.isMatch) {
456
+ phoneNumberForSource.number = buildingsPhoneNumber;
457
+ }
458
+ if (!this.LeadSourceClient?.leadSource && phoneNumberForSource) {
459
+ phoneNumberForSource.number = buildingsPhoneNumber;
460
+ }
461
+ }
462
+ if (featureFlagInsertDNIWebsite && phoneNumberForSource?.number) {
463
+ const totalReplacements = insertDNIIntoWebsite(
464
+ phoneNumberForSource.number,
465
+ this.orgSlug,
466
+ this.buildingSlug
467
+ );
468
+ if (totalReplacements > 0) {
469
+ logSignal({
470
+ params: {
471
+ org_slug: this.orgSlug,
472
+ building_slug: this.buildingSlug,
473
+ is_dni_insert_override_success: true,
474
+ extra_data: {
475
+ total_replacements: totalReplacements,
476
+ },
477
+ },
478
+ sampleRate: this.sampleRateToUse,
479
+ });
480
+ }
481
+ }
482
+
483
+ const devTestPhoneNumber = localStorage.getItem(
484
+ "eliseai_phone_number_devtest"
485
+ );
486
+ if (devTestPhoneNumber) {
487
+ // eslint-disable-next-line no-console
488
+ console.info("Using dev test phone number: ", devTestPhoneNumber);
489
+ insertDNIIntoWebsite(devTestPhoneNumber, this.orgSlug, this.buildingSlug);
490
+ }
491
+
492
+ // if the building does NOT have IVR setup, we want to use the building's phone number
493
+ if (!phoneNumberForSource) {
494
+ this.phoneNumberForSource = {
495
+ number: buildingsPhoneNumber,
496
+ isMatch: false,
497
+ isPropertyWebsiteCatchall: true,
498
+ };
499
+ } else {
500
+ this.phoneNumberForSource = phoneNumberForSource;
501
+ }
502
+
503
+ const currentLeadSource = this.LeadSourceClient?.leadSource ?? null;
504
+ if (currentLeadSource) {
505
+ if (!this.leadSources.includes(currentLeadSource)) {
506
+ this.leadSources.push(currentLeadSource);
507
+ }
508
+ }
509
+ getRawAvailabilities(buildingDetails.id); // we're not using this here, just want to cache the result
510
+
511
+ if (
512
+ featureFlagUseApplicationsLinkReplacement &&
513
+ this.buildingSlug &&
514
+ this.buildingWebchatView?.isLiveOnApplications
515
+ ) {
516
+ const totalReplacements = replaceSelectButtonsWithNewLink(
517
+ `https://applications.eliseai.com/building/${this.buildingSlug}`,
518
+ this.orgSlug,
519
+ this.buildingSlug
520
+ );
521
+ if (totalReplacements > 0) {
522
+ logSignal({
523
+ params: {
524
+ org_slug: this.orgSlug,
525
+ building_slug: this.buildingSlug,
526
+ is_application_link_replacement_success: true,
527
+ extra_data: {
528
+ total_replacements: totalReplacements,
529
+ },
530
+ },
531
+ sampleRate: this.sampleRateToUse,
532
+ });
533
+ }
534
+ }
535
+ };
536
+
537
+ private initializeChatVariables = async (): Promise<void> => {
538
+ if (!this.buildingWebchatView) return;
539
+
540
+ // important to distinguish between a user who has never opened the chat and a user who has opened the chat but closed it
541
+ const currentChatStorageKey = getChatStorageKey(this.buildingSlug);
542
+ const updatedChatStorageKey = isChatKeyValid(
543
+ this.buildingSlug,
544
+ currentChatStorageKey
545
+ )
546
+ ? currentChatStorageKey
547
+ : createChatStorageKey(this.buildingSlug, false);
548
+
549
+ if (!updatedChatStorageKey.leadId) {
550
+ throw new Error("Lead ID is null");
551
+ }
552
+ this.LeadSourceClient = new LeadSourceClient();
553
+ this.myPubnub = new MyPubnub(
554
+ this.buildingSlug,
555
+ this.buildingWebchatView,
556
+ this.orgSlug,
557
+ null, // initialize lead source as null initially
558
+ updatedChatStorageKey.leadId,
559
+ this.LeadSourceClient,
560
+ this.widgetType
561
+ );
562
+ this.LeadSourceClient.chatId = this.myPubnub.channel;
563
+ await this.LeadSourceClient?.generateUserLeadSource({
564
+ leadId: this.myPubnub?.leadUserId ?? "",
565
+ orgSlug: this.orgSlug,
566
+ buildingSlug: this.buildingSlug,
567
+ });
568
+
569
+ // eslint-disable-next-line no-console
570
+ console.log(
571
+ "EliseAI Mapped Lead Source source: ",
572
+ this.LeadSourceClient?.leadSource
573
+ );
574
+
575
+ if (getDefaultLeadSourceAttribution(this.orgSlug) !== "property-website") {
576
+ this.myPubnub.leadSource =
577
+ this.LeadSourceClient?.leadSource ??
578
+ getDefaultLeadSourceAttribution(this.orgSlug);
579
+ } else {
580
+ this.myPubnub.leadSource = this.LeadSourceClient?.leadSource ?? null;
581
+ }
582
+ if (updatedChatStorageKey.initiatedChat) {
583
+ await this.myPubnub.initializePubnub(currentChatStorageKey);
584
+ }
585
+ };
586
+
587
+ private shouldAutoOpenChat = (buildingHasAutoOpen: boolean): boolean => {
588
+ const alreadyAutoOpenedTimestamp = sessionStorage.getItem(
589
+ "alreadyAutoOpenedTimestamp"
590
+ ); // we dont want to autoopen on EVERY single page load, so we'll only do it once every 15 minutes max
591
+ const shouldAutoOpen =
592
+ !alreadyAutoOpenedTimestamp ||
593
+ !(
594
+ alreadyAutoOpenedTimestamp &&
595
+ !isPast(parseISO(alreadyAutoOpenedTimestamp))
596
+ );
597
+ return !!buildingHasAutoOpen && !!shouldAutoOpen && !isMobile();
598
+ };
599
+
600
+ private async handleChatInitializeAnalytics(): Promise<void> {
601
+ this.analytics = new Analytics(
602
+ this.orgSlug,
603
+ this.buildingSlug,
604
+ this.myPubnub?.channel ?? "", // this will be empty if the user does not have a current chat session.
605
+ this.LeadSourceClient?.leadSource ?? null
606
+ );
607
+
608
+ // Too many logs kills our DB, so we'll only log 5% of the time
609
+ if (Math.random() < 0.05) {
610
+ this.analytics.ping("webchat_heartbeat");
611
+ }
612
+ }
613
+
614
+ public async restartConversation(): Promise<void> {
615
+ this.myPubnub?.handleDisconnect();
616
+ clearChatStorageKey(this.buildingSlug);
617
+ this.myPubnub = null;
618
+ this.displayPubnubChat = false;
619
+ await this.setupWebchat();
620
+ this.hideLauncher = false;
621
+ }
622
+
623
+ private updateAlreadyAutoOpenedTimestamp = (): void => {
624
+ sessionStorage.setItem(
625
+ "alreadyAutoOpenedTimestamp",
626
+ formatISO(addMinutes(new Date(), 15))
627
+ );
628
+ };
629
+
630
+ /** Show the chat button on the screen if it was previously hidden. */
631
+ show(): void {
632
+ if (!this.launcher) {
633
+ return;
634
+ }
635
+ this.launcher.style.display = "";
636
+ }
637
+
638
+ /** Hide the chat button from the screen (but don't remove from the DOM). */
639
+ hide(): void {
640
+ if (!this.launcher) {
641
+ return;
642
+ }
643
+ this.launcher.style.display = "none";
644
+ }
645
+
646
+ handleContactClicked = (e: MouseEvent): void => {
647
+ this.displayPubnubChat = false;
648
+ this.hideLauncher = false;
649
+ this.showTourNextToChat = false;
650
+
651
+ this.launcherRef.value?.onClickEmailOption(e);
652
+ };
653
+
654
+ handleTourClicked = (e: MouseEvent): void => {
655
+ if (this.showTourNextToChat) {
656
+ this.launcherRef.value?.onClickSSTOption(e);
657
+ return;
658
+ }
659
+ if (!this.hideLauncher) {
660
+ this.displayPubnubChat = false;
661
+ } else {
662
+ this.showTourNextToChat = true;
663
+ }
664
+
665
+ this.hideLauncher = false;
666
+ this.launcherRef.value?.onClickSSTOption(e);
667
+ };
668
+
669
+ handleContactTabClicked = (e: MouseEvent): void => {
670
+ this.displayPubnubChat = false;
671
+ this.showTourNextToChat = false;
672
+ this.hideLauncher = false;
673
+ this.launcherRef.value?.onClickPhoneOption(e);
674
+ };
675
+
676
+ // Depending on the rendered element of the chat widget, we need to adjust the position of the chat additional actions
677
+ // these will be aligned at the bottom. It is much easier to handle here at the parent of the launchers rather than
678
+ // in pubnub-chat.
679
+ adjustPositionChatAdditionalActions = (): void => {
680
+ const headerRef = this.shadowRoot?.getElementById(
681
+ "chatAdditionalActionsPubnub"
682
+ );
683
+ if (!headerRef) return;
684
+
685
+ const pubnubContainerElement = this.shadowRoot
686
+ ?.getElementById("pubnub-chat")
687
+ ?.shadowRoot?.getElementById("pubnub-chat-container");
688
+
689
+ const pubnubPopupCoords = pubnubContainerElement?.getBoundingClientRect();
690
+ if (!pubnubPopupCoords) return;
691
+ headerRef.style.left = `${pubnubPopupCoords.left}px`;
692
+ headerRef.style.top = `${pubnubPopupCoords.bottom}px`;
693
+ };
694
+
695
+ connectedCallback(): void {
696
+ super.connectedCallback();
697
+ window.addEventListener("resize", this.adjustPositionChatAdditionalActions);
698
+ window.addEventListener("keydown", this.handleKeydownTab);
699
+ }
700
+
701
+ handleKeydownTab = (e: KeyboardEvent): void => {
702
+ if (e.key === "Tab") {
703
+ const pubnubContainerElement = this.displayPubnubChat
704
+ ? this.shadowRoot // pubnub chat
705
+ ?.getElementById("pubnub-chat")
706
+ ?.shadowRoot?.getElementById("pubnub-chat-container")
707
+ : this.shadowRoot // email us form
708
+ ?.getElementById("meetelise-launcher")
709
+ ?.shadowRoot?.getElementById("email-us-window")
710
+ ?.shadowRoot?.getElementById("email-us-form") ??
711
+ this.shadowRoot // tour booking form
712
+ ?.getElementById("meetelise-launcher")
713
+ ?.shadowRoot?.getElementById("tour-scheduler-window")
714
+ ?.shadowRoot?.getElementById("scheduler-container") ??
715
+ this.shadowRoot // text us form
716
+ ?.getElementById("meetelise-launcher")
717
+ ?.shadowRoot?.getElementById("call-us-window")
718
+ ?.shadowRoot?.getElementById("details-window");
719
+
720
+ if (!pubnubContainerElement) return;
721
+ e.preventDefault();
722
+
723
+ const inputsRef = pubnubContainerElement.querySelectorAll(
724
+ "input, me-select, textarea, action-confirm-button, [tabindex]"
725
+ );
726
+
727
+ const inputsArray = Array.from(inputsRef) as HTMLElement[];
728
+ const currentFocusedIndex = inputsArray.findIndex((el) =>
729
+ el.matches(":focus")
730
+ );
731
+
732
+ const nextIndex = (currentFocusedIndex + 1) % inputsArray.length;
733
+ inputsArray[nextIndex].focus();
734
+ }
735
+ };
736
+
737
+ getDeepActiveElement(): Element | null {
738
+ let activeElement = document.activeElement;
739
+ let count = 0;
740
+ while (
741
+ activeElement?.shadowRoot &&
742
+ activeElement?.shadowRoot.activeElement
743
+ ) {
744
+ count += 1;
745
+ activeElement = activeElement?.shadowRoot.activeElement;
746
+ if (count > 100) {
747
+ // just incase we get stuck in an infinite loop
748
+ sendLoggingEvent({
749
+ logType: LogType.error,
750
+ buildingSlug: this.buildingSlug,
751
+ logTitle: "[ERROR_GETTING_DEEP_ELEMENT]",
752
+ logData: { message: "error getting deep active element" },
753
+ });
754
+ break;
755
+ }
756
+ }
757
+ return activeElement;
758
+ }
759
+
760
+ updated(): void {
761
+ this.adjustPositionChatAdditionalActions();
762
+ }
763
+
764
+ disconnectedCallback(): void {
765
+ this.myPubnub?.handleDisconnect();
766
+ window.removeEventListener(
767
+ "resize",
768
+ this.adjustPositionChatAdditionalActions
769
+ );
770
+ window.removeEventListener("keydown", this.handleKeydownTab);
771
+
772
+ super.disconnectedCallback();
773
+ }
774
+
775
+ onClickMinimize = (e: MouseEvent): void => {
776
+ e.preventDefault();
777
+ e.stopPropagation();
778
+ this.isMinimized = !this.isMinimized;
779
+ this.showTourNextToChat = false;
780
+ };
781
+
782
+ private attachOnClickToLauncher = () => {
783
+ const launcher = this.launcherRef.value;
784
+ if (!launcher) {
785
+ return;
786
+ }
787
+
788
+ launcher.onChatTapped = async () => {
789
+ this.displayPubnubChat = true;
790
+ this.hideLauncher = true;
791
+ this.hasMounted = true;
792
+ localStorage.setItem("isChatOpen", "true");
793
+ };
794
+ launcher.onExitChat = this.onExitChat;
795
+ };
796
+
797
+ onExitChat = (): void => {
798
+ this.displayPubnubChat = false;
799
+ this.hideLauncher = false;
800
+ this.showTourNextToChat = false;
801
+ localStorage.setItem("isChatOpen", "false");
802
+ };
803
+
804
+ render(): TemplateResult {
805
+ installLauncher();
806
+ const showChatAdditionalActions =
807
+ this.hideLauncher && !this.isLoading && !isMobile();
808
+
809
+ return html`
810
+ <meta name="viewport" content="width=device-width, initial-scale=1">
811
+ <div id="aria-describe-info" style="display: none;">
812
+ EliseAI widget that allows you to chat with a virtual assistant, book
813
+ a tour, contact the leasing office, and more.
814
+ </div>
815
+
816
+
817
+ ${
818
+ this.displayPubnubChat
819
+ ? html`
820
+ <pubnub-chat
821
+ id="pubnub-chat"
822
+ .channel=${this.myPubnub?.channel}
823
+ .myPubnub=${this.myPubnub}
824
+ .orgSlug=${this.orgSlug}
825
+ .buildingSlug=${this.buildingSlug}
826
+ .widgetType=${WidgetType.Default}
827
+ .building=${this.buildingWebchatView}
828
+ .primaryColor=${this.primaryColor}
829
+ .backgroundColor=${this.backgroundColor}
830
+ .onClickExit=${this.onExitChat}
831
+ .onMount=${() => {
832
+ this.adjustPositionChatAdditionalActions();
833
+ }}
834
+ .requiresConsent=${this.requiresConsent}
835
+ .hideMobileFeatures=${this.hideMobileFeatures}
836
+ .privacyPolicyUrl=${this.privacyPolicyUrl}
837
+ .top=${this.top}
838
+ .bottom=${this.bottom}
839
+ .left=${this.left}
840
+ .right=${this.right}
841
+ ></pubnub-chat>
842
+ <chat-additional-actions
843
+ id="chatAdditionalActionsPubnub"
844
+ .showChatAdditionalActions=${showChatAdditionalActions ||
845
+ this.showTourNextToChat}
846
+ .buildingWebchatView=${this.buildingWebchatView}
847
+ .primaryColor=${this.primaryColor}
848
+ .backgroundColor=${this.backgroundColor}
849
+ .enabledChatWidgets=${this.enabledChatWidgets}
850
+ .onClickMinimize=${this.onClickMinimize}
851
+ .onClickEmailOption=${this.handleContactClicked}
852
+ .onClickPhoneOption=${this.handleContactTabClicked}
853
+ .onClickSSTOption=${this.handleTourClicked}
854
+ .onClose=${() => {
855
+ this.displayPubnubChat = false;
856
+ this.showTourNextToChat = false;
857
+ this.hideLauncher = false;
858
+ localStorage.setItem("isChatOpen", "false");
859
+ }}
860
+ ></chat-additional-actions>
861
+ `
862
+ : ""
863
+ }
864
+
865
+ <div
866
+ id='meetelise-chat-launcher-container'
867
+ class=${classMap({
868
+ launcherContainer: true,
869
+ ["launcher__mobile"]: isMobile(),
870
+ ["launcher__desktop"]: !isMobile(),
871
+ ["meetelise-chat"]: true,
872
+ launcher: true,
873
+ shouldBeVisible: true,
874
+ ["hideTab"]: this.isLoading,
875
+ })}
876
+ >
877
+ ${
878
+ this.buildingWebchatView
879
+ ? html`<meetelise-launcher
880
+ id="meetelise-launcher"
881
+ ${ref(this.launcherRef)}
882
+ .isFirstMount=${!this.hasMounted}
883
+ .buildingId=${this.buildingWebchatView?.id ?? 0}
884
+ .hasDynamicSchedulingEnabled=${this.buildingWebchatView
885
+ ?.usesDynamicScheduling ?? false}
886
+ .orgLegalName=${this.buildingWebchatView?.orgLegalName ?? ""}
887
+ .tourTypeOptions=${this.buildingWebchatView
888
+ ?.tourTypeOptions ?? []}
889
+ .launcherStyles=${this.launcherStyles}
890
+ .primaryColor=${this.primaryColor}
891
+ .backgroundColor=${this.backgroundColor}
892
+ .foregroundColorOnPrimaryBackgroundColor=${this
893
+ .foregroundColorOnPrimaryBackgroundColor ?? "white"}
894
+ .foregroundColorOnSecondaryBackgroundColor=${this
895
+ .foregroundColorOnSecondaryBackgroundColor ?? "black"}
896
+ .isMinimized=${this.isMinimized}
897
+ .onClickMinimize=${this.onClickMinimize}
898
+ .onSstClose=${this.onSstClose}
899
+ .overrideRentgrata=${this.overrideRentgrata}
900
+ .autoOpenChatWidget=${this.buildingWebchatView
901
+ ?.autoOpenChat ?? false}
902
+ .mobileStyles=${isMobile() || this.isMinimized
903
+ ? this.mobileStyles
904
+ : {}}
905
+ chatCallUsHeader=${this.buildingWebchatView
906
+ ?.chatCallUsHeader ?? ""}
907
+ chatId="${this.chatId}"
908
+ phoneNumber="${this.phoneNumberForSource?.number ??
909
+ this.buildingWebchatView?.phoneNumber ??
910
+ ""}"
911
+ buildingName=${this.buildingWebchatView?.name ?? ""}
912
+ orgSlug="${this.orgSlug}"
913
+ buildingSlug="${this.buildingSlug}"
914
+ sgtUrl="${this.buildingWebchatView?.sgtUrl ?? ""}"
915
+ selfGuidedToursTypeOffered="${this.buildingWebchatView
916
+ ?.selfGuidedToursTypeOffered}"
917
+ selfGuidedTourEnabled="${this.buildingWebchatView
918
+ ?.isSelfGuidedTourEnabled}"
919
+ designConcept="${this.designConcept ?? ""}"
920
+ currentLeadSource="${this.LeadSourceClient?.leadSource ??
921
+ null ??
922
+ ""}"
923
+ featureFlagShowDropdown="${this.featureFlagShowDropdown}"
924
+ .leadSources="${this.leadSources ?? []}"
925
+ escortedToursLink="${this.buildingWebchatView
926
+ ?.escortedToursLink ?? ""}"
927
+ virtualToursLink="${this.buildingWebchatView
928
+ ?.virtualToursLink ?? ""}"
929
+ .showTourNextToChat="${this.showTourNextToChat ?? ""}"
930
+ @closeShowTourNextToChat=${() => {
931
+ this.showTourNextToChat = false;
932
+ this.hideLauncher = true;
933
+ }}
934
+ ?hidden=${this.hideLauncher}
935
+ .hasCallUsEnabledDesktop=${this.enabledChatWidgets
936
+ .callDesktop}
937
+ .hasCallUsEnabledMobile=${this.enabledChatWidgets.callMobile}
938
+ .hasChatEnabledDesktop=${this.enabledChatWidgets.chatDesktop}
939
+ .hasChatEnabledMobile=${this.enabledChatWidgets.chatMobile}
940
+ .hasEmailEnabledDesktop=${this.enabledChatWidgets
941
+ .emailDesktop}
942
+ .hasEmailEnabledMobile=${this.enabledChatWidgets.emailMobile}
943
+ .hasTextUsEnabledDesktop=${this.enabledChatWidgets
944
+ .textDesktop}
945
+ .hasTextUsEnabledMobile=${this.enabledChatWidgets.textMobile}
946
+ .hasSSTEnabledDesktop=${this.enabledChatWidgets.sstDesktop}
947
+ .hasSSTEnabledMobile=${this.enabledChatWidgets.sstMobile}
948
+ .hasApplyNowEnabledDesktop=${this.buildingWebchatView
949
+ .shouldShowApplyNowDesktop}
950
+ .hasApplyNowEnabledMobile=${this.buildingWebchatView
951
+ .shouldShowApplyNowMobile}
952
+ .applicationLink=${this.buildingWebchatView.applicationLink}
953
+ .hasPricingCalculatorEnabledDesktop=${this.enabledChatWidgets
954
+ .calcDesktop}
955
+ .hasPricingCalculatorEnabledMobile=${this.enabledChatWidgets
956
+ .calcMobile}
957
+ .hasHideMobileFeatures=${this.hideMobileFeatures}
958
+ .top=${this.top}
959
+ .bottom=${this.bottom}
960
+ .left=${this.left}
961
+ .right=${this.right}
962
+ .leadSourceClient=${this.LeadSourceClient}
963
+ >
964
+ </meetelise-launcher>`
965
+ : ""
966
+ }
967
+ </div>
968
+ </meta>
969
+ `;
970
+ }
971
+ }
972
+
973
+ declare global {
974
+ interface HTMLElementTagNameMap {
975
+ "me-chat": MEChat;
976
+ }
977
+
978
+ interface Window {
979
+ RCTPCampaign?: { CampaignDetails: { Source: string } };
980
+ }
981
+ }
982
+
983
+ const findElementById = (priorityIds: string[]): HTMLElement | null => {
984
+ for (const id of priorityIds) {
985
+ const element = document.getElementById(id);
986
+ if (element) {
987
+ return element;
988
+ }
989
+ }
990
+ return null;
991
+ };
992
+
993
+ const overrideContactUsForm = async (
994
+ orgSlug: string,
995
+ chatId: string | null,
996
+ sampleRateToUse: number,
997
+ buildingSlug?: string,
998
+ leadSource?: string | null
999
+ ): Promise<void> => {
1000
+ if (!buildingSlug || !orgSlug) return;
1001
+ const shouldUseOverrideContactUsForm =
1002
+ await fetchFeatureFlagUseOverrideContactUsForm(buildingSlug);
1003
+ if (!shouldUseOverrideContactUsForm) return;
1004
+
1005
+ const form = findElementById([
1006
+ "myContactForm",
1007
+ "contactus",
1008
+ "fpa-myContactForm",
1009
+ ]);
1010
+ let btn = undefined;
1011
+
1012
+ if (!form || !(form instanceof HTMLFormElement)) {
1013
+ const foundForm = document.querySelector("form");
1014
+ if (window.location.pathname.toLowerCase().includes("contactus")) {
1015
+ logContactUsFormError(
1016
+ buildingSlug,
1017
+ orgSlug,
1018
+ "Could not find form",
1019
+ foundForm ? { id: foundForm.id } : undefined
1020
+ );
1021
+ }
1022
+ return;
1023
+ }
1024
+
1025
+ // If the form is apart of the schedule tour form, do not override
1026
+ // https://meetelise.slack.com/archives/C01BT9GB9LZ/p1706132172167819
1027
+ try {
1028
+ if (form.parentElement && form.parentElement.id === "scheduleTourForm") {
1029
+ return logContactUsFormError(
1030
+ buildingSlug,
1031
+ orgSlug,
1032
+ "Form is apart of schedule tour form!"
1033
+ );
1034
+ }
1035
+ } catch (error) {
1036
+ // eslint-disable-next-line no-console
1037
+ console.error("error", error);
1038
+ }
1039
+
1040
+ // Loop through the elements of the form
1041
+ for (let i = 0; i < form.elements.length; i++) {
1042
+ const element = form.elements[i];
1043
+ if (
1044
+ element.tagName.toLowerCase() === "button" &&
1045
+ element.getAttribute("data-selenium-id") === "fakebutton"
1046
+ ) {
1047
+ btn = element;
1048
+ break;
1049
+ }
1050
+ if (element.getAttribute("id") === "fpa-contact-submit") {
1051
+ btn = element;
1052
+ break;
1053
+ }
1054
+ }
1055
+
1056
+ if (!btn) {
1057
+ return logContactUsFormError(
1058
+ buildingSlug,
1059
+ orgSlug,
1060
+ "Could not find submit button"
1061
+ );
1062
+ }
1063
+
1064
+ const eliseUrl =
1065
+ "https://app.meetelise.com/platformApi/state/create/contactMe";
1066
+
1067
+ const getFormElements = () => {
1068
+ const firstName = findElementById([
1069
+ "firstname",
1070
+ "txtName",
1071
+ "fpa-firstname",
1072
+ "Form_FirstName",
1073
+ ]) as HTMLInputElement | null;
1074
+ const lastName = findElementById([
1075
+ "lastname",
1076
+ "txtName2",
1077
+ "fpa-lastname",
1078
+ "Form_LastName",
1079
+ ]) as HTMLInputElement | null;
1080
+ const email = findElementById([
1081
+ "email",
1082
+ "txtEmail",
1083
+ "fpa-email",
1084
+ "Form_Email",
1085
+ ]) as HTMLInputElement | null;
1086
+ const phone = findElementById([
1087
+ "phonenumber",
1088
+ "txtPhone",
1089
+ "fpa-phonenumber",
1090
+ "Form_Phone",
1091
+ ]) as HTMLInputElement | null;
1092
+ const message = findElementById([
1093
+ "message",
1094
+ "txtComments",
1095
+ "fpa-email",
1096
+ "Form_Message",
1097
+ ]) as HTMLTextAreaElement | null;
1098
+
1099
+ const formElements = { firstName, lastName, email, phone, message };
1100
+ return formElements;
1101
+ };
1102
+
1103
+ const isMissingFormElements = () => {
1104
+ if (Object.values(getFormElements()).some((el) => el === null)) {
1105
+ return true;
1106
+ }
1107
+ return false;
1108
+ };
1109
+
1110
+ const validateFormElements = () => {
1111
+ Object.values(getFormElements()).forEach((el) => {
1112
+ if (el?.getAttribute("aria-required") === "true" && !el.value) {
1113
+ el.className = "form-control required is-invalid";
1114
+ }
1115
+ });
1116
+ };
1117
+
1118
+ const isValid = () => {
1119
+ return Object.values(getFormElements()).every((el) => {
1120
+ if (el === null) return false;
1121
+ if (el.getAttribute("aria-required") === "true" && !el.value) {
1122
+ return false;
1123
+ }
1124
+ if (
1125
+ (el.id === "email" || el.id === "phonenumber") &&
1126
+ el.getAttribute("aria-invalid") === "true"
1127
+ ) {
1128
+ return false;
1129
+ }
1130
+ return true;
1131
+ });
1132
+ };
1133
+
1134
+ if (isMissingFormElements()) {
1135
+ const missingElements = Object.entries(getFormElements())
1136
+ .filter(([, val]) => val === null)
1137
+ .map(([key]) => key);
1138
+ return logContactUsFormError(
1139
+ buildingSlug,
1140
+ orgSlug,
1141
+ "Missing the following form elements: " + missingElements.join(", ")
1142
+ );
1143
+ }
1144
+ const originalButtonText = btn.textContent;
1145
+
1146
+ // Replace the original form element with the cloned one
1147
+ const clonedButton = btn.cloneNode(true) as HTMLButtonElement;
1148
+ btn.parentNode?.replaceChild(clonedButton, btn);
1149
+
1150
+ if (clonedButton) {
1151
+ logSignal({
1152
+ params: {
1153
+ chat_id: chatId,
1154
+ org_slug: orgSlug,
1155
+ building_slug: buildingSlug,
1156
+ is_form_override_success: true,
1157
+ },
1158
+ sampleRate: sampleRateToUse,
1159
+ });
1160
+ }
1161
+
1162
+ clonedButton.onclick = async function (event) {
1163
+ validateFormElements();
1164
+
1165
+ if (!isValid()) return;
1166
+ event.preventDefault();
1167
+
1168
+ clonedButton.textContent = "Processing request...";
1169
+ clonedButton.disabled = true;
1170
+
1171
+ const formValues: { [key: string]: string | undefined } = {};
1172
+ Object.entries(getFormElements()).forEach(
1173
+ ([key, val]) => (formValues[key] = val?.value)
1174
+ );
1175
+
1176
+ const buildingWebchatView = await fetchBuildingWebchatView(
1177
+ orgSlug,
1178
+ buildingSlug
1179
+ );
1180
+
1181
+ if (!buildingWebchatView) {
1182
+ logContactUsFormError(buildingSlug, orgSlug, "Could not find building");
1183
+ return null;
1184
+ }
1185
+
1186
+ const data = {
1187
+ email_address: formValues.email,
1188
+ first_name: formValues.firstName,
1189
+ last_name: formValues.lastName,
1190
+ phone_number: formValues.phone,
1191
+ first_message: formValues.message,
1192
+ building_id: buildingWebchatView.id,
1193
+ is_external_form: true,
1194
+ lead_sources: [
1195
+ ...new Set(
1196
+ leadSource
1197
+ ? [leadSource, getDefaultLeadSourceAttribution(orgSlug)]
1198
+ : [getDefaultLeadSourceAttribution(orgSlug)]
1199
+ ),
1200
+ ],
1201
+ };
1202
+
1203
+ const jsonData = JSON.stringify(data);
1204
+
1205
+ fetch(eliseUrl, {
1206
+ method: "POST",
1207
+ headers: {
1208
+ "Content-Type": "application/json",
1209
+ "building-slug": buildingSlug,
1210
+ "org-slug": orgSlug,
1211
+ },
1212
+ body: jsonData,
1213
+ })
1214
+ .then((response) => {
1215
+ if (!response.ok) {
1216
+ throw new Error(`HTTP error ${response.status}`);
1217
+ }
1218
+
1219
+ if (form) {
1220
+ form.reset();
1221
+ }
1222
+
1223
+ Object.values(getFormElements()).forEach((el) => {
1224
+ if (!el) return;
1225
+ el.disabled = true;
1226
+ });
1227
+ clonedButton.textContent = "Submitted";
1228
+ clonedButton.disabled = true;
1229
+
1230
+ return response.json();
1231
+ })
1232
+ .catch(() => {
1233
+ clonedButton.textContent = originalButtonText;
1234
+ });
1235
+ };
1236
+ };
1237
+
1238
+ const logContactUsFormError = (
1239
+ buildingSlug: string,
1240
+ orgSlug: string,
1241
+ reason?: string,
1242
+ formIdInfo?: Record<string, unknown>
1243
+ ) => {
1244
+ const url = `https://app.meetelise.com/platformApi/webchat/form-override-error`;
1245
+ const body = JSON.stringify({
1246
+ orgSlug,
1247
+ buildingSlug,
1248
+ reason,
1249
+ url: window.location.href,
1250
+ formIdInfo,
1251
+ });
1252
+
1253
+ fetch(url, {
1254
+ method: "POST",
1255
+ headers: {
1256
+ "Content-Type": "application/json",
1257
+ "building-slug": buildingSlug,
1258
+ "org-slug": orgSlug,
1259
+ },
1260
+ body,
1261
+ });
1262
+ };