@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,1373 @@
1
+ import { html, LitElement, PropertyValueMap, TemplateResult } from "lit";
2
+ import { customElement, property, query, state } from "lit/decorators.js";
3
+ import {
4
+ shortcutKeyIsPressed,
5
+ formatToPhoneInput,
6
+ isPrintableCharacter,
7
+ } from "../actions/formatPhoneNumber";
8
+ import "./tour-type-option.ts";
9
+ import "./date-picker.ts";
10
+ import "./time-picker.ts";
11
+ import "../me-select.ts";
12
+ import {
13
+ DateWithTimeZoneOffset,
14
+ getAvailabilitiesGroupedByDay,
15
+ getExistenceOfAvailabilitiesByTourType,
16
+ resetAvailabilitiesCache,
17
+ } from "../../getAvailabilities";
18
+ import { TourAvailabilityResponseRankOrderedSupportedTourTypesEnum } from "../../types/rest-sdk.types";
19
+ import { format } from "date-fns";
20
+ import { DatePicker } from "./date-picker";
21
+ import { MESelect } from "../me-select";
22
+ import { TimePicker } from "./time-picker";
23
+ import { LabeledOption } from "../../fetchBuildingInfo";
24
+ import { isMobile, isValidPhoneNumber } from "../../utils";
25
+ import axios, { AxiosError } from "axios";
26
+ import mapValues from "lodash/mapValues";
27
+ import classnames from "classnames";
28
+ import parseISO from "date-fns/parseISO";
29
+ import compareAsc from "date-fns/compareAsc";
30
+ import { InputStyles } from "../actions/InputStyles";
31
+ import { classMap } from "lit/directives/class-map.js";
32
+ import { FeatureFlagsShowDropdown } from "../../fetchFeatureFlag";
33
+ import { pushGtmEvent } from "../../gtm";
34
+ import disclaimer from "../../disclaimers";
35
+ import { tourSchedulerStyles } from "./tourSchedulerStyles";
36
+ import { defaultPrimaryColor, defaultBackgroundColor } from "../../themes";
37
+ import { LogType, sendLoggingEvent } from "../../analytics";
38
+ import LeadSourceClient, {
39
+ getDefaultLeadSourceAttribution,
40
+ } from "../LeadSourceClient";
41
+ import { getShouldAllowScheduling } from "../../getShouldAllowScheduling";
42
+ import { TourVirtuallyIcon } from "../icons/TourVirtuallyIcon";
43
+ import { TourSelfGuidedIcon } from "../icons/TourSelfGuidedIcon";
44
+ import { TourWithAgentIcon } from "../icons/TourWithAgentIcon";
45
+
46
+ @customElement("tour-scheduler")
47
+ export class TourScheduler extends LitElement {
48
+ @property({ attribute: false })
49
+ tourTypeOptions: LabeledOption[] = [];
50
+ @property({ attribute: true })
51
+ chatId = "";
52
+ @property({ type: Number })
53
+ buildingId = 0;
54
+ @property({ attribute: true })
55
+ buildingSlug = "";
56
+ @property({ attribute: true })
57
+ sgtUrl = "";
58
+ @property({ attribute: true })
59
+ selfGuidedToursTypeOffered = "";
60
+ @property({ attribute: true })
61
+ selfGuidedTourEnabled = false;
62
+ @property({ attribute: true })
63
+ escortedToursLink = "";
64
+ @property({ attribute: true })
65
+ virtualToursLink = "";
66
+ @property({ attribute: true })
67
+ orgSlug = "";
68
+ @property({ attribute: true })
69
+ hasDynamicSchedulingEnabled = false;
70
+
71
+ @property({ attribute: true })
72
+ private leadSourceClient: LeadSourceClient | null = null;
73
+
74
+ @property({ attribute: true })
75
+ buildingName = "";
76
+
77
+ onCloseClicked?: (e: MouseEvent) => void;
78
+
79
+ @state()
80
+ private tourType: TourType | null = null;
81
+ @state()
82
+ private shouldShowTourType = {
83
+ [TourType.Guided]: true,
84
+ [TourType.Self]: true,
85
+ [TourType.Virtual]: true,
86
+ };
87
+ @state()
88
+ private email = "";
89
+ @state()
90
+ private phoneNumber = "";
91
+ @state()
92
+ private availabilitiesGroupedByDay: {
93
+ [day: string]: DateWithTimeZoneOffset[];
94
+ } = {};
95
+ @state()
96
+ private waitingForAvailabilities = true;
97
+ @state()
98
+ private selectedDate?: Date;
99
+ @state()
100
+ private selectedTime?: DateWithTimeZoneOffset | null;
101
+ @state()
102
+ private mobilePageIndex = 0;
103
+ @state()
104
+ private isSubmitting = false;
105
+ @state()
106
+ private tourIsBooked = false;
107
+ @state()
108
+ private shouldAllowScheduling = false;
109
+ @state()
110
+ private shouldAllowScheduleLoading = true;
111
+ @state()
112
+ private promptForReschedule = false;
113
+ @state()
114
+ private canceledReschedule = false;
115
+
116
+ @property({ attribute: false })
117
+ leadSources: string[] = [];
118
+
119
+ @property({ attribute: true })
120
+ orgLegalName = "";
121
+
122
+ @property({ attribute: true })
123
+ currentLeadSource = ""; // the default lead source based on referrer and query params.
124
+
125
+ @property({ attribute: true })
126
+ featureFlagShowDropdown = "";
127
+
128
+ @property({ attribute: true })
129
+ compactDesign = false;
130
+
131
+ @property({ attribute: true })
132
+ primaryColor: string = defaultPrimaryColor;
133
+
134
+ @property({ attribute: true })
135
+ backgroundColor: string = defaultBackgroundColor;
136
+
137
+ @property({ attribute: true })
138
+ foregroundColorOnPrimaryBackgroundColor = "white";
139
+
140
+ @property({ attribute: true })
141
+ foregroundColorOnSecondaryBackgroundColor = "black";
142
+
143
+ @query(".nameContainer#firstName input")
144
+ firstNameInput!: HTMLInputElement;
145
+ @query(".nameContainer#lastName input")
146
+ lastNameInput!: HTMLInputElement;
147
+ @query(".inputContainer#email input")
148
+ emailInput!: HTMLInputElement;
149
+ @query(".inputContainer#phone input")
150
+ phoneInput!: HTMLInputElement;
151
+ @query("me-select#leadSource")
152
+ selectedLeadSource!: MESelect;
153
+
154
+ @state()
155
+ firstNameInputValue = "";
156
+ @state()
157
+ lastNameInputValue = "";
158
+ @state()
159
+ leadSourceInputValue = "";
160
+
161
+ @state()
162
+ errorGettingAvailabilities = false;
163
+
164
+ _setAvailabilities = async (): Promise<void> => {
165
+ try {
166
+ const [allowScheduling, availabilitiesExistForTourType] =
167
+ await Promise.all([
168
+ getShouldAllowScheduling(
169
+ this.buildingId,
170
+ this.hasDynamicSchedulingEnabled
171
+ ),
172
+ getExistenceOfAvailabilitiesByTourType(),
173
+ ]);
174
+
175
+ this.shouldAllowScheduling = allowScheduling;
176
+ this.shouldAllowScheduleLoading = false;
177
+
178
+ sendLoggingEvent({
179
+ logTitle: "AVAILABILITIES_EXIST_FOR_TOUR_TYPE",
180
+ logData: availabilitiesExistForTourType,
181
+ logType: LogType.info,
182
+ buildingSlug: this.buildingSlug,
183
+ orgSlug: this.orgSlug,
184
+ });
185
+ const allValuesAreFalse = Object.values(
186
+ availabilitiesExistForTourType
187
+ ).every((v) => v === false);
188
+ const hasNoTourLinks =
189
+ !this.sgtUrl && !this.escortedToursLink && !this.virtualToursLink;
190
+ if (allValuesAreFalse && hasNoTourLinks) {
191
+ this.errorGettingAvailabilities = true;
192
+ this.waitingForAvailabilities = false;
193
+ sendLoggingEvent({
194
+ logTitle: "NO_AVAILABILITIES_EXIST",
195
+ logData: null,
196
+ logType: LogType.warn,
197
+ buildingSlug: this.buildingSlug,
198
+ orgSlug: this.orgSlug,
199
+ });
200
+ return;
201
+ }
202
+ this.shouldShowTourType = {
203
+ [TourType.Guided]:
204
+ this.tourTypeOptions.map((o) => o.value).includes("WITH_AGENT") &&
205
+ (availabilitiesExistForTourType[TourType.Guided] ||
206
+ !!this.escortedToursLink),
207
+ [TourType.Self]:
208
+ this.tourTypeOptions.map((o) => o.value).includes("SELF_GUIDED") &&
209
+ (availabilitiesExistForTourType[TourType.Self] || !!this.sgtUrl),
210
+ [TourType.Virtual]:
211
+ this.tourTypeOptions
212
+ .map((o) => o.value)
213
+ .includes("VIRTUAL_SHOWING") &&
214
+ (availabilitiesExistForTourType[TourType.Virtual] ||
215
+ !!this.virtualToursLink),
216
+ };
217
+
218
+ // if there is only a single option for tour type, have that be the default
219
+ const hasOneShowTourType =
220
+ Object.values(this.shouldShowTourType).reduce((acc, cur) => {
221
+ if (cur === true) {
222
+ return acc + 1;
223
+ } else {
224
+ return acc;
225
+ }
226
+ }, 0) === 1;
227
+
228
+ if (hasOneShowTourType) {
229
+ this.tourType = Object.keys(this.shouldShowTourType).find(
230
+ (key) => this.shouldShowTourType[key as TourType]
231
+ ) as TourType;
232
+ } else {
233
+ // if we have multiple tour types, then check if there is a default that the client wants selected
234
+ // HACK: FOR DEMO, FOR https://www.sofioceanhills.com/ -
235
+ // THIS SHOULD BE ADDED TO WEBCHAT CONFIGURATION
236
+ // if (
237
+ // this.buildingSlug === "1ac49f90-6150-11ed-b327-1b3f05e7b9db" &&
238
+ // this.shouldShowTourType[TourType.Self]
239
+ // ) {
240
+ // this.tourType = TourType.Self;
241
+ // }
242
+ }
243
+
244
+ if (this.tourType === null) {
245
+ this.waitingForAvailabilities = false;
246
+ return;
247
+ }
248
+ this.availabilitiesGroupedByDay = await getAvailabilitiesGroupedByDay(
249
+ tourTypeMap[this.tourType]
250
+ );
251
+ this.waitingForAvailabilities = false;
252
+ } catch (e) {
253
+ this.errorGettingAvailabilities = true;
254
+ this.waitingForAvailabilities = false;
255
+ sendLoggingEvent({
256
+ logTitle: "ERROR_LOADING_AVAILABILITIES",
257
+ logData: { error: e },
258
+ logType: LogType.error,
259
+ buildingSlug: this.buildingSlug,
260
+ orgSlug: this.orgSlug,
261
+ });
262
+ }
263
+ };
264
+
265
+ firstUpdated = async (): Promise<void> => {
266
+ await this._setAvailabilities();
267
+ };
268
+
269
+ protected willUpdate = async (
270
+ _changedProperties:
271
+ | PropertyValueMap<{ tourType: TourType }>
272
+ | Map<PropertyKey, unknown>
273
+ ): Promise<void> => {
274
+ if (_changedProperties.has("tourType") && this.tourType) {
275
+ this.availabilitiesGroupedByDay = await getAvailabilitiesGroupedByDay(
276
+ tourTypeMap[this.tourType]
277
+ );
278
+ }
279
+ };
280
+
281
+ handlePhoneKeydown = (e: Event): void => {
282
+ // these should always be true, this is just here to mollify TypeScript
283
+ if (
284
+ !(e instanceof KeyboardEvent) ||
285
+ !(e.target instanceof HTMLInputElement) ||
286
+ e.target.selectionStart === null
287
+ // !e.target.selectionStart
288
+ )
289
+ return;
290
+
291
+ const cursorPosition = e.target.selectionStart;
292
+
293
+ if (isPrintableCharacter(e) && !shortcutKeyIsPressed(e)) {
294
+ // If e.key is a character, and no modifier key is pressed, insert it at the cursor, filter out non-numbers, and auto-format
295
+ e.preventDefault();
296
+ e.stopPropagation();
297
+ const updated =
298
+ this.phoneNumber.slice(0, cursorPosition) +
299
+ e.key +
300
+ this.phoneNumber.slice(cursorPosition);
301
+ this.phoneNumber = formatToPhoneInput(updated.replace(/\D/g, ""));
302
+ this.phoneInput.value = this.phoneNumber;
303
+ } else if (e.key === "Backspace") {
304
+ /*
305
+ Handling backspace:
306
+ - A single backspace should delete the last digit before the cursor, not just a punctuation character; the user shouldn't interact directly with the punctuation
307
+ - Let the OS handle backspace combos like `Alt + Backspace`, then re-autoformat if necessary (in keyup)
308
+ - If the user wants to select and backspace a range of text, let them, then auto-format the remainder
309
+ */
310
+
311
+ // backspace combos
312
+ if (shortcutKeyIsPressed(e)) {
313
+ return;
314
+ }
315
+
316
+ // backspace selection
317
+ if (
318
+ this.phoneInput.selectionEnd &&
319
+ this.phoneInput.selectionStart &&
320
+ this.phoneInput.selectionEnd - this.phoneInput.selectionStart > 0
321
+ ) {
322
+ return;
323
+ }
324
+
325
+ // regular backspace
326
+ const originalCharacterCount = this.phoneNumber.length;
327
+ const digitsBeforeCursor = this.phoneNumber
328
+ .slice(0, cursorPosition)
329
+ .replace(/\D/g, "");
330
+ const digitsAfterCursor = this.phoneNumber
331
+ .slice(cursorPosition)
332
+ .replace(/\D/g, "");
333
+ const updatedDigits = `${digitsBeforeCursor.slice(
334
+ 0,
335
+ -1
336
+ )}${digitsAfterCursor}`;
337
+ this.phoneNumber = formatToPhoneInput(updatedDigits);
338
+ this.phoneInput.value = this.phoneNumber;
339
+ const numOfCharactersDeleted =
340
+ originalCharacterCount - this.phoneNumber.length;
341
+ const newCursorPosition = cursorPosition - numOfCharactersDeleted;
342
+ this.phoneInput.setSelectionRange(newCursorPosition, newCursorPosition);
343
+ e.preventDefault();
344
+ e.stopPropagation();
345
+ return;
346
+ } else if (
347
+ ["ArrowLeft", "ArrowRight"].includes(e.key) &&
348
+ !shortcutKeyIsPressed(e) &&
349
+ !e.shiftKey
350
+ ) {
351
+ // when navigating with arrow keys, skip punctuation
352
+ if (e.key === "ArrowLeft") {
353
+ const charactersBeforeCursor = this.phoneNumber.slice(
354
+ 0,
355
+ cursorPosition
356
+ );
357
+ const numberOfNonDigitsBeforeCursor =
358
+ charactersBeforeCursor.split(/\d+/).at(-1)?.length || 0;
359
+ const moveLeftBy = numberOfNonDigitsBeforeCursor + 1;
360
+ const newCursorPosition =
361
+ cursorPosition - moveLeftBy > -1 ? cursorPosition - moveLeftBy : 0;
362
+ this.phoneInput.setSelectionRange(newCursorPosition, newCursorPosition);
363
+ }
364
+ if (e.key === "ArrowRight") {
365
+ const charactersAfterCursor = this.phoneNumber.slice(cursorPosition);
366
+ const numberOfNonDigitsAfterCursor =
367
+ charactersAfterCursor.split(/\d+/)[0].length || 0;
368
+ const moveRightBy = numberOfNonDigitsAfterCursor + 1;
369
+ const newCursorPosition =
370
+ cursorPosition + moveRightBy < this.phoneNumber.length
371
+ ? cursorPosition + moveRightBy
372
+ : this.phoneNumber.length;
373
+ this.phoneInput.setSelectionRange(newCursorPosition, newCursorPosition);
374
+ }
375
+ e.preventDefault();
376
+ e.stopPropagation();
377
+ } else {
378
+ // Let browser/OS handle anything else. We'll handle any changes to the phone input in the `keyup` handler.
379
+ // Could be a keyboard shortcut that modifies the input (like `Cmd/Ctrl + V`, which we'll handle in `keyup`),
380
+ // or a keyboard shortcut that doesn't (like `Cmd + L` to jump to URL bar or `Cmd + R` to reload the page),
381
+ // or Tab, an arrow key, etc.
382
+ return;
383
+ }
384
+ };
385
+
386
+ handlePhoneKeyup = (e: KeyboardEvent): void => {
387
+ if (!e.key) {
388
+ return;
389
+ }
390
+ // After formatting, place the cursor where it was before, defined as "before the digit that followed it before formatting, if any, otherwise at the end".
391
+ // (We never want the cursor to be before a punctuation mark because the next digit typed will appear after the punctuation mark, not before.)
392
+ // If we don't do this, the cursor automatically goes to the end when we set `this.phoneNumber`.
393
+ // This is sometimes undesired: for example, if we've ended up here because a Mac user typed `Alt + Backspace` in the middle.
394
+
395
+ // Arrow keys are intended to change the cursor position, so don't get in their way
396
+ if (
397
+ e.key.includes("Arrow") ||
398
+ ["Meta", "Shift", "Control", "Alt"].includes(e.key)
399
+ ) {
400
+ return;
401
+ }
402
+
403
+ const cursorPosition = this.phoneInput.selectionStart;
404
+ // find the numbers it's before and count backward from end after formatting
405
+ const numbersAfterCursor = cursorPosition
406
+ ? this.phoneInput.value.slice(cursorPosition).replace(/\D/g, "")
407
+ : "";
408
+ this.phoneNumber = formatToPhoneInput(this.phoneInput.value);
409
+
410
+ // EXAMPLES: (123)| 4 numbersAfterCursor will be '4'.
411
+ let cursorNegativeIndex = 0;
412
+ let numbersLeft = numbersAfterCursor.length;
413
+ while (numbersLeft) {
414
+ if (this.phoneNumber.at(cursorNegativeIndex)?.match(/\d/)) {
415
+ numbersLeft--;
416
+ }
417
+ cursorNegativeIndex++;
418
+ }
419
+ const cursorPositiveIndex =
420
+ this.phoneInput.value.length - cursorNegativeIndex + 1;
421
+ this.phoneInput.setSelectionRange(cursorPositiveIndex, cursorPositiveIndex);
422
+ };
423
+
424
+ onChangeEmail = (e: Event): void => {
425
+ if (!e.target) {
426
+ return;
427
+ }
428
+
429
+ this.email = (e.target as HTMLInputElement).value;
430
+ };
431
+
432
+ validators = {
433
+ tourType: (): boolean => this.tourType !== null,
434
+ dateAndTime: (): boolean => !!this.selectedDate && !!this.selectedTime,
435
+ leadInfo: (): boolean => {
436
+ return (
437
+ (!!this.firstNameInput?.value || !!this.lastNameInput?.value) &&
438
+ this.emailInput?.value.includes("@") &&
439
+ // TODO: deleting phone number doesn't cause validation to fail, at least on mobile
440
+ !!this.phoneNumber &&
441
+ this.phoneNumber.length === 14 &&
442
+ isValidPhoneNumber(this.phoneNumber)
443
+ );
444
+ },
445
+ };
446
+
447
+ formIsValidForSubmission = (): boolean => {
448
+ const isValid =
449
+ this.validators.tourType() &&
450
+ this.validators.dateAndTime() &&
451
+ this.validators.leadInfo();
452
+ return isValid;
453
+ };
454
+
455
+ /** E.g., `timeStringToHoursAndMinutes("4:15pm")` -> `[16, 15]`
456
+ */
457
+ timeStringToHoursAndMinutes = (
458
+ timeString: string
459
+ ): [hours: number, minutes: number] => {
460
+ const [hoursString, minutesString] = timeString.split(/\D/g);
461
+ const hours =
462
+ parseInt(hoursString) +
463
+ (timeString.toLowerCase().includes("pm") ? 12 : 0);
464
+ const minutes = parseInt(minutesString);
465
+
466
+ return [hours, minutes];
467
+ };
468
+
469
+ submit = async (): Promise<void> => {
470
+ if (!this.selectedDate || !this.selectedTime || this.tourType === null) {
471
+ return;
472
+ }
473
+ const queryParams = new URLSearchParams(window.location.search);
474
+
475
+ let parsedLeadSource = null;
476
+ if (
477
+ this.selectedLeadSource &&
478
+ this.selectedLeadSource.value &&
479
+ this.selectedLeadSource.value.length > 0
480
+ ) {
481
+ parsedLeadSource = this.selectedLeadSource.value;
482
+ } else if (
483
+ this.leadSourceInputValue &&
484
+ this.leadSourceInputValue.length > 0
485
+ ) {
486
+ parsedLeadSource = this.leadSourceInputValue;
487
+ } else {
488
+ parsedLeadSource = this.currentLeadSource;
489
+ }
490
+
491
+ const leadSources = [
492
+ ...new Set(
493
+ parsedLeadSource
494
+ ? [parsedLeadSource, "property-website"]
495
+ : ["property-website"]
496
+ ),
497
+ ];
498
+ if (this.firstNameInput) {
499
+ this.firstNameInputValue = this.firstNameInput.value;
500
+ }
501
+ if (this.lastNameInput) {
502
+ this.lastNameInputValue = this.lastNameInput.value;
503
+ }
504
+ this.leadSourceInputValue = parsedLeadSource;
505
+ pushGtmEvent("scheduleTourSubmitted", {
506
+ email: this.email,
507
+ phone: `+1${this.phoneNumber.match(/\d/g)?.join("")}`,
508
+ firstName: this.firstNameInput?.value ?? this.firstNameInputValue,
509
+ lastName: this.lastNameInput?.value ?? this.lastNameInputValue,
510
+ tourType: tourTypeForSubmission[this.tourType],
511
+ tourTime: `${this.selectedTime.datetime}${this.selectedTime.offset}`,
512
+ originatingSource:
513
+ leadSources.find((i) => i !== "property-website") || null,
514
+ });
515
+ const data = {
516
+ referrer: document.referrer,
517
+ email_address: this.email,
518
+ phone_number: `+1${this.phoneNumber.match(/\d/g)?.join("")}`, // e.g. +12125555555
519
+ building_id: this.buildingId,
520
+ first_name: this.firstNameInput?.value ?? this.firstNameInputValue,
521
+ last_name: this.lastNameInput?.value ?? this.lastNameInputValue,
522
+ tour_type: tourTypeForSubmission[this.tourType],
523
+ tour_time: `${this.selectedTime.datetime}${this.selectedTime.offset}`, // e.g., "2022-06-27T09:00:00-07:00"
524
+ cancel_existing_tours: this.promptForReschedule,
525
+ lead_sources: [
526
+ ...new Set(
527
+ parsedLeadSource
528
+ ? [parsedLeadSource, getDefaultLeadSourceAttribution(this.orgSlug)]
529
+ : [getDefaultLeadSourceAttribution(this.orgSlug)]
530
+ ),
531
+ ],
532
+ query_params: Object.fromEntries(queryParams.entries()),
533
+ conversation_tracking_id: this.leadSourceClient?.chatId,
534
+ };
535
+
536
+ const url = `https://app.meetelise.com/platformApi/state/create/v2/scheduleMe`;
537
+ this.isSubmitting = true;
538
+ try {
539
+ await axios.post(url, data, {
540
+ headers: {
541
+ ["building-slug"]: this.buildingSlug,
542
+ ["X-SecurityKey"]: "JRL8jV4VcSCwOSir5gWkpgNLfKghmhBG",
543
+ ["org-slug"]: this.orgSlug,
544
+ },
545
+ });
546
+
547
+ this.leadSourceClient?.checkAndHandleForLogLeadSource({
548
+ webchatAction: "tour-scheduler",
549
+ stateId: null,
550
+ });
551
+ this.isSubmitting = false;
552
+ this.tourIsBooked = true;
553
+ } catch (e: unknown) {
554
+ const typedError = e as AxiosError<{ detail: string }>;
555
+ const message =
556
+ typedError.response?.data?.["detail"] || "Failed to book tour";
557
+ if (
558
+ typedError.response &&
559
+ typedError.response.headers &&
560
+ typedError.response.headers[
561
+ "funnel-prospect-conflicting-appointment-error"
562
+ ]
563
+ ) {
564
+ if (this.selectedDate && this.selectedTime) {
565
+ const dateAvailabilities =
566
+ this.availabilitiesGroupedByDay[
567
+ format(this.selectedDate, "yyyy-MM-dd")
568
+ ];
569
+ if (dateAvailabilities) {
570
+ this.availabilitiesGroupedByDay[
571
+ format(this.selectedDate, "yyyy-MM-dd")
572
+ ] = dateAvailabilities.filter(
573
+ (availability) => availability !== this.selectedTime
574
+ );
575
+ }
576
+ }
577
+ } else if (
578
+ typedError.response?.status === 409 &&
579
+ !this.promptForReschedule &&
580
+ message === "User already has a tour scheduled for this building"
581
+ ) {
582
+ this.promptForReschedule = true;
583
+ this.isSubmitting = false;
584
+ this.tourIsBooked = false;
585
+ return;
586
+ } else if (
587
+ typedError.response?.status === 409 &&
588
+ !this.promptForReschedule
589
+ ) {
590
+ this.isSubmitting = false;
591
+ this.tourIsBooked = false;
592
+ resetAvailabilitiesCache();
593
+ this.availabilitiesGroupedByDay = await getAvailabilitiesGroupedByDay(
594
+ tourTypeMap[this.tourType],
595
+ this.buildingId
596
+ );
597
+ alert(
598
+ "This timeslot is no longer available. Please select a different time."
599
+ );
600
+ return;
601
+ }
602
+ alert(message);
603
+ this.isSubmitting = false;
604
+ this.tourIsBooked = false;
605
+ }
606
+ };
607
+
608
+ static styles = [tourSchedulerStyles, InputStyles];
609
+
610
+ tourTypeMenu(): TemplateResult {
611
+ return html`<h2 class="journey-header">Tour Type</h2>
612
+ <div id="tour-type-menu">
613
+ ${this.shouldShowTourType[TourType.Self]
614
+ ? html` <tour-type-option
615
+ heading="Take a tour"
616
+ subtitle="on your own"
617
+ .primaryColor=${this.primaryColor}
618
+ .backgroundColor=${this.backgroundColor}
619
+ @click="${async () => {
620
+ this.tourType = TourType.Self;
621
+ if (
622
+ this.sgtUrl &&
623
+ this.selfGuidedToursTypeOffered !==
624
+ "SCHEDULED_BY_ME_MANAGED_BY_ME"
625
+ ) {
626
+ window.open(this.sgtUrl, "_blank");
627
+ }
628
+ this.availabilitiesGroupedByDay =
629
+ await getAvailabilitiesGroupedByDay(
630
+ tourTypeMap[TourType.Self]
631
+ );
632
+ }}"
633
+ @keydown="${(e: KeyboardEvent) => {
634
+ if ([" ", "Enter"].includes(e.key)) {
635
+ e.preventDefault();
636
+ this.tourType = TourType.Self;
637
+ }
638
+ }}"
639
+ ?selected="${this.tourType === TourType.Self}"
640
+ >
641
+ ${TourSelfGuidedIcon(
642
+ this.tourType === TourType.Self
643
+ ? this.foregroundColorOnPrimaryBackgroundColor
644
+ : this.foregroundColorOnSecondaryBackgroundColor
645
+ )}
646
+ </tour-type-option>`
647
+ : ""}
648
+ ${this.shouldShowTourType[TourType.Guided]
649
+ ? html` <tour-type-option
650
+ heading="Guided tour"
651
+ subtitle="with an agent"
652
+ .primaryColor=${this.primaryColor}
653
+ .backgroundColor=${this.backgroundColor}
654
+ .foregroundColorOnPrimaryBackgroundColor=${this
655
+ .foregroundColorOnPrimaryBackgroundColor}
656
+ .foregroundColorOnSecondaryBackgroundColor=${this
657
+ .foregroundColorOnSecondaryBackgroundColor}
658
+ @click="${async () => {
659
+ this.tourType = TourType.Guided;
660
+
661
+ if (this.escortedToursLink) {
662
+ window.open(this.escortedToursLink, "_blank");
663
+ }
664
+ this.availabilitiesGroupedByDay =
665
+ await getAvailabilitiesGroupedByDay(
666
+ tourTypeMap[TourType.Guided]
667
+ );
668
+ }}"
669
+ @keydown="${(e: KeyboardEvent) => {
670
+ if ([" ", "Enter"].includes(e.key)) {
671
+ e.preventDefault();
672
+ this.tourType = TourType.Guided;
673
+ }
674
+ }}"
675
+ ?selected="${this.tourType === TourType.Guided}"
676
+ >
677
+ ${TourWithAgentIcon(
678
+ this.tourType === TourType.Guided
679
+ ? this.foregroundColorOnPrimaryBackgroundColor
680
+ : this.foregroundColorOnSecondaryBackgroundColor
681
+ )}
682
+ </tour-type-option>`
683
+ : ""}
684
+ ${this.shouldShowTourType[TourType.Virtual]
685
+ ? html` <tour-type-option
686
+ heading="Virtual tour"
687
+ subtitle="over video"
688
+ .primaryColor=${this.primaryColor}
689
+ .backgroundColor=${this.backgroundColor}
690
+ @click="${async () => {
691
+ this.tourType = TourType.Virtual;
692
+ if (this.virtualToursLink) {
693
+ window.open(this.virtualToursLink, "_blank");
694
+ }
695
+ this.availabilitiesGroupedByDay =
696
+ await getAvailabilitiesGroupedByDay(
697
+ tourTypeMap[TourType.Virtual]
698
+ );
699
+ }}"
700
+ @keydown="${(e: KeyboardEvent) => {
701
+ if ([" ", "Enter"].includes(e.key)) {
702
+ e.preventDefault();
703
+ this.tourType = TourType.Virtual;
704
+ }
705
+ }}"
706
+ ?selected="${this.tourType === TourType.Virtual}"
707
+ >
708
+ ${TourVirtuallyIcon(
709
+ this.tourType === TourType.Virtual
710
+ ? this.foregroundColorOnPrimaryBackgroundColor
711
+ : this.foregroundColorOnSecondaryBackgroundColor
712
+ )}
713
+ </tour-type-option>`
714
+ : ""}
715
+ </div>`;
716
+ }
717
+
718
+ dateAndTimeMenu(): TemplateResult {
719
+ if (
720
+ this.tourType === TourType.Self &&
721
+ this.sgtUrl &&
722
+ this.selfGuidedToursTypeOffered !== "SCHEDULED_BY_ME_MANAGED_BY_ME"
723
+ ) {
724
+ return html`<h2 class="journey-header">Date and Time</h2>
725
+ <div id="dateAndTimeMenu">
726
+ <button
727
+ id="self-guided-redirect-bttn"
728
+ @click=${() => window.open(this.sgtUrl, "_blank")}
729
+ >
730
+ View Self Guided Tour Times
731
+ </button>
732
+ </div>`;
733
+ }
734
+ return html`<h2 class="journey-header">Date and Time</h2>
735
+ <div id="dateAndTimeMenu">
736
+ <div id="datePicker">
737
+ <date-picker
738
+ .availabilities=${mapValues(
739
+ this.availabilitiesGroupedByDay,
740
+ (dates) => dates.map((date) => new Date(date.offset))
741
+ )}
742
+ @change=${(e: Event) => {
743
+ // if the user clicked a tour type that is suppose to redirect, we redirect that use when they select a date
744
+ // This can happen if the user clicks, is redirect, and then comes back to the webchat
745
+ if (
746
+ this.tourType === TourType.Self &&
747
+ this.sgtUrl &&
748
+ this.selfGuidedToursTypeOffered !==
749
+ "SCHEDULED_BY_ME_MANAGED_BY_ME"
750
+ ) {
751
+ window.open(this.sgtUrl, "_blank");
752
+ return;
753
+ }
754
+ if (this.tourType === TourType.Guided && this.escortedToursLink) {
755
+ window.open(this.escortedToursLink, "_blank");
756
+ return;
757
+ }
758
+ if (this.tourType === TourType.Virtual && this.virtualToursLink) {
759
+ window.open(this.virtualToursLink, "_blank");
760
+ return;
761
+ }
762
+
763
+ if (e.target instanceof DatePicker) {
764
+ this.selectedDate = e.target.selectedDate;
765
+ }
766
+ }}
767
+ ></date-picker>
768
+ <div>
769
+ <time-picker
770
+ ?selecteddateexists=${!!this.selectedDate}
771
+ .options=${this.selectedDate
772
+ ? this.availabilitiesGroupedByDay[
773
+ format(this.selectedDate, "y-MM-dd")
774
+ ]
775
+ ?.sort((a, b) =>
776
+ compareAsc(parseISO(a.datetime), parseISO(b.datetime))
777
+ )
778
+ .map((date) => {
779
+ return {
780
+ dateWithTimeZoneOffset: date,
781
+ displayTime: format(
782
+ parseISO(`${date.datetime}${date.offset}`),
783
+ "h:mmaaa"
784
+ ),
785
+ };
786
+ })
787
+ : []}
788
+ @change=${(e: Event) => {
789
+ if (e.target instanceof TimePicker) {
790
+ if (!this.selectedDate) return;
791
+ const daysAvailabilities =
792
+ this.availabilitiesGroupedByDay[
793
+ format(new Date(this.selectedDate), "y-MM-dd")
794
+ ];
795
+ const index = e.target.selectedTime
796
+ ? daysAvailabilities.indexOf(
797
+ e.target.selectedTime.dateWithTimeZoneOffset
798
+ )
799
+ : null;
800
+ this.selectedTime =
801
+ index !== null ? daysAvailabilities[index] : null;
802
+ }
803
+ }}
804
+ ></time-picker>
805
+ </div>
806
+ </div>
807
+ </div>`;
808
+ }
809
+
810
+ closeButton(): TemplateResult {
811
+ return html` <button
812
+ id="close-button"
813
+ @click=${this.onCloseClicked}
814
+ aria-label="Close"
815
+ aria-describedby="close-button"
816
+ >
817
+ <svg
818
+ width="19"
819
+ height="19"
820
+ viewBox="0 0 19 19"
821
+ fill="none"
822
+ xmlns="http://www.w3.org/2000/svg"
823
+ >
824
+ <line
825
+ x1="0.986863"
826
+ y1="18.2746"
827
+ x2="18.2929"
828
+ y2="0.968593"
829
+ stroke="#202020"
830
+ stroke-width="2"
831
+ />
832
+ <path
833
+ d="M1.01394 0.999997L18.0103 18.0243"
834
+ stroke="#202020"
835
+ stroke-width="2"
836
+ />
837
+ </svg>
838
+ </button>`;
839
+ }
840
+
841
+ mobilePages = [
842
+ {
843
+ validate: this.validators.tourType,
844
+ nextButtonText: "Next",
845
+ nextButtonAction: (): void => {
846
+ this.mobilePageIndex++;
847
+ },
848
+ render: (): TemplateResult => {
849
+ return html`${this.tourTypeMenu()}`;
850
+ },
851
+ },
852
+ {
853
+ validate: this.validators.dateAndTime,
854
+ nextButtonText: "Next",
855
+ nextButtonAction: (): void => {
856
+ this.mobilePageIndex++;
857
+ },
858
+ render: (): TemplateResult => {
859
+ return html`${this.dateAndTimeMenu()}`;
860
+ },
861
+ },
862
+ {
863
+ validate: (): boolean => this.validators.leadInfo(),
864
+ // last page gets <action-confirm-button> instead of the regular button
865
+ nextButtonText: null,
866
+ renderNextButton: (): TemplateResult => html` <action-confirm-button
867
+ id="schedule-bttn"
868
+ .onClick=${this.submit}
869
+ .isLoading=${this.isSubmitting}
870
+ height="50px"
871
+ width="145px"
872
+ text="Schedule tour"
873
+ ?disabled=${!this.formIsValidForSubmission()}
874
+ ></action-confirm-button>`,
875
+ nextButtonAction: null,
876
+ render: (): TemplateResult => {
877
+ return html`${this.userInfoAndLayoutMenu()}`;
878
+ },
879
+ },
880
+ ];
881
+
882
+ userInfoAndLayoutMenu(): TemplateResult {
883
+ return html`<h2 class="journey-header">Your Information</h2>
884
+ <div id="yourInformationMenu">
885
+ <div id="namesWrapper">
886
+ <div class="nameContainer" id="firstName">
887
+ <input
888
+ class=${classMap({
889
+ ["webchat-input"]: true,
890
+ ["nameInput"]: true,
891
+ ["webchat-font__desktop"]: !isMobile(),
892
+ ["webchat-font__mobile"]: isMobile(),
893
+ })}
894
+ type="text"
895
+ placeholder="First name"
896
+ name="firstName"
897
+ autocomplete="given-name"
898
+ @input=${() => this.requestUpdate()}
899
+ />
900
+ </div>
901
+ <div class="nameContainer" id="lastName">
902
+ <input
903
+ class=${classMap({
904
+ ["webchat-input"]: true,
905
+ ["nameInput"]: true,
906
+ ["webchat-font__desktop"]: !isMobile(),
907
+ ["webchat-font__mobile"]: isMobile(),
908
+ })}
909
+ type="text"
910
+ placeholder="Last name"
911
+ name="lastName"
912
+ autocomplete="family-name"
913
+ @input=${() => this.requestUpdate()}
914
+ />
915
+ </div>
916
+ </div>
917
+
918
+ <div class="inputContainer" id="email">
919
+ <input
920
+ class=${classMap({
921
+ ["webchat-input"]: true,
922
+ ["webchat-font__desktop"]: !isMobile(),
923
+ ["webchat-font__mobile"]: isMobile(),
924
+ })}
925
+ type="email"
926
+ inputmode="email"
927
+ placeholder="Email"
928
+ name="email"
929
+ autocomplete="email"
930
+ .value=${this.email}
931
+ @input=${this.onChangeEmail}
932
+ />
933
+ </div>
934
+ <div class="inputContainer" id="phone">
935
+ <input
936
+ class=${classMap({
937
+ ["webchat-input"]: true,
938
+ ["webchat-font__desktop"]: !isMobile(),
939
+ ["webchat-font__mobile"]: isMobile(),
940
+ ["webchat-input__error"]:
941
+ this.phoneNumber.length === 14 &&
942
+ !isValidPhoneNumber(this.phoneNumber),
943
+ })}
944
+ type="tel"
945
+ inputmode="tel"
946
+ placeholder="Phone"
947
+ name="phone"
948
+ autocomplete="tel-national"
949
+ maxlength="14"
950
+ .value=${this.phoneNumber}
951
+ @keydown=${this.handlePhoneKeydown}
952
+ @keyup=${this.handlePhoneKeyup}
953
+ @input=${(e: Event) => {
954
+ if (!e.target) {
955
+ return;
956
+ }
957
+ this.phoneNumber = formatToPhoneInput(
958
+ (e.target as HTMLInputElement).value
959
+ );
960
+ }}
961
+ />
962
+ ${this.phoneNumber.length === 14 &&
963
+ !isValidPhoneNumber(this.phoneNumber)
964
+ ? html`<p class="error-message">Invalid phone number</p>`
965
+ : ""}
966
+ </div>
967
+ ${this.leadSources.length > 0 &&
968
+ (this.featureFlagShowDropdown === FeatureFlagsShowDropdown.always ||
969
+ (this.featureFlagShowDropdown ===
970
+ FeatureFlagsShowDropdown.onAttributionFailure &&
971
+ this.currentLeadSource.length === 0))
972
+ ? html` <me-select
973
+ id="leadSource"
974
+ value="${this.currentLeadSource}"
975
+ placeholder="How did you hear about us?"
976
+ .options="${this.leadSources.map((i) => ({
977
+ label: i,
978
+ value: i,
979
+ }))}"
980
+ @change=${() => {
981
+ this.requestUpdate();
982
+ }}
983
+ >
984
+ </me-select>`
985
+ : ""}
986
+ </div> `;
987
+ }
988
+
989
+ confirmationMessage(): TemplateResult {
990
+ if (!this.selectedDate || !this.selectedTime || this.tourType === null)
991
+ return html``;
992
+ // format example: "November 9th, 2022 at 11:00pm"
993
+ const readableDateAndTime = format(
994
+ parseISO(`${this.selectedTime.datetime}${this.selectedTime.offset}`),
995
+ "h:mmaaa"
996
+ );
997
+
998
+ if (this.canceledReschedule) {
999
+ return html`
1000
+ <div id="confirmationMessage">
1001
+ <svg
1002
+ width="20"
1003
+ height="20"
1004
+ viewBox="0 0 20 20"
1005
+ fill="none"
1006
+ xmlns="http://www.w3.org/2000/svg"
1007
+ >
1008
+ <path
1009
+ d="M7 0V2H13V0H15V2H19C19.2652 2 19.5196 2.10536 19.7071 2.29289C19.8946 2.48043 20 2.73478 20 3V19C20 19.2652 19.8946 19.5196 19.7071 19.7071C19.5196 19.8946 19.2652 20 19 20H1C0.734784 20 0.48043 19.8946 0.292893 19.7071C0.105357 19.5196 0 19.2652 0 19V3C0 2.73478 0.105357 2.48043 0.292893 2.29289C0.48043 2.10536 0.734784 2 1 2H5V0H7ZM18 9H2V18H18V9ZM13.036 10.136L14.45 11.55L9.5 16.5L5.964 12.964L7.38 11.55L9.501 13.672L13.037 10.136H13.036ZM5 4H2V7H18V4H15V5H13V4H7V5H5V4Z"
1010
+ fill="#202020"
1011
+ />
1012
+ </svg>
1013
+ <p>Thank you!</p>
1014
+ <p>We'll see you at your originally schedule tour time.</p>
1015
+ </div>
1016
+ `;
1017
+ }
1018
+
1019
+ return html`
1020
+ <div id="confirmationMessage">
1021
+ <svg
1022
+ width="20"
1023
+ height="20"
1024
+ viewBox="0 0 20 20"
1025
+ fill="none"
1026
+ xmlns="http://www.w3.org/2000/svg"
1027
+ >
1028
+ <path
1029
+ d="M7 0V2H13V0H15V2H19C19.2652 2 19.5196 2.10536 19.7071 2.29289C19.8946 2.48043 20 2.73478 20 3V19C20 19.2652 19.8946 19.5196 19.7071 19.7071C19.5196 19.8946 19.2652 20 19 20H1C0.734784 20 0.48043 19.8946 0.292893 19.7071C0.105357 19.5196 0 19.2652 0 19V3C0 2.73478 0.105357 2.48043 0.292893 2.29289C0.48043 2.10536 0.734784 2 1 2H5V0H7ZM18 9H2V18H18V9ZM13.036 10.136L14.45 11.55L9.5 16.5L5.964 12.964L7.38 11.55L9.501 13.672L13.037 10.136H13.036ZM5 4H2V7H18V4H15V5H13V4H7V5H5V4Z"
1030
+ fill="#202020"
1031
+ />
1032
+ </svg>
1033
+ <p>Thank you!</p>
1034
+ <p>
1035
+ Your
1036
+ ${{
1037
+ [TourType.Guided]: "guided",
1038
+ [TourType.Self]: "self-guided",
1039
+ [TourType.Virtual]: "virtual",
1040
+ }[this.tourType]}
1041
+ tour is
1042
+ ${this.promptForReschedule ? html`rescheduled` : html`scheduled`} for
1043
+ ${readableDateAndTime}.
1044
+ </p>
1045
+ <p>
1046
+ Look for an email confirmation along with instructions and directions.
1047
+ </p>
1048
+ <p>You can make changes at any time, just reply to the email.</p>
1049
+ </div>
1050
+ `;
1051
+ }
1052
+
1053
+ loadingIcon(size = 21): TemplateResult {
1054
+ return html` <svg
1055
+ id="loadingIcon"
1056
+ width=${size}
1057
+ height=${size}
1058
+ viewBox="0 0 21 21"
1059
+ fill="none"
1060
+ xmlns="http://www.w3.org/2000/svg"
1061
+ >
1062
+ <path
1063
+ d="M17.835 13.1045C18.4839 11.5628 18.6332 9.85647 18.2621 8.22548C17.8909 6.5945 17.018 5.12084 15.7659 4.0117C14.5139 2.90256 12.9457 2.21372 11.2818 2.04201C9.618 1.87031 7.94218 2.22438 6.49 3.05445L5.498 1.31745C7.01563 0.450066 8.73419 -0.00418222 10.4822 2.90165e-05C12.2302 0.00424025 13.9466 0.466764 15.46 1.34145C19.95 3.93345 21.67 9.48345 19.577 14.1115L20.919 14.8855L16.754 17.0995L16.589 12.3855L17.835 13.1045ZM3.085 6.89845C2.43614 8.44015 2.28678 10.1464 2.65792 11.7774C3.02905 13.4084 3.90201 14.8821 5.15407 15.9912C6.40613 17.1003 7.97432 17.7892 9.63816 17.9609C11.302 18.1326 12.9778 17.7785 14.43 16.9485L15.422 18.6855C13.9044 19.5528 12.1858 20.0071 10.4378 20.0029C8.68979 19.9987 6.97344 19.5361 5.46 18.6615C0.97 16.0695 -0.75 10.5195 1.343 5.89145L0 5.11845L4.165 2.90445L4.33 7.61845L3.084 6.89945L3.085 6.89845Z"
1064
+ fill="#1E1E1E"
1065
+ />
1066
+ </svg>`;
1067
+ }
1068
+
1069
+ render(): TemplateResult {
1070
+ const isLoading =
1071
+ this.waitingForAvailabilities || this.shouldAllowScheduleLoading;
1072
+ if (!this.shouldAllowScheduling && !isLoading) {
1073
+ return html` <div
1074
+ id="tour-scheduler-inner-form"
1075
+ class="${classnames({
1076
+ "tour-scheduler-full": !this.compactDesign && !isMobile(),
1077
+ "tour-scheduler-compact": this.compactDesign && !isMobile(),
1078
+ "tour-scheduler-mobile": isMobile(),
1079
+ })}"
1080
+ >
1081
+ <div id="top-header">
1082
+ <div></div>
1083
+ ${this.closeButton()}
1084
+ </div>
1085
+ <div class="center-tour-not-avail">
1086
+ <h1>Sorry, there are currently no tour availabilities</h1>
1087
+ <p>Please check back again later</p>
1088
+ </div>
1089
+ </div>`;
1090
+ }
1091
+ if (isLoading) {
1092
+ return html` <div
1093
+ id="tour-scheduler-inner-form"
1094
+ class="${classnames({
1095
+ "tour-scheduler-full": !this.compactDesign && !isMobile(),
1096
+ "tour-scheduler-compact": this.compactDesign && !isMobile(),
1097
+ "tour-scheduler-mobile": isMobile(),
1098
+ })}"
1099
+ >
1100
+ <div class="center-tour-not-avail">
1101
+ <h1>Searching for availabilities...</h1>
1102
+ <div class="loading-entire-tour-icon">${this.loadingIcon(48)}</div>
1103
+ </div>
1104
+ </div>`;
1105
+ }
1106
+ if (this.errorGettingAvailabilities) {
1107
+ return html` <div
1108
+ class="${classnames({
1109
+ "tour-scheduler-full": !this.compactDesign && !isMobile(),
1110
+ "tour-scheduler-compact": this.compactDesign && !isMobile(),
1111
+ "tour-scheduler-mobile": isMobile(),
1112
+ })}"
1113
+ >
1114
+ <div id="top-header">
1115
+ <div></div>
1116
+ ${this.closeButton()}
1117
+ </div>
1118
+ <div class="center-tour-not-avail">
1119
+ <div>
1120
+ <p>Sorry, there are currently no tour availabilities!</p>
1121
+ <p>
1122
+ We apologize, but there are currently no tours available. This
1123
+ could be due to all slots being filled, off-season periods, or
1124
+ maintenance.
1125
+ </p>
1126
+ <p>
1127
+ We understand that this may be disappointing and we apologize for
1128
+ any inconvenience. We recommend checking back soon as our schedule
1129
+ frequently updates.
1130
+ </p>
1131
+ <p>
1132
+ In the meantime, feel free to explore our website for other
1133
+ information and attractions. Thank you for your understanding and
1134
+ patience!
1135
+ </p>
1136
+ </div>
1137
+ </div>
1138
+ </div>`;
1139
+ }
1140
+ if (!isMobile()) {
1141
+ return html`
1142
+ <div
1143
+ class="${classnames({
1144
+ "tour-scheduler-full": !this.compactDesign,
1145
+ "tour-scheduler-compact": this.compactDesign,
1146
+ loading: isLoading,
1147
+ })}"
1148
+ @leadsource="${(e: CustomEvent) =>
1149
+ (this.selectedLeadSource = e.detail.selectedLeadSource)}"
1150
+ >
1151
+ <div id="top-header">
1152
+ <h1 id="tour-header-title">
1153
+ ${isLoading
1154
+ ? html`${this.loadingIcon()} Searching availabilities...`
1155
+ : "Schedule a Tour"}
1156
+ </h1>
1157
+ ${this.closeButton()}
1158
+ </div>
1159
+
1160
+ ${this.tourIsBooked || this.canceledReschedule
1161
+ ? html`
1162
+ <div class="tour-scheduler">${this.confirmationMessage()}</div>
1163
+ `
1164
+ : this.promptForReschedule
1165
+ ? html`
1166
+ <div id="scheduler-container">
1167
+ <div>
1168
+ <h2 class="journey-header">Reschedule Tour</h2>
1169
+ <p class="explanation">
1170
+ You already have a tour scheduled. Would you like to
1171
+ reschedule?
1172
+ </p>
1173
+ </div>
1174
+ </div>
1175
+ <div id="tour-scheduler-footer">
1176
+ <p class="explanation">
1177
+ We'll send a confirmation and any follow-ups to your email
1178
+ address, please reply to the email to make any appointment
1179
+ changes.
1180
+ ${disclaimer({
1181
+ buildingName: this.buildingName,
1182
+ phoneNumberInput: this.phoneInput?.value,
1183
+ emailInput: this.emailInput?.value,
1184
+ orgLegalName: this.orgLegalName,
1185
+ })}
1186
+ </p>
1187
+
1188
+ <div class="reschedule-buttons-wrapper">
1189
+ <action-confirm-button
1190
+ id="cancel-reschedule-bttn"
1191
+ .onClick=${() => {
1192
+ this.canceledReschedule = true;
1193
+ }}
1194
+ .isLoading=${this.isSubmitting}
1195
+ height="50px"
1196
+ width="145px"
1197
+ text="Cancel"
1198
+ ?disabled=${!this.formIsValidForSubmission()}
1199
+ ></action-confirm-button>
1200
+ <action-confirm-button
1201
+ id="reschedule-button"
1202
+ .onClick=${this.submit}
1203
+ .isLoading=${this.isSubmitting}
1204
+ height="50px"
1205
+ width="145px"
1206
+ text="Reschedule"
1207
+ ?disabled=${!this.formIsValidForSubmission()}
1208
+ ></action-confirm-button>
1209
+ </div>
1210
+ </div>
1211
+ `
1212
+ : html`
1213
+ <div id="scheduler-container">
1214
+ <div id="book-tour-journey-items">
1215
+ <div id="tour-type-menu-outer-container">
1216
+ ${this.tourTypeMenu()}
1217
+ </div>
1218
+ <div id="date-and-time-menu-outer-container">
1219
+ ${this.dateAndTimeMenu()}
1220
+ </div>
1221
+
1222
+ <div id="user-info-and-layout-menu-outer-container">
1223
+ ${this.userInfoAndLayoutMenu()}
1224
+ </div>
1225
+ </div>
1226
+ </div>
1227
+ <div id="tour-scheduler-footer">
1228
+ <p class="explanation">
1229
+ We'll send a confirmation and any follow-ups to your email
1230
+ address, please reply to the email to make any appointment
1231
+ changes.
1232
+ ${disclaimer({
1233
+ buildingName: this.buildingName,
1234
+ phoneNumberInput: this.phoneInput?.value,
1235
+ emailInput: this.emailInput?.value,
1236
+ orgLegalName: this.orgLegalName,
1237
+ })}
1238
+ </p>
1239
+
1240
+ <action-confirm-button
1241
+ id="schedule-bttn"
1242
+ .onClick=${this.submit}
1243
+ .isLoading=${this.isSubmitting}
1244
+ height="50px"
1245
+ width="145px"
1246
+ text="Schedule tour"
1247
+ ?disabled=${!this.formIsValidForSubmission()}
1248
+ ></action-confirm-button>
1249
+ </div>
1250
+ `}
1251
+ </div>
1252
+ `;
1253
+ } else {
1254
+ const currentPage = this.mobilePages[this.mobilePageIndex];
1255
+ return html`
1256
+ <div
1257
+ class="${classnames("tour-scheduler-mobile", {
1258
+ loading: isLoading,
1259
+ })}"
1260
+ >
1261
+ <div id="top-header">
1262
+ <h1 id="scheduleATour">
1263
+ ${isLoading
1264
+ ? html`${this.loadingIcon()} Searching availabilities...`
1265
+ : "Schedule a tour"}
1266
+ </h1>
1267
+ ${this.closeButton()}
1268
+ </div>
1269
+ <div id="mobile-body-container">
1270
+ ${this.canceledReschedule
1271
+ ? html`
1272
+ <div id="confirmationMessage">
1273
+ <svg
1274
+ width="20"
1275
+ height="20"
1276
+ viewBox="0 0 20 20"
1277
+ fill="none"
1278
+ xmlns="http://www.w3.org/2000/svg"
1279
+ >
1280
+ <path
1281
+ d="M7 0V2H13V0H15V2H19C19.2652 2 19.5196 2.10536 19.7071 2.29289C19.8946 2.48043 20 2.73478 20 3V19C20 19.2652 19.8946 19.5196 19.7071 19.7071C19.5196 19.8946 19.2652 20 19 20H1C0.734784 20 0.48043 19.8946 0.292893 19.7071C0.105357 19.5196 0 19.2652 0 19V3C0 2.73478 0.105357 2.48043 0.292893 2.29289C0.48043 2.10536 0.734784 2 1 2H5V0H7ZM18 9H2V18H18V9ZM13.036 10.136L14.45 11.55L9.5 16.5L5.964 12.964L7.38 11.55L9.501 13.672L13.037 10.136H13.036ZM5 4H2V7H18V4H15V5H13V4H7V5H5V4Z"
1282
+ fill="#202020"
1283
+ />
1284
+ </svg>
1285
+ <p>Thank you!</p>
1286
+ <p>We'll see you at your originally schedule tour time.</p>
1287
+ </div>
1288
+ `
1289
+ : this.tourIsBooked
1290
+ ? this.confirmationMessage()
1291
+ : this.promptForReschedule
1292
+ ? html` <div id="scheduler-container">
1293
+ <div>
1294
+ <h2 class="journey-header">Reschedule Tour</h2>
1295
+ <p class="explanation">
1296
+ You already have a tour scheduled. Would you like to
1297
+ reschedule?
1298
+ </p>
1299
+ </div>
1300
+ </div>
1301
+ <div id="tour-scheduler-footer">
1302
+ <div class="reschedule-buttons-wrapper">
1303
+ <action-confirm-button
1304
+ id="cancel-reschedule-bttn"
1305
+ .onClick=${() => {
1306
+ this.canceledReschedule = true;
1307
+ }}
1308
+ .isLoading=${this.isSubmitting}
1309
+ height="50px"
1310
+ width="145px"
1311
+ text="Cancel"
1312
+ ?disabled=${!this.formIsValidForSubmission()}
1313
+ ></action-confirm-button>
1314
+ <action-confirm-button
1315
+ id="reschedule-button"
1316
+ .onClick=${this.submit}
1317
+ .isLoading=${this.isSubmitting}
1318
+ height="50px"
1319
+ width="145px"
1320
+ text="Reschedule"
1321
+ ?disabled=${!this.formIsValidForSubmission()}
1322
+ ></action-confirm-button>
1323
+ </div>
1324
+ </div>`
1325
+ : html` ${currentPage.render()}
1326
+ ${!currentPage.renderNextButton
1327
+ ? html` <button
1328
+ id="mobile-next-bttn"
1329
+ @click=${currentPage.nextButtonAction}
1330
+ ?disabled=${(() => {
1331
+ return !currentPage.validate() || isLoading;
1332
+ })()}
1333
+ >
1334
+ ${currentPage.nextButtonText}
1335
+ </button>`
1336
+ : currentPage.renderNextButton()}`}
1337
+ ${this.mobilePageIndex + 1 === this.mobilePages.length
1338
+ ? html`
1339
+ ${disclaimer({
1340
+ buildingName: this.buildingName,
1341
+ phoneNumberInput: this.phoneInput?.value,
1342
+ emailInput: this.emailInput?.value,
1343
+ orgLegalName: this.orgLegalName,
1344
+ })}
1345
+ `
1346
+ : html``}
1347
+ </div>
1348
+ </div>
1349
+ `;
1350
+ }
1351
+ }
1352
+ }
1353
+
1354
+ export enum TourType {
1355
+ Guided = "guided",
1356
+ Self = "self",
1357
+ Virtual = "virtual",
1358
+ }
1359
+
1360
+ const tourTypeMap = {
1361
+ [TourType.Guided]:
1362
+ TourAvailabilityResponseRankOrderedSupportedTourTypesEnum.WithAgent,
1363
+ [TourType.Self]:
1364
+ TourAvailabilityResponseRankOrderedSupportedTourTypesEnum.SelfGuided,
1365
+ [TourType.Virtual]:
1366
+ TourAvailabilityResponseRankOrderedSupportedTourTypesEnum.VirtualShowing,
1367
+ };
1368
+
1369
+ const tourTypeForSubmission = {
1370
+ [TourType.Guided]: "escorted-tour",
1371
+ [TourType.Self]: "self-guided-tour",
1372
+ [TourType.Virtual]: "live-virtual-tour",
1373
+ };