@meetelise/chat 1.13.7 → 1.13.10

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.
@@ -11,7 +11,7 @@ import "./time-picker.ts";
11
11
  import "./me-select.ts";
12
12
  import {
13
13
  DateWithTimeZoneOffset,
14
- getAvailabilitiesGroupedByDayCached,
14
+ getAvailabilitiesGroupedByDay,
15
15
  } from "../../getAvailabilities";
16
16
  import { TourAvailabilityResponseRankOrderedSupportedTourTypesEnum } from "@meetelise/rest-sdk";
17
17
  import { format } from "date-fns";
@@ -63,7 +63,7 @@ export class TourScheduler extends LitElement {
63
63
  unitTypeSelect!: MESelect;
64
64
 
65
65
  firstUpdated = async (): Promise<void> => {
66
- this.availabilitiesGroupedByDay = await getAvailabilitiesGroupedByDayCached(
66
+ this.availabilitiesGroupedByDay = await getAvailabilitiesGroupedByDay(
67
67
  tourTypeMap[this.tourType]
68
68
  );
69
69
  };
@@ -74,8 +74,9 @@ export class TourScheduler extends LitElement {
74
74
  | Map<PropertyKey, unknown>
75
75
  ): Promise<void> => {
76
76
  if (_changedProperties.has("tourType")) {
77
- this.availabilitiesGroupedByDay =
78
- await getAvailabilitiesGroupedByDayCached(tourTypeMap[this.tourType]);
77
+ this.availabilitiesGroupedByDay = await getAvailabilitiesGroupedByDay(
78
+ tourTypeMap[this.tourType]
79
+ );
79
80
  }
80
81
  };
81
82
 
@@ -185,6 +186,9 @@ export class TourScheduler extends LitElement {
185
186
  };
186
187
 
187
188
  handlePhoneKeyup = (e: KeyboardEvent): void => {
189
+ if (!e.key) {
190
+ return;
191
+ }
188
192
  // 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".
189
193
  // (We never want the cursor to be before a punctuation mark because the next digit typed will appear after the punctuation mark, not before.)
190
194
  // If we don't do this, the cursor automatically goes to the end when we set `this.phoneNumber`.
@@ -230,12 +234,15 @@ export class TourScheduler extends LitElement {
230
234
  validators = {
231
235
  tourType: (): boolean => !isNaN(this.tourType),
232
236
  dateAndTime: (): boolean => !!this.selectedDate && !!this.selectedTime,
233
- leadInfo: (): boolean =>
234
- !!this.nameInput?.value &&
235
- this.emailInput?.value.includes("@") &&
236
- // TODO: deleting phone number doesn't cause validation to fail, at least on mobile
237
- !!this.phoneNumber &&
238
- this.phoneNumber.length === 14,
237
+ leadInfo: (): boolean => {
238
+ return (
239
+ !!this.nameInput?.value &&
240
+ this.emailInput?.value.includes("@") &&
241
+ // TODO: deleting phone number doesn't cause validation to fail, at least on mobile
242
+ !!this.phoneNumber &&
243
+ this.phoneNumber.length === 14
244
+ );
245
+ },
239
246
  };
240
247
 
241
248
  formIsValidForSubmission = (): boolean => {
@@ -266,7 +273,7 @@ export class TourScheduler extends LitElement {
266
273
  email_address: this.email,
267
274
  phone_number: `+1${this.phoneNumber.match(/\d/g)?.join("")}`, // e.g. +12125555555
268
275
  building_id: this.buildingId,
269
- // TODO: this is very bad dumb name-splitting logic! I'm only doing it because the design had one name field and the backend expects two
276
+ // TODO: this is very bad dumb name-splitting logic! Instead, split the name input into first and last name.
270
277
  first_name: this.nameInput.value.split(" ")[0],
271
278
  last_name: this.nameInput.value.split(" ").slice(1).join(" "),
272
279
  tour_type: tourTypeForSubmission[this.tourType],
@@ -600,7 +607,6 @@ export class TourScheduler extends LitElement {
600
607
  <div id="tourTypeMenu">
601
608
  ${this.tourTypeOptions.map((o) => o.value).includes("WITH_AGENT")
602
609
  ? html` <tour-type-option
603
- tourtype="guided"
604
610
  heading="Guided tour"
605
611
  subtitle="with an agent"
606
612
  @click="${() => {
@@ -633,7 +639,6 @@ export class TourScheduler extends LitElement {
633
639
  : ""}
634
640
  ${this.tourTypeOptions.map((o) => o.value).includes("SELF_GUIDED")
635
641
  ? html`<tour-type-option
636
- tourtype="self"
637
642
  heading="Take a tour"
638
643
  subtitle="on your own"
639
644
  @click="${() => {
@@ -664,9 +669,8 @@ export class TourScheduler extends LitElement {
664
669
  </svg>
665
670
  </tour-type-option>`
666
671
  : ""}
667
- ${this.tourTypeOptions.map((o) => o.value).includes("SELF_GUIDED")
672
+ ${this.tourTypeOptions.map((o) => o.value).includes("VIRTUAL_SHOWING")
668
673
  ? html`<tour-type-option
669
- tourtype="guided"
670
674
  heading="Virtual tour"
671
675
  subtitle="over video"
672
676
  @click="${() => {
@@ -803,14 +807,19 @@ export class TourScheduler extends LitElement {
803
807
  userInfoAndLayoutMenu(): TemplateResult {
804
808
  return html`<h2 id="yourInformation">Your information</h2>
805
809
  <div id="yourInformationMenu">
806
- <input type="text" placeholder="Name" id="name" />
810
+ <input
811
+ type="text"
812
+ placeholder="Name"
813
+ id="name"
814
+ @input=${() => this.requestUpdate()}
815
+ />
807
816
  <input
808
817
  type="email"
809
818
  inputmode="email"
810
819
  placeholder="Email"
811
820
  id="email"
812
821
  .value=${this.email}
813
- @keyup=${this.onChangeEmail}
822
+ @input=${this.onChangeEmail}
814
823
  />
815
824
  <input
816
825
  type="tel"
@@ -821,28 +830,21 @@ export class TourScheduler extends LitElement {
821
830
  .value=${this.phoneNumber}
822
831
  @keydown=${this.handlePhoneKeydown}
823
832
  @keyup=${this.handlePhoneKeyup}
824
- @change=${() => this.requestUpdate()}
833
+ @input=${(e: Event) => {
834
+ if (!e.target) {
835
+ return;
836
+ }
837
+ this.phoneNumber = formatToPhone(
838
+ (e.target as HTMLInputElement).value
839
+ );
840
+ }}
825
841
  />
826
842
  </div>
827
-
828
- ${this.layoutOptions.length > 0
829
- ? html`<div id="unitChoiceMenu">
830
- <h2 id="unitChoice">What would you like to view?</h2>
831
- <div id="unitOptions">
832
- <me-select
833
- id="unitType"
834
- placeholder="Select type"
835
- .options="${this.layoutOptions.map((o) => o.label)}"
836
- defaultOption="Studio"
837
- @change=${() => {
838
- // to revalidate the form
839
- this.requestUpdate();
840
- }}
841
- >Studio
842
- </me-select>
843
- </div>
844
- </div>`
845
- : ""} `;
843
+ <!--
844
+ Layout dropdown would go here, but has been removed pending backend support.
845
+ Here is the code to add it back:
846
+ https://github.com/MeetElise/chat-ui/blob/e17aca8b39a0eed9430f22c182f2ebcdfb796417/src/WebComponent/Scheduler/tour-scheduler.ts#L846-L863
847
+ --> `;
846
848
  }
847
849
 
848
850
  confirmationMessage(): TemplateResult {
@@ -1,20 +1,16 @@
1
1
  import { css, html, LitElement, TemplateResult } from "lit";
2
2
  import { customElement, property } from "lit/decorators.js";
3
- import { TourType } from "./tour-scheduler";
4
3
 
5
4
  @customElement("tour-type-option")
6
5
  export class TourTypeOption extends LitElement {
7
- @property({ type: String })
8
- tourtype = "";
9
6
  @property({ type: String })
10
7
  heading = "";
11
8
  @property({ type: String })
12
9
  subtitle = "";
13
10
  @property({ type: Boolean })
14
11
  selected = false;
15
-
16
12
  @property({ attribute: false })
17
- onClick?: (tourType: TourType) => void;
13
+ onClick?: () => void;
18
14
 
19
15
  static styles = [
20
16
  css`
@@ -46,7 +46,7 @@ export default function createConversation(
46
46
  building.avatarType === "image" && building.avatarSrc
47
47
  ? avatarSrc
48
48
  : defaultAvatarUrl,
49
- // uncomment this to test changes to the default avatar if your test building has its own avatar
49
+ // uncomment the following line to test changes to the default avatar if your test building has its own avatar
50
50
  // avatarUrl: defaultAvatarUrl,
51
51
  };
52
52
  return conversation;
@@ -12,29 +12,46 @@ import {
12
12
  import groupBy from "lodash/groupBy";
13
13
 
14
14
  const availabilitiesCache: {
15
- [buildingId: number]: TourAvailabilityResponse;
16
- } = {};
15
+ buildingId?: number | null;
16
+ availabilities: { [buildingId: number]: TourAvailabilityResponse };
17
+ } = { buildingId: null, availabilities: {} };
17
18
 
19
+ /**
20
+ * Returns the raw availabilities.
21
+ *
22
+ * If no `buildingId` is provided, it will use a cached buildingId from the previous call.
23
+ * If there is none cached, it will throw an error.
24
+ */
18
25
  export const getRawAvailabilities = async (
19
- buildingId: number
26
+ buildingId?: number
20
27
  ): Promise<TourAvailabilityResponse> => {
21
- if (availabilitiesCache[buildingId]) {
22
- return availabilitiesCache[buildingId];
28
+ if (buildingId) {
29
+ availabilitiesCache.buildingId = buildingId;
30
+ }
31
+ const buildingIdToUse = availabilitiesCache.buildingId;
32
+ if (!buildingIdToUse) {
33
+ throw new Error(
34
+ "No buildingId was provided to getRawAvailabilities and there is no buildingId cached."
35
+ );
36
+ }
37
+ const availabilities = availabilitiesCache.availabilities;
38
+ if (availabilities[buildingIdToUse]) {
39
+ return availabilities[buildingIdToUse];
23
40
  }
24
41
  const startTime = startOfToday();
25
42
  const endTime = formatISO(endOfDay(addDays(startTime, 30)));
26
- const url = `https://app.meetelise.com/api/pub/v1/buildings/${buildingId}/tour/availabilities?startTime=${formatISO(
43
+ const url = `https://app.meetelise.com/api/pub/v1/buildings/${buildingIdToUse}/tour/availabilities?startTime=${formatISO(
27
44
  startTime
28
45
  )}&endTime=${endTime}`;
29
46
  const result = await axios.get<TourAvailabilityResponse>(url);
30
- availabilitiesCache[buildingId] = result.data;
47
+ availabilitiesCache.availabilities[buildingIdToUse] = result.data;
31
48
  // The endpoint INCORRECTLY states that these are returned as dates. They are, in fact, strings.
32
49
  return result.data;
33
50
  };
34
51
 
35
52
  export const getAvailabilitiesForTourType = async (
36
- buildingId: number,
37
- tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum
53
+ tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum,
54
+ buildingId?: number
38
55
  ): Promise<{
39
56
  /**
40
57
  *
@@ -69,12 +86,12 @@ export interface DateWithTimeZoneOffset {
69
86
  }
70
87
 
71
88
  export const getAvailabilitiesGroupedByDay = async (
72
- buildingId: number,
73
- tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum
89
+ tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum,
90
+ buildingId?: number
74
91
  ): Promise<{ [day: string]: DateWithTimeZoneOffset[] }> => {
75
92
  const availabilitiesForTourTypeRaw = await getAvailabilitiesForTourType(
76
- buildingId,
77
- tourType
93
+ tourType,
94
+ buildingId
78
95
  );
79
96
  if (!availabilitiesForTourTypeRaw) {
80
97
  return {};
@@ -97,16 +114,6 @@ export const getAvailabilitiesGroupedByDay = async (
97
114
  );
98
115
  };
99
116
 
100
- // TODO: if cache is empty, observe it and wait for it to be filled in.
101
- // TODO: alternative to this: cache the building id when getRawAvailabilities is called. Then I can call the normal methods.
102
- export const getAvailabilitiesGroupedByDayCached = async (
103
- tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum
104
- ): Promise<{ [day: string]: DateWithTimeZoneOffset[] }> =>
105
- getAvailabilitiesGroupedByDay(
106
- Object.keys(availabilitiesCache).map(Number)[0],
107
- tourType
108
- );
109
-
110
117
  /**
111
118
  * Takes an ISO 8601 date string with time zone offset and returns
112
119
  * an object of our custom type DateWithTimeZoneOffset.