@meetelise/chat 1.15.0 → 1.15.1

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.
@@ -50,7 +50,7 @@ export class Launcher extends LitElement {
50
50
  onChatTapped: () => void = () => {
51
51
  return;
52
52
  };
53
- @property({ attribute: false })
53
+ @property()
54
54
  launcherStyles: StyleInfo = {};
55
55
 
56
56
  @state()
@@ -85,13 +85,7 @@ export class Launcher extends LitElement {
85
85
  );
86
86
  this.hasTextUsEnabled =
87
87
  registeredPhoneNumbers.length > 0 && this.buildingId !== 4895;
88
- // TODO: replace this with a real API call once the endpoint exists
89
- const schedulingIsEnabled = await (async function putApiCallHere(
90
- buildingId: number
91
- ) {
92
- return !!buildingId;
93
- })(this.buildingId);
94
- if (this.buildingId === 3660 && schedulingIsEnabled) {
88
+ if (this.buildingId === 3660) {
95
89
  this.hasSSTEnabled = true;
96
90
  }
97
91
  }
@@ -11,8 +11,7 @@ import "./time-picker.ts";
11
11
  import "./me-select.ts";
12
12
  import {
13
13
  DateWithTimeZoneOffset,
14
- getAvailabilitiesGroupedByDay,
15
- getExistenceOfAvailabilitiesByTourType,
14
+ getAvailabilitiesGroupedByDayCached,
16
15
  } from "../../getAvailabilities";
17
16
  import { TourAvailabilityResponseRankOrderedSupportedTourTypesEnum } from "@meetelise/rest-sdk";
18
17
  import { format } from "date-fns";
@@ -23,7 +22,6 @@ import { LabeledOption } from "../../fetchBuildingInfo";
23
22
  import { isMobile } from "../../utils";
24
23
  import axios from "axios";
25
24
  import { mapValues } from "lodash";
26
- import classnames from "classnames";
27
25
 
28
26
  @customElement("tour-scheduler")
29
27
  export class TourScheduler extends LitElement {
@@ -39,12 +37,6 @@ export class TourScheduler extends LitElement {
39
37
  @state()
40
38
  private tourType = TourType.Guided;
41
39
  @state()
42
- private shouldShowTourType = {
43
- [TourType.Guided]: true,
44
- [TourType.Self]: true,
45
- [TourType.Virtual]: true,
46
- };
47
- @state()
48
40
  private email = "";
49
41
  @state()
50
42
  private phoneNumber = "";
@@ -53,8 +45,6 @@ export class TourScheduler extends LitElement {
53
45
  [day: string]: DateWithTimeZoneOffset[];
54
46
  } = {};
55
47
  @state()
56
- private waitingForAvailabilities = true;
57
- @state()
58
48
  private selectedDate?: Date;
59
49
  @state()
60
50
  private selectedTime?: DateWithTimeZoneOffset | null;
@@ -63,38 +53,19 @@ export class TourScheduler extends LitElement {
63
53
  @state()
64
54
  private tourIsBooked = false;
65
55
 
66
- @query(".inputContainer#firstName input")
67
- firstNameInput!: HTMLInputElement;
68
- @query(".inputContainer#lastName input")
69
- lastNameInput!: HTMLInputElement;
70
- @query(".inputContainer#email input")
56
+ @query("input#name")
57
+ nameInput!: HTMLInputElement;
58
+ @query("input#email")
71
59
  emailInput!: HTMLInputElement;
72
- @query(".inputContainer#phone input")
60
+ @query("input#phone")
73
61
  phoneInput!: HTMLInputElement;
74
62
  @query("me-select#unitType")
75
63
  unitTypeSelect!: MESelect;
76
64
 
77
65
  firstUpdated = async (): Promise<void> => {
78
- this.availabilitiesGroupedByDay = await getAvailabilitiesGroupedByDay(
66
+ this.availabilitiesGroupedByDay = await getAvailabilitiesGroupedByDayCached(
79
67
  tourTypeMap[this.tourType]
80
68
  );
81
- this.waitingForAvailabilities = false;
82
-
83
- // Show a tour type only if it is supported by the building and has
84
- // time slots available.
85
- const availabilitiesExistForTourType =
86
- await getExistenceOfAvailabilitiesByTourType();
87
- this.shouldShowTourType = {
88
- [TourType.Guided]:
89
- this.tourTypeOptions.map((o) => o.value).includes("WITH_AGENT") &&
90
- availabilitiesExistForTourType[TourType.Guided],
91
- [TourType.Self]:
92
- this.tourTypeOptions.map((o) => o.value).includes("SELF_GUIDED") &&
93
- availabilitiesExistForTourType[TourType.Self],
94
- [TourType.Virtual]:
95
- this.tourTypeOptions.map((o) => o.value).includes("VIRTUAL_SHOWING") &&
96
- availabilitiesExistForTourType[TourType.Virtual],
97
- };
98
69
  };
99
70
 
100
71
  protected willUpdate = async (
@@ -103,9 +74,8 @@ export class TourScheduler extends LitElement {
103
74
  | Map<PropertyKey, unknown>
104
75
  ): Promise<void> => {
105
76
  if (_changedProperties.has("tourType")) {
106
- this.availabilitiesGroupedByDay = await getAvailabilitiesGroupedByDay(
107
- tourTypeMap[this.tourType]
108
- );
77
+ this.availabilitiesGroupedByDay =
78
+ await getAvailabilitiesGroupedByDayCached(tourTypeMap[this.tourType]);
109
79
  }
110
80
  };
111
81
 
@@ -265,7 +235,7 @@ export class TourScheduler extends LitElement {
265
235
  dateAndTime: (): boolean => !!this.selectedDate && !!this.selectedTime,
266
236
  leadInfo: (): boolean => {
267
237
  return (
268
- (!!this.firstNameInput?.value || !!this.lastNameInput?.value) &&
238
+ !!this.nameInput?.value &&
269
239
  this.emailInput?.value.includes("@") &&
270
240
  // TODO: deleting phone number doesn't cause validation to fail, at least on mobile
271
241
  !!this.phoneNumber &&
@@ -302,8 +272,9 @@ export class TourScheduler extends LitElement {
302
272
  email_address: this.email,
303
273
  phone_number: `+1${this.phoneNumber.match(/\d/g)?.join("")}`, // e.g. +12125555555
304
274
  building_id: this.buildingId,
305
- first_name: this.firstNameInput.value,
306
- last_name: this.lastNameInput.value,
275
+ // 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
+ first_name: this.nameInput.value.split(" ")[0],
277
+ last_name: this.nameInput.value.split(" ").slice(1).join(" "),
307
278
  tour_type: tourTypeForSubmission[this.tourType],
308
279
  tour_time: `${this.selectedTime.datetime}${this.selectedTime.offset}`, // e.g., "2022-06-27T09:00:00-07:00"
309
280
  };
@@ -431,7 +402,7 @@ export class TourScheduler extends LitElement {
431
402
  gap: 12px;
432
403
  }
433
404
 
434
- #yourInformationMenu input {
405
+ #yourInformationMenu > input {
435
406
  width: 305px;
436
407
  height: 49px;
437
408
  border: 1px solid #83818e;
@@ -443,7 +414,7 @@ export class TourScheduler extends LitElement {
443
414
  color: #202020;
444
415
  }
445
416
 
446
- #yourInformationMenu input::placeholder {
417
+ #yourInformationMenu > input::placeholder {
447
418
  color: #202020;
448
419
  }
449
420
 
@@ -503,15 +474,6 @@ export class TourScheduler extends LitElement {
503
474
  box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.5);
504
475
  }
505
476
 
506
- button#schedule:not(:disabled):hover {
507
- opacity: 0.7;
508
- }
509
-
510
- button#schedule:not(:disabled):active {
511
- box-shadow: none;
512
- opacity: 1;
513
- }
514
-
515
477
  button#schedule:disabled {
516
478
  background: #e7e7e7;
517
479
  box-shadow: none;
@@ -526,81 +488,6 @@ export class TourScheduler extends LitElement {
526
488
  font-size: 18px;
527
489
  }
528
490
 
529
- /* Loading styles: pulsing gray overlay on all the form elements */
530
-
531
- @keyframes spin {
532
- 0% {
533
- transform: none;
534
- }
535
- 50% {
536
- transform: rotateZ(180deg);
537
- }
538
- 100% {
539
- transform: rotateZ(360deg);
540
- }
541
- }
542
-
543
- svg#loadingIcon {
544
- animation: spin 2s infinite linear;
545
- }
546
-
547
- .tour-scheduler.loading #scheduleATour {
548
- display: flex;
549
- gap: 10px;
550
- }
551
-
552
- @keyframes loadingPulse {
553
- 0% {
554
- background-color: #e7e7e7;
555
- }
556
- 50% {
557
- background-color: white;
558
- }
559
- 100% {
560
- background-color: #e7e7e7;
561
- }
562
- }
563
-
564
- tour-type-option,
565
- date-picker,
566
- #yourInformationMenu .inputContainer {
567
- position: relative;
568
- }
569
-
570
- .tour-scheduler.loading
571
- :is(tour-type-option, date-picker, #yourInformationMenu
572
- .inputContainer)::after {
573
- content: "";
574
- position: absolute;
575
- top: 0;
576
- left: 0;
577
- height: 100%;
578
- z-index: 1;
579
- animation: loadingPulse 2s infinite;
580
- }
581
-
582
- .tour-scheduler.loading tour-type-option::after {
583
- border-radius: 10px;
584
- width: 200px;
585
- }
586
-
587
- .tour-scheduler.loading date-picker::after {
588
- border-radius: 10px;
589
- width: 205px;
590
- }
591
-
592
- .tour-scheduler.loading #yourInformationMenu .inputContainer::after {
593
- width: 100%;
594
- }
595
-
596
- .tour-scheduler.loading #yourInformationMenu .inputContainer input {
597
- visibility: hidden;
598
- }
599
-
600
- .tour-scheduler.loading time-picker {
601
- display: none;
602
- }
603
-
604
491
  @media (max-width: 767px) {
605
492
  /* TODO: separate styles into general, desktop-specific, and mobile-specific.
606
493
  basically everything I have "unset" or "initial" on should become desktop-specific. the grid layout is only for desktop.
@@ -717,8 +604,9 @@ export class TourScheduler extends LitElement {
717
604
  tourTypeMenu(): TemplateResult {
718
605
  return html`<h2 id="tourType">Tour Type</h2>
719
606
  <div id="tourTypeMenu">
720
- ${this.shouldShowTourType[TourType.Guided]
607
+ ${this.tourTypeOptions.map((o) => o.value).includes("WITH_AGENT")
721
608
  ? html` <tour-type-option
609
+ tourtype="guided"
722
610
  heading="Guided tour"
723
611
  subtitle="with an agent"
724
612
  @click="${() => {
@@ -749,8 +637,9 @@ export class TourScheduler extends LitElement {
749
637
  </svg>
750
638
  </tour-type-option>`
751
639
  : ""}
752
- ${this.shouldShowTourType[TourType.Self]
640
+ ${this.tourTypeOptions.map((o) => o.value).includes("SELF_GUIDED")
753
641
  ? html`<tour-type-option
642
+ tourtype="self"
754
643
  heading="Take a tour"
755
644
  subtitle="on your own"
756
645
  @click="${() => {
@@ -781,8 +670,9 @@ export class TourScheduler extends LitElement {
781
670
  </svg>
782
671
  </tour-type-option>`
783
672
  : ""}
784
- ${this.shouldShowTourType[TourType.Virtual]
673
+ ${this.tourTypeOptions.map((o) => o.value).includes("SELF_GUIDED")
785
674
  ? html`<tour-type-option
675
+ tourtype="guided"
786
676
  heading="Virtual tour"
787
677
  subtitle="over video"
788
678
  @click="${() => {
@@ -851,8 +741,7 @@ export class TourScheduler extends LitElement {
851
741
  .map((date) => format(new Date(date.datetime), "h:mmaaa"))
852
742
  .indexOf(e.target.selectedTime)
853
743
  : null;
854
- this.selectedTime =
855
- index !== null ? daysAvailabilities[index] : null;
744
+ this.selectedTime = index ? daysAvailabilities[index] : null; // this.selectedAvailabilityString ?
856
745
  }
857
746
  }}
858
747
  ></time-picker>
@@ -920,62 +809,58 @@ export class TourScheduler extends LitElement {
920
809
  userInfoAndLayoutMenu(): TemplateResult {
921
810
  return html`<h2 id="yourInformation">Your information</h2>
922
811
  <div id="yourInformationMenu">
923
- <div class="inputContainer" id="firstName">
924
- <input
925
- type="text"
926
- placeholder="First name"
927
- name="firstName"
928
- autocomplete="given-name"
929
- @input=${() => this.requestUpdate()}
930
- />
931
- </div>
932
- <div class="inputContainer" id="lastName">
933
- <input
934
- type="text"
935
- placeholder="Last name"
936
- name="lastName"
937
- autocomplete="family-name"
938
- @input=${() => this.requestUpdate()}
939
- />
940
- </div>
941
- <div class="inputContainer" id="email">
942
- <input
943
- type="email"
944
- inputmode="email"
945
- placeholder="Email"
946
- name="email"
947
- autocomplete="email"
948
- .value=${this.email}
949
- @input=${this.onChangeEmail}
950
- />
951
- </div>
952
- <div class="inputContainer" id="phone">
953
- <input
954
- type="tel"
955
- inputmode="tel"
956
- placeholder="Phone"
957
- name="phone"
958
- autocomplete="tel-national"
959
- maxlength="14"
960
- .value=${this.phoneNumber}
961
- @keydown=${this.handlePhoneKeydown}
962
- @keyup=${this.handlePhoneKeyup}
963
- @input=${(e: Event) => {
964
- if (!e.target) {
965
- return;
966
- }
967
- this.phoneNumber = formatToPhone(
968
- (e.target as HTMLInputElement).value
969
- );
970
- }}
971
- />
972
- </div>
812
+ <input
813
+ type="text"
814
+ placeholder="Name"
815
+ id="name"
816
+ @input=${() => this.requestUpdate()}
817
+ />
818
+ <input
819
+ type="email"
820
+ inputmode="email"
821
+ placeholder="Email"
822
+ id="email"
823
+ .value=${this.email}
824
+ @input=${this.onChangeEmail}
825
+ />
826
+ <input
827
+ type="tel"
828
+ inputmode="tel"
829
+ placeholder="Phone"
830
+ id="phone"
831
+ maxlength="14"
832
+ .value=${this.phoneNumber}
833
+ @keydown=${this.handlePhoneKeydown}
834
+ @keyup=${this.handlePhoneKeyup}
835
+ @input=${(e: Event) => {
836
+ if (!e.target) {
837
+ return;
838
+ }
839
+ this.phoneNumber = formatToPhone(
840
+ (e.target as HTMLInputElement).value
841
+ );
842
+ }}
843
+ />
973
844
  </div>
974
- <!--
975
- Layout dropdown would go here, but has been removed pending backend support.
976
- Here is the code to add it back:
977
- https://github.com/MeetElise/chat-ui/blob/e17aca8b39a0eed9430f22c182f2ebcdfb796417/src/WebComponent/Scheduler/tour-scheduler.ts#L846-L863
978
- --> `;
845
+
846
+ ${this.layoutOptions.length > 0
847
+ ? html`<div id="unitChoiceMenu">
848
+ <h2 id="unitChoice">What would you like to view?</h2>
849
+ <div id="unitOptions">
850
+ <me-select
851
+ id="unitType"
852
+ placeholder="Select type"
853
+ .options="${this.layoutOptions.map((o) => o.label)}"
854
+ defaultOption="Studio"
855
+ @change=${() => {
856
+ // to revalidate the form
857
+ this.requestUpdate();
858
+ }}
859
+ >Studio
860
+ </me-select>
861
+ </div>
862
+ </div>`
863
+ : ""} `;
979
864
  }
980
865
 
981
866
  confirmationMessage(): TemplateResult {
@@ -1002,13 +887,7 @@ export class TourScheduler extends LitElement {
1002
887
  <p>
1003
888
  Thank you!
1004
889
  <br />
1005
- Your
1006
- ${{
1007
- [TourType.Guided]: "guided",
1008
- [TourType.Self]: "self-guided",
1009
- [TourType.Virtual]: "virtual",
1010
- }[this.tourType]}
1011
- tour is scheduled for ${readableDateAndTime}.
890
+ Your guided tour is scheduled for ${readableDateAndTime}.
1012
891
  </p>
1013
892
  <p>
1014
893
  Look for an email confirmation along with instructions and directions.
@@ -1018,35 +897,11 @@ export class TourScheduler extends LitElement {
1018
897
  `;
1019
898
  }
1020
899
 
1021
- loadingIcon(): TemplateResult {
1022
- return html`<svg
1023
- id="loadingIcon"
1024
- width="21"
1025
- height="21"
1026
- viewBox="0 0 21 21"
1027
- fill="none"
1028
- xmlns="http://www.w3.org/2000/svg"
1029
- >
1030
- <path
1031
- d="M17.835 13.1045C18.4839 11.5628 18.6332 9.85647 18.2621 8.22548C17.8909 6.5945 17.018 5.12084 15.7659 4.0117C14.5139 2.90256 12.9457 2.21372 11.2818 2.04201C9.618 1.87031 7.94218 2.22438 6.49 3.05445L5.498 1.31745C7.01563 0.450066 8.73419 -0.00418222 10.4822 2.90165e-05C12.2302 0.00424025 13.9466 0.466764 15.46 1.34145C19.95 3.93345 21.67 9.48345 19.577 14.1115L20.919 14.8855L16.754 17.0995L16.589 12.3855L17.835 13.1045ZM3.085 6.89845C2.43614 8.44015 2.28678 10.1464 2.65792 11.7774C3.02905 13.4084 3.90201 14.8821 5.15407 15.9912C6.40613 17.1003 7.97432 17.7892 9.63816 17.9609C11.302 18.1326 12.9778 17.7785 14.43 16.9485L15.422 18.6855C13.9044 19.5528 12.1858 20.0071 10.4378 20.0029C8.68979 19.9987 6.97344 19.5361 5.46 18.6615C0.97 16.0695 -0.75 10.5195 1.343 5.89145L0 5.11845L4.165 2.90445L4.33 7.61845L3.084 6.89945L3.085 6.89845Z"
1032
- fill="#1E1E1E"
1033
- />
1034
- </svg>`;
1035
- }
1036
-
1037
900
  render(): TemplateResult {
1038
901
  if (!isMobile()) {
1039
902
  return html`
1040
- <div
1041
- class="${classnames("tour-scheduler", {
1042
- loading: this.waitingForAvailabilities,
1043
- })}"
1044
- >
1045
- <h1 id="scheduleATour">
1046
- ${this.waitingForAvailabilities
1047
- ? html`${this.loadingIcon()} Searching availabilities...`
1048
- : "Schedule a tour"}
1049
- </h1>
903
+ <div class="tour-scheduler">
904
+ <h1 id="scheduleATour">Schedule a tour</h1>
1050
905
  ${this.closeButton()}
1051
906
  ${this.tourIsBooked
1052
907
  ? html`
@@ -1071,17 +926,9 @@ export class TourScheduler extends LitElement {
1071
926
  } else {
1072
927
  const currentPage = this.mobilePages[this.mobilePageIndex];
1073
928
  return html`
1074
- <div
1075
- class="${classnames("tour-scheduler", {
1076
- loading: this.waitingForAvailabilities,
1077
- })}"
1078
- >
929
+ <div class="tour-scheduler">
1079
930
  <div id="topControls">
1080
- <h1 id="scheduleATour">
1081
- ${this.waitingForAvailabilities
1082
- ? html`${this.loadingIcon()} Searching availabilities...`
1083
- : "Schedule a tour"}
1084
- </h1>
931
+ <h1 id="scheduleATour">Schedule a tour</h1>
1085
932
  ${this.closeButton()}
1086
933
  </div>
1087
934
  ${this.tourIsBooked
@@ -1091,9 +938,7 @@ export class TourScheduler extends LitElement {
1091
938
  id="next"
1092
939
  @click=${currentPage.nextButtonAction}
1093
940
  ?disabled=${(() => {
1094
- return (
1095
- !currentPage.validate() || this.waitingForAvailabilities
1096
- );
941
+ return !currentPage.validate();
1097
942
  })()}
1098
943
  >
1099
944
  ${currentPage.nextButtonText}
@@ -1110,6 +955,8 @@ export enum TourType {
1110
955
  Virtual,
1111
956
  }
1112
957
 
958
+ // TODO: we have three UI options and five TourAvailabilityResponseRankOrderedSupportedTourTypesEnum values
959
+ // how should they map?
1113
960
  const tourTypeMap = {
1114
961
  [TourType.Guided]:
1115
962
  TourAvailabilityResponseRankOrderedSupportedTourTypesEnum.WithAgent,
@@ -1,16 +1,20 @@
1
1
  import { css, html, LitElement, TemplateResult } from "lit";
2
2
  import { customElement, property } from "lit/decorators.js";
3
+ import { TourType } from "./tour-scheduler";
3
4
 
4
5
  @customElement("tour-type-option")
5
6
  export class TourTypeOption extends LitElement {
7
+ @property({ type: String })
8
+ tourtype = "";
6
9
  @property({ type: String })
7
10
  heading = "";
8
11
  @property({ type: String })
9
12
  subtitle = "";
10
13
  @property({ type: Boolean })
11
14
  selected = false;
15
+
12
16
  @property({ attribute: false })
13
- onClick?: () => void;
17
+ onClick?: (tourType: TourType) => void;
14
18
 
15
19
  static styles = [
16
20
  css`
@@ -263,7 +263,7 @@ export class MEChat extends LitElement {
263
263
  };
264
264
 
265
265
  render(): TemplateResult {
266
- if (this.building?.orgId === 182 || this.building?.id === 4930) {
266
+ if (this.building?.id === 4930) {
267
267
  return html``;
268
268
  }
269
269
  installLauncher();
@@ -321,6 +321,7 @@ declare global {
321
321
  interface HTMLElementTagNameMap {
322
322
  "me-chat": MEChat;
323
323
  }
324
+
324
325
  interface Window {
325
326
  RCTPCampaign?: { CampaignDetails: { Source: string } };
326
327
  }
@@ -46,7 +46,7 @@ export default function createConversation(
46
46
  building.avatarType === "image" && building.avatarSrc
47
47
  ? avatarSrc
48
48
  : defaultAvatarUrl,
49
- // uncomment the following line to test changes to the default avatar if your test building has its own avatar
49
+ // uncomment this 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;
@@ -10,49 +10,31 @@ import {
10
10
  TourAvailabilityResponseRankOrderedSupportedTourTypesEnum,
11
11
  } from "@meetelise/rest-sdk";
12
12
  import groupBy from "lodash/groupBy";
13
- import { TourType } from "./WebComponent/Scheduler/tour-scheduler";
14
13
 
15
14
  const availabilitiesCache: {
16
- buildingId?: number | null;
17
- availabilities: { [buildingId: number]: TourAvailabilityResponse };
18
- } = { buildingId: null, availabilities: {} };
15
+ [buildingId: number]: TourAvailabilityResponse;
16
+ } = {};
19
17
 
20
- /**
21
- * Returns the raw availabilities.
22
- *
23
- * If no `buildingId` is provided, it will use a cached buildingId from the previous call.
24
- * If there is none cached, it will throw an error.
25
- */
26
18
  export const getRawAvailabilities = async (
27
- buildingId?: number
19
+ buildingId: number
28
20
  ): Promise<TourAvailabilityResponse> => {
29
- if (buildingId) {
30
- availabilitiesCache.buildingId = buildingId;
31
- }
32
- const buildingIdToUse = availabilitiesCache.buildingId;
33
- if (!buildingIdToUse) {
34
- throw new Error(
35
- "No buildingId was provided to getRawAvailabilities and there is no buildingId cached."
36
- );
37
- }
38
- const availabilities = availabilitiesCache.availabilities;
39
- if (availabilities[buildingIdToUse]) {
40
- return availabilities[buildingIdToUse];
21
+ if (availabilitiesCache[buildingId]) {
22
+ return availabilitiesCache[buildingId];
41
23
  }
42
24
  const startTime = startOfToday();
43
25
  const endTime = formatISO(endOfDay(addDays(startTime, 30)));
44
- const url = `https://app.meetelise.com/api/pub/v1/buildings/${buildingIdToUse}/tour/availabilities?startTime=${formatISO(
26
+ const url = `https://app.meetelise.com/api/pub/v1/buildings/${buildingId}/tour/availabilities?startTime=${formatISO(
45
27
  startTime
46
28
  )}&endTime=${endTime}`;
47
29
  const result = await axios.get<TourAvailabilityResponse>(url);
48
- availabilitiesCache.availabilities[buildingIdToUse] = result.data;
30
+ availabilitiesCache[buildingId] = result.data;
49
31
  // The endpoint INCORRECTLY states that these are returned as dates. They are, in fact, strings.
50
32
  return result.data;
51
33
  };
52
34
 
53
35
  export const getAvailabilitiesForTourType = async (
54
- tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum,
55
- buildingId?: number
36
+ buildingId: number,
37
+ tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum
56
38
  ): Promise<{
57
39
  /**
58
40
  *
@@ -86,46 +68,13 @@ export interface DateWithTimeZoneOffset {
86
68
  offset: string;
87
69
  }
88
70
 
89
- /**
90
- * Returns an object that reveals whether each tour type supported by
91
- * `tour-scheduler` has availabilities (time slots available for scheduling) in
92
- * the time window of interest.
93
- *
94
- * Note that the existence of current availabilities is distinct from the
95
- * question of whether the community supports the tour type at all. The first
96
- * implies the second but not vice versa.
97
- */
98
- export const getExistenceOfAvailabilitiesByTourType = async (): Promise<{
99
- [TourType.Guided]: boolean;
100
- [TourType.Self]: boolean;
101
- [TourType.Virtual]: boolean;
102
- }> => {
103
- return {
104
- [TourType.Guided]: !!(
105
- await getAvailabilitiesForTourType(
106
- TourAvailabilityResponseRankOrderedSupportedTourTypesEnum.WithAgent
107
- )
108
- )?.availableTourStartTimes?.length,
109
- [TourType.Self]: !!(
110
- await getAvailabilitiesForTourType(
111
- TourAvailabilityResponseRankOrderedSupportedTourTypesEnum.SelfGuided
112
- )
113
- )?.availableTourStartTimes?.length,
114
- [TourType.Virtual]: !!(
115
- await getAvailabilitiesForTourType(
116
- TourAvailabilityResponseRankOrderedSupportedTourTypesEnum.VirtualShowing
117
- )
118
- )?.availableTourStartTimes?.length,
119
- };
120
- };
121
-
122
71
  export const getAvailabilitiesGroupedByDay = async (
123
- tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum,
124
- buildingId?: number
72
+ buildingId: number,
73
+ tourType: TourAvailabilityResponseRankOrderedSupportedTourTypesEnum
125
74
  ): Promise<{ [day: string]: DateWithTimeZoneOffset[] }> => {
126
75
  const availabilitiesForTourTypeRaw = await getAvailabilitiesForTourType(
127
- tourType,
128
- buildingId
76
+ buildingId,
77
+ tourType
129
78
  );
130
79
  if (!availabilitiesForTourTypeRaw) {
131
80
  return {};
@@ -148,6 +97,16 @@ export const getAvailabilitiesGroupedByDay = async (
148
97
  );
149
98
  };
150
99
 
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
+
151
110
  /**
152
111
  * Takes an ISO 8601 date string with time zone offset and returns
153
112
  * an object of our custom type DateWithTimeZoneOffset.