@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.
- package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/addon-table.d.ts +4 -4
- package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/index.d.ts +0 -1
- package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/table-qty-selectors.d.ts +3 -2
- package/dist/src/WebComponent/FeeCalculator/components/charge-inputs/charge-inputs.d.ts +27 -0
- package/dist/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout.d.ts +12 -12
- package/dist/src/WebComponent/FeeCalculator/components/fee-item/fee-item.d.ts +1 -1
- package/dist/src/WebComponent/FeeCalculator/components/fee-item/grouped-rentable-item.d.ts +0 -1
- package/dist/src/WebComponent/FeeCalculator/components/index.d.ts +1 -0
- package/dist/src/WebComponent/FeeCalculator/fee-calculator.d.ts +6 -5
- package/dist/src/WebComponent/FeeCalculator/model/desired-addon.d.ts +1 -1
- package/dist/src/WebComponent/FeeCalculator/model/fee-quote.d.ts +3 -4
- package/dist/src/WebComponent/FeeCalculator/model/index.d.ts +1 -4
- package/dist/src/WebComponent/FeeCalculator/model/item-combination.d.ts +1 -3
- package/dist/src/WebComponent/FeeCalculator/model/marketable-fee-new.d.ts +75 -0
- package/dist/src/WebComponent/FeeCalculator/model/marketable-fee.d.ts +79 -0
- package/dist/src/WebComponent/FeeCalculator/model/pricing-matrix.d.ts +1 -15
- package/dist/src/WebComponent/FeeCalculator/model/pricing-metadata.d.ts +1 -9
- package/dist/src/WebComponent/FeeCalculator/model/quote.d.ts +16 -2
- package/dist/src/disclaimers.d.ts +2 -1
- package/dist/src/services/fees/calculateQuote.d.ts +13 -3
- package/dist/src/services/fees/fetchBuildingFeesV2.d.ts +2 -2
- package/package.json +1 -1
- package/public/dist/index.js +571 -462
- package/src/WebComponent/FeeCalculator/components/addons/addon-table/addon-table.ts +28 -59
- package/src/WebComponent/FeeCalculator/components/addons/addon-table/index.ts +0 -1
- package/src/WebComponent/FeeCalculator/components/addons/addon-table/table-qty-selectors.ts +7 -10
- package/src/WebComponent/FeeCalculator/components/charge-inputs/charge-inputs.ts +351 -0
- package/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout-styles.ts +26 -0
- package/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout.ts +120 -86
- package/src/WebComponent/FeeCalculator/components/fee-card/fee-card.ts +19 -14
- package/src/WebComponent/FeeCalculator/components/fee-item/fee-item.ts +23 -12
- package/src/WebComponent/FeeCalculator/components/fee-item/grouped-rentable-item.ts +3 -13
- package/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector.ts +1 -1
- package/src/WebComponent/FeeCalculator/components/index.ts +1 -0
- package/src/WebComponent/FeeCalculator/fee-calculator.ts +57 -64
- package/src/WebComponent/FeeCalculator/model/desired-addon.ts +1 -1
- package/src/WebComponent/FeeCalculator/model/fee-quote.ts +3 -4
- package/src/WebComponent/FeeCalculator/model/index.ts +1 -4
- package/src/WebComponent/FeeCalculator/model/item-combination.ts +2 -12
- package/src/WebComponent/FeeCalculator/model/marketable-fee-new.ts +81 -0
- package/src/WebComponent/FeeCalculator/model/marketable-fee.ts +124 -0
- package/src/WebComponent/FeeCalculator/model/pricing-matrix.ts +3 -39
- package/src/WebComponent/FeeCalculator/model/pricing-metadata.ts +2 -10
- package/src/WebComponent/FeeCalculator/model/quote.ts +16 -2
- package/src/WebComponent/Scheduler/tour-scheduler.ts +3 -0
- package/src/WebComponent/actions/email-us-window.ts +1 -0
- package/src/disclaimers.ts +17 -13
- package/src/services/fees/calculateQuote.ts +17 -8
- package/src/services/fees/downloadQuotePdf.ts +6 -8
- package/src/services/fees/fetchBuildingFeesV2.ts +10 -6
- package/src/utils.ts +1 -1
- package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/table-matrix-qty-selector-styles.d.ts +0 -1
- package/dist/src/WebComponent/FeeCalculator/components/addons/addon-table/table-matrix-qty-selector.d.ts +0 -23
- package/dist/src/WebComponent/FeeCalculator/model/building-fee-view.d.ts +0 -42
- package/dist/src/WebComponent/FeeCalculator/model/pricing-rule.d.ts +0 -10
- package/dist/src/WebComponent/FeeCalculator/model/pricing-type.d.ts +0 -10
- package/dist/src/services/fees/downloadQuotePdf.d.ts +0 -12
- package/src/WebComponent/FeeCalculator/components/addons/addon-table/table-matrix-qty-selector-styles.ts +0 -82
- package/src/WebComponent/FeeCalculator/components/addons/addon-table/table-matrix-qty-selector.ts +0 -203
- package/src/WebComponent/FeeCalculator/model/building-fee-view.ts +0 -105
- package/src/WebComponent/FeeCalculator/model/pricing-rule.ts +0 -11
- 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: (
|
|
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(): (
|
|
35
|
+
get addons(): (MarketableFee | RentableItemSummary)[] {
|
|
37
36
|
return this._addons;
|
|
38
37
|
}
|
|
39
|
-
private _addons: (
|
|
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:
|
|
82
|
+
addon: MarketableFee,
|
|
84
83
|
newQuantities: AddonQuantityState
|
|
85
84
|
): void {
|
|
86
|
-
// Simple addon
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
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) =>
|
|
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:
|
|
213
|
-
return addon instanceof
|
|
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(): (
|
|
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:
|
|
201
|
+
addon: MarketableFee | RentableItemSummary
|
|
226
202
|
): boolean {
|
|
227
|
-
if (addon instanceof
|
|
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:
|
|
208
|
+
addon: MarketableFee | RentableItemSummary
|
|
233
209
|
): TemplateResult {
|
|
234
|
-
if (addon instanceof
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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,10 +1,7 @@
|
|
|
1
1
|
import { LitElement, html, TemplateResult } from "lit";
|
|
2
2
|
import { customElement, property } from "lit/decorators.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
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:
|
|
18
|
-
private _previousFeeItemId:
|
|
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?.
|
|
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
|
|
56
|
+
id: this.feeItem.id,
|
|
60
57
|
quantities: [
|
|
61
58
|
{
|
|
62
|
-
itemType: this.feeItem.
|
|
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
|
`;
|