@meetelise/chat 1.21.0 → 1.21.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/.github/pull_request_template.md +61 -0
- package/.idea/codeStyles/Project.xml +57 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/workspace.xml +67 -0
- package/README.md +29 -14
- package/declarations.d.ts +12 -0
- package/package.json +5 -1
- package/public/demo/index.html +62 -4
- package/public/demo/secret.html +63 -0
- package/public/dist/index.js +3184 -1105
- package/public/dist/index.js.LICENSE.txt +19 -9
- package/public/index.html +6 -4
- package/src/MEChat.ts +207 -52
- package/src/MyPubnub.ts +657 -0
- package/src/WebComponent/LeadSourceClient.ts +300 -0
- package/src/WebComponent/Scheduler/date-picker.ts +1 -1
- package/src/WebComponent/Scheduler/time-picker.ts +86 -76
- package/src/WebComponent/Scheduler/tour-scheduler.ts +694 -764
- package/src/WebComponent/Scheduler/tour-type-option.ts +17 -3
- package/src/WebComponent/Scheduler/tourSchedulerStyles.ts +418 -0
- package/src/WebComponent/actions/InputStyles.ts +32 -10
- package/src/WebComponent/actions/action-confirm-button.ts +16 -11
- package/src/WebComponent/actions/call-us-window.ts +341 -58
- package/src/WebComponent/actions/details-window.ts +30 -16
- package/src/WebComponent/actions/email-us-window.ts +89 -58
- package/src/WebComponent/actions/formatPhoneNumber.ts +15 -1
- package/src/WebComponent/actions/minimize-expand-button.ts +92 -0
- package/src/WebComponent/health-chat.ts +267 -0
- package/src/WebComponent/healthcare/healthcare-launcher-styles.ts +34 -0
- package/src/WebComponent/healthcare/healthcare-launcher.ts +100 -0
- package/src/WebComponent/healthchat-styles.ts +119 -0
- package/src/WebComponent/index.ts +1 -1
- package/src/WebComponent/launcher/Launcher.ts +919 -0
- package/src/WebComponent/{launcherStyles.ts → launcher/launcherStyles.ts} +172 -29
- package/src/WebComponent/launcher/mobile-launcher.ts +127 -0
- package/src/WebComponent/launcher/typeEmojiStyles.ts +161 -0
- package/src/WebComponent/launcher/typeMiniStyles.ts +60 -0
- package/src/WebComponent/launcher/typeMobileStyles.ts +50 -0
- package/src/WebComponent/leasing-chat-styles.ts +114 -0
- package/src/WebComponent/me-chat.ts +964 -351
- package/src/WebComponent/me-select.ts +48 -21
- package/src/WebComponent/mini-loader.ts +28 -0
- package/src/WebComponent/pubnub-chat-styles.ts +192 -0
- package/src/WebComponent/pubnub-chat.ts +707 -0
- package/src/WebComponent/pubnub-media.ts +208 -0
- package/src/WebComponent/pubnub-message-styles.ts +54 -0
- package/src/WebComponent/pubnub-message.ts +421 -0
- package/src/analytics.ts +114 -14
- package/src/assetUrls.ts +2 -0
- package/src/disclaimers.ts +56 -0
- package/src/fetchBuildingABTestType.ts +4 -0
- package/src/fetchBuildingInfo.ts +25 -17
- package/src/fetchFeatureFlag.ts +147 -0
- package/src/fetchLeadSources.ts +67 -1
- package/src/fetchPhoneNumberFromSource.ts +31 -0
- package/src/fetchWebchatPreferences.ts +55 -0
- package/src/getAvailabilities.ts +7 -3
- package/src/getBuildingPhoneNumber.ts +26 -0
- package/src/getShouldAllowScheduling.ts +16 -0
- package/src/getTimezoneString.ts +39 -0
- package/src/gtm.ts +17 -0
- package/src/handleChatId.ts +101 -0
- package/src/insertDNIIntoWebsite.ts +136 -0
- package/src/insertLeadSourceIntoSchedulerLinks.ts +50 -0
- package/src/postLeadSources.ts +39 -35
- package/src/svgIcons.ts +62 -53
- package/src/themes.ts +47 -121
- package/src/utils.ts +88 -1
- package/src/WebComponent/Launcher.ts +0 -559
- package/src/WebComponent/actions/text-us-window.ts +0 -279
- package/src/chatID.ts +0 -64
- package/src/createConversation.ts +0 -57
- package/src/fetchCurrentParsedLeadSource.ts +0 -24
- package/src/getRegisteredPhoneNumbers.ts +0 -56
package/src/analytics.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { getCookieValue } from "./utils";
|
|
3
|
+
|
|
1
4
|
declare global {
|
|
2
5
|
interface Window {
|
|
3
6
|
RCTPCampaign?: { CampaignDetails: { Source: string } };
|
|
@@ -9,21 +12,29 @@ export interface CampaignSources {
|
|
|
9
12
|
yardiCampaignSource: string | null;
|
|
10
13
|
realpageCampaignSource: string | null;
|
|
11
14
|
utmCampaignSource: string | null;
|
|
15
|
+
bozzutoAdSource: string | null;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
|
-
* A connection to the MeetElise API to send analytics events
|
|
19
|
+
* A connection to the MeetElise API to send analytics events
|
|
16
20
|
*/
|
|
17
21
|
export default class Analytics {
|
|
18
22
|
private org: string;
|
|
19
23
|
private building: string;
|
|
20
24
|
private featureFlags?: Record<string, boolean>;
|
|
21
25
|
public chatId: string;
|
|
26
|
+
private incomingProcessedLeadSource: string | null;
|
|
22
27
|
|
|
23
|
-
constructor(
|
|
28
|
+
constructor(
|
|
29
|
+
org: string,
|
|
30
|
+
building: string,
|
|
31
|
+
chatId: string,
|
|
32
|
+
incomingProcessedLeadSource: string | null
|
|
33
|
+
) {
|
|
24
34
|
this.org = org;
|
|
25
35
|
this.building = building;
|
|
26
36
|
this.chatId = chatId;
|
|
37
|
+
this.incomingProcessedLeadSource = incomingProcessedLeadSource;
|
|
27
38
|
this.featureFlags = {};
|
|
28
39
|
}
|
|
29
40
|
|
|
@@ -57,6 +68,8 @@ export default class Analytics {
|
|
|
57
68
|
referrer: document.referrer,
|
|
58
69
|
featureFlags: this.featureFlags,
|
|
59
70
|
campaignSources: getCampaignSources(),
|
|
71
|
+
incomingProcessedLeadSource: this.incomingProcessedLeadSource, // if we already know the lead source, send it along
|
|
72
|
+
webchatVersion: process.env.npm_package_version ?? "1.20.155",
|
|
60
73
|
}),
|
|
61
74
|
}
|
|
62
75
|
);
|
|
@@ -64,17 +77,104 @@ export default class Analytics {
|
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
export const getCampaignSources = (): CampaignSources => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
try {
|
|
81
|
+
const queryParams = new URL(window.location.href).searchParams;
|
|
82
|
+
const yardiCampaignSource =
|
|
83
|
+
window.RCTPCampaign?.CampaignDetails?.Source ?? null;
|
|
84
|
+
const entrataCampaignSource = queryParams.get("switch_cls[id]");
|
|
85
|
+
const realpageCampaignSource = queryParams.get("ilm");
|
|
86
|
+
const utmCampaignSource = queryParams.get("utm_source");
|
|
87
|
+
const bozzutoAdSource = getCookieValue("bozzuto_ad_source");
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
entrataCampaignSource,
|
|
91
|
+
realpageCampaignSource,
|
|
92
|
+
utmCampaignSource,
|
|
93
|
+
yardiCampaignSource,
|
|
94
|
+
bozzutoAdSource,
|
|
95
|
+
};
|
|
96
|
+
} catch (e) {
|
|
97
|
+
/**
|
|
98
|
+
* Some clients have pages that overwrite the `URL` class and turn it into a string. This is a workaround for that.
|
|
99
|
+
* Example site: https://ther3.com
|
|
100
|
+
*/
|
|
101
|
+
// eslint-disable-next-line no-console
|
|
102
|
+
console.warn(`Couldnt get campaign sources from url, error: ${e}`);
|
|
103
|
+
const iframe = document.createElement("iframe");
|
|
104
|
+
iframe.style.display = "none";
|
|
105
|
+
document.body.appendChild(iframe);
|
|
106
|
+
if (!iframe.contentWindow) {
|
|
107
|
+
return {
|
|
108
|
+
entrataCampaignSource: null,
|
|
109
|
+
realpageCampaignSource: null,
|
|
110
|
+
utmCampaignSource: null,
|
|
111
|
+
yardiCampaignSource: null,
|
|
112
|
+
bozzutoAdSource: null,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
117
|
+
const OriginalURL = (iframe.contentWindow as any).URL;
|
|
118
|
+
if (!OriginalURL) {
|
|
119
|
+
return {
|
|
120
|
+
entrataCampaignSource: null,
|
|
121
|
+
realpageCampaignSource: null,
|
|
122
|
+
utmCampaignSource: null,
|
|
123
|
+
yardiCampaignSource: null,
|
|
124
|
+
bozzutoAdSource: null,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const queryParams = new OriginalURL(window.location.href).searchParams;
|
|
128
|
+
const yardiCampaignSource =
|
|
129
|
+
window.RCTPCampaign?.CampaignDetails?.Source ?? null;
|
|
130
|
+
const entrataCampaignSource = queryParams.get("switch_cls[id]");
|
|
131
|
+
const realpageCampaignSource = queryParams.get("ilm");
|
|
132
|
+
const utmCampaignSource = queryParams.get("utm_source");
|
|
133
|
+
const bozzutoAdSource = getCookieValue("bozzuto_ad_source");
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
entrataCampaignSource,
|
|
137
|
+
realpageCampaignSource,
|
|
138
|
+
utmCampaignSource,
|
|
139
|
+
yardiCampaignSource,
|
|
140
|
+
bozzutoAdSource,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export enum LogType {
|
|
146
|
+
error = "error",
|
|
147
|
+
info = "info",
|
|
148
|
+
warn = "warn",
|
|
149
|
+
}
|
|
73
150
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
151
|
+
export const sendLoggingEvent = async ({
|
|
152
|
+
logType,
|
|
153
|
+
buildingSlug,
|
|
154
|
+
orgSlug,
|
|
155
|
+
logTitle,
|
|
156
|
+
logData,
|
|
157
|
+
}: {
|
|
158
|
+
logType?: LogType;
|
|
159
|
+
buildingSlug?: string;
|
|
160
|
+
orgSlug?: string;
|
|
161
|
+
logTitle: string;
|
|
162
|
+
logData: Record<string, unknown> | null;
|
|
163
|
+
}): Promise<void> => {
|
|
164
|
+
const host = "https://app.meetelise.com";
|
|
165
|
+
await axios.post(
|
|
166
|
+
`${host}/platformApi/webchat/logging`,
|
|
167
|
+
{
|
|
168
|
+
...logData,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
headers: {
|
|
172
|
+
"Content-Type": "application/json",
|
|
173
|
+
"building-slug": buildingSlug ?? "",
|
|
174
|
+
"org-slug": orgSlug ?? "",
|
|
175
|
+
"log-type": logType ?? LogType.error,
|
|
176
|
+
"log-title": logTitle.toUpperCase().replace(/\s/g, "_"),
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
);
|
|
80
180
|
};
|
package/src/assetUrls.ts
CHANGED
|
@@ -2,3 +2,5 @@ export const glowBarMp4 =
|
|
|
2
2
|
"https://eliseusercontent.meetelise.com/webchat/HorizontalBar-Shadow.mp4";
|
|
3
3
|
export const glowBarWebm =
|
|
4
4
|
"https://eliseusercontent.meetelise.com/webchat/HorizontalBar-Shadow.webm";
|
|
5
|
+
export const glowBackgroundMp4 =
|
|
6
|
+
"https://eliseusercontent.meetelise.com/webchat/01-EliseAI-Hero_1.mp4";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { html, TemplateResult } from "lit";
|
|
2
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
3
|
+
|
|
4
|
+
const disclaimerStyles = {
|
|
5
|
+
container: {
|
|
6
|
+
fontSize: "10px",
|
|
7
|
+
paddingTop: "4px",
|
|
8
|
+
},
|
|
9
|
+
link: {
|
|
10
|
+
color: "#4287f5",
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const disclaimer = ({
|
|
15
|
+
buildingName,
|
|
16
|
+
phoneNumberInput,
|
|
17
|
+
emailInput,
|
|
18
|
+
}: {
|
|
19
|
+
buildingName: string;
|
|
20
|
+
phoneNumberInput?: string;
|
|
21
|
+
emailInput?: string;
|
|
22
|
+
}): TemplateResult => {
|
|
23
|
+
if (phoneNumberInput) {
|
|
24
|
+
return html` <div style=${styleMap(disclaimerStyles.container)}>
|
|
25
|
+
By providing your number and clicking submit, you consent to recurring
|
|
26
|
+
marketing text messages and calls from or for
|
|
27
|
+
${buildingName.length > 0 ? buildingName : "this building"} at this
|
|
28
|
+
number, which may be sent by an autodialer system. Replies may be AI or
|
|
29
|
+
human generated. This consent is not required to lease at this property.
|
|
30
|
+
Msg & Data rates may apply. You consent to this
|
|
31
|
+
<a
|
|
32
|
+
style=${styleMap(disclaimerStyles.link)}
|
|
33
|
+
href="http://bit.ly/me_privacy_policy"
|
|
34
|
+
target="_blank"
|
|
35
|
+
rel="noopener noreferrer"
|
|
36
|
+
>privacy policy</a
|
|
37
|
+
>, including having your communications recorded by a third party.
|
|
38
|
+
</div>`;
|
|
39
|
+
}
|
|
40
|
+
if (emailInput) {
|
|
41
|
+
return html` <div style=${styleMap(disclaimerStyles.container)}>
|
|
42
|
+
By entering your email and clicking submit, you consent to this
|
|
43
|
+
<a
|
|
44
|
+
style=${styleMap(disclaimerStyles.link)}
|
|
45
|
+
href="http://bit.ly/me_privacy_policy"
|
|
46
|
+
target="_blank"
|
|
47
|
+
rel="noopener noreferrer"
|
|
48
|
+
>privacy policy</a
|
|
49
|
+
>, including having your email address and communications collected and
|
|
50
|
+
recorded by a third party.
|
|
51
|
+
</div>`;
|
|
52
|
+
}
|
|
53
|
+
return html``;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default disclaimer;
|
|
@@ -2,6 +2,9 @@ import axios from "axios";
|
|
|
2
2
|
interface BuildingABType {
|
|
3
3
|
abTestType: string;
|
|
4
4
|
}
|
|
5
|
+
export enum abTestTypes {
|
|
6
|
+
"ConceptEmoji" = "Concept 2 (Blue/White)",
|
|
7
|
+
}
|
|
5
8
|
export default async function fetchBuildingABTestType(
|
|
6
9
|
buildingSlug: string
|
|
7
10
|
): Promise<BuildingABType | null> {
|
|
@@ -10,6 +13,7 @@ export default async function fetchBuildingABTestType(
|
|
|
10
13
|
const abTestTypeResponse = await axios.get(
|
|
11
14
|
`${host}/platformApi/webchat/${buildingSlug}/ab-test-type`
|
|
12
15
|
);
|
|
16
|
+
//return { abTestType: "Concept 2 (Blue/White)" };
|
|
13
17
|
return abTestTypeResponse.data;
|
|
14
18
|
} catch (_) {
|
|
15
19
|
return null;
|
package/src/fetchBuildingInfo.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
1
3
|
export interface LabeledOption {
|
|
2
4
|
label: string;
|
|
3
5
|
value: string | number;
|
|
@@ -24,17 +26,21 @@ export interface Building {
|
|
|
24
26
|
tourTypeOptions: LabeledOption[];
|
|
25
27
|
chatWidgets?: string[] | null;
|
|
26
28
|
chatCallUsHeader?: string;
|
|
27
|
-
unitOptionsV2: UnitV2[];
|
|
28
|
-
layoutOptionsV2: string[];
|
|
29
29
|
autoOpenChatWidget: boolean;
|
|
30
30
|
sgtUrl: string;
|
|
31
31
|
escortedToursLink: string;
|
|
32
32
|
virtualToursLink: string;
|
|
33
|
+
active: number;
|
|
34
|
+
usesDynamicScheduling: boolean;
|
|
35
|
+
textWithUsPhoneNumber: string | null;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
export type UnitV2 = {
|
|
36
39
|
name: string;
|
|
37
40
|
layout: string;
|
|
41
|
+
availabilityStage: number;
|
|
42
|
+
active: number;
|
|
43
|
+
occupied: boolean;
|
|
38
44
|
};
|
|
39
45
|
|
|
40
46
|
/**
|
|
@@ -49,22 +55,24 @@ export default async function fetchBuildingInfo(
|
|
|
49
55
|
buildingSlug: string
|
|
50
56
|
): Promise<Building> {
|
|
51
57
|
const host = "https://app.meetelise.com";
|
|
52
|
-
const
|
|
53
|
-
const response = await fetch(url);
|
|
54
|
-
const building: Building = await response.json();
|
|
58
|
+
const buildingUrl = `${host}/api/pub/v1/organization/${orgSlug}/building/${buildingSlug}`;
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
// We will fetch these units/layouts from elise-crm-api and supplement the DTO
|
|
58
|
-
const unitsResponse = await fetch(
|
|
59
|
-
`${host}/eliseCrmApi/pub/building/${buildingSlug}/units`
|
|
60
|
-
);
|
|
61
|
-
const units: UnitV2[] = await unitsResponse.json();
|
|
62
|
-
const layoutsResponse = await fetch(
|
|
63
|
-
`${host}/eliseCrmApi/pub/building/${buildingSlug}/layouts`
|
|
64
|
-
);
|
|
65
|
-
const layouts: string[] = await layoutsResponse.json();
|
|
60
|
+
const buildingResponse = await axios.get(buildingUrl);
|
|
66
61
|
|
|
67
|
-
building
|
|
68
|
-
|
|
62
|
+
const building: Building = buildingResponse.data;
|
|
63
|
+
try {
|
|
64
|
+
building.welcomeMessage = replaceName(
|
|
65
|
+
building.welcomeMessage ?? "",
|
|
66
|
+
building.userFirstName
|
|
67
|
+
);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
// eslint-disable-next-line no-console
|
|
70
|
+
console.error(e);
|
|
71
|
+
}
|
|
69
72
|
return building;
|
|
70
73
|
}
|
|
74
|
+
|
|
75
|
+
const replaceName = (originalText: string, newName: string) => {
|
|
76
|
+
const regex = /elise/gi;
|
|
77
|
+
return originalText.replace(regex, newName);
|
|
78
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
// V1 reads from the launch darkly flag
|
|
4
|
+
const featureFlagEndpointV1 = (buildingSlug: string): string => {
|
|
5
|
+
const host = "https://app.meetelise.com";
|
|
6
|
+
return `${host}/platformApi/webchat/${buildingSlug}/chat-ui-feature-flag`;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// V2 reads from conversation flag table (preferred)
|
|
10
|
+
const featureFlagEndpointV2 = (buildingSlug: string): string => {
|
|
11
|
+
const host = "https://app.meetelise.com";
|
|
12
|
+
return `${host}/platformApi/webchat/${buildingSlug}/v2/chat-ui-feature-flag`;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export enum FeatureFlagsShowDropdown {
|
|
16
|
+
onAttributionFailure = "on-attribution-failure",
|
|
17
|
+
never = "never", // note, the dropdown will NOT show up if there are also no lead sources (list)!
|
|
18
|
+
always = "always",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function fetchFeatureFlagShowMarketingSourceDropdown(
|
|
22
|
+
buildingSlug: string
|
|
23
|
+
): Promise<FeatureFlagsShowDropdown> {
|
|
24
|
+
if (!buildingSlug) {
|
|
25
|
+
return FeatureFlagsShowDropdown.always;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const featureFlagResponse = await axios.get(
|
|
29
|
+
featureFlagEndpointV1(buildingSlug),
|
|
30
|
+
{
|
|
31
|
+
params: {
|
|
32
|
+
building_slug: buildingSlug,
|
|
33
|
+
flag_type: "string",
|
|
34
|
+
feature_flag: "webchat-marketing-source-dropdown-configuration",
|
|
35
|
+
default_str: "always",
|
|
36
|
+
default_bool: true,
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
if (featureFlagResponse.data === "on-attribution-failure") {
|
|
41
|
+
return FeatureFlagsShowDropdown.onAttributionFailure;
|
|
42
|
+
}
|
|
43
|
+
if (featureFlagResponse.data === "never") {
|
|
44
|
+
return FeatureFlagsShowDropdown.never;
|
|
45
|
+
}
|
|
46
|
+
return FeatureFlagsShowDropdown.always;
|
|
47
|
+
} catch (_) {
|
|
48
|
+
return FeatureFlagsShowDropdown.always;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function fetchFeatureFlagUsePhoneNumberBySource(
|
|
53
|
+
buildingSlug: string
|
|
54
|
+
): Promise<boolean> {
|
|
55
|
+
if (!buildingSlug) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const featureFlagResponse = await axios.get(
|
|
60
|
+
featureFlagEndpointV1(buildingSlug),
|
|
61
|
+
{
|
|
62
|
+
params: {
|
|
63
|
+
building_slug: buildingSlug,
|
|
64
|
+
flag_type: "bool",
|
|
65
|
+
feature_flag: "webchat-use-dni-phone-number-by-source",
|
|
66
|
+
default_str: null,
|
|
67
|
+
default_bool: false,
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
return featureFlagResponse.data;
|
|
72
|
+
} catch (_) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function fetchFeatureFlagUseOverrideContactUsForm(
|
|
78
|
+
buildingSlug: string
|
|
79
|
+
): Promise<boolean> {
|
|
80
|
+
if (!buildingSlug) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const featureFlagResponse = await axios.get(
|
|
85
|
+
featureFlagEndpointV1(buildingSlug),
|
|
86
|
+
{
|
|
87
|
+
params: {
|
|
88
|
+
building_slug: buildingSlug,
|
|
89
|
+
flag_type: "bool",
|
|
90
|
+
feature_flag: "webchat-use-override-contact-us-form",
|
|
91
|
+
default_str: null,
|
|
92
|
+
default_bool: false,
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
return featureFlagResponse.data;
|
|
97
|
+
} catch (_) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export async function fetchFeatureFlagUsePubnub(
|
|
102
|
+
buildingSlug?: string
|
|
103
|
+
): Promise<boolean> {
|
|
104
|
+
if (!buildingSlug) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const featureFlagResponse = await axios.get(
|
|
109
|
+
featureFlagEndpointV1(buildingSlug),
|
|
110
|
+
{
|
|
111
|
+
params: {
|
|
112
|
+
building_slug: buildingSlug,
|
|
113
|
+
flag_type: "bool",
|
|
114
|
+
feature_flag: "use-pubnub-chat-provider",
|
|
115
|
+
default_str: null,
|
|
116
|
+
default_bool: false,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
return featureFlagResponse.data;
|
|
121
|
+
} catch (_) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function fetchFeatureFlagInsertDNIWebsite(
|
|
127
|
+
buildingSlug?: string
|
|
128
|
+
): Promise<boolean> {
|
|
129
|
+
if (!buildingSlug) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const featureFlagResponse = await axios.get(
|
|
134
|
+
featureFlagEndpointV2(buildingSlug),
|
|
135
|
+
{
|
|
136
|
+
params: {
|
|
137
|
+
building_slug: buildingSlug,
|
|
138
|
+
feature_flag_name: "insert_dni_into_website",
|
|
139
|
+
default_value: false,
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
return featureFlagResponse.data;
|
|
144
|
+
} catch (_) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
package/src/fetchLeadSources.ts
CHANGED
|
@@ -10,8 +10,74 @@ export default async function fetchLeadSources(
|
|
|
10
10
|
const leadSourcesResponse = await axios.get(
|
|
11
11
|
`${host}/platformApi/webchat/${buildingSlug}/lead-sources`
|
|
12
12
|
);
|
|
13
|
-
|
|
13
|
+
if (leadSourcesResponse.data) {
|
|
14
|
+
return filterOutBuildingSpecificLeadSources(
|
|
15
|
+
leadSourcesResponse.data,
|
|
16
|
+
buildingSlug
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return [];
|
|
14
20
|
} catch (_) {
|
|
15
21
|
return [];
|
|
16
22
|
}
|
|
17
23
|
}
|
|
24
|
+
|
|
25
|
+
const filterOutBuildingSpecificLeadSources = (
|
|
26
|
+
leadSources: string[],
|
|
27
|
+
buildingSlug: string
|
|
28
|
+
): string[] => {
|
|
29
|
+
// Dinerstien asked to filter out some sources based on their buildings
|
|
30
|
+
let removeLeadSources: string[] = [];
|
|
31
|
+
switch (buildingSlug) {
|
|
32
|
+
case "d41619ec-0f79-11ee-8439-93855926ad58": // Infinity Lofts in the Gulch
|
|
33
|
+
removeLeadSources = [
|
|
34
|
+
"Forthea - PPC",
|
|
35
|
+
"HotPads",
|
|
36
|
+
"Forthea",
|
|
37
|
+
"Locator",
|
|
38
|
+
"Website-apartmentguide.com",
|
|
39
|
+
"Forthea - organic search",
|
|
40
|
+
"Tdc employee",
|
|
41
|
+
"University newspaper",
|
|
42
|
+
"Nashville apartment locators",
|
|
43
|
+
"Forthea - paid social",
|
|
44
|
+
"Apartment list",
|
|
45
|
+
"Off campus partners",
|
|
46
|
+
"Waze ads",
|
|
47
|
+
"Athletic event",
|
|
48
|
+
"Website-apartment guide",
|
|
49
|
+
];
|
|
50
|
+
break;
|
|
51
|
+
case "d4160560-0f79-11ee-8438-fba837bf3a46": // Sawyer on Lincoln
|
|
52
|
+
removeLeadSources = [
|
|
53
|
+
"Forthea - PPC",
|
|
54
|
+
"Rentgrata",
|
|
55
|
+
"Website-ApartmentFinder.com",
|
|
56
|
+
"Word of Mouth",
|
|
57
|
+
"Texas A&M A-Frame on Campus",
|
|
58
|
+
"Rent.com",
|
|
59
|
+
"Website-craigslist",
|
|
60
|
+
"Apartment List",
|
|
61
|
+
"Website-apartments.com",
|
|
62
|
+
"Forthea - paid social",
|
|
63
|
+
"Forthea - organic search",
|
|
64
|
+
"Website-apartmentguide.com",
|
|
65
|
+
"Forthea",
|
|
66
|
+
"Uhomes",
|
|
67
|
+
"Phone book",
|
|
68
|
+
"Property - website",
|
|
69
|
+
];
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
removeLeadSources = [];
|
|
73
|
+
}
|
|
74
|
+
leadSources.sort();
|
|
75
|
+
return leadSources.filter((leadSource) => {
|
|
76
|
+
const lowerCaseLeadSource = leadSource.toLowerCase();
|
|
77
|
+
const lowerCaseRemoveLeadSources = removeLeadSources.map((source) =>
|
|
78
|
+
source.toLowerCase()
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return !lowerCaseRemoveLeadSources.includes(lowerCaseLeadSource);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { formatPhoneNumber } from "./WebComponent/actions/formatPhoneNumber";
|
|
3
|
+
|
|
4
|
+
export interface NumberForSelectedSource {
|
|
5
|
+
number: string;
|
|
6
|
+
isMatch: boolean;
|
|
7
|
+
isPropertyWebsiteCatchall: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default async function fetchPhoneNumberFromSource(
|
|
11
|
+
buildingSlug: string,
|
|
12
|
+
source: string | null
|
|
13
|
+
): Promise<NumberForSelectedSource | null> {
|
|
14
|
+
const host = "https://app.meetelise.com";
|
|
15
|
+
try {
|
|
16
|
+
const phoneNumberResponse = await axios.get(
|
|
17
|
+
`${host}/platformApi/webchat/${buildingSlug}/phone-number-by-source?source=${source}`
|
|
18
|
+
);
|
|
19
|
+
if (phoneNumberResponse.data) {
|
|
20
|
+
return {
|
|
21
|
+
number: formatPhoneNumber(phoneNumberResponse.data.number),
|
|
22
|
+
isMatch: phoneNumberResponse.data.match,
|
|
23
|
+
isPropertyWebsiteCatchall:
|
|
24
|
+
phoneNumberResponse.data.isPropertyWebsiteCatchall,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
} catch (_) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { camelize } from "./utils";
|
|
3
|
+
|
|
4
|
+
export interface WebchatSettings {
|
|
5
|
+
config: WebchatConfigurationPreferences;
|
|
6
|
+
isInheriting: boolean;
|
|
7
|
+
isGlobalDefault: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface WebchatConfigurationPreferences {
|
|
10
|
+
autoOpenChat: boolean;
|
|
11
|
+
shouldShowChat: boolean;
|
|
12
|
+
shouldShowChatDesktop: boolean;
|
|
13
|
+
shouldShowChatMobile: boolean;
|
|
14
|
+
shouldShowEmail: boolean;
|
|
15
|
+
shouldShowEmailDesktop: boolean;
|
|
16
|
+
shouldShowEmailMobile: boolean;
|
|
17
|
+
shouldShowPhone: boolean;
|
|
18
|
+
shouldShowPhoneDesktop: boolean;
|
|
19
|
+
shouldShowPhoneMobile: boolean;
|
|
20
|
+
shouldShowText: boolean;
|
|
21
|
+
shouldShowTextDesktop: boolean;
|
|
22
|
+
shouldShowTextMobile: boolean;
|
|
23
|
+
shouldShowSst: boolean;
|
|
24
|
+
shouldShowSstDesktop: boolean;
|
|
25
|
+
shouldShowSstMobile: boolean;
|
|
26
|
+
displayStyle: DesignConcepts;
|
|
27
|
+
primaryColor: string;
|
|
28
|
+
backgroundColor: string;
|
|
29
|
+
requiresConsent: boolean;
|
|
30
|
+
privacyPolicyUrl: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export enum DesignConcepts {
|
|
34
|
+
EMOJI = "emoji",
|
|
35
|
+
PILLS = "pills",
|
|
36
|
+
MINIMIZED = "minimized", // this is also mobile
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default async function fetchWebchatPreferences(
|
|
40
|
+
buildingId: number
|
|
41
|
+
): Promise<WebchatSettings | null> {
|
|
42
|
+
const host = "https://app.meetelise.com";
|
|
43
|
+
try {
|
|
44
|
+
const webchatPreferencesResponse = await axios.get(
|
|
45
|
+
`${host}/eliseCrmApi/webchat/config/building/${buildingId}`
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (webchatPreferencesResponse.data) {
|
|
49
|
+
return camelize(webchatPreferencesResponse.data);
|
|
50
|
+
}
|
|
51
|
+
} catch (_) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
package/src/getAvailabilities.ts
CHANGED
|
@@ -44,9 +44,13 @@ export const getRawAvailabilities = async (
|
|
|
44
44
|
}
|
|
45
45
|
const startTime = startOfToday();
|
|
46
46
|
const endTime = formatISO(endOfDay(addDays(startTime, 30)));
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
|
|
48
|
+
// We MUST encode the URI components because a positive timezone offset counts as a space in urls!
|
|
49
|
+
const timeParams = `startTime=${encodeURIComponent(
|
|
50
|
+
formatISO(startTime)
|
|
51
|
+
)}&endTime=${encodeURIComponent(endTime)}`;
|
|
52
|
+
const url = `https://app.meetelise.com/api/pub/v1/buildings/${buildingIdToUse}/tour/availabilities?${timeParams}`;
|
|
53
|
+
|
|
50
54
|
const result = await axios.get<TourAvailabilityResponse>(url);
|
|
51
55
|
availabilitiesCache.availabilities[buildingIdToUse] = result.data;
|
|
52
56
|
// The endpoint INCORRECTLY states that these are returned as dates. They are, in fact, strings.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { LogType, sendLoggingEvent } from "./analytics";
|
|
3
|
+
|
|
4
|
+
export const getBuildingPhoneNumber = async (
|
|
5
|
+
buildingSlug: string
|
|
6
|
+
): Promise<string | null> => {
|
|
7
|
+
try {
|
|
8
|
+
const host = "https://app.meetelise.com";
|
|
9
|
+
const webchatPreferencesResponse = await axios.get(
|
|
10
|
+
`${host}/platformApi/webchat/${buildingSlug}/phone-number`
|
|
11
|
+
);
|
|
12
|
+
if (webchatPreferencesResponse.data) {
|
|
13
|
+
return webchatPreferencesResponse.data;
|
|
14
|
+
}
|
|
15
|
+
} catch (error) {
|
|
16
|
+
sendLoggingEvent({
|
|
17
|
+
logType: LogType.error,
|
|
18
|
+
buildingSlug,
|
|
19
|
+
logTitle: "[ERROR_GETTING_PHONE_NUMBERS]",
|
|
20
|
+
logData: { error },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
export const getShouldAllowScheduling = async (
|
|
4
|
+
buildingId: number,
|
|
5
|
+
isDynamicSchedulingEnabled = false
|
|
6
|
+
): Promise<boolean> => {
|
|
7
|
+
const host = "https://app.meetelise.com";
|
|
8
|
+
const url = `${host}/eliseCrmApi/building/${buildingId}/scheduling_status?observe_dynamic_scheduling=${isDynamicSchedulingEnabled}`;
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const response = await axios.get(url);
|
|
12
|
+
return response.data.is_enabled;
|
|
13
|
+
} catch (e) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
};
|