@meetelise/chat 1.43.5 → 1.43.7

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.
Files changed (62) hide show
  1. package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/addon-table.d.ts +4 -4
  2. package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/index.d.ts +0 -1
  3. package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/table-qty-selectors.d.ts +3 -2
  4. package/dist/src/WebComponent/FeeCalculator/components/charge-inputs/charge-inputs.d.ts +27 -0
  5. package/dist/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout.d.ts +12 -12
  6. package/dist/src/WebComponent/FeeCalculator/components/fee-item/fee-item.d.ts +1 -1
  7. package/dist/src/WebComponent/FeeCalculator/components/fee-item/grouped-rentable-item.d.ts +0 -1
  8. package/dist/src/WebComponent/FeeCalculator/components/index.d.ts +1 -0
  9. package/dist/src/WebComponent/FeeCalculator/fee-calculator.d.ts +6 -5
  10. package/dist/src/WebComponent/FeeCalculator/model/desired-addon.d.ts +1 -1
  11. package/dist/src/WebComponent/FeeCalculator/model/fee-quote.d.ts +3 -4
  12. package/dist/src/WebComponent/FeeCalculator/model/index.d.ts +1 -4
  13. package/dist/src/WebComponent/FeeCalculator/model/item-combination.d.ts +1 -3
  14. package/dist/src/WebComponent/FeeCalculator/model/marketable-fee-new.d.ts +75 -0
  15. package/dist/src/WebComponent/FeeCalculator/model/marketable-fee.d.ts +79 -0
  16. package/dist/src/WebComponent/FeeCalculator/model/pricing-matrix.d.ts +1 -15
  17. package/dist/src/WebComponent/FeeCalculator/model/pricing-metadata.d.ts +1 -9
  18. package/dist/src/WebComponent/FeeCalculator/model/quote.d.ts +16 -2
  19. package/dist/src/disclaimers.d.ts +2 -1
  20. package/dist/src/services/fees/calculateQuote.d.ts +13 -3
  21. package/dist/src/services/fees/fetchBuildingFeesV2.d.ts +2 -2
  22. package/package.json +1 -1
  23. package/public/dist/index.js +571 -462
  24. package/src/WebComponent/FeeCalculator/components/addons/addon-table/addon-table.ts +28 -59
  25. package/src/WebComponent/FeeCalculator/components/addons/addon-table/index.ts +0 -1
  26. package/src/WebComponent/FeeCalculator/components/addons/addon-table/table-qty-selectors.ts +7 -10
  27. package/src/WebComponent/FeeCalculator/components/charge-inputs/charge-inputs.ts +351 -0
  28. package/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout-styles.ts +26 -0
  29. package/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout.ts +120 -86
  30. package/src/WebComponent/FeeCalculator/components/fee-card/fee-card.ts +19 -14
  31. package/src/WebComponent/FeeCalculator/components/fee-item/fee-item.ts +23 -12
  32. package/src/WebComponent/FeeCalculator/components/fee-item/grouped-rentable-item.ts +3 -13
  33. package/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector.ts +1 -1
  34. package/src/WebComponent/FeeCalculator/components/index.ts +1 -0
  35. package/src/WebComponent/FeeCalculator/fee-calculator.ts +57 -64
  36. package/src/WebComponent/FeeCalculator/model/desired-addon.ts +1 -1
  37. package/src/WebComponent/FeeCalculator/model/fee-quote.ts +3 -4
  38. package/src/WebComponent/FeeCalculator/model/index.ts +1 -4
  39. package/src/WebComponent/FeeCalculator/model/item-combination.ts +2 -12
  40. package/src/WebComponent/FeeCalculator/model/marketable-fee-new.ts +81 -0
  41. package/src/WebComponent/FeeCalculator/model/marketable-fee.ts +124 -0
  42. package/src/WebComponent/FeeCalculator/model/pricing-matrix.ts +3 -39
  43. package/src/WebComponent/FeeCalculator/model/pricing-metadata.ts +2 -10
  44. package/src/WebComponent/FeeCalculator/model/quote.ts +16 -2
  45. package/src/WebComponent/Scheduler/tour-scheduler.ts +3 -0
  46. package/src/WebComponent/actions/email-us-window.ts +1 -0
  47. package/src/disclaimers.ts +17 -13
  48. package/src/services/fees/calculateQuote.ts +17 -8
  49. package/src/services/fees/downloadQuotePdf.ts +6 -8
  50. package/src/services/fees/fetchBuildingFeesV2.ts +10 -6
  51. package/src/utils.ts +1 -1
  52. package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/table-matrix-qty-selector-styles.d.ts +0 -1
  53. package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/table-matrix-qty-selector.d.ts +0 -23
  54. package/dist/src/WebComponent/FeeCalculator/model/building-fee-view.d.ts +0 -42
  55. package/dist/src/WebComponent/FeeCalculator/model/pricing-rule.d.ts +0 -10
  56. package/dist/src/WebComponent/FeeCalculator/model/pricing-type.d.ts +0 -10
  57. package/dist/src/services/fees/downloadQuotePdf.d.ts +0 -12
  58. package/src/WebComponent/FeeCalculator/components/addons/addon-table/table-matrix-qty-selector-styles.ts +0 -82
  59. package/src/WebComponent/FeeCalculator/components/addons/addon-table/table-matrix-qty-selector.ts +0 -203
  60. package/src/WebComponent/FeeCalculator/model/building-fee-view.ts +0 -105
  61. package/src/WebComponent/FeeCalculator/model/pricing-rule.ts +0 -11
  62. package/src/WebComponent/FeeCalculator/model/pricing-type.ts +0 -11
@@ -1,7 +1,7 @@
1
1
  import { LitElement, html, TemplateResult } from "lit";
2
2
  import { customElement, property, state } from "lit/decorators.js";
3
+ import { MarketableFee } from "../../../model/marketable-fee";
3
4
  import {
4
- BuildingFeeView,
5
5
  DesiredAddon,
6
6
  DesiredRentableItem,
7
7
  RentableItemSummary,
@@ -16,7 +16,6 @@ import styles from "./addon-table-styles";
16
16
 
17
17
  import "../../../../shared/simple-tooltip";
18
18
  import "./table-qty-selectors";
19
- import "./table-matrix-qty-selector";
20
19
 
21
20
  type AddonQuantityState = Map<string | number, ItemQuantity[] | number>;
22
21
 
@@ -25,7 +24,7 @@ export class AddonTable extends LitElement {
25
24
  static styles = styles;
26
25
 
27
26
  @property({ type: Array })
28
- set addons(value: (BuildingFeeView | RentableItemSummary)[]) {
27
+ set addons(value: (MarketableFee | RentableItemSummary)[]) {
29
28
  const oldValue = this._addons;
30
29
  this._addons = value;
31
30
  if (value !== oldValue) {
@@ -33,10 +32,10 @@ export class AddonTable extends LitElement {
33
32
  }
34
33
  this.requestUpdate("addons", oldValue);
35
34
  }
36
- get addons(): (BuildingFeeView | RentableItemSummary)[] {
35
+ get addons(): (MarketableFee | RentableItemSummary)[] {
37
36
  return this._addons;
38
37
  }
39
- private _addons: (BuildingFeeView | RentableItemSummary)[] = [];
38
+ private _addons: (MarketableFee | RentableItemSummary)[] = [];
40
39
 
41
40
  @property({ type: Boolean })
42
41
  disabled = false;
@@ -80,38 +79,13 @@ export class AddonTable extends LitElement {
80
79
  }
81
80
 
82
81
  private initializeBuildingFeeQuantity(
83
- addon: BuildingFeeView,
82
+ addon: MarketableFee,
84
83
  newQuantities: AddonQuantityState
85
84
  ): void {
86
- // Simple addon
87
- if (!addon.hasMatrix) {
88
- const key = addon.unitLabel ?? 0;
89
- const existingQuantity = this.preserveExistingQuantity(key, 0);
90
- newQuantities.set(key, existingQuantity);
91
- return;
92
- }
93
-
94
- // Matrix addon
95
- const key = addon.id ?? 0;
96
- const existingQuantities = this.preserveExistingQuantity(
97
- key,
98
- null as ItemQuantity[] | null
99
- );
100
-
101
- if (existingQuantities && Array.isArray(existingQuantities)) {
102
- // Preserve existing matrix quantities
103
- newQuantities.set(key, existingQuantities);
104
- } else {
105
- // Initialize new matrix quantities
106
- const initialQuantities = addon.customMatrixData
107
- ?.toRuleArray()?.[0]
108
- ?.combination?.quantities.map((q: ItemQuantity) => ({
109
- ...q,
110
- quantity: 0,
111
- })) ?? [{ itemType: "default", quantity: 0 }];
112
-
113
- newQuantities.set(key, initialQuantities);
114
- }
85
+ // Simple addon - MarketableFee doesn't have matrix anymore
86
+ const key = addon.id;
87
+ const existingQuantity = this.preserveExistingQuantity(key, 0);
88
+ newQuantities.set(key, existingQuantity);
115
89
  }
116
90
 
117
91
  private initializeQuantities(): void {
@@ -119,7 +93,7 @@ export class AddonTable extends LitElement {
119
93
 
120
94
  // Process each addon and preserve existing quantities
121
95
  this.addons.forEach((addon) => {
122
- if (addon instanceof BuildingFeeView) {
96
+ if (!(addon instanceof RentableItemSummary)) {
123
97
  this.initializeBuildingFeeQuantity(addon, newQuantities);
124
98
  } else {
125
99
  this.initializeRentableItemQuantity(addon, newQuantities);
@@ -152,7 +126,7 @@ export class AddonTable extends LitElement {
152
126
  this.requestUpdate("quantities");
153
127
 
154
128
  const rentableItem = this.addons.find(
155
- (a) => !(a instanceof BuildingFeeView) && a.type === change.type
129
+ (a) => a instanceof RentableItemSummary && a.type === change.type
156
130
  ) as RentableItemSummary | undefined;
157
131
 
158
132
  if (!rentableItem) return;
@@ -209,11 +183,13 @@ export class AddonTable extends LitElement {
209
183
  this.requestUpdate("selectedRentableItemIds");
210
184
  };
211
185
 
212
- private getName(addon: BuildingFeeView | RentableItemSummary): string {
213
- return addon instanceof BuildingFeeView ? addon.feeName : addon.displayName;
186
+ private getName(addon: MarketableFee | RentableItemSummary): string {
187
+ return addon instanceof RentableItemSummary
188
+ ? addon.displayName
189
+ : addon.fee_name;
214
190
  }
215
191
 
216
- private getSortedAddons(): (BuildingFeeView | RentableItemSummary)[] {
192
+ private getSortedAddons(): (MarketableFee | RentableItemSummary)[] {
217
193
  return [...this.addons].sort((a, b) => {
218
194
  const aName = this.getName(a);
219
195
  const bName = this.getName(b);
@@ -222,31 +198,24 @@ export class AddonTable extends LitElement {
222
198
  }
223
199
 
224
200
  private isRentableItemAvailable(
225
- addon: BuildingFeeView | RentableItemSummary
201
+ addon: MarketableFee | RentableItemSummary
226
202
  ): boolean {
227
- if (addon instanceof BuildingFeeView) return true;
203
+ if (!(addon instanceof RentableItemSummary)) return true;
228
204
  return (addon.allItems?.filter((item) => item.available).length ?? 0) > 0;
229
205
  }
230
206
 
231
207
  private renderQtySelector(
232
- addon: BuildingFeeView | RentableItemSummary
208
+ addon: MarketableFee | RentableItemSummary
233
209
  ): TemplateResult {
234
- if (addon instanceof BuildingFeeView) {
235
- return addon.hasMatrix
236
- ? html`
237
- <table-matrix-qty-selector
238
- .feeItem=${addon}
239
- .onQuantityChange=${this.handleAddonQuantityChange}
240
- .disabled=${this.disabled}
241
- ></table-matrix-qty-selector>
242
- `
243
- : html`
244
- <table-addon-qty-selector
245
- .feeItem=${addon}
246
- .onQuantityChange=${this.handleAddonQuantityChange}
247
- .disabled=${this.disabled}
248
- ></table-addon-qty-selector>
249
- `;
210
+ if (!(addon instanceof RentableItemSummary)) {
211
+ // Render addon selector for MarketableFee
212
+ return html`
213
+ <table-addon-qty-selector
214
+ .feeItem=${addon}
215
+ .onQuantityChange=${this.handleAddonQuantityChange}
216
+ .disabled=${this.disabled}
217
+ ></table-addon-qty-selector>
218
+ `;
250
219
  } else {
251
220
  const currentQuantity = (this.quantities.get(addon.type) as number) ?? 0;
252
221
  return html`
@@ -1,3 +1,2 @@
1
1
  export * from "./addon-table";
2
2
  export * from "./table-qty-selectors";
3
- export * from "./table-matrix-qty-selector";
@@ -1,10 +1,7 @@
1
1
  import { LitElement, html, TemplateResult } from "lit";
2
2
  import { customElement, property } from "lit/decorators.js";
3
- import {
4
- BuildingFeeView,
5
- DesiredAddon,
6
- RentableItemSummary,
7
- } from "../../../model";
3
+ import { MarketableFee } from "../../../model/marketable-fee";
4
+ import { DesiredAddon, RentableItemSummary } from "../../../model";
8
5
  import { tableQtyStyles } from "./table-qty-selectors-styles";
9
6
 
10
7
  const DEFAULT_MAX_QUANTITY = 10;
@@ -14,8 +11,8 @@ export class TableAddonQtySelector extends LitElement {
14
11
  static styles = tableQtyStyles;
15
12
 
16
13
  @property({ type: Object })
17
- feeItem: BuildingFeeView | null = null;
18
- private _previousFeeItemId: number | null = null;
14
+ feeItem: MarketableFee | null = null;
15
+ private _previousFeeItemId: string | null = null;
19
16
 
20
17
  @property()
21
18
  onQuantityChange: ((addon: DesiredAddon) => void) | null = null;
@@ -27,7 +24,7 @@ export class TableAddonQtySelector extends LitElement {
27
24
  quantity = 0;
28
25
 
29
26
  private get maxQuantity(): number {
30
- return this.feeItem?.maxAmount ?? DEFAULT_MAX_QUANTITY;
27
+ return this.feeItem?.max_estimate ?? DEFAULT_MAX_QUANTITY;
31
28
  }
32
29
 
33
30
  updated(changedProps: Map<string, unknown>): void {
@@ -56,10 +53,10 @@ export class TableAddonQtySelector extends LitElement {
56
53
  if (!this.onQuantityChange || !this.feeItem) return;
57
54
 
58
55
  this.onQuantityChange({
59
- id: this.feeItem.id ?? 0,
56
+ id: this.feeItem.id,
60
57
  quantities: [
61
58
  {
62
- itemType: this.feeItem.unitLabel ?? "",
59
+ itemType: this.feeItem.fee_name,
63
60
  quantity: this.quantity,
64
61
  },
65
62
  ],
@@ -0,0 +1,351 @@
1
+ import { LitElement, html, TemplateResult, css } from "lit";
2
+ import { customElement, property } from "lit/decorators.js";
3
+ import { ChargeInputs } from "../../../../services/fees/calculateQuote";
4
+ import { RentableItemSummary } from "../../model/rentable-item-summary";
5
+ import { DesiredRentableItem } from "../../model/desired-rentable-item";
6
+
7
+ @customElement("charge-inputs")
8
+ export class ChargeInputsComponent extends LitElement {
9
+ static styles = css`
10
+ .charge-inputs-container {
11
+ background: white;
12
+ border-radius: 8px;
13
+ padding: 16px;
14
+ margin-bottom: 16px;
15
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
16
+ }
17
+
18
+ .charge-inputs-title {
19
+ font-size: 16px;
20
+ font-weight: 600;
21
+ color: #1f2937;
22
+ margin-bottom: 16px;
23
+ }
24
+
25
+ .section-group {
26
+ margin-bottom: 16px;
27
+ }
28
+
29
+ .section-group:last-child {
30
+ margin-bottom: 0;
31
+ }
32
+
33
+ .section-subtitle {
34
+ font-size: 14px;
35
+ font-weight: 500;
36
+ color: #6b7280;
37
+ margin-bottom: 8px;
38
+ text-transform: uppercase;
39
+ letter-spacing: 0.05em;
40
+ }
41
+
42
+ .charge-input-row {
43
+ display: flex;
44
+ justify-content: space-between;
45
+ align-items: center;
46
+ padding: 8px 0;
47
+ border-bottom: 1px solid #f3f4f6;
48
+ }
49
+
50
+ .charge-input-row:last-child {
51
+ border-bottom: none;
52
+ }
53
+
54
+ .charge-input-label {
55
+ font-size: 14px;
56
+ color: #374151;
57
+ flex: 1;
58
+ }
59
+
60
+ .charge-input-control {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 8px;
64
+ }
65
+
66
+ .charge-input-button {
67
+ width: 24px;
68
+ height: 24px;
69
+ border: 1px solid #d1d5db;
70
+ background: #f9fafb;
71
+ border-radius: 4px;
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ cursor: pointer;
76
+ font-size: 14px;
77
+ color: #374151;
78
+ transition: all 0.2s;
79
+ }
80
+
81
+ .charge-input-button:hover {
82
+ background: #e5e7eb;
83
+ border-color: #9ca3af;
84
+ }
85
+
86
+ .charge-input-button:disabled {
87
+ opacity: 0.5;
88
+ cursor: not-allowed;
89
+ }
90
+
91
+ .charge-input-value {
92
+ min-width: 32px;
93
+ text-align: center;
94
+ font-size: 14px;
95
+ font-weight: 500;
96
+ color: #1f2937;
97
+ }
98
+
99
+ .rentable-item-row {
100
+ display: flex;
101
+ justify-content: space-between;
102
+ align-items: center;
103
+ padding: 8px 0;
104
+ border-bottom: 1px solid #f3f4f6;
105
+ }
106
+
107
+ .rentable-item-row:last-child {
108
+ border-bottom: none;
109
+ }
110
+
111
+ .rentable-item-info {
112
+ flex: 1;
113
+ }
114
+
115
+ .rentable-item-label {
116
+ font-size: 14px;
117
+ color: #374151;
118
+ font-weight: 500;
119
+ }
120
+
121
+ .rentable-item-type {
122
+ font-size: 12px;
123
+ color: #9ca3af;
124
+ margin-top: 1px;
125
+ font-weight: 500;
126
+ text-transform: uppercase;
127
+ letter-spacing: 0.025em;
128
+ }
129
+
130
+ .rentable-item-price {
131
+ font-size: 12px;
132
+ color: #6b7280;
133
+ margin-top: 2px;
134
+ }
135
+
136
+ .rentable-item-checkbox {
137
+ width: 18px;
138
+ height: 18px;
139
+ cursor: pointer;
140
+ }
141
+
142
+ .rentable-item-checkbox:disabled {
143
+ opacity: 0.5;
144
+ cursor: not-allowed;
145
+ }
146
+ `;
147
+
148
+ @property({ type: Object })
149
+ chargeInputs: ChargeInputs = {
150
+ base_rent: 0,
151
+ num_pets: 0,
152
+ num_vehicles: 0,
153
+ num_applicants: 1,
154
+ num_cats: 0,
155
+ num_dogs: 0,
156
+ num_other_pets: 0,
157
+ };
158
+
159
+ @property()
160
+ onChargeInputsChange: ((newInputs: Partial<ChargeInputs>) => void) | null =
161
+ null;
162
+
163
+ @property({ type: Boolean })
164
+ disabled = false;
165
+
166
+ @property({ type: Array })
167
+ rentableItems: RentableItemSummary[] = [];
168
+
169
+ @property({ type: Array })
170
+ selectedRentableItems: DesiredRentableItem[] = [];
171
+
172
+ @property()
173
+ onRentableItemAdd: ((item: DesiredRentableItem) => void) | null = null;
174
+
175
+ @property()
176
+ onRentableItemRemove: ((item: DesiredRentableItem) => void) | null = null;
177
+
178
+ private handleIncrement(field: keyof ChargeInputs): void {
179
+ const currentValue = this.chargeInputs[field] ?? 0;
180
+ const newValue = currentValue + 1;
181
+
182
+ if (
183
+ field === "num_cats" ||
184
+ field === "num_dogs" ||
185
+ field === "num_other_pets"
186
+ ) {
187
+ this.updatePetCount({ [field]: newValue });
188
+ } else {
189
+ this.onChargeInputsChange?.({ [field]: newValue });
190
+ }
191
+ }
192
+
193
+ private handleDecrement(field: keyof ChargeInputs): void {
194
+ const currentValue = this.chargeInputs[field] ?? 0;
195
+ const minValue = field === "num_applicants" ? 1 : 0; // Minimum 1 applicant
196
+ const newValue = Math.max(minValue, currentValue - 1);
197
+
198
+ if (
199
+ field === "num_cats" ||
200
+ field === "num_dogs" ||
201
+ field === "num_other_pets"
202
+ ) {
203
+ this.updatePetCount({ [field]: newValue });
204
+ } else {
205
+ this.onChargeInputsChange?.({ [field]: newValue });
206
+ }
207
+ }
208
+
209
+ private updatePetCount(petUpdate: Partial<ChargeInputs>): void {
210
+ const updatedInputs = { ...this.chargeInputs, ...petUpdate };
211
+ const totalPets =
212
+ (updatedInputs.num_cats ?? 0) +
213
+ (updatedInputs.num_dogs ?? 0) +
214
+ (updatedInputs.num_other_pets ?? 0);
215
+
216
+ this.onChargeInputsChange?.({
217
+ ...petUpdate,
218
+ num_pets: totalPets,
219
+ });
220
+ }
221
+
222
+ private handleRentableItemToggle = (item: RentableItemSummary): void => {
223
+ const isSelected = this.selectedRentableItems.some(
224
+ (selected) => selected.type === item.type
225
+ );
226
+
227
+ if (isSelected) {
228
+ const desiredItem = this.selectedRentableItems.find(
229
+ (selected) => selected.type === item.type
230
+ );
231
+ if (desiredItem) {
232
+ this.onRentableItemRemove?.(desiredItem);
233
+ }
234
+ } else {
235
+ // Select the cheapest available item
236
+ const cheapestItem = item.minAvailableItem || item.allItems[0];
237
+ if (cheapestItem) {
238
+ const desiredItem: DesiredRentableItem = {
239
+ id: cheapestItem.id,
240
+ type: item.type,
241
+ };
242
+ this.onRentableItemAdd?.(desiredItem);
243
+ }
244
+ }
245
+ };
246
+
247
+ private getFieldLabel(field: keyof ChargeInputs): string {
248
+ const labels: Record<string, string> = {
249
+ base_rent: "Base Rent ($)",
250
+ num_pets: "Pets",
251
+ num_vehicles: "Vehicles",
252
+ num_applicants: "Applicants",
253
+ num_cats: "Cats",
254
+ num_dogs: "Dogs",
255
+ num_other_pets: "All Other Pets",
256
+ };
257
+ return labels[field] || field;
258
+ }
259
+
260
+ private renderRentableItemRow(item: RentableItemSummary): TemplateResult {
261
+ const isSelected = this.selectedRentableItems.some(
262
+ (selected) => selected.type === item.type
263
+ );
264
+
265
+ return html`
266
+ <div class="rentable-item-row">
267
+ <div class="rentable-item-info">
268
+ <div class="rentable-item-label">${item.description}</div>
269
+ <div class="rentable-item-type">${item.type}</div>
270
+ <div class="rentable-item-price">${item.priceDescription}</div>
271
+ </div>
272
+ <input
273
+ type="checkbox"
274
+ class="rentable-item-checkbox"
275
+ .checked=${isSelected}
276
+ ?disabled=${this.disabled}
277
+ @change=${() => this.handleRentableItemToggle(item)}
278
+ aria-label="Select ${item.description} (${item.type})"
279
+ />
280
+ </div>
281
+ `;
282
+ }
283
+
284
+ private renderChargeInputRow(field: keyof ChargeInputs): TemplateResult {
285
+ const value = this.chargeInputs[field] ?? 0;
286
+ const minValue = field === "num_applicants" ? 1 : 0;
287
+
288
+ return html`
289
+ <div class="charge-input-row">
290
+ <div class="charge-input-label">${this.getFieldLabel(field)}</div>
291
+ <div class="charge-input-control">
292
+ <button
293
+ class="charge-input-button"
294
+ @click=${() => this.handleDecrement(field)}
295
+ ?disabled=${value <= minValue}
296
+ aria-label="Decrease ${this.getFieldLabel(field)}"
297
+ >
298
+
299
+ </button>
300
+ <div class="charge-input-value">
301
+ ${field === "base_rent" ? `$${value}` : value}
302
+ </div>
303
+ <button
304
+ class="charge-input-button"
305
+ @click=${() => this.handleIncrement(field)}
306
+ aria-label="Increase ${this.getFieldLabel(field)}"
307
+ >
308
+ +
309
+ </button>
310
+ </div>
311
+ </div>
312
+ `;
313
+ }
314
+
315
+ render(): TemplateResult {
316
+ const householdFields: (keyof ChargeInputs)[] = [
317
+ "num_applicants",
318
+ "num_cats",
319
+ "num_dogs",
320
+ "num_other_pets",
321
+ ];
322
+
323
+ return html`
324
+ <div class="charge-inputs-container">
325
+ <div class="charge-inputs-title">Customize Your Quote</div>
326
+
327
+ <div class="section-group">
328
+ <div class="section-subtitle">Household Details</div>
329
+ ${householdFields.map((field) => this.renderChargeInputRow(field))}
330
+ </div>
331
+
332
+ ${this.rentableItems.length > 0
333
+ ? html`
334
+ <div class="section-group">
335
+ <div class="section-subtitle">Rentable Items</div>
336
+ ${this.rentableItems.map((item) =>
337
+ this.renderRentableItemRow(item)
338
+ )}
339
+ </div>
340
+ `
341
+ : ""}
342
+ </div>
343
+ `;
344
+ }
345
+ }
346
+
347
+ declare global {
348
+ interface HTMLElementTagNameMap {
349
+ "charge-inputs": ChargeInputsComponent;
350
+ }
351
+ }
@@ -176,4 +176,30 @@ export default css`
176
176
  box-sizing: border-box;
177
177
  }
178
178
  }
179
+
180
+ .total-cost-section {
181
+ margin-top: 20px;
182
+ padding: 16px;
183
+ background-color: #f8f9fa;
184
+ border-radius: 4px;
185
+ border: 2px solid #0066cc;
186
+ }
187
+
188
+ .total-cost-row {
189
+ display: flex;
190
+ justify-content: space-between;
191
+ align-items: center;
192
+ }
193
+
194
+ .total-cost-label {
195
+ font-size: 18px;
196
+ font-weight: bold;
197
+ color: #333;
198
+ }
199
+
200
+ .total-cost-amount {
201
+ font-size: 24px;
202
+ font-weight: bold;
203
+ color: #0066cc;
204
+ }
179
205
  `;