@meetelise/chat 1.12.13 → 1.13.0

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.
@@ -40,6 +40,15 @@ object-assign
40
40
  * SPDX-License-Identifier: BSD-3-Clause
41
41
  */
42
42
 
43
+ /**
44
+ * @license
45
+ * Lodash <https://lodash.com/>
46
+ * Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
47
+ * Released under MIT license <https://lodash.com/license>
48
+ * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
49
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
50
+ */
51
+
43
52
  /** @license React v17.0.2
44
53
  * react.production.min.js
45
54
  *
@@ -1,5 +1,3 @@
1
- // TODO: remove this when sst fixed
2
- /* eslint-disable lit/binding-positions */
3
1
  import classnames from "classnames";
4
2
  import { html, LitElement, TemplateResult } from "lit";
5
3
  import { customElement, property, state } from "lit/decorators.js";
@@ -41,8 +39,6 @@ export class Launcher extends LitElement {
41
39
  @property({ type: Boolean })
42
40
  hasEmailEnabled = true;
43
41
  @property({ type: Boolean })
44
- // TODO: re-enable
45
- // hasSSTEnabled = true;
46
42
  hasSSTEnabled = false;
47
43
  @property({ type: Boolean })
48
44
  hasTextUsEnabled = false;
@@ -70,8 +66,7 @@ export class Launcher extends LitElement {
70
66
  return [
71
67
  this.hasEmailEnabled,
72
68
  !!this.phoneNumber,
73
- // TODO: re-enable
74
- // this.hasSSTEnabled,
69
+ this.hasSSTEnabled,
75
70
  this.hasTextUsEnabled,
76
71
  ].filter((v) => v).length;
77
72
  };
@@ -90,6 +85,9 @@ export class Launcher extends LitElement {
90
85
  );
91
86
  this.hasTextUsEnabled =
92
87
  registeredPhoneNumbers.length > 0 && this.buildingId !== 4895;
88
+ if (this.buildingId === 3660) {
89
+ this.hasSSTEnabled = true;
90
+ }
93
91
  }
94
92
  };
95
93
 
@@ -207,8 +205,7 @@ export class Launcher extends LitElement {
207
205
  ${this.hasEmailEnabled ? this.renderEmailOption() : ""}
208
206
  ${this.phoneNumber ? this.renderCallUsOption() : ""}
209
207
  ${this.hasTextUsEnabled ? this.renderTextUsOption() : ""}
210
- <!-- TODO: re-enable -->
211
- <!-- ${this.hasSSTEnabled ? this.renderSSTOption() : ""} -->
208
+ ${this.hasSSTEnabled ? this.renderSSTOption() : ""}
212
209
  </div>
213
210
  `;
214
211
  };
@@ -326,8 +323,7 @@ export class Launcher extends LitElement {
326
323
  true
327
324
  )
328
325
  : ""}
329
- <!-- TODO: re-enable -->
330
- <!-- ${this.hasSSTEnabled && !this.isCallToActionWindowOpen()
326
+ ${this.hasSSTEnabled && !this.isCallToActionWindowOpen()
331
327
  ? this.renderMiniOption(
332
328
  html`
333
329
  <svg
@@ -346,7 +342,7 @@ export class Launcher extends LitElement {
346
342
  this.onClickSSTOption,
347
343
  true
348
344
  )
349
- : ""} -->
345
+ : ""}
350
346
  ${this.phoneNumber && !this.isCallToActionWindowOpen()
351
347
  ? this.renderMiniOption(
352
348
  html`
@@ -9,7 +9,10 @@ import "./tour-type-option.ts";
9
9
  import "./date-picker.ts";
10
10
  import "./time-picker.ts";
11
11
  import "./me-select.ts";
12
- import { getAvailabilitiesGroupedByDayCached } from "../../getAvailabilities";
12
+ import {
13
+ DateWithTimeZoneOffset,
14
+ getAvailabilitiesGroupedByDayCached,
15
+ } from "../../getAvailabilities";
13
16
  import { TourAvailabilityResponseRankOrderedSupportedTourTypesEnum } from "@meetelise/rest-sdk";
14
17
  import { format } from "date-fns";
15
18
  import { DatePicker } from "./date-picker";
@@ -18,6 +21,7 @@ import { TimePicker } from "./time-picker";
18
21
  import { LabeledOption } from "../../fetchBuildingInfo";
19
22
  import { isMobile } from "../../utils";
20
23
  import axios from "axios";
24
+ import { mapValues } from "lodash";
21
25
 
22
26
  @customElement("tour-scheduler")
23
27
  export class TourScheduler extends LitElement {
@@ -37,11 +41,13 @@ export class TourScheduler extends LitElement {
37
41
  @state()
38
42
  private phoneNumber = "";
39
43
  @state()
40
- private availabilitiesGroupedByDay: { [day: string]: Date[] } = {};
44
+ private availabilitiesGroupedByDay: {
45
+ [day: string]: DateWithTimeZoneOffset[];
46
+ } = {};
41
47
  @state()
42
48
  private selectedDate?: Date;
43
49
  @state()
44
- private selectedTime?: string;
50
+ private selectedTime?: DateWithTimeZoneOffset | null;
45
51
  @state()
46
52
  private mobilePageIndex = 0;
47
53
  @state()
@@ -256,12 +262,6 @@ export class TourScheduler extends LitElement {
256
262
 
257
263
  submit = async (): Promise<void> => {
258
264
  if (!this.selectedDate || !this.selectedTime) return;
259
- const [hours, minutes] = this.timeStringToHoursAndMinutes(
260
- this.selectedTime
261
- );
262
- const tourTime = new Date(this.selectedDate.getTime());
263
- tourTime.setHours(hours, minutes);
264
- const tourTimeString = tourTime.toISOString();
265
265
  const data = {
266
266
  email_address: this.email,
267
267
  phone_number: `+1${this.phoneNumber.match(/\d/g)?.join("")}`, // e.g. +12125555555
@@ -270,7 +270,7 @@ export class TourScheduler extends LitElement {
270
270
  first_name: this.nameInput.value.split(" ")[0],
271
271
  last_name: this.nameInput.value.split(" ").slice(1).join(" "),
272
272
  tour_type: tourTypeForSubmission[this.tourType],
273
- tour_time: tourTimeString,
273
+ tour_time: `${this.selectedTime.datetime}${this.selectedTime.offset}`, // e.g., "2022-06-27T09:00:00-07:00"
274
274
  };
275
275
  const url = `https://app.meetelise.com/platformApi/state/create/scheduleMe`;
276
276
  const response = await axios.post(url, data, {
@@ -705,7 +705,10 @@ export class TourScheduler extends LitElement {
705
705
  <div id="dateAndTimeMenu">
706
706
  <div id="datePicker">
707
707
  <date-picker
708
- .availabilities=${this.availabilitiesGroupedByDay}
708
+ .availabilities=${mapValues(
709
+ this.availabilitiesGroupedByDay,
710
+ (dates) => dates.map((date) => new Date(date.offset))
711
+ )}
709
712
  @change=${(e: Event) => {
710
713
  if (e.target instanceof DatePicker) {
711
714
  this.selectedDate = e.target.selectedDate;
@@ -718,11 +721,21 @@ export class TourScheduler extends LitElement {
718
721
  .options=${this.selectedDate
719
722
  ? this.availabilitiesGroupedByDay[
720
723
  format(this.selectedDate, "y-MM-dd")
721
- ]?.map((date) => format(date, "h:mmaaa"))
724
+ ]?.map((date) => format(new Date(date.datetime), "h:mmaaa"))
722
725
  : []}
723
726
  @change=${(e: Event) => {
724
727
  if (e.target instanceof TimePicker) {
725
- this.selectedTime = e.target.selectedTime;
728
+ if (!this.selectedDate) return;
729
+ const daysAvailabilities =
730
+ this.availabilitiesGroupedByDay[
731
+ format(new Date(this.selectedDate), "y-MM-dd")
732
+ ];
733
+ const index = e.target.selectedTime
734
+ ? daysAvailabilities
735
+ .map((date) => format(new Date(date.datetime), "h:mmaaa"))
736
+ .indexOf(e.target.selectedTime)
737
+ : null;
738
+ this.selectedTime = index ? daysAvailabilities[index] : null; // this.selectedAvailabilityString ?
726
739
  }
727
740
  }}
728
741
  ></time-picker>
@@ -833,12 +846,12 @@ export class TourScheduler extends LitElement {
833
846
  }
834
847
 
835
848
  confirmationMessage(): TemplateResult {
836
- if (!this.selectedDate) return html``;
849
+ if (!this.selectedDate || !this.selectedTime) return html``;
837
850
  // format example: "November 9th, 2022 at 11:00am"
838
851
  const readableDateAndTime = `${format(
839
- this.selectedDate,
840
- "MMMM Lo, y"
841
- )} at ${this.selectedTime}`;
852
+ new Date(this.selectedDate),
853
+ "MMMM do, y"
854
+ )} at ${format(new Date(this.selectedTime.datetime), "h:mmaaa")}`;
842
855
  return html`
843
856
  <div id="confirmationMessage">
844
857
  <svg
@@ -4,10 +4,9 @@ export const InputStyles = css`
4
4
  .webchat-input {
5
5
  border: 1px solid #83818e;
6
6
  outline: none;
7
- font-size: 14px;
8
7
  color: #202020;
9
8
  font-weight: 400;
10
- font-size: 14px;
9
+ font-size: 16px;
11
10
  line-height: 22px;
12
11
  font-family: "Poppins";
13
12
  padding-left: 12px;
@@ -25,6 +24,6 @@ export const InputStyles = css`
25
24
  .webchat-input::placeholder {
26
25
  color: #202020;
27
26
  font-family: "Poppins";
28
- font-size: 14px;
27
+ font-size: 16px;
29
28
  }
30
29
  `;
@@ -103,6 +103,11 @@ export class MEChat extends LitElement {
103
103
  this.chatId = getChatID(this.orgSlug, this.buildingSlug);
104
104
  this.avatarSrc = this.avatarSrc || this.building.avatarSrc;
105
105
  this.theme = getTheme(this.themeId ?? this.building.themeId);
106
+ this.analytics = new Analytics(
107
+ this.orgSlug,
108
+ this.buildingSlug,
109
+ this.chatId
110
+ );
106
111
  };
107
112
 
108
113
  private initializeLaunchJS = async () => {
@@ -153,7 +158,6 @@ export class MEChat extends LitElement {
153
158
  !this.analytics ||
154
159
  !this.analytics.chatId ||
155
160
  !this.building ||
156
- !this.theme ||
157
161
  !this.popup ||
158
162
  !MEChat.session
159
163
  ) {
@@ -2,14 +2,12 @@ import formatISO from "date-fns/formatISO";
2
2
  import startOfToday from "date-fns/startOfToday";
3
3
  import endOfDay from "date-fns/endOfDay";
4
4
  import addDays from "date-fns/addDays";
5
- import parseISO from "date-fns/parseISO";
6
5
  import format from "date-fns/format";
7
6
  import axios from "axios";
8
7
 
9
8
  import {
10
9
  TourAvailabilityResponse,
11
10
  TourAvailabilityResponseRankOrderedSupportedTourTypesEnum,
12
- TourTypeAvailability,
13
11
  } from "@meetelise/rest-sdk";
14
12
  import groupBy from "lodash/groupBy";
15
13
 
@@ -37,13 +35,25 @@ export const getRawAvailabilities = async (
37
35
  export const getAvailabilitiesForTourType = async (
38
36
  buildingId: number,
39
37
  tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum
40
- ): Promise<TourTypeAvailability | null> => {
38
+ ): Promise<{
39
+ /**
40
+ *
41
+ * @type {number}
42
+ * @memberof TourTypeAvailability
43
+ */
44
+ tourDurationInMinutes?: number;
45
+ /**
46
+ *
47
+ * @type {Array<string>}
48
+ * @memberof TourTypeAvailability
49
+ */
50
+ availableTourStartTimes?: Array<string>;
51
+ } | null> => {
41
52
  // The endpoint INCORRECTLY states that these are returned as dates. They are, in fact, strings.
42
53
  const availabilities = await getRawAvailabilities(buildingId);
43
54
  const availableTimes = availabilities.availability?.[tourType]
44
- ? availabilities.availability?.[tourType].availableTourStartTimes?.map(
45
- (startTime) => parseISO(startTime as unknown as string)
46
- )
55
+ ? (availabilities.availability?.[tourType]
56
+ .availableTourStartTimes as unknown as string[])
47
57
  : [];
48
58
  return availabilities.availability
49
59
  ? {
@@ -53,20 +63,37 @@ export const getAvailabilitiesForTourType = async (
53
63
  : null;
54
64
  };
55
65
 
66
+ export interface DateWithTimeZoneOffset {
67
+ datetime: string;
68
+ offset: string;
69
+ }
70
+
56
71
  export const getAvailabilitiesGroupedByDay = async (
57
72
  buildingId: number,
58
73
  tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum
59
- ): Promise<{ [day: string]: Date[] }> => {
60
- const availabilitiesForTourType = await getAvailabilitiesForTourType(
74
+ ): Promise<{ [day: string]: DateWithTimeZoneOffset[] }> => {
75
+ const availabilitiesForTourTypeRaw = await getAvailabilitiesForTourType(
61
76
  buildingId,
62
77
  tourType
63
78
  );
64
- if (!availabilitiesForTourType) {
79
+ if (!availabilitiesForTourTypeRaw) {
65
80
  return {};
66
81
  }
82
+ // Specify offset for each datetime in case availabilities span a daylight savings boundary, when offset will change.
83
+ // Cast availableTourStartTimes to string[], since the Date[] typing of the API is incorrect
84
+ const availabilitiesForTourType: {
85
+ availableTourStartTimes: DateWithTimeZoneOffset[] | undefined;
86
+ } = {
87
+ availableTourStartTimes:
88
+ (availabilitiesForTourTypeRaw.availableTourStartTimes
89
+ ? (availabilitiesForTourTypeRaw.availableTourStartTimes as unknown as string[])
90
+ : []
91
+ )?.map((dateString) => dateStringToDateWithTimeZoneOffset(dateString)),
92
+ };
93
+
67
94
  return groupBy(
68
95
  availabilitiesForTourType.availableTourStartTimes ?? [],
69
- (startTime) => format(startTime, "yyyy-MM-dd")
96
+ (startTime) => format(new Date(startTime.datetime), "yyyy-MM-dd")
70
97
  );
71
98
  };
72
99
 
@@ -74,8 +101,23 @@ export const getAvailabilitiesGroupedByDay = async (
74
101
  // TODO: alternative to this: cache the building id when getRawAvailabilities is called. Then I can call the normal methods.
75
102
  export const getAvailabilitiesGroupedByDayCached = async (
76
103
  tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum
77
- ): Promise<{ [day: string]: Date[] }> =>
104
+ ): Promise<{ [day: string]: DateWithTimeZoneOffset[] }> =>
78
105
  getAvailabilitiesGroupedByDay(
79
106
  Object.keys(availabilitiesCache).map(Number)[0],
80
107
  tourType
81
108
  );
109
+
110
+ /**
111
+ * Takes an ISO 8601 date string with time zone offset and returns
112
+ * an object of our custom type DateWithTimeZoneOffset.
113
+ *
114
+ * @example
115
+ * // returns { datetime: '2022-06-27T09:00:00', offset: '-07:00' }
116
+ * dateStringToDateWithTimeZoneOffset('2022-06-27T09:00:00-07:00')
117
+ * */
118
+ const dateStringToDateWithTimeZoneOffset = (
119
+ dateString: string
120
+ ): DateWithTimeZoneOffset => ({
121
+ datetime: dateString.slice(0, 19),
122
+ offset: dateString.slice(19),
123
+ });