@meetelise/chat 1.43.37 → 1.43.39

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.
@@ -66,6 +66,9 @@ export class FeeCalculatorLayout extends LitElement {
66
66
  @property({ type: Boolean })
67
67
  isLoadingUnits = false;
68
68
 
69
+ @property({ type: Boolean })
70
+ isLoadingLayouts = false;
71
+
69
72
  @property()
70
73
  onSelectLayout: ((layoutIds: number[]) => void) | null = null;
71
74
 
@@ -277,6 +280,7 @@ export class FeeCalculatorLayout extends LitElement {
277
280
  .selectedLayoutIds=${this.selectedLayoutIds}
278
281
  .units=${this.units}
279
282
  .isLoading=${this.isLoadingUnits}
283
+ .isLoadingLayouts=${this.isLoadingLayouts}
280
284
  .onSelectLayout=${this.onSelectLayout}
281
285
  .onUnitSelect=${this.onUnitSelect}
282
286
  .onMoveInDateChange=${this.onMoveInDateChange}
@@ -35,6 +35,13 @@ const feeItemStyles = css`
35
35
  margin-top: 4px;
36
36
  }
37
37
 
38
+ .fee-additional-info {
39
+ color: #868e96;
40
+ font-size: 0.8rem;
41
+ margin-top: 2px;
42
+ font-style: italic;
43
+ }
44
+
38
45
  .fee-amount {
39
46
  font-size: 1rem;
40
47
  align-self: center;
@@ -32,6 +32,10 @@ export class FeeItemComponent extends LitElement {
32
32
  return this.feeQuote?.marketableFee?.description ?? "";
33
33
  }
34
34
 
35
+ get additionalInfo(): string {
36
+ return this.feeQuote?.marketableFee?.additionalInfo ?? "";
37
+ }
38
+
35
39
  get triggeredEvent(): string {
36
40
  const event = this.feeQuote?.marketableFee?.triggeredByEvent;
37
41
  if (!event) return "";
@@ -61,7 +65,14 @@ export class FeeItemComponent extends LitElement {
61
65
  <div class="fee-item">
62
66
  <div class="fee-info">
63
67
  <div class="fee-name">${this.name}</div>
64
- <div class="fee-description">${this.description}</div>
68
+ ${this.description
69
+ ? html`<div class="fee-description">${this.description}</div>`
70
+ : ""}
71
+ ${this.additionalInfo
72
+ ? html`<div class="fee-additional-info">
73
+ ${this.additionalInfo}
74
+ </div>`
75
+ : ""}
65
76
  ${this.triggeredEvent
66
77
  ? html`<div class="fee-triggered-event">
67
78
  ${this.triggeredEvent}
@@ -49,6 +49,40 @@ const floorPlanSelectorStyles = css`
49
49
  color: white;
50
50
  }
51
51
 
52
+ .floor-plan-badge.skeleton {
53
+ cursor: default;
54
+ width: 56px;
55
+ height: 18px;
56
+ padding: 0;
57
+ border: 1px solid #eaeaea;
58
+ }
59
+
60
+ .unit-card-skeleton {
61
+ display: flex;
62
+ min-width: 18rem;
63
+ width: 100%;
64
+ height: 4.75rem;
65
+ border-radius: 4px;
66
+ border: 2px solid transparent;
67
+ background-color: #f8f9fa;
68
+ box-sizing: border-box;
69
+ }
70
+
71
+ .shimmer {
72
+ background: linear-gradient(90deg, #eaeaea 25%, #f5f5f5 50%, #eaeaea 75%);
73
+ background-size: 400px 100%;
74
+ animation: shimmer 1.4s ease-in-out infinite;
75
+ }
76
+
77
+ @keyframes shimmer {
78
+ 0% {
79
+ background-position: -200px 0;
80
+ }
81
+ 100% {
82
+ background-position: 200px 0;
83
+ }
84
+ }
85
+
52
86
  .advanced-section {
53
87
  max-height: 0;
54
88
  overflow: hidden;
@@ -7,7 +7,9 @@ import floorPlanSelectorStyles from "./floor-plan-selector-styles";
7
7
  import { TODAY } from "../../../../globals";
8
8
 
9
9
  import "../floorplan-image-card/floorplan-image-card";
10
- import "../../../loaders/mega-loader";
10
+
11
+ const SKELETON_BADGE_COUNT = 4;
12
+ const SKELETON_UNIT_COUNT = 3;
11
13
 
12
14
  @customElement("floor-plan-selector")
13
15
  export class FloorPlanSelector extends LitElement {
@@ -28,6 +30,9 @@ export class FloorPlanSelector extends LitElement {
28
30
  @property({ type: Boolean })
29
31
  isLoading = false;
30
32
 
33
+ @property({ type: Boolean })
34
+ isLoadingLayouts = false;
35
+
31
36
  @property()
32
37
  onSelectLayout: ((layoutIds: number[]) => void) | null = null;
33
38
 
@@ -141,8 +146,11 @@ export class FloorPlanSelector extends LitElement {
141
146
  if (this.isLoading) {
142
147
  return html`
143
148
  <div class="unit-selection">
144
- <div class="loading-container">
145
- <mega-loader size="32"></mega-loader>
149
+ <div class="image-carousel">
150
+ ${Array.from(
151
+ { length: SKELETON_UNIT_COUNT },
152
+ () => html`<div class="unit-card-skeleton shimmer"></div>`
153
+ )}
146
154
  </div>
147
155
  </div>
148
156
  `;
@@ -151,7 +159,7 @@ export class FloorPlanSelector extends LitElement {
151
159
  return html`
152
160
  <div class="unit-selection">
153
161
  <div class="image-carousel">
154
- ${!this.isLoading && this.units?.length === 0
162
+ ${this.units?.length === 0
155
163
  ? html`
156
164
  <div class="no-floorplans-container">
157
165
  <p>No floorplans found</p>
@@ -173,6 +181,17 @@ export class FloorPlanSelector extends LitElement {
173
181
  };
174
182
 
175
183
  renderBadges = (): TemplateResult => {
184
+ if (this.isLoadingLayouts) {
185
+ return html`
186
+ <div class="floor-plan-selector-badges">
187
+ ${Array.from(
188
+ { length: SKELETON_BADGE_COUNT },
189
+ () => html`<div class="floor-plan-badge skeleton shimmer"></div>`
190
+ )}
191
+ </div>
192
+ `;
193
+ }
194
+
176
195
  return html`
177
196
  <div class="floor-plan-selector-badges">
178
197
  ${this.displayOptions.map(
@@ -268,12 +268,6 @@ export const feeCalculatorStyles = css`
268
268
  background-color: var(--primary-color, #ffffff);
269
269
  }
270
270
 
271
- .recalculating-loader {
272
- display: flex;
273
- align-items: center;
274
- justify-content: center;
275
- }
276
-
277
271
  @media (max-width: 767px) {
278
272
  .fee-calculator-container {
279
273
  width: 100vw;
@@ -68,10 +68,10 @@ export class FeeCalculator extends LitElement {
68
68
  onClickTextUsOption: ((e: MouseEvent) => void) | null = null;
69
69
 
70
70
  @state()
71
- private isFirstCalculation = true;
71
+ private isCalculatingQuote = false;
72
72
 
73
73
  @state()
74
- private isCalculatingQuote = false;
74
+ private isLoadingLayouts = true;
75
75
 
76
76
  @state()
77
77
  private isExporting = false;
@@ -212,7 +212,6 @@ export class FeeCalculator extends LitElement {
212
212
  } finally {
213
213
  if (!signal.aborted) {
214
214
  this.isCalculatingQuote = false;
215
- this.isFirstCalculation = false;
216
215
  }
217
216
  }
218
217
  }
@@ -226,26 +225,38 @@ export class FeeCalculator extends LitElement {
226
225
  this.style.setProperty("--primary-color", this.primaryColor);
227
226
  this.style.setProperty("--background-color", this.backgroundColor);
228
227
 
229
- await this.setupCalculator();
230
- await this.fetchUnits();
228
+ await Promise.allSettled([this.setupCalculator(), this.fetchUnits()]);
231
229
  };
232
230
 
233
231
  setupCalculator = async (): Promise<void> => {
234
- if (!this.buildingSlug || !this.orgSlug) return;
235
-
236
- const [buildingWebchatView, buildingFeeResponse] = await Promise.all([
237
- fetchBuildingWebchatView(this.orgSlug, this.buildingSlug),
238
- fetchBuildingFeesV2(this.buildingSlug),
239
- ]);
232
+ if (!this.buildingSlug || !this.orgSlug) {
233
+ this.isLoadingLayouts = false;
234
+ return;
235
+ }
240
236
 
241
- if (!buildingWebchatView || !buildingFeeResponse) return;
237
+ // Resolve independently /marketable-fees can be slow and shouldn't gate the layout selector.
238
+ const layoutsPromise = fetchBuildingWebchatView(
239
+ this.orgSlug,
240
+ this.buildingSlug
241
+ )
242
+ .then((view) => {
243
+ if (view) this.buildingWebchatView = view;
244
+ })
245
+ .finally(() => {
246
+ this.isLoadingLayouts = false;
247
+ });
242
248
 
243
- this.buildingWebchatView = buildingWebchatView;
244
- this.groupedFees = this.groupFees(buildingFeeResponse.fees);
245
- this.rentableItems = buildingFeeResponse.rentableItems;
249
+ const feesPromise = fetchBuildingFeesV2(this.buildingSlug).then(
250
+ (response) => {
251
+ if (!response) return;
252
+ this.groupedFees = this.groupFees(response.fees);
253
+ this.rentableItems = response.rentableItems;
254
+ // TODO(Leo): Re-enable
255
+ // this.incentives = response.buildingIncentives;
256
+ }
257
+ );
246
258
 
247
- // TODO(Leo): Re-enable
248
- // this.incentives = buildingFeeResponse.buildingIncentives;
259
+ await Promise.allSettled([layoutsPromise, feesPromise]);
249
260
  };
250
261
 
251
262
  updated(changedProperties: Map<string, unknown>): void {
@@ -294,7 +305,6 @@ export class FeeCalculator extends LitElement {
294
305
 
295
306
  handleUnitSelect = (unit: Unit): void => {
296
307
  const isInitialSelection = !this.selectedUnit;
297
- this.isFirstCalculation = true; // Reset flag to show skeleton loader
298
308
  this.selectedUnit = unit;
299
309
 
300
310
  if (isInitialSelection) {
@@ -451,9 +461,6 @@ export class FeeCalculator extends LitElement {
451
461
  };
452
462
 
453
463
  render(): TemplateResult {
454
- const showLoader =
455
- this.isCalculatingQuote && !this.isExporting && !this.isFirstCalculation;
456
-
457
464
  const exportDisabled =
458
465
  this.isExporting ||
459
466
  this.isCalculatingQuote ||
@@ -471,12 +478,6 @@ export class FeeCalculator extends LitElement {
471
478
  <h1>Estimated Cost Calculator</h1>
472
479
 
473
480
  <div class="calculator-header-right">
474
- ${showLoader
475
- ? html`<div class="recalculating-loader">
476
- <mega-loader .size=${16} .spinSpeed=${0.54}></mega-loader>
477
- </div>`
478
- : ""}
479
-
480
481
  <button
481
482
  class="share-button"
482
483
  @click=${this.handleShare}
@@ -498,7 +499,8 @@ export class FeeCalculator extends LitElement {
498
499
  "noopener,noreferrer"
499
500
  );
500
501
  }}
501
- ?disabled=${!this.quote?.applicationLink}
502
+ ?disabled=${this.isCalculatingQuote ||
503
+ !this.quote?.applicationLink}
502
504
  >
503
505
  Apply
504
506
  </button>
@@ -531,7 +533,7 @@ export class FeeCalculator extends LitElement {
531
533
 
532
534
  <fee-calculator-layout
533
535
  .buildingSlug=${this.buildingSlug}
534
- .showSkeletonLoader=${this.isFirstCalculation}
536
+ .showSkeletonLoader=${this.isCalculatingQuote}
535
537
  .selectedUnit=${this.selectedUnit}
536
538
  .groupedFees=${this.groupedFees}
537
539
  .rentableItems=${this.rentableItems}
@@ -540,6 +542,7 @@ export class FeeCalculator extends LitElement {
540
542
  .selectedLayoutIds=${this.selectedLayoutIds}
541
543
  .units=${this.units}
542
544
  .isLoadingUnits=${this.isLoadingUnits}
545
+ .isLoadingLayouts=${this.isLoadingLayouts}
543
546
  .onSelectLayout=${this.handleSelectLayout}
544
547
  .onUnitSelect=${this.handleUnitSelect}
545
548
  .onMoveInDateChange=${this.handleMoveInDateChange}
@@ -140,7 +140,7 @@ export class DatePicker extends LitElement {
140
140
  margin-bottom: 13px;
141
141
  }
142
142
 
143
- h1 {
143
+ h2 {
144
144
  font-weight: 600;
145
145
  font-size: 12px;
146
146
  margin: 0;
@@ -154,6 +154,23 @@ export class DatePicker extends LitElement {
154
154
  align-items: center;
155
155
  }
156
156
 
157
+ #arrows button {
158
+ background: none;
159
+ border: none;
160
+ padding: 0;
161
+ margin: 0;
162
+ cursor: pointer;
163
+ display: inline-flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ color: inherit;
167
+ }
168
+
169
+ #arrows button:disabled {
170
+ cursor: default;
171
+ opacity: 0.4;
172
+ }
173
+
157
174
  #rows {
158
175
  display: flex;
159
176
  flex-direction: column;
@@ -324,7 +341,9 @@ export class DatePicker extends LitElement {
324
341
  return html`
325
342
  <div id="calendar">
326
343
  <div id="header">
327
- <h1>${monthNames[this.monthShown]} ${this.yearShown}</h1>
344
+ <h2 aria-live="polite" aria-atomic="true">
345
+ ${monthNames[this.monthShown]} ${this.yearShown}
346
+ </h2>
328
347
  <div
329
348
  id="arrows"
330
349
  @click="${(e: MouseEvent) => {
@@ -341,55 +360,47 @@ export class DatePicker extends LitElement {
341
360
  this.monthShown++;
342
361
  }
343
362
  }}"
344
- @keydown="${(e: KeyboardEvent) => {
345
- if (![" ", "Enter"].includes(e.key)) {
346
- return;
347
- }
348
- if (
349
- (e.target as HTMLElement)?.closest("#back") &&
350
- !isSameMonth(
351
- this.now,
352
- new Date(this.yearShown, this.monthShown, 1)
353
- )
354
- ) {
355
- e.preventDefault();
356
- e.stopPropagation();
357
- this.monthShown--;
358
- } else if ((e.target as HTMLElement)?.closest("#forward")) {
359
- e.preventDefault();
360
- e.stopPropagation();
361
- this.monthShown++;
362
- }
363
- }}"
364
363
  >
365
- <svg
364
+ <button
366
365
  id="back"
367
- tabindex="0"
368
- width="9"
369
- height="16"
370
- viewBox="0 0 9 16"
371
- fill="none"
372
- xmlns="http://www.w3.org/2000/svg"
373
- >
374
- <path
375
- d="M8.67727 2.34317L7.26305 0.928955L0.192017 8.00001L7.26308 15.0711L8.6773 13.6569L3.02044 8L8.67727 2.34317Z"
376
- fill="#83818E"
377
- />
378
- </svg>
379
- <svg
380
- id="forward"
381
- tabindex="0"
382
- width="9"
383
- height="16"
384
- viewBox="0 0 9 16"
385
- fill="none"
386
- xmlns="http://www.w3.org/2000/svg"
366
+ type="button"
367
+ aria-label="Previous month"
368
+ ?disabled=${isSameMonth(
369
+ this.now,
370
+ new Date(this.yearShown, this.monthShown, 1)
371
+ )}
387
372
  >
388
- <path
389
- d="M0.157227 2.34315L1.57144 0.928932L8.64251 8L1.57144 15.0711L0.157227 13.6569L5.81408 8L0.157227 2.34315Z"
390
- fill="#83818E"
391
- />
392
- </svg>
373
+ <svg
374
+ aria-hidden="true"
375
+ focusable="false"
376
+ width="9"
377
+ height="16"
378
+ viewBox="0 0 9 16"
379
+ fill="none"
380
+ xmlns="http://www.w3.org/2000/svg"
381
+ >
382
+ <path
383
+ d="M8.67727 2.34317L7.26305 0.928955L0.192017 8.00001L7.26308 15.0711L8.6773 13.6569L3.02044 8L8.67727 2.34317Z"
384
+ fill="#83818E"
385
+ />
386
+ </svg>
387
+ </button>
388
+ <button id="forward" type="button" aria-label="Next month">
389
+ <svg
390
+ aria-hidden="true"
391
+ focusable="false"
392
+ width="9"
393
+ height="16"
394
+ viewBox="0 0 9 16"
395
+ fill="none"
396
+ xmlns="http://www.w3.org/2000/svg"
397
+ >
398
+ <path
399
+ d="M0.157227 2.34315L1.57144 0.928932L8.64251 8L1.57144 15.0711L0.157227 13.6569L5.81408 8L0.157227 2.34315Z"
400
+ fill="#83818E"
401
+ />
402
+ </svg>
403
+ </button>
393
404
  </div>
394
405
  </div>
395
406
 
@@ -797,9 +797,11 @@ export class Launcher extends LitElement {
797
797
  const verticalPillListItems: {
798
798
  pillKey: string;
799
799
  pill: TemplateResult | null;
800
+ optionLabel: string;
800
801
  }[] = [
801
802
  {
802
803
  pillKey: "Chat",
804
+ optionLabel: "Chat with us",
803
805
  pill: this.hasChatEnabledDesktop
804
806
  ? html`
805
807
  <button
@@ -844,6 +846,7 @@ export class Launcher extends LitElement {
844
846
  },
845
847
  {
846
848
  pillKey: "Price Calculator",
849
+ optionLabel: "Calculate cost",
847
850
  pill: this.hasPricingCalculatorEnabledDesktop
848
851
  ? html`
849
852
  <button
@@ -875,6 +878,7 @@ export class Launcher extends LitElement {
875
878
  },
876
879
  {
877
880
  pillKey: "SST",
881
+ optionLabel: "Book a tour",
878
882
  pill: this.hasSSTEnabledDesktop
879
883
  ? html`
880
884
  <button
@@ -921,6 +925,7 @@ export class Launcher extends LitElement {
921
925
  },
922
926
  {
923
927
  pillKey: "Email",
928
+ optionLabel: "Email an agent",
924
929
  pill: this.hasEmailEnabledDesktop
925
930
  ? html`
926
931
  <button
@@ -958,6 +963,12 @@ export class Launcher extends LitElement {
958
963
  },
959
964
  {
960
965
  pillKey: "Phone",
966
+ optionLabel:
967
+ this.hasCallUsEnabledDesktop && this.hasTextUsEnabledDesktop
968
+ ? "Call or text us"
969
+ : this.hasCallUsEnabledDesktop
970
+ ? "Call us"
971
+ : "Text us",
961
972
  pill:
962
973
  this.phoneNumber &&
963
974
  (this.hasCallUsEnabledDesktop || this.hasTextUsEnabledDesktop)
@@ -1036,6 +1047,7 @@ export class Launcher extends LitElement {
1036
1047
  },
1037
1048
  {
1038
1049
  pillKey: "Apply",
1050
+ optionLabel: "Apply now",
1039
1051
  pill:
1040
1052
  this.applicationLink && this.hasApplyNowEnabledDesktop
1041
1053
  ? html`
@@ -1081,8 +1093,15 @@ export class Launcher extends LitElement {
1081
1093
  pills.unshift(sstPill);
1082
1094
  }
1083
1095
  }
1096
+ const optionLabels = pills.map((p) => p.optionLabel);
1097
+ if (this.overrideRentgrata && hasRentgrata()) {
1098
+ optionLabels.push("Contact a resident");
1099
+ }
1100
+ const navAriaLabel = optionLabels.length
1101
+ ? `EliseAI options: ${optionLabels.join(", ")}`
1102
+ : "EliseAI options";
1084
1103
  return html`
1085
- <div class="vertical-pill-list">
1104
+ <nav class="vertical-pill-list" aria-label=${navAriaLabel}>
1086
1105
  <div class="vertical-pill-list">
1087
1106
  ${this.hasChatEnabledDesktop
1088
1107
  ? html`<button
@@ -1119,7 +1138,7 @@ export class Launcher extends LitElement {
1119
1138
  </button>`
1120
1139
  : ""}
1121
1140
  </div>
1122
- </div>
1141
+ </nav>
1123
1142
  `;
1124
1143
  };
1125
1144
 
@@ -65,10 +65,9 @@ export default class MEChat {
65
65
  installFont();
66
66
  const healthChat = document.createElement("health-chat");
67
67
  healthChat.setAttribute("class", "health-chat");
68
- healthChat.setAttribute("role", "dialog");
68
+ healthChat.setAttribute("role", "region");
69
69
  healthChat.setAttribute("aria-label", "EliseAI Healthcare Widget");
70
70
  healthChat.setAttribute("aria-describedby", "aria-describe-info");
71
- healthChat.setAttribute("aria-modal", "true");
72
71
  healthChat.setAttribute("healthcareId", opts.id);
73
72
 
74
73
  healthChat.setAttribute("right", opts.right?.toString() || "unset");
@@ -140,10 +139,9 @@ export default class MEChat {
140
139
  opts.widgetType || WidgetType.Default
141
140
  );
142
141
  meChatElement.setAttribute("class", "meetelise-chat");
143
- meChatElement.setAttribute("role", "dialog");
142
+ meChatElement.setAttribute("role", "region");
144
143
  meChatElement.setAttribute("aria-label", "EliseAI Widget");
145
144
  meChatElement.setAttribute("aria-describedby", "aria-describe-info");
146
- meChatElement.setAttribute("aria-modal", "true");
147
145
 
148
146
  if (opts.brandColor) {
149
147
  this.brandColor = opts.brandColor;