@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
package/src/disclaimers.ts
CHANGED
|
@@ -20,22 +20,25 @@ const formDisclaimer = ({
|
|
|
20
20
|
emailInput,
|
|
21
21
|
orgLegalName,
|
|
22
22
|
orgSlug,
|
|
23
|
+
clickingButtonText = "send",
|
|
23
24
|
}: {
|
|
24
25
|
buildingName: string;
|
|
25
26
|
phoneNumberInput?: string;
|
|
26
27
|
emailInput?: string;
|
|
27
28
|
orgLegalName: string;
|
|
28
29
|
orgSlug: string;
|
|
30
|
+
clickingButtonText?: string;
|
|
29
31
|
}): TemplateResult => {
|
|
30
32
|
if (phoneNumberInput && orgSlug === "greystar") {
|
|
31
33
|
return html`<div style=${styleMap(disclaimerStyles.container)}>
|
|
32
|
-
By providing your phone number and clicking
|
|
33
|
-
recurring marketing calls and voice and text messages
|
|
34
|
-
its behalf at this number using artificial voice
|
|
35
|
-
Messages may be AI or human-generated. Your phone
|
|
36
|
-
communications are shared with and recorded and used by
|
|
37
|
-
not required to provide your phone number or consent to
|
|
38
|
-
lease at this property. Msg & Data rates may apply. You
|
|
34
|
+
By providing your phone number and clicking ${clickingButtonText}, you
|
|
35
|
+
consent to receive recurring marketing calls and voice and text messages
|
|
36
|
+
from Greystar or on its behalf at this number using artificial voice
|
|
37
|
+
and/or an autodialer. Messages may be AI or human-generated. Your phone
|
|
38
|
+
number and communications are shared with and recorded and used by
|
|
39
|
+
EliseAI. You are not required to provide your phone number or consent to
|
|
40
|
+
these terms to lease at this property. Msg & Data rates may apply. You
|
|
41
|
+
consent to
|
|
39
42
|
<a
|
|
40
43
|
style=${styleMap(disclaimerStyles.link)}
|
|
41
44
|
href="https://www.greystar.com/privacy"
|
|
@@ -55,11 +58,11 @@ const formDisclaimer = ({
|
|
|
55
58
|
}
|
|
56
59
|
if (phoneNumberInput) {
|
|
57
60
|
return html` <div style=${styleMap(disclaimerStyles.container)}>
|
|
58
|
-
By providing your number and clicking
|
|
59
|
-
recurring marketing calls and voice and text messages from or
|
|
60
|
-
${orgLegalName} at this number using artificial voice or an
|
|
61
|
-
system. Messages may be AI or human generated. This consent is
|
|
62
|
-
required to lease at this property. Msg & Data rates may apply. You
|
|
61
|
+
By providing your number and clicking ${clickingButtonText}, you consent
|
|
62
|
+
to receive recurring marketing calls and voice and text messages from or
|
|
63
|
+
on behalf of ${orgLegalName} at this number using artificial voice or an
|
|
64
|
+
autodialer system. Messages may be AI or human generated. This consent is
|
|
65
|
+
not required to lease at this property. Msg & Data rates may apply. You
|
|
63
66
|
consent to this
|
|
64
67
|
<a
|
|
65
68
|
style=${styleMap(disclaimerStyles.link)}
|
|
@@ -73,7 +76,8 @@ const formDisclaimer = ({
|
|
|
73
76
|
}
|
|
74
77
|
if (emailInput) {
|
|
75
78
|
return html` <div style=${styleMap(disclaimerStyles.container)}>
|
|
76
|
-
By entering your email and clicking
|
|
79
|
+
By entering your email and clicking ${clickingButtonText}, you consent to
|
|
80
|
+
this
|
|
77
81
|
<a
|
|
78
82
|
style=${styleMap(disclaimerStyles.link)}
|
|
79
83
|
href="http://bit.ly/me_privacy_policy"
|
|
@@ -2,17 +2,25 @@ import axios, { AxiosError } from "axios";
|
|
|
2
2
|
|
|
3
3
|
import { LogType, sendLoggingEvent } from "../../analytics";
|
|
4
4
|
import { camelize, snakify } from "../../utils";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
import { DesiredRentableItem } from "../../WebComponent/FeeCalculator/model";
|
|
6
|
+
|
|
7
|
+
// ChargeInputs interface matching backend structure
|
|
8
|
+
export interface ChargeInputs {
|
|
9
|
+
base_rent: number;
|
|
10
|
+
num_pets: number;
|
|
11
|
+
num_vehicles: number;
|
|
12
|
+
num_applicants: number;
|
|
13
|
+
num_cats?: number;
|
|
14
|
+
num_dogs?: number;
|
|
15
|
+
num_other_pets?: number;
|
|
16
|
+
}
|
|
9
17
|
import { Quote } from "../../WebComponent/FeeCalculator/model/quote";
|
|
10
18
|
import { BASE_DOMAIN } from "../../globals";
|
|
11
19
|
|
|
12
20
|
type CalculateQuoteRequest = {
|
|
13
21
|
buildingSlug: string;
|
|
14
22
|
unitId: number;
|
|
15
|
-
|
|
23
|
+
chargeInputs: ChargeInputs;
|
|
16
24
|
rentableItems: DesiredRentableItem[];
|
|
17
25
|
leaseTerm: number;
|
|
18
26
|
moveInDate: string;
|
|
@@ -20,6 +28,7 @@ type CalculateQuoteRequest = {
|
|
|
20
28
|
|
|
21
29
|
export type CalculateQuoteResponse = {
|
|
22
30
|
quote: Quote;
|
|
31
|
+
pdfUrl?: string;
|
|
23
32
|
};
|
|
24
33
|
|
|
25
34
|
const BASE_URL = BASE_DOMAIN;
|
|
@@ -28,7 +37,7 @@ export const calculateQuote = async (
|
|
|
28
37
|
{
|
|
29
38
|
buildingSlug,
|
|
30
39
|
unitId,
|
|
31
|
-
|
|
40
|
+
chargeInputs,
|
|
32
41
|
rentableItems,
|
|
33
42
|
leaseTerm,
|
|
34
43
|
moveInDate,
|
|
@@ -37,10 +46,10 @@ export const calculateQuote = async (
|
|
|
37
46
|
): Promise<CalculateQuoteResponse> => {
|
|
38
47
|
try {
|
|
39
48
|
const feesResponse = await axios.post(
|
|
40
|
-
`${BASE_URL}/platformApi/webchat/${buildingSlug}/
|
|
49
|
+
`${BASE_URL}/platformApi/webchat/${buildingSlug}/quote/calculate`,
|
|
41
50
|
snakify({
|
|
42
51
|
unitId,
|
|
43
|
-
|
|
52
|
+
chargeInputs,
|
|
44
53
|
rentableItems,
|
|
45
54
|
leaseTerm,
|
|
46
55
|
moveInDate,
|
|
@@ -2,17 +2,15 @@ import axios from "axios";
|
|
|
2
2
|
|
|
3
3
|
import { LogType, sendLoggingEvent } from "../../analytics";
|
|
4
4
|
import { snakify } from "../../utils";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
DesiredRentableItem,
|
|
8
|
-
} from "../../WebComponent/FeeCalculator/model";
|
|
5
|
+
import { DesiredRentableItem } from "../../WebComponent/FeeCalculator/model";
|
|
6
|
+
import { ChargeInputs } from "./calculateQuote";
|
|
9
7
|
import { BASE_DOMAIN } from "../../globals";
|
|
10
8
|
|
|
11
9
|
type DownloadQuotePdfRequest = {
|
|
12
10
|
buildingSlug: string;
|
|
13
11
|
unitId: number;
|
|
14
12
|
unitNumber: string;
|
|
15
|
-
|
|
13
|
+
chargeInputs: ChargeInputs;
|
|
16
14
|
rentableItems: DesiredRentableItem[];
|
|
17
15
|
leaseTerm: number;
|
|
18
16
|
moveInDate: string;
|
|
@@ -24,17 +22,17 @@ export const downloadQuotePdf = async ({
|
|
|
24
22
|
buildingSlug,
|
|
25
23
|
unitId,
|
|
26
24
|
unitNumber,
|
|
27
|
-
|
|
25
|
+
chargeInputs,
|
|
28
26
|
rentableItems,
|
|
29
27
|
leaseTerm,
|
|
30
28
|
moveInDate,
|
|
31
29
|
}: DownloadQuotePdfRequest): Promise<void> => {
|
|
32
30
|
try {
|
|
33
31
|
const response = await axios.post(
|
|
34
|
-
`${BASE_URL}/platformApi/webchat/${buildingSlug}/
|
|
32
|
+
`${BASE_URL}/platformApi/webchat/${buildingSlug}/quote/download-pdf`,
|
|
35
33
|
snakify({
|
|
36
34
|
unitId,
|
|
37
|
-
|
|
35
|
+
chargeInputs,
|
|
38
36
|
rentableItems,
|
|
39
37
|
leaseTerm,
|
|
40
38
|
moveInDate,
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import { LogType, sendLoggingEvent } from "../../analytics";
|
|
3
|
-
import { BASE_DOMAIN } from "../../globals";
|
|
4
3
|
import { camelize } from "../../utils";
|
|
5
4
|
import { IncentiveV2 } from "../../types/incentive-v2";
|
|
6
5
|
import { RentableItemSummary } from "../../WebComponent/FeeCalculator/model/rentable-item-summary";
|
|
7
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
MarketableFee,
|
|
8
|
+
MarketableFeeModel,
|
|
9
|
+
} from "../../WebComponent/FeeCalculator/model/marketable-fee";
|
|
8
10
|
import QueryParamBuilder from "../../utils/queryParamBuilder";
|
|
11
|
+
import { BASE_DOMAIN } from "../../globals";
|
|
9
12
|
|
|
10
13
|
const BASE_URL = BASE_DOMAIN;
|
|
11
14
|
|
|
12
15
|
type BuildingFeeResponse = {
|
|
13
|
-
fees:
|
|
16
|
+
fees: MarketableFee[];
|
|
14
17
|
rentableItems: RentableItemSummary[];
|
|
15
18
|
buildingIncentives: IncentiveV2[];
|
|
16
19
|
};
|
|
@@ -25,12 +28,12 @@ const fetchBuildingFeesV2 = async (
|
|
|
25
28
|
layoutIds?.map((id) => id.toString())
|
|
26
29
|
);
|
|
27
30
|
const feesResponse = await axios.get(
|
|
28
|
-
`${BASE_URL}/platformApi/webchat/${buildingSlug}/fees?${params.build()}`
|
|
31
|
+
`${BASE_URL}/platformApi/webchat/${buildingSlug}/marketable-fees?${params.build()}`
|
|
29
32
|
);
|
|
30
33
|
if (feesResponse.data) {
|
|
31
34
|
const data = camelize<BuildingFeeResponse>(feesResponse.data);
|
|
32
35
|
return {
|
|
33
|
-
fees: data.fees.map(
|
|
36
|
+
fees: data.fees.map(toMarketableFeeClass),
|
|
34
37
|
rentableItems: data.rentableItems.map(toRentableItemSummary),
|
|
35
38
|
buildingIncentives: data.buildingIncentives,
|
|
36
39
|
};
|
|
@@ -51,7 +54,8 @@ const fetchBuildingFeesV2 = async (
|
|
|
51
54
|
};
|
|
52
55
|
};
|
|
53
56
|
|
|
54
|
-
const
|
|
57
|
+
const toMarketableFeeClass = (fee: MarketableFee) =>
|
|
58
|
+
new MarketableFeeModel(fee);
|
|
55
59
|
|
|
56
60
|
const toRentableItemSummary = (item: RentableItemSummary) =>
|
|
57
61
|
new RentableItemSummary(item);
|
package/src/utils.ts
CHANGED
|
@@ -117,7 +117,7 @@ export const formatCurrency = (
|
|
|
117
117
|
return Intl.NumberFormat("en-US", {
|
|
118
118
|
style: "currency",
|
|
119
119
|
currency: "USD",
|
|
120
|
-
minimumFractionDigits:
|
|
120
|
+
minimumFractionDigits: maximumFractionDigits,
|
|
121
121
|
maximumFractionDigits,
|
|
122
122
|
}).format(amount ?? 0);
|
|
123
123
|
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const tableMatrixStyles: import("lit").CSSResult;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { LitElement, TemplateResult } from "lit";
|
|
2
|
-
import { BuildingFeeView, DesiredAddon } from "../../../model";
|
|
3
|
-
export declare class TableMatrixQtySelector extends LitElement {
|
|
4
|
-
static styles: import("lit").CSSResult;
|
|
5
|
-
set feeItem(value: BuildingFeeView | null);
|
|
6
|
-
get feeItem(): BuildingFeeView | null;
|
|
7
|
-
private _feeItem;
|
|
8
|
-
onQuantityChange: ((addon: DesiredAddon) => void) | null;
|
|
9
|
-
disabled: boolean;
|
|
10
|
-
private quantities;
|
|
11
|
-
private _availableTypes;
|
|
12
|
-
private get matrixRules();
|
|
13
|
-
private getAvailableTypes;
|
|
14
|
-
private get itemTypes();
|
|
15
|
-
private getMaxQuantityForType;
|
|
16
|
-
private isValidCombination;
|
|
17
|
-
private handleIncrement;
|
|
18
|
-
private handleDecrement;
|
|
19
|
-
private notifyQuantityChange;
|
|
20
|
-
private formatItemType;
|
|
21
|
-
private renderItemType;
|
|
22
|
-
render(): TemplateResult;
|
|
23
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { PricingMatrix } from "./pricing-matrix";
|
|
2
|
-
export declare enum BuildingFeeFrequency {
|
|
3
|
-
OneTime = "One Time",
|
|
4
|
-
Monthly = "Monthly",
|
|
5
|
-
Quarterly = "Quarterly",
|
|
6
|
-
Annually = "Annually",
|
|
7
|
-
PerOccurrence = "Per Occurrence"
|
|
8
|
-
}
|
|
9
|
-
export declare class BuildingFeeView {
|
|
10
|
-
id?: number;
|
|
11
|
-
buildingId: number;
|
|
12
|
-
feeName: string;
|
|
13
|
-
description?: string;
|
|
14
|
-
category: string;
|
|
15
|
-
unitIds?: number[];
|
|
16
|
-
layoutIds?: number[];
|
|
17
|
-
startDate?: Date;
|
|
18
|
-
endDate?: Date;
|
|
19
|
-
source?: string;
|
|
20
|
-
externalKey?: string;
|
|
21
|
-
syncActive: boolean;
|
|
22
|
-
hiddenReason?: string;
|
|
23
|
-
refundable: boolean;
|
|
24
|
-
bundleKey?: string;
|
|
25
|
-
publicFacing: boolean;
|
|
26
|
-
chargeCode?: string;
|
|
27
|
-
prorated?: boolean;
|
|
28
|
-
pricingCategory: "mandatory" | "situational";
|
|
29
|
-
pricingType: "fixed" | "per-item" | "usage-based" | "percent-rent" | "custom";
|
|
30
|
-
frequency: BuildingFeeFrequency;
|
|
31
|
-
nlpPricing?: string;
|
|
32
|
-
isAddon: boolean;
|
|
33
|
-
amount?: number;
|
|
34
|
-
percentRent?: number;
|
|
35
|
-
unitLabel?: string;
|
|
36
|
-
minAmount?: number;
|
|
37
|
-
maxAmount?: number;
|
|
38
|
-
customMatrixData?: PricingMatrix;
|
|
39
|
-
constructor(data?: Partial<BuildingFeeView>);
|
|
40
|
-
get isOptional(): boolean;
|
|
41
|
-
get hasMatrix(): boolean;
|
|
42
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { ItemCombination } from "./item-combination";
|
|
2
|
-
export interface PricingRule {
|
|
3
|
-
combination: ItemCombination;
|
|
4
|
-
price?: number;
|
|
5
|
-
percentRent?: number;
|
|
6
|
-
percentRentMin?: number;
|
|
7
|
-
percentRentMax?: number;
|
|
8
|
-
minimumPrice?: number;
|
|
9
|
-
maximumPrice?: number;
|
|
10
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
declare enum PricingType {
|
|
2
|
-
FIXED = "FIXED",
|
|
3
|
-
USAGE_BASED = "USAGE_BASED",
|
|
4
|
-
PERCENT_RENT = "PERCENT_RENT",
|
|
5
|
-
PER_ITEM = "PER_ITEM",
|
|
6
|
-
CUSTOM = "CUSTOM",
|
|
7
|
-
FIXED_MAX_PERCENT_RENT = "FIXED_MAX_PERCENT_RENT",
|
|
8
|
-
FIXED_MIN_PERCENT_RENT = "FIXED_MIN_PERCENT_RENT"
|
|
9
|
-
}
|
|
10
|
-
export default PricingType;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { DesiredAddon, DesiredRentableItem } from "../../WebComponent/FeeCalculator/model";
|
|
2
|
-
declare type DownloadQuotePdfRequest = {
|
|
3
|
-
buildingSlug: string;
|
|
4
|
-
unitId: number;
|
|
5
|
-
unitNumber: string;
|
|
6
|
-
addons: DesiredAddon[];
|
|
7
|
-
rentableItems: DesiredRentableItem[];
|
|
8
|
-
leaseTerm: number;
|
|
9
|
-
moveInDate: string;
|
|
10
|
-
};
|
|
11
|
-
export declare const downloadQuotePdf: ({ buildingSlug, unitId, unitNumber, addons, rentableItems, leaseTerm, moveInDate, }: DownloadQuotePdfRequest) => Promise<void>;
|
|
12
|
-
export default downloadQuotePdf;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { css } from "lit";
|
|
2
|
-
|
|
3
|
-
export const tableMatrixStyles = css`
|
|
4
|
-
:host {
|
|
5
|
-
display: block;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.matrix-container {
|
|
9
|
-
display: flex;
|
|
10
|
-
flex-direction: column;
|
|
11
|
-
gap: 8px;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.item-type-row {
|
|
15
|
-
display: flex;
|
|
16
|
-
align-items: center;
|
|
17
|
-
justify-content: space-between;
|
|
18
|
-
gap: 8px;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.item-label {
|
|
22
|
-
flex: 1;
|
|
23
|
-
font-size: 0.875rem;
|
|
24
|
-
color: #4b5563;
|
|
25
|
-
text-align: left;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.quantity-control {
|
|
29
|
-
display: inline-flex;
|
|
30
|
-
justify-content: center;
|
|
31
|
-
align-items: center;
|
|
32
|
-
border: 1px solid #dee2e6;
|
|
33
|
-
border-radius: 4px;
|
|
34
|
-
background-color: #fff;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.quantity-control.unavailable {
|
|
38
|
-
background-color: #f9f9f9;
|
|
39
|
-
border-color: #e0e0e0;
|
|
40
|
-
cursor: not-allowed;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.operator-sign {
|
|
44
|
-
display: inline-block;
|
|
45
|
-
line-height: 1;
|
|
46
|
-
position: relative;
|
|
47
|
-
transform: translateY(1px);
|
|
48
|
-
width: 0.8rem;
|
|
49
|
-
text-align: center;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.quantity-button {
|
|
53
|
-
font-size: 1.1rem;
|
|
54
|
-
padding: 0.3rem 0.4rem;
|
|
55
|
-
background: none;
|
|
56
|
-
border: none;
|
|
57
|
-
color: #347ff7;
|
|
58
|
-
cursor: pointer;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.quantity-button:hover {
|
|
62
|
-
background-color: #f3f4f6;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.quantity-button:disabled {
|
|
66
|
-
opacity: 0.5;
|
|
67
|
-
cursor: not-allowed;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.quantity-value {
|
|
71
|
-
padding: 0.25rem;
|
|
72
|
-
min-width: 1.5rem;
|
|
73
|
-
text-align: center;
|
|
74
|
-
font-size: 0.875rem;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.unavailable-text {
|
|
78
|
-
color: #9ca3af;
|
|
79
|
-
font-style: italic;
|
|
80
|
-
cursor: not-allowed;
|
|
81
|
-
}
|
|
82
|
-
`;
|
package/src/WebComponent/FeeCalculator/components/addons/addon-table/table-matrix-qty-selector.ts
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { LitElement, html, TemplateResult } from "lit";
|
|
2
|
-
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
-
import { BuildingFeeView, DesiredAddon, PricingRule } from "../../../model";
|
|
4
|
-
|
|
5
|
-
import { tableMatrixStyles } from "./table-matrix-qty-selector-styles";
|
|
6
|
-
|
|
7
|
-
@customElement("table-matrix-qty-selector")
|
|
8
|
-
export class TableMatrixQtySelector extends LitElement {
|
|
9
|
-
static styles = tableMatrixStyles;
|
|
10
|
-
|
|
11
|
-
@property({ type: Object })
|
|
12
|
-
set feeItem(value: BuildingFeeView | null) {
|
|
13
|
-
const oldValue = this._feeItem;
|
|
14
|
-
const oldId = oldValue?.id ?? null;
|
|
15
|
-
const newId = value?.id ?? null;
|
|
16
|
-
|
|
17
|
-
this._feeItem = value;
|
|
18
|
-
if (newId !== oldId) {
|
|
19
|
-
this.quantities = {};
|
|
20
|
-
this._availableTypes = this.getAvailableTypes();
|
|
21
|
-
}
|
|
22
|
-
this.requestUpdate("feeItem", oldValue);
|
|
23
|
-
}
|
|
24
|
-
get feeItem(): BuildingFeeView | null {
|
|
25
|
-
return this._feeItem;
|
|
26
|
-
}
|
|
27
|
-
private _feeItem: BuildingFeeView | null = null;
|
|
28
|
-
|
|
29
|
-
@property()
|
|
30
|
-
onQuantityChange: ((addon: DesiredAddon) => void) | null = null;
|
|
31
|
-
|
|
32
|
-
@property({ type: Boolean, reflect: true })
|
|
33
|
-
disabled = false;
|
|
34
|
-
|
|
35
|
-
@state()
|
|
36
|
-
private quantities: Record<string, number> = {};
|
|
37
|
-
|
|
38
|
-
// Cache available types to prevent recalculation on each render
|
|
39
|
-
private _availableTypes: string[] = [];
|
|
40
|
-
|
|
41
|
-
private get matrixRules(): PricingRule[] {
|
|
42
|
-
return this.feeItem?.customMatrixData?.toRuleArray() ?? [];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private getAvailableTypes(): string[] {
|
|
46
|
-
const availableTypes = new Set<string>();
|
|
47
|
-
this.matrixRules.forEach((rule) => {
|
|
48
|
-
rule.combination.quantities.forEach((q) =>
|
|
49
|
-
availableTypes.add(q.itemType)
|
|
50
|
-
);
|
|
51
|
-
});
|
|
52
|
-
return Array.from(availableTypes).sort();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private get itemTypes(): string[] {
|
|
56
|
-
return this._availableTypes;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
private getMaxQuantityForType(itemType: string): number {
|
|
60
|
-
let max = 0;
|
|
61
|
-
this.matrixRules.forEach((rule) => {
|
|
62
|
-
const itemQty = rule.combination.quantities.find(
|
|
63
|
-
(q) => q.itemType === itemType
|
|
64
|
-
);
|
|
65
|
-
if (itemQty) {
|
|
66
|
-
max = Math.max(max, itemQty.quantity);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
return max;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private isValidCombination(type: string, newQuantity: number): boolean {
|
|
73
|
-
const proposedQuantities = { ...this.quantities, [type]: newQuantity };
|
|
74
|
-
|
|
75
|
-
const relevantRules = this.matrixRules.filter((rule) =>
|
|
76
|
-
rule.combination.quantities.some((q) => q.itemType === type)
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
return relevantRules.some((rule) => {
|
|
80
|
-
const ruleQuantities = new Map(
|
|
81
|
-
rule.combination.quantities.map((q) => [q.itemType, q.quantity])
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
return Array.from(ruleQuantities.entries()).every(
|
|
85
|
-
([itemType, quantity]) => {
|
|
86
|
-
const proposedQty = proposedQuantities[itemType] ?? 0;
|
|
87
|
-
return proposedQty === quantity;
|
|
88
|
-
}
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private handleIncrement(type: string): void {
|
|
94
|
-
if (this.disabled) return;
|
|
95
|
-
|
|
96
|
-
const currentQty = this.quantities[type] ?? 0;
|
|
97
|
-
const newQty = currentQty + 1;
|
|
98
|
-
|
|
99
|
-
if (newQty > this.getMaxQuantityForType(type)) return;
|
|
100
|
-
if (!this.isValidCombination(type, newQty)) return;
|
|
101
|
-
|
|
102
|
-
const newQuantities = { ...this.quantities };
|
|
103
|
-
newQuantities[type] = newQty;
|
|
104
|
-
this.quantities = newQuantities;
|
|
105
|
-
|
|
106
|
-
this.notifyQuantityChange();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private handleDecrement(type: string): void {
|
|
110
|
-
if (this.disabled) return;
|
|
111
|
-
|
|
112
|
-
const currentQty = this.quantities[type] ?? 0;
|
|
113
|
-
if (currentQty <= 0) return;
|
|
114
|
-
|
|
115
|
-
const newQty = currentQty - 1;
|
|
116
|
-
if (!this.isValidCombination(type, newQty)) return;
|
|
117
|
-
|
|
118
|
-
const newQuantities = { ...this.quantities };
|
|
119
|
-
newQuantities[type] = newQty;
|
|
120
|
-
this.quantities = newQuantities;
|
|
121
|
-
|
|
122
|
-
this.notifyQuantityChange();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private notifyQuantityChange(): void {
|
|
126
|
-
if (!this.onQuantityChange || !this.feeItem) return;
|
|
127
|
-
|
|
128
|
-
// Include all available types, using 0 as default quantity if not set
|
|
129
|
-
// e.g. { "dog": 1, "cat": 2 }
|
|
130
|
-
const quantities = this.itemTypes.map((type) => ({
|
|
131
|
-
itemType: type,
|
|
132
|
-
quantity: this.quantities[type] ?? 0,
|
|
133
|
-
}));
|
|
134
|
-
|
|
135
|
-
this.onQuantityChange({
|
|
136
|
-
id: this.feeItem.id ?? 0,
|
|
137
|
-
quantities,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
private formatItemType(type: string): string {
|
|
142
|
-
return type
|
|
143
|
-
.split(/[_-]/)
|
|
144
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
145
|
-
.join(" ");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
private renderItemType(type: string, isSingleItem: boolean): TemplateResult {
|
|
149
|
-
const quantity = this.quantities[type] ?? 0;
|
|
150
|
-
const maxQty = this.getMaxQuantityForType(type);
|
|
151
|
-
const canIncrement =
|
|
152
|
-
this.isValidCombination(type, quantity + 1) && quantity < maxQty;
|
|
153
|
-
const formattedType = this.formatItemType(type);
|
|
154
|
-
|
|
155
|
-
const showTypeLabel = !isSingleItem;
|
|
156
|
-
|
|
157
|
-
return html`
|
|
158
|
-
<div class="item-type-row">
|
|
159
|
-
${showTypeLabel
|
|
160
|
-
? html`<div class="item-label">${formattedType}:</div>`
|
|
161
|
-
: html`<div></div>`}
|
|
162
|
-
<div class="quantity-control ${this.disabled ? "unavailable" : ""}">
|
|
163
|
-
<button
|
|
164
|
-
class="quantity-button"
|
|
165
|
-
@click=${() => this.handleDecrement(type)}
|
|
166
|
-
?disabled=${this.disabled || quantity <= 0}
|
|
167
|
-
aria-label="Decrease ${type} quantity"
|
|
168
|
-
>
|
|
169
|
-
<span class="operator-sign">−</span>
|
|
170
|
-
</button>
|
|
171
|
-
<span
|
|
172
|
-
class="quantity-value ${this.disabled ? "unavailable-text" : ""}"
|
|
173
|
-
>
|
|
174
|
-
${quantity}
|
|
175
|
-
</span>
|
|
176
|
-
<button
|
|
177
|
-
class="quantity-button"
|
|
178
|
-
@click=${() => this.handleIncrement(type)}
|
|
179
|
-
?disabled=${this.disabled || !canIncrement}
|
|
180
|
-
aria-label="Increase ${type} quantity"
|
|
181
|
-
>
|
|
182
|
-
<span class="operator-sign">+</span>
|
|
183
|
-
</button>
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
render(): TemplateResult {
|
|
190
|
-
if (!this.feeItem || !this.matrixRules.length) return html``;
|
|
191
|
-
|
|
192
|
-
const types = this.itemTypes;
|
|
193
|
-
if (!types.length) return html``;
|
|
194
|
-
|
|
195
|
-
const isSingleItem = types.length === 1;
|
|
196
|
-
|
|
197
|
-
return html`
|
|
198
|
-
<div class="matrix-container">
|
|
199
|
-
${types.map((type) => this.renderItemType(type, isSingleItem))}
|
|
200
|
-
</div>
|
|
201
|
-
`;
|
|
202
|
-
}
|
|
203
|
-
}
|