@meetelise/chat 1.51.0 → 1.51.2
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.
- package/dist/src/WebComponent/Scheduler/tour-scheduler.d.ts +7 -3
- package/dist/src/WebComponent/actions/email-us-window.d.ts +5 -4
- package/dist/src/WebComponent/actions/formatPhoneNumber.d.ts +22 -3
- package/dist/src/WebComponent/actions/international-phone-input.d.ts +30 -0
- package/package.json +2 -1
- package/public/dist/index.js +695 -575
- package/src/WebComponent/LeadSourceClient.ts +2 -19
- package/src/WebComponent/Scheduler/tour-scheduler.ts +44 -194
- package/src/WebComponent/actions/call-us-window.ts +2 -2
- package/src/WebComponent/actions/email-us-window.ts +27 -54
- package/src/WebComponent/actions/formatPhoneNumber.ts +146 -12
- package/src/WebComponent/actions/international-phone-input.ts +308 -0
- package/src/formatPhoneNumber.test.ts +91 -0
- package/src/internationalPhoneInput.test.ts +253 -0
- package/src/internationalPhoneSubmission.test.ts +145 -0
|
@@ -32,8 +32,6 @@ class LeadSourceClient {
|
|
|
32
32
|
|
|
33
33
|
private apiHost = "https://app.meetelise.com";
|
|
34
34
|
public leadSource: string | null = null;
|
|
35
|
-
// UTM params associated with the resolved lead source (e.g. switch_code=<code>
|
|
36
|
-
// for AMLI). Forwarded onto external tour-vendor links (e.g. Tour24).
|
|
37
35
|
public leadSourceParams: { [key: string]: string } | null = null;
|
|
38
36
|
private localStorageTTLDays = 30;
|
|
39
37
|
|
|
@@ -152,26 +150,11 @@ class LeadSourceClient {
|
|
|
152
150
|
campaign_sources: campaignSources,
|
|
153
151
|
};
|
|
154
152
|
try {
|
|
155
|
-
// Only AMLI (org 52) needs the resolved source's params forwarded to its
|
|
156
|
-
// self-guided tour vendor (Tour24); every other org uses the original
|
|
157
|
-
// endpoint that returns just the source string.
|
|
158
|
-
if (this.orgId !== 52) {
|
|
159
|
-
const response = await axios.post(
|
|
160
|
-
`${this.apiHost}/platformApi/webchat/current-parsed-lead-source`,
|
|
161
|
-
requestBody
|
|
162
|
-
);
|
|
163
|
-
return response.data;
|
|
164
|
-
}
|
|
165
153
|
const response = await axios.post(
|
|
166
|
-
`${this.apiHost}/platformApi/webchat/current-parsed-lead-source
|
|
154
|
+
`${this.apiHost}/platformApi/webchat/current-parsed-lead-source`,
|
|
167
155
|
requestBody
|
|
168
156
|
);
|
|
169
|
-
|
|
170
|
-
source: string | null;
|
|
171
|
-
utm_params: { [key: string]: string } | null;
|
|
172
|
-
};
|
|
173
|
-
this.leadSourceParams = data?.utm_params ?? null;
|
|
174
|
-
return data?.source ?? null;
|
|
157
|
+
return response.data;
|
|
175
158
|
} catch (_) {
|
|
176
159
|
return null;
|
|
177
160
|
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
import { html, LitElement, PropertyValueMap, TemplateResult } from "lit";
|
|
3
3
|
import { customElement, property, query, state } from "lit/decorators.js";
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
formatToPhoneInput,
|
|
7
|
-
isPrintableCharacter,
|
|
8
|
-
} from "../actions/formatPhoneNumber";
|
|
4
|
+
import "../actions/international-phone-input";
|
|
5
|
+
import type { InternationalPhoneChangeDetail } from "../actions/international-phone-input";
|
|
9
6
|
import "./tour-type-option.ts";
|
|
10
7
|
import "./date-picker.ts";
|
|
11
8
|
import "./time-picker.ts";
|
|
@@ -30,12 +27,7 @@ import { MESelect } from "../me-select";
|
|
|
30
27
|
import { TimePicker } from "./time-picker";
|
|
31
28
|
import { LabeledOption } from "../../fetchBuildingInfo";
|
|
32
29
|
import { LayoutOption } from "../../fetchBuildingWebchatView";
|
|
33
|
-
import {
|
|
34
|
-
isMobile,
|
|
35
|
-
isValidEmail,
|
|
36
|
-
isValidPhoneNumber,
|
|
37
|
-
snakify,
|
|
38
|
-
} from "../../utils";
|
|
30
|
+
import { isMobile, isValidEmail, snakify } from "../../utils";
|
|
39
31
|
import axios, { AxiosError } from "axios";
|
|
40
32
|
import mapValues from "lodash/mapValues";
|
|
41
33
|
import classnames from "classnames";
|
|
@@ -44,7 +36,10 @@ import compareAsc from "date-fns/compareAsc";
|
|
|
44
36
|
import { InputStyles } from "../actions/InputStyles";
|
|
45
37
|
import { classMap } from "lit/directives/class-map.js";
|
|
46
38
|
import { FeatureFlagsShowDropdown } from "../../fetchFeatureFlag";
|
|
47
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
appendParamsToUrl,
|
|
41
|
+
getUrlQueryParameters,
|
|
42
|
+
} from "../../postLeadSources";
|
|
48
43
|
import { pushGtmEvent } from "../../gtm";
|
|
49
44
|
import formDisclaimer from "../../disclaimers";
|
|
50
45
|
import { tourSchedulerStyles } from "./tourSchedulerStyles";
|
|
@@ -87,13 +82,12 @@ export class TourScheduler extends LitElement {
|
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
// Open the configured self-guided tour (e.g. Tour24) link. For org 52 (AMLI)
|
|
90
|
-
// we
|
|
91
|
-
//
|
|
92
|
-
// link unchanged.
|
|
85
|
+
// we read URL params fresh at open-time so returning visitors (cached lead
|
|
86
|
+
// source) still get the current page's query string forwarded.
|
|
93
87
|
private openSelfGuidedTour(): void {
|
|
94
88
|
const url =
|
|
95
89
|
this.orgId === 52
|
|
96
|
-
? appendParamsToUrl(this.sgtUrl,
|
|
90
|
+
? appendParamsToUrl(this.sgtUrl, getUrlQueryParameters())
|
|
97
91
|
: this.sgtUrl;
|
|
98
92
|
window.open(url, "_blank");
|
|
99
93
|
}
|
|
@@ -155,6 +149,14 @@ export class TourScheduler extends LitElement {
|
|
|
155
149
|
@state()
|
|
156
150
|
private phoneNumber = "";
|
|
157
151
|
@state()
|
|
152
|
+
private phoneNumberE164: string | null = null;
|
|
153
|
+
@state()
|
|
154
|
+
private phoneCountry: string | null = null;
|
|
155
|
+
@state()
|
|
156
|
+
private phoneNumberIsValid = false;
|
|
157
|
+
@state()
|
|
158
|
+
private phoneNumberHasBlurred = false;
|
|
159
|
+
@state()
|
|
158
160
|
private availabilitiesGroupedByDay: {
|
|
159
161
|
[day: string]: DateWithTimeZoneOffset[];
|
|
160
162
|
} = {};
|
|
@@ -217,8 +219,6 @@ export class TourScheduler extends LitElement {
|
|
|
217
219
|
lastNameInput!: HTMLInputElement;
|
|
218
220
|
@query(".inputContainer#email input")
|
|
219
221
|
emailInput!: HTMLInputElement;
|
|
220
|
-
@query(".inputContainer#phone input")
|
|
221
|
-
phoneInput!: HTMLInputElement;
|
|
222
222
|
@query("me-select#leadSource")
|
|
223
223
|
selectedLeadSource!: MESelect;
|
|
224
224
|
@query("me-select#layout")
|
|
@@ -630,147 +630,13 @@ export class TourScheduler extends LitElement {
|
|
|
630
630
|
}
|
|
631
631
|
};
|
|
632
632
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
)
|
|
641
|
-
return;
|
|
642
|
-
|
|
643
|
-
const cursorPosition = e.target.selectionStart;
|
|
644
|
-
|
|
645
|
-
if (isPrintableCharacter(e) && !shortcutKeyIsPressed(e)) {
|
|
646
|
-
// If e.key is a character, and no modifier key is pressed, insert it at the cursor, filter out non-numbers, and auto-format
|
|
647
|
-
e.preventDefault();
|
|
648
|
-
e.stopPropagation();
|
|
649
|
-
const updated =
|
|
650
|
-
this.phoneNumber.slice(0, cursorPosition) +
|
|
651
|
-
e.key +
|
|
652
|
-
this.phoneNumber.slice(cursorPosition);
|
|
653
|
-
this.phoneNumber = formatToPhoneInput(updated.replace(/\D/g, ""));
|
|
654
|
-
this.phoneInput.value = this.phoneNumber;
|
|
655
|
-
} else if (e.key === "Backspace") {
|
|
656
|
-
/*
|
|
657
|
-
Handling backspace:
|
|
658
|
-
- 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
|
|
659
|
-
- Let the OS handle backspace combos like `Alt + Backspace`, then re-autoformat if necessary (in keyup)
|
|
660
|
-
- If the user wants to select and backspace a range of text, let them, then auto-format the remainder
|
|
661
|
-
*/
|
|
662
|
-
|
|
663
|
-
// backspace combos
|
|
664
|
-
if (shortcutKeyIsPressed(e)) {
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// backspace selection
|
|
669
|
-
if (
|
|
670
|
-
this.phoneInput.selectionEnd &&
|
|
671
|
-
this.phoneInput.selectionStart &&
|
|
672
|
-
this.phoneInput.selectionEnd - this.phoneInput.selectionStart > 0
|
|
673
|
-
) {
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// regular backspace
|
|
678
|
-
const originalCharacterCount = this.phoneNumber.length;
|
|
679
|
-
const digitsBeforeCursor = this.phoneNumber
|
|
680
|
-
.slice(0, cursorPosition)
|
|
681
|
-
.replace(/\D/g, "");
|
|
682
|
-
const digitsAfterCursor = this.phoneNumber
|
|
683
|
-
.slice(cursorPosition)
|
|
684
|
-
.replace(/\D/g, "");
|
|
685
|
-
const updatedDigits = `${digitsBeforeCursor.slice(
|
|
686
|
-
0,
|
|
687
|
-
-1
|
|
688
|
-
)}${digitsAfterCursor}`;
|
|
689
|
-
this.phoneNumber = formatToPhoneInput(updatedDigits);
|
|
690
|
-
this.phoneInput.value = this.phoneNumber;
|
|
691
|
-
const numOfCharactersDeleted =
|
|
692
|
-
originalCharacterCount - this.phoneNumber.length;
|
|
693
|
-
const newCursorPosition = cursorPosition - numOfCharactersDeleted;
|
|
694
|
-
this.phoneInput.setSelectionRange(newCursorPosition, newCursorPosition);
|
|
695
|
-
e.preventDefault();
|
|
696
|
-
e.stopPropagation();
|
|
697
|
-
return;
|
|
698
|
-
} else if (
|
|
699
|
-
["ArrowLeft", "ArrowRight"].includes(e.key) &&
|
|
700
|
-
!shortcutKeyIsPressed(e) &&
|
|
701
|
-
!e.shiftKey
|
|
702
|
-
) {
|
|
703
|
-
// when navigating with arrow keys, skip punctuation
|
|
704
|
-
if (e.key === "ArrowLeft") {
|
|
705
|
-
const charactersBeforeCursor = this.phoneNumber.slice(
|
|
706
|
-
0,
|
|
707
|
-
cursorPosition
|
|
708
|
-
);
|
|
709
|
-
const numberOfNonDigitsBeforeCursor =
|
|
710
|
-
charactersBeforeCursor.split(/\d+/).at(-1)?.length || 0;
|
|
711
|
-
const moveLeftBy = numberOfNonDigitsBeforeCursor + 1;
|
|
712
|
-
const newCursorPosition =
|
|
713
|
-
cursorPosition - moveLeftBy > -1 ? cursorPosition - moveLeftBy : 0;
|
|
714
|
-
this.phoneInput.setSelectionRange(newCursorPosition, newCursorPosition);
|
|
715
|
-
}
|
|
716
|
-
if (e.key === "ArrowRight") {
|
|
717
|
-
const charactersAfterCursor = this.phoneNumber.slice(cursorPosition);
|
|
718
|
-
const numberOfNonDigitsAfterCursor =
|
|
719
|
-
charactersAfterCursor.split(/\d+/)[0].length || 0;
|
|
720
|
-
const moveRightBy = numberOfNonDigitsAfterCursor + 1;
|
|
721
|
-
const newCursorPosition =
|
|
722
|
-
cursorPosition + moveRightBy < this.phoneNumber.length
|
|
723
|
-
? cursorPosition + moveRightBy
|
|
724
|
-
: this.phoneNumber.length;
|
|
725
|
-
this.phoneInput.setSelectionRange(newCursorPosition, newCursorPosition);
|
|
726
|
-
}
|
|
727
|
-
e.preventDefault();
|
|
728
|
-
e.stopPropagation();
|
|
729
|
-
} else {
|
|
730
|
-
// Let browser/OS handle anything else. We'll handle any changes to the phone input in the `keyup` handler.
|
|
731
|
-
// Could be a keyboard shortcut that modifies the input (like `Cmd/Ctrl + V`, which we'll handle in `keyup`),
|
|
732
|
-
// or a keyboard shortcut that doesn't (like `Cmd + L` to jump to URL bar or `Cmd + R` to reload the page),
|
|
733
|
-
// or Tab, an arrow key, etc.
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
};
|
|
737
|
-
|
|
738
|
-
handlePhoneKeyup = (e: KeyboardEvent): void => {
|
|
739
|
-
if (!e.key) {
|
|
740
|
-
return;
|
|
741
|
-
}
|
|
742
|
-
// 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".
|
|
743
|
-
// (We never want the cursor to be before a punctuation mark because the next digit typed will appear after the punctuation mark, not before.)
|
|
744
|
-
// If we don't do this, the cursor automatically goes to the end when we set `this.phoneNumber`.
|
|
745
|
-
// This is sometimes undesired: for example, if we've ended up here because a Mac user typed `Alt + Backspace` in the middle.
|
|
746
|
-
|
|
747
|
-
// Arrow keys are intended to change the cursor position, so don't get in their way
|
|
748
|
-
if (
|
|
749
|
-
e.key.includes("Arrow") ||
|
|
750
|
-
["Meta", "Shift", "Control", "Alt"].includes(e.key)
|
|
751
|
-
) {
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
const cursorPosition = this.phoneInput.selectionStart;
|
|
756
|
-
// find the numbers it's before and count backward from end after formatting
|
|
757
|
-
const numbersAfterCursor = cursorPosition
|
|
758
|
-
? this.phoneInput.value.slice(cursorPosition).replace(/\D/g, "")
|
|
759
|
-
: "";
|
|
760
|
-
this.phoneNumber = formatToPhoneInput(this.phoneInput.value);
|
|
761
|
-
|
|
762
|
-
// EXAMPLES: (123)| 4 numbersAfterCursor will be '4'.
|
|
763
|
-
let cursorNegativeIndex = 0;
|
|
764
|
-
let numbersLeft = numbersAfterCursor.length;
|
|
765
|
-
while (numbersLeft) {
|
|
766
|
-
if (this.phoneNumber.at(cursorNegativeIndex)?.match(/\d/)) {
|
|
767
|
-
numbersLeft--;
|
|
768
|
-
}
|
|
769
|
-
cursorNegativeIndex++;
|
|
770
|
-
}
|
|
771
|
-
const cursorPositiveIndex =
|
|
772
|
-
this.phoneInput.value.length - cursorNegativeIndex + 1;
|
|
773
|
-
this.phoneInput.setSelectionRange(cursorPositiveIndex, cursorPositiveIndex);
|
|
633
|
+
onChangePhoneNumber = (
|
|
634
|
+
event: CustomEvent<InternationalPhoneChangeDetail>
|
|
635
|
+
): void => {
|
|
636
|
+
this.phoneNumber = event.detail.formattedValue;
|
|
637
|
+
this.phoneNumberE164 = event.detail.e164;
|
|
638
|
+
this.phoneCountry = event.detail.country;
|
|
639
|
+
this.phoneNumberIsValid = event.detail.isValid;
|
|
774
640
|
};
|
|
775
641
|
|
|
776
642
|
onChangeEmail = (e: Event): void => {
|
|
@@ -788,10 +654,8 @@ export class TourScheduler extends LitElement {
|
|
|
788
654
|
return (
|
|
789
655
|
(!!this.firstNameInput?.value || !!this.lastNameInput?.value) &&
|
|
790
656
|
isValidEmail(this.emailInput?.value ?? "") &&
|
|
791
|
-
// TODO: deleting phone number doesn't cause validation to fail, at least on mobile
|
|
792
657
|
!!this.phoneNumber &&
|
|
793
|
-
this.
|
|
794
|
-
isValidPhoneNumber(this.phoneNumber)
|
|
658
|
+
!!this.phoneNumberE164
|
|
795
659
|
);
|
|
796
660
|
},
|
|
797
661
|
};
|
|
@@ -822,6 +686,11 @@ export class TourScheduler extends LitElement {
|
|
|
822
686
|
if (!this.selectedDate || !this.selectedTime || this.tourType === null) {
|
|
823
687
|
return;
|
|
824
688
|
}
|
|
689
|
+
const phoneNumber = this.phoneNumberE164;
|
|
690
|
+
if (!phoneNumber) {
|
|
691
|
+
this.phoneNumberIsValid = false;
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
825
694
|
const queryParams = new URLSearchParams(window.location.search);
|
|
826
695
|
|
|
827
696
|
let parsedLeadSource = null;
|
|
@@ -860,7 +729,7 @@ export class TourScheduler extends LitElement {
|
|
|
860
729
|
this.selectedUnitEl?.value || this.selectedUnitValue || null;
|
|
861
730
|
pushGtmEvent("scheduleTourSubmitted", {
|
|
862
731
|
email: this.email,
|
|
863
|
-
phone:
|
|
732
|
+
phone: phoneNumber,
|
|
864
733
|
firstName: this.firstNameInput?.value ?? this.firstNameInputValue,
|
|
865
734
|
lastName: this.lastNameInput?.value ?? this.lastNameInputValue,
|
|
866
735
|
tourType: tourTypeForSubmission[this.tourType],
|
|
@@ -874,7 +743,7 @@ export class TourScheduler extends LitElement {
|
|
|
874
743
|
const data = {
|
|
875
744
|
referrer: document.referrer,
|
|
876
745
|
email_address: this.email,
|
|
877
|
-
phone_number:
|
|
746
|
+
phone_number: phoneNumber,
|
|
878
747
|
building_id: this.buildingId,
|
|
879
748
|
first_name: this.firstNameInput?.value ?? this.firstNameInputValue,
|
|
880
749
|
last_name: this.lastNameInput?.value ?? this.lastNameInputValue,
|
|
@@ -1414,35 +1283,16 @@ export class TourScheduler extends LitElement {
|
|
|
1414
1283
|
: ""}
|
|
1415
1284
|
</div>
|
|
1416
1285
|
<div class="inputContainer" id="phone">
|
|
1417
|
-
<input
|
|
1418
|
-
|
|
1419
|
-
["webchat-input"]: true,
|
|
1420
|
-
["webchat-font__desktop"]: !isMobile(),
|
|
1421
|
-
["webchat-font__mobile"]: isMobile(),
|
|
1422
|
-
["webchat-input__error"]:
|
|
1423
|
-
this.phoneNumber.length === 14 &&
|
|
1424
|
-
!isValidPhoneNumber(this.phoneNumber),
|
|
1425
|
-
})}
|
|
1426
|
-
type="tel"
|
|
1427
|
-
inputmode="tel"
|
|
1428
|
-
placeholder="Phone"
|
|
1429
|
-
name="phone"
|
|
1430
|
-
autocomplete="tel-national"
|
|
1431
|
-
maxlength="14"
|
|
1286
|
+
<international-phone-input
|
|
1287
|
+
.country=${this.phoneCountry ?? this.country}
|
|
1432
1288
|
.value=${this.phoneNumber}
|
|
1433
|
-
|
|
1434
|
-
@
|
|
1435
|
-
@
|
|
1436
|
-
|
|
1437
|
-
return;
|
|
1438
|
-
}
|
|
1439
|
-
this.phoneNumber = formatToPhoneInput(
|
|
1440
|
-
(e.target as HTMLInputElement).value
|
|
1441
|
-
);
|
|
1289
|
+
.invalid=${this.phoneNumberHasBlurred && !this.phoneNumberIsValid}
|
|
1290
|
+
@phone-change=${this.onChangePhoneNumber}
|
|
1291
|
+
@phone-blur=${() => {
|
|
1292
|
+
this.phoneNumberHasBlurred = true;
|
|
1442
1293
|
}}
|
|
1443
|
-
|
|
1444
|
-
${this.
|
|
1445
|
-
!isValidPhoneNumber(this.phoneNumber)
|
|
1294
|
+
></international-phone-input>
|
|
1295
|
+
${this.phoneNumberHasBlurred && !this.phoneNumberIsValid
|
|
1446
1296
|
? html`<p class="error-message">Invalid phone number</p>`
|
|
1447
1297
|
: ""}
|
|
1448
1298
|
</div>
|
|
@@ -1777,7 +1627,7 @@ export class TourScheduler extends LitElement {
|
|
|
1777
1627
|
changes.
|
|
1778
1628
|
${formDisclaimer({
|
|
1779
1629
|
buildingName: this.buildingName,
|
|
1780
|
-
phoneNumberInput: this.
|
|
1630
|
+
phoneNumberInput: this.phoneNumber,
|
|
1781
1631
|
emailInput: this.emailInput?.value,
|
|
1782
1632
|
orgLegalName: this.orgLegalName,
|
|
1783
1633
|
orgSlug: this.orgSlug,
|
|
@@ -1832,7 +1682,7 @@ export class TourScheduler extends LitElement {
|
|
|
1832
1682
|
changes.
|
|
1833
1683
|
${formDisclaimer({
|
|
1834
1684
|
buildingName: this.buildingName,
|
|
1835
|
-
phoneNumberInput: this.
|
|
1685
|
+
phoneNumberInput: this.phoneNumber,
|
|
1836
1686
|
emailInput: this.emailInput?.value,
|
|
1837
1687
|
orgLegalName: this.orgLegalName,
|
|
1838
1688
|
orgSlug: this.orgSlug,
|
|
@@ -1942,7 +1792,7 @@ export class TourScheduler extends LitElement {
|
|
|
1942
1792
|
? html`
|
|
1943
1793
|
${formDisclaimer({
|
|
1944
1794
|
buildingName: this.buildingName,
|
|
1945
|
-
phoneNumberInput: this.
|
|
1795
|
+
phoneNumberInput: this.phoneNumber,
|
|
1946
1796
|
emailInput: this.emailInput?.value,
|
|
1947
1797
|
orgLegalName: this.orgLegalName,
|
|
1948
1798
|
orgSlug: this.orgSlug,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
|
|
18
18
|
import { getOfficeHourText } from "../OfficeHours";
|
|
19
19
|
import {
|
|
20
|
-
|
|
20
|
+
formatToUsPhoneInput,
|
|
21
21
|
isModifierKey,
|
|
22
22
|
isNumericInput,
|
|
23
23
|
} from "./formatPhoneNumber";
|
|
@@ -279,7 +279,7 @@ export class CallUsWindow extends LitElement {
|
|
|
279
279
|
}
|
|
280
280
|
const inputElement = e.target as HTMLInputElement;
|
|
281
281
|
|
|
282
|
-
this.phoneNumberToText =
|
|
282
|
+
this.phoneNumberToText = formatToUsPhoneInput(inputElement.value);
|
|
283
283
|
|
|
284
284
|
this.phoneNumberInputRef.value.value = this.phoneNumberToText;
|
|
285
285
|
};
|
|
@@ -2,16 +2,12 @@ import { css, html, LitElement, TemplateResult } from "lit";
|
|
|
2
2
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3
3
|
import { customElement, property, query, state } from "lit/decorators.js";
|
|
4
4
|
import { classMap } from "lit/directives/class-map.js";
|
|
5
|
-
import { createRef, ref, Ref } from "lit/directives/ref.js";
|
|
6
5
|
import { installActionConfirmButton } from "./action-confirm-button";
|
|
7
6
|
import { installDetailsWindow } from "./details-window";
|
|
8
7
|
import "../me-select.ts";
|
|
9
8
|
import { MESelect } from "../me-select";
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
isModifierKey,
|
|
13
|
-
isNumericInput,
|
|
14
|
-
} from "./formatPhoneNumber";
|
|
9
|
+
import "./international-phone-input";
|
|
10
|
+
import type { InternationalPhoneChangeDetail } from "./international-phone-input";
|
|
15
11
|
import { InputStyles } from "./InputStyles";
|
|
16
12
|
import axios from "axios";
|
|
17
13
|
import { FeatureFlagsShowDropdown } from "../../fetchFeatureFlag";
|
|
@@ -135,8 +131,6 @@ export class EmailUsWindow extends LitElement {
|
|
|
135
131
|
@property({ attribute: true })
|
|
136
132
|
featureFlagShowDropdown = "";
|
|
137
133
|
|
|
138
|
-
phoneNumberInputRef: Ref<HTMLInputElement> = createRef();
|
|
139
|
-
|
|
140
134
|
@property()
|
|
141
135
|
privatePolicyUrl = "https://www.meetelise.com/privacy";
|
|
142
136
|
@property()
|
|
@@ -151,6 +145,10 @@ export class EmailUsWindow extends LitElement {
|
|
|
151
145
|
@state()
|
|
152
146
|
phoneNumber = "";
|
|
153
147
|
@state()
|
|
148
|
+
phoneNumberE164: string | null = null;
|
|
149
|
+
@state()
|
|
150
|
+
phoneCountry: string | null = null;
|
|
151
|
+
@state()
|
|
154
152
|
message = "";
|
|
155
153
|
|
|
156
154
|
@query("me-select#leadSource")
|
|
@@ -200,18 +198,12 @@ export class EmailUsWindow extends LitElement {
|
|
|
200
198
|
this.email = (e.target as HTMLInputElement).value;
|
|
201
199
|
};
|
|
202
200
|
|
|
203
|
-
onChangePhoneNumber = (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
const inputElement = e.target as HTMLInputElement;
|
|
211
|
-
|
|
212
|
-
this.phoneNumber = formatToPhoneInput(inputElement.value);
|
|
213
|
-
|
|
214
|
-
this.phoneNumberInputRef.value.value = this.phoneNumber;
|
|
201
|
+
onChangePhoneNumber = (
|
|
202
|
+
e: CustomEvent<InternationalPhoneChangeDetail>
|
|
203
|
+
): void => {
|
|
204
|
+
this.phoneNumber = e.detail.formattedValue;
|
|
205
|
+
this.phoneNumberE164 = e.detail.e164;
|
|
206
|
+
this.phoneCountry = e.detail.country;
|
|
215
207
|
};
|
|
216
208
|
|
|
217
209
|
onChangeMessage = (e: Event): void => {
|
|
@@ -222,12 +214,6 @@ export class EmailUsWindow extends LitElement {
|
|
|
222
214
|
this.message = (e.target as HTMLTextAreaElement).value;
|
|
223
215
|
};
|
|
224
216
|
|
|
225
|
-
enforceFormat = (e: KeyboardEvent): void => {
|
|
226
|
-
if (!isNumericInput(e) && !isModifierKey(e)) {
|
|
227
|
-
e.preventDefault();
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
217
|
validateFormFields = (): void => {
|
|
232
218
|
this.hasNameError = false;
|
|
233
219
|
this.hasEmailError = false;
|
|
@@ -240,7 +226,7 @@ export class EmailUsWindow extends LitElement {
|
|
|
240
226
|
if (!this.email || !isValidEmail(this.email)) {
|
|
241
227
|
this.hasEmailError = true;
|
|
242
228
|
}
|
|
243
|
-
if (!this.
|
|
229
|
+
if (!this.phoneNumberE164) {
|
|
244
230
|
this.hasPhoneNumberError = true;
|
|
245
231
|
}
|
|
246
232
|
this.windowHeight = 525 + 30 * this.getNumErrors();
|
|
@@ -259,6 +245,11 @@ export class EmailUsWindow extends LitElement {
|
|
|
259
245
|
) {
|
|
260
246
|
return;
|
|
261
247
|
}
|
|
248
|
+
const phoneNumber = this.phoneNumberE164;
|
|
249
|
+
if (!phoneNumber) {
|
|
250
|
+
this.hasPhoneNumberError = true;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
262
253
|
try {
|
|
263
254
|
this.isSubmitting = true;
|
|
264
255
|
// Height of the button when it's in the loading state
|
|
@@ -271,7 +262,7 @@ export class EmailUsWindow extends LitElement {
|
|
|
271
262
|
this.firstName,
|
|
272
263
|
this.lastName,
|
|
273
264
|
this.email,
|
|
274
|
-
|
|
265
|
+
phoneNumber,
|
|
275
266
|
this.message,
|
|
276
267
|
this.buildingId,
|
|
277
268
|
this.orgSlug,
|
|
@@ -427,23 +418,12 @@ export class EmailUsWindow extends LitElement {
|
|
|
427
418
|
`
|
|
428
419
|
: ""}
|
|
429
420
|
<div class="email-us__vertical-spacer"></div>
|
|
430
|
-
<input
|
|
431
|
-
|
|
432
|
-
type="text"
|
|
433
|
-
placeholder="Phone"
|
|
434
|
-
autocomplete="tel-national"
|
|
435
|
-
inputmode="tel"
|
|
436
|
-
class=${classMap({
|
|
437
|
-
["webchat-input"]: true,
|
|
438
|
-
["email-us__contact-input"]: true,
|
|
439
|
-
["webchat-font__desktop"]: !isMobile(),
|
|
440
|
-
["webchat-font__mobile"]: isMobile(),
|
|
441
|
-
})}
|
|
421
|
+
<international-phone-input
|
|
422
|
+
.country=${this.phoneCountry ?? this.country}
|
|
442
423
|
.value=${this.phoneNumber}
|
|
443
|
-
|
|
444
|
-
@
|
|
445
|
-
|
|
446
|
-
/>
|
|
424
|
+
.invalid=${this.hasPhoneNumberError}
|
|
425
|
+
@phone-change=${this.onChangePhoneNumber}
|
|
426
|
+
></international-phone-input>
|
|
447
427
|
${this.hasPhoneNumberError
|
|
448
428
|
? html`
|
|
449
429
|
<div class="email-us__error-text">
|
|
@@ -527,7 +507,7 @@ const createEmail = async (
|
|
|
527
507
|
firstName: string,
|
|
528
508
|
lastName: string,
|
|
529
509
|
email: string,
|
|
530
|
-
|
|
510
|
+
phoneNumber: string,
|
|
531
511
|
message: string,
|
|
532
512
|
buildingId: number,
|
|
533
513
|
orgSlug: string,
|
|
@@ -536,13 +516,6 @@ const createEmail = async (
|
|
|
536
516
|
chatId?: string | null,
|
|
537
517
|
leadSourcesWithTimestamps?: LeadSourceSubmittedListItem[]
|
|
538
518
|
) => {
|
|
539
|
-
const formattedPhoneNumber =
|
|
540
|
-
"+1" +
|
|
541
|
-
rawPhoneNumber
|
|
542
|
-
.replace("(", "")
|
|
543
|
-
.replace(")", "")
|
|
544
|
-
.replace(" ", "")
|
|
545
|
-
.replace("-", "");
|
|
546
519
|
const queryParams = new URLSearchParams(window.location.search);
|
|
547
520
|
const requestBody = {
|
|
548
521
|
email_address: email,
|
|
@@ -551,7 +524,7 @@ const createEmail = async (
|
|
|
551
524
|
first_message: message,
|
|
552
525
|
first_name: firstName,
|
|
553
526
|
last_name: lastName,
|
|
554
|
-
phone_number:
|
|
527
|
+
phone_number: phoneNumber,
|
|
555
528
|
referrer: document.referrer,
|
|
556
529
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
557
530
|
// @ts-ignore
|
|
@@ -565,7 +538,7 @@ const createEmail = async (
|
|
|
565
538
|
firstName,
|
|
566
539
|
lastName,
|
|
567
540
|
email,
|
|
568
|
-
phone:
|
|
541
|
+
phone: phoneNumber,
|
|
569
542
|
message,
|
|
570
543
|
originatingSource:
|
|
571
544
|
leadSources.find((i) => i !== "property-website") || null,
|