@meetelise/chat 1.20.65 → 1.20.67

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.
@@ -389,9 +389,13 @@ export class TourScheduler extends LitElement {
389
389
  this.unitTypeSelect && this.unitTypeSelect.value
390
390
  ? [this.unitTypeSelect.value]
391
391
  : null,
392
- lead_sources: parsedLeadSource
393
- ? [parsedLeadSource, "property-website"]
394
- : ["property-website"],
392
+ lead_sources: [
393
+ ...new Set(
394
+ parsedLeadSource
395
+ ? [parsedLeadSource, "property-website"]
396
+ : ["property-website"]
397
+ ),
398
+ ],
395
399
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
396
400
  // @ts-ignore
397
401
  query_params: Object.fromEntries(queryParams.entries()),
@@ -247,9 +247,13 @@ export class EmailUsWindow extends LitElement {
247
247
  this.buildingId,
248
248
  this.orgSlug,
249
249
  this.buildingSlug,
250
- parsedLeadSource
251
- ? [parsedLeadSource, "property-website"]
252
- : ["property-website"]
250
+ [
251
+ ...new Set(
252
+ parsedLeadSource
253
+ ? [parsedLeadSource, "property-website"]
254
+ : ["property-website"]
255
+ ),
256
+ ]
253
257
  );
254
258
  postLeadSources({
255
259
  chatId: this.chatId,
@@ -126,9 +126,13 @@ export class TextUsWindow extends LitElement {
126
126
  this.buildingId,
127
127
  this.orgSlug,
128
128
  this.buildingSlug,
129
- this.currentLeadSource
130
- ? [this.currentLeadSource, "property-website"]
131
- : ["property-website"]
129
+ [
130
+ ...new Set(
131
+ this.currentLeadSource
132
+ ? [this.currentLeadSource, "property-website"]
133
+ : ["property-website"]
134
+ ),
135
+ ]
132
136
  );
133
137
  this.hasSubmittedForm = true;
134
138
  this.isSubmitting = false;
@@ -200,7 +200,7 @@ export const launcherStyles = css`
200
200
  .launcher__mini-launcher-wrapper {
201
201
  position: fixed;
202
202
  right: 0px;
203
- bottom: 210px;
203
+ bottom: 110px;
204
204
  z-index: 100000;
205
205
  display: flex;
206
206
  align-items: center;
@@ -21,7 +21,7 @@ import fetchPhoneNumberFromSource, {
21
21
  NumberForSelectedSource,
22
22
  } from "../fetchPhoneNumberFromSource";
23
23
  import { defaultBrandColor, getTheme, Theme } from "../themes";
24
- import { isMobile } from "../utils";
24
+ import { isMobile, sleep } from "../utils";
25
25
  import { installLauncher } from "./launcher/Launcher";
26
26
  import parseISO from "date-fns/parseISO";
27
27
  import isPast from "date-fns/isPast";
@@ -161,12 +161,23 @@ export class MEChat extends LitElement {
161
161
  await this.initializeLaunchJS();
162
162
  this.attachOnClickToLauncher();
163
163
  this.isLoading = false;
164
+ if (localStorage.getItem("overrideContactUsForm") === "true") {
165
+ this
166
+ .overrideContactUsForm
167
+ // {
168
+ // orgSlug: this.orgSlug,
169
+ // buildingSlug: this.buildingSlug,
170
+ // buildingId: this.building?.id ?? null,
171
+ // }
172
+ ();
173
+ }
164
174
  };
165
175
 
166
176
  setBuildingDerivedInfo = async (): Promise<void> => {
167
177
  if (!this.buildingSlug || !this.orgSlug) {
168
178
  return;
169
179
  }
180
+ const beginTime = Date.now(); // used for widget delay
170
181
  const [
171
182
  building,
172
183
  buildingABTest,
@@ -199,6 +210,15 @@ export class MEChat extends LitElement {
199
210
  if (this.buildingABTestType === null) {
200
211
  this.buildingABTestType = webchatPreferences.designConcept ?? null;
201
212
  }
213
+ if (webchatPreferences.delayOpen) {
214
+ // We take into account the time it took to fetch the building info
215
+ const secondsAlreadyPassed = (Date.now() - beginTime) / 1000;
216
+ if (secondsAlreadyPassed < webchatPreferences.delayOpen) {
217
+ await sleep(
218
+ (webchatPreferences.delayOpen - secondsAlreadyPassed) * 1000
219
+ ); // delayOpen is in seconds
220
+ }
221
+ }
202
222
  }
203
223
 
204
224
  if (this.brandColor === null) {
@@ -474,6 +494,10 @@ export class MEChat extends LitElement {
474
494
  const showChatAdditionalActions =
475
495
  this.hideLauncher && !this.isLoading && !this.isMobile;
476
496
 
497
+ if (this.isLoading) {
498
+ return html``;
499
+ }
500
+
477
501
  return html`
478
502
  <meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=1">
479
503
  <div id="aria-describe-info" style="display: none;">
@@ -591,6 +615,147 @@ export class MEChat extends LitElement {
591
615
  this.hasMounted = true;
592
616
  };
593
617
  };
618
+
619
+ private overrideContactUsForm = (): // {
620
+ // orgSlug,
621
+ // buildingSlug,
622
+ // buildingId,
623
+ // }: {
624
+ // orgSlug: MEChat["orgSlug"];
625
+ // buildingSlug: MEChat["buildingSlug"];
626
+ // buildingId: Building["id"] | null;
627
+ // }
628
+ void => {
629
+ const form = document.getElementById(
630
+ "myContactForm"
631
+ ) as HTMLFormElement | null;
632
+ let btn = undefined;
633
+ if (!form || !(form instanceof HTMLFormElement)) {
634
+ // TODO: Add log
635
+ // return console.log("No Contact Us Form Found");
636
+ return;
637
+ }
638
+
639
+ // Loop through the elements of the form
640
+ for (let i = 0; i < form.elements.length; i++) {
641
+ const element = form.elements[i];
642
+ if (
643
+ element.tagName.toLowerCase() === "button" &&
644
+ element.getAttribute("data-selenium-id") === "fakebutton"
645
+ ) {
646
+ btn = element;
647
+ break;
648
+ }
649
+ }
650
+
651
+ if (!btn) {
652
+ // TODO: Add log
653
+ // return console.log('No button with data-selenium-id="fakebutton" found');
654
+ return;
655
+ }
656
+
657
+ // Replace the original form element with the cloned one
658
+ const clonedButton = btn.cloneNode(true) as HTMLButtonElement;
659
+ btn.parentNode?.replaceChild(clonedButton, btn);
660
+ //TODO: Remove textContent after testing
661
+ clonedButton.textContent = "Elise Submit";
662
+
663
+ const eliseUrl =
664
+ "https://app.meetelise.com/platformApi/state/create/contactMe";
665
+
666
+ const getFormElements = () => {
667
+ const firstName = document.getElementById(
668
+ "firstname"
669
+ ) as HTMLInputElement | null;
670
+ const lastName = document.getElementById(
671
+ "lastname"
672
+ ) as HTMLInputElement | null;
673
+ const email = document.getElementById("email") as HTMLInputElement | null;
674
+ const phone = document.getElementById(
675
+ "phonenumber"
676
+ ) as HTMLInputElement | null;
677
+ const message = document.getElementById(
678
+ "message"
679
+ ) as HTMLTextAreaElement | null;
680
+
681
+ const formElements = { firstName, lastName, email, phone, message };
682
+
683
+ if (Object.values(formElements).some((el) => el === null)) {
684
+ // console.log("Form is missing elements");
685
+ // TODO: Add logger to log that form scraper is boken on el.name in current route or buildingId
686
+ }
687
+
688
+ return formElements;
689
+ };
690
+ const getFormValues = () => {
691
+ const formValues: { [key: string]: string | undefined } = {};
692
+ Object.entries(getFormElements()).forEach(
693
+ ([key, val]) => (formValues[key] = val?.value)
694
+ );
695
+ return formValues;
696
+ };
697
+
698
+ const isValid = () => {
699
+ return Object.values(getFormElements()).every((el) => {
700
+ if (el === null) return false;
701
+ //TODO: May need to not depend on aria-invalid
702
+ return el.getAttribute("aria-invalid") !== "true";
703
+ });
704
+ };
705
+
706
+ clonedButton.onclick = function (event) {
707
+ if (!isValid()) return;
708
+ event.preventDefault();
709
+ const formValues = getFormValues();
710
+
711
+ const data = {
712
+ email_address: formValues.email,
713
+ first_name: formValues.firstName,
714
+ last_name: formValues.lastName,
715
+ phone_number: formValues.phone,
716
+ first_message: formValues.message,
717
+
718
+ //TODO: Replace after testing is done
719
+ building_id: 3660,
720
+ };
721
+
722
+ // Convert the data object to a JSON string
723
+ const jsonData = JSON.stringify(data);
724
+
725
+ fetch(eliseUrl, {
726
+ method: "POST",
727
+ headers: {
728
+ "Content-Type": "application/json",
729
+ "building-slug": "e2e-test-yardi-building",
730
+ "org-slug": "test-company",
731
+ //TODO: Replace org and building slugs
732
+ },
733
+ body: jsonData,
734
+ }).then((response) => {
735
+ // TODO: What's needed here?
736
+ // console.log(response);
737
+ // Check if the request was successful
738
+ if (!response.ok) {
739
+ throw new Error(`HTTP error ${response.status}`);
740
+ }
741
+
742
+ form.reset();
743
+
744
+ // Parse the response as JSON
745
+ return response.json();
746
+ });
747
+ // .then((responseData) => {
748
+ // // Handle the JSON response data
749
+ // // TODO: What's needed here?
750
+ // // console.log("Response data:", responseData);
751
+ // })
752
+ // .catch((error) => {
753
+ // // Handle any errors that occurred during the request
754
+ // // TODO: What's needed here?
755
+ // // console.error("Error:", error);
756
+ // });
757
+ };
758
+ };
594
759
  }
595
760
 
596
761
  declare global {
package/src/utils.ts CHANGED
@@ -78,3 +78,7 @@ export const hexToAlmostWhite = (hexColor: string, ratio: number): string => {
78
78
  b.toString(16).padStart(2, "0")
79
79
  );
80
80
  };
81
+
82
+ export const sleep = (ms: number): Promise<void> => {
83
+ return new Promise((resolve) => setTimeout(resolve, ms));
84
+ };