@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.
- package/package.json +1 -1
- package/public/dist/index.js +58 -61
- package/public/dist/index.js.LICENSE.txt +9 -0
- package/src/WebComponent/Launcher.ts +7 -11
- package/src/WebComponent/Scheduler/tour-scheduler.ts +30 -17
- package/src/WebComponent/actions/InputStyles.ts +2 -3
- package/src/WebComponent/me-chat.ts +5 -1
- package/src/getAvailabilities.ts +53 -11
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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: {
|
|
44
|
+
private availabilitiesGroupedByDay: {
|
|
45
|
+
[day: string]: DateWithTimeZoneOffset[];
|
|
46
|
+
} = {};
|
|
41
47
|
@state()
|
|
42
48
|
private selectedDate?: Date;
|
|
43
49
|
@state()
|
|
44
|
-
private selectedTime?:
|
|
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:
|
|
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=${
|
|
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.
|
|
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
|
|
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:
|
|
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:
|
|
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
|
) {
|
package/src/getAvailabilities.ts
CHANGED
|
@@ -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<
|
|
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]
|
|
45
|
-
|
|
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]:
|
|
60
|
-
const
|
|
74
|
+
): Promise<{ [day: string]: DateWithTimeZoneOffset[] }> => {
|
|
75
|
+
const availabilitiesForTourTypeRaw = await getAvailabilitiesForTourType(
|
|
61
76
|
buildingId,
|
|
62
77
|
tourType
|
|
63
78
|
);
|
|
64
|
-
if (!
|
|
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]:
|
|
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
|
+
});
|