@vtex/faststore-plugin-buyer-portal 1.3.17 → 1.3.18
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/CHANGELOG.md +17 -6
- package/package.json +1 -1
- package/src/features/budgets/components/BudgetDeleteDrawer/BudgetDeleteDrawer.tsx +1 -1
- package/src/features/budgets/components/BudgetEditNotificationDrawer/BudgetEditNotificationDrawer.tsx +139 -0
- package/src/features/budgets/components/BudgetEditNotificationDrawer/budget-edit-notification-drawer.scss +34 -0
- package/src/features/budgets/components/BudgetNotificationForm/BudgetNotificationForm.tsx +361 -0
- package/src/features/budgets/components/BudgetNotificationForm/budget-notification-form.scss +219 -0
- package/src/features/budgets/components/BudgetNotificationsInfo/BudgetNotificationsInfo.tsx +116 -0
- package/src/features/budgets/components/BudgetNotificationsInfo/budget-notifications-info.scss +97 -0
- package/src/features/budgets/components/BudgetUsersTable/BudgetUsersTable.tsx +118 -0
- package/src/features/budgets/components/BudgetUsersTable/budget-users-table.scss +65 -0
- package/src/features/budgets/components/BudgetsTable/BudgetsTable.tsx +10 -0
- package/src/features/budgets/components/CreateBudgetAllocationDrawer/CreateBudgetAllocationDrawer.tsx +1 -1
- package/src/features/budgets/components/CreateBudgetDrawer/CreateBudgetDrawer.tsx +86 -25
- package/src/features/budgets/components/CreateBudgetDrawer/create-budget-drawer.scss +6 -0
- package/src/features/budgets/components/DeleteBudgetAllocationDrawer/DeleteBudgetAllocationDrawer.tsx +1 -1
- package/src/features/budgets/components/EditBudgetDrawer/EditBudgetDrawer.tsx +40 -1
- package/src/features/budgets/components/EditBudgetDrawer/edit-budget-drawer.scss +5 -0
- package/src/features/budgets/hooks/useDebouncedSearchBudgetNotification.ts +37 -0
- package/src/features/budgets/hooks/useListUsers.ts +1 -1
- package/src/features/budgets/layouts/BudgetsDetailsLayout/BudgetsDetailsLayout.tsx +9 -1
- package/src/features/budgets/layouts/BudgetsDetailsLayout/budget-details-layout.scss +14 -1
- package/src/features/budgets/layouts/BudgetsLayout/BudgetsLayout.tsx +39 -0
- package/src/features/budgets/layouts/BudgetsLayout/budgets-layout.scss +1 -1
- package/src/features/budgets/types/index.ts +17 -0
- package/src/features/shared/components/AutocompleteDropdown/AutocompleteDropdownItem.tsx +4 -0
- package/src/features/shared/components/QuantitySelectorWithPercentage/QuantitySelectorWithPercentage.tsx +150 -0
- package/src/features/shared/components/index.ts +24 -23
- package/src/features/shared/types/CurrencyType.d.ts +4 -0
- package/src/features/shared/types/index.ts +4 -3
- package/src/features/shared/utils/budgetAmountParse.ts +24 -0
- package/src/features/shared/utils/constants.ts +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { Icon, IconButton, Input } from "@faststore/ui";
|
|
4
|
+
|
|
5
|
+
interface QuantitySelectorWithPercentageParams {
|
|
6
|
+
testId?: string;
|
|
7
|
+
max?: number;
|
|
8
|
+
min?: number;
|
|
9
|
+
initial?: number;
|
|
10
|
+
unitMultiplier?: number;
|
|
11
|
+
useUnitMultiplier?: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
onChange?: (value: number) => void;
|
|
14
|
+
onValidateBlur?: (min: number, maxValue: number, quantity: number) => void;
|
|
15
|
+
|
|
16
|
+
formatAsPercent?: boolean;
|
|
17
|
+
allowPercentToggle?: boolean;
|
|
18
|
+
onFormatToggle?: (formatAsPercent: boolean) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const QuantitySelectorWithPercentage = ({
|
|
22
|
+
max,
|
|
23
|
+
min = 1,
|
|
24
|
+
unitMultiplier = 1,
|
|
25
|
+
useUnitMultiplier,
|
|
26
|
+
initial,
|
|
27
|
+
disabled = false,
|
|
28
|
+
onChange,
|
|
29
|
+
onValidateBlur,
|
|
30
|
+
testId = "fs-quantity-selector",
|
|
31
|
+
formatAsPercent = true,
|
|
32
|
+
...otherProps
|
|
33
|
+
}: QuantitySelectorWithPercentageParams) => {
|
|
34
|
+
const [quantity, setQuantity] = useState<number>(initial ?? min);
|
|
35
|
+
const [multipliedUnit, setMultipliedUnit] = useState<number>(
|
|
36
|
+
quantity * unitMultiplier
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const [showPercent, setShowPercent] = useState<boolean>(formatAsPercent);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (initial) setQuantity(initial);
|
|
43
|
+
}, [initial]);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
setShowPercent(formatAsPercent);
|
|
47
|
+
}, [formatAsPercent]);
|
|
48
|
+
|
|
49
|
+
const roundUpQuantityIfNeeded = (q: number) => {
|
|
50
|
+
if (!useUnitMultiplier) return q;
|
|
51
|
+
return Math.ceil(q / unitMultiplier) * unitMultiplier;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const isLeftDisabled = quantity === min;
|
|
55
|
+
const isRightDisabled = quantity === max;
|
|
56
|
+
|
|
57
|
+
const validateQuantityBounds = (n: number): number => {
|
|
58
|
+
const maxValue = min ? Math.max(n, min) : n;
|
|
59
|
+
return max
|
|
60
|
+
? Math.min(maxValue, useUnitMultiplier ? max * unitMultiplier : max)
|
|
61
|
+
: maxValue;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const increase = () => changeQuantity(1);
|
|
65
|
+
const decrease = () => changeQuantity(-1);
|
|
66
|
+
|
|
67
|
+
const changeQuantity = (delta: number) => {
|
|
68
|
+
const next = validateQuantityBounds(quantity + delta);
|
|
69
|
+
onChange?.(next);
|
|
70
|
+
setQuantity(next);
|
|
71
|
+
setMultipliedUnit(next * unitMultiplier);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const validateBlur = () => {
|
|
75
|
+
const q = validateQuantityBounds(quantity);
|
|
76
|
+
const rounded = roundUpQuantityIfNeeded(q);
|
|
77
|
+
|
|
78
|
+
const maxValue = max ?? (min ? Math.max(quantity, min) : quantity);
|
|
79
|
+
const isOut = quantity > maxValue || quantity < min;
|
|
80
|
+
if (isOut) {
|
|
81
|
+
onValidateBlur?.(min, maxValue, rounded);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setQuantity(() => {
|
|
85
|
+
setMultipliedUnit(rounded);
|
|
86
|
+
onChange?.(rounded / unitMultiplier);
|
|
87
|
+
return rounded / unitMultiplier;
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const changeInputValue = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
92
|
+
const raw = e.target.value.replace(/\s+/g, "").replace(/[^\d]/g, "");
|
|
93
|
+
|
|
94
|
+
const numeric = Number(raw || "0");
|
|
95
|
+
setQuantity(numeric);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const displayValue = useMemo(() => {
|
|
99
|
+
const base = useUnitMultiplier ? multipliedUnit : quantity;
|
|
100
|
+
if (showPercent) return `${base}%`;
|
|
101
|
+
return String(base);
|
|
102
|
+
}, [showPercent, useUnitMultiplier, multipliedUnit, quantity]);
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<div
|
|
106
|
+
data-fs-quantity-selector={disabled ? "disabled" : "true"}
|
|
107
|
+
data-testid={testId}
|
|
108
|
+
{...otherProps}
|
|
109
|
+
style={{ display: "inline-flex", alignItems: "center", gap: 8 }}
|
|
110
|
+
>
|
|
111
|
+
<IconButton
|
|
112
|
+
data-quantity-selector-button="left"
|
|
113
|
+
icon={<Icon name="Minus" width={16} height={16} weight="bold" />}
|
|
114
|
+
aria-label="Decrement Quantity"
|
|
115
|
+
aria-controls="quantity-selector-input"
|
|
116
|
+
disabled={isLeftDisabled || disabled}
|
|
117
|
+
onClick={decrease}
|
|
118
|
+
testId={`${testId}-left-button`}
|
|
119
|
+
size="small"
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<Input
|
|
123
|
+
data-quantity-selector-input
|
|
124
|
+
id="quantity-selector-input"
|
|
125
|
+
aria-label={showPercent ? "Quantity (percent)" : "Quantity"}
|
|
126
|
+
value={displayValue}
|
|
127
|
+
onChange={changeInputValue}
|
|
128
|
+
onBlur={validateBlur}
|
|
129
|
+
onInput={(event: React.FormEvent<HTMLInputElement>) => {
|
|
130
|
+
const input = event.currentTarget;
|
|
131
|
+
input.value = input.value.replace(/[^0-9%]/g, "");
|
|
132
|
+
}}
|
|
133
|
+
disabled={disabled}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<IconButton
|
|
137
|
+
data-quantity-selector-button="right"
|
|
138
|
+
aria-controls="quantity-selector-input"
|
|
139
|
+
aria-label="Increment Quantity"
|
|
140
|
+
disabled={isRightDisabled || disabled}
|
|
141
|
+
icon={<Icon name="Plus" width={16} height={16} weight="bold" />}
|
|
142
|
+
onClick={increase}
|
|
143
|
+
testId={`${testId}-right-button`}
|
|
144
|
+
size="small"
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default QuantitySelectorWithPercentage;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export {
|
|
2
2
|
AutocompleteDropdown,
|
|
3
|
+
useAutocompleteDropdownContext,
|
|
3
4
|
type AutocompleteDropdownContextProps,
|
|
4
5
|
type AutocompleteDropdownProps,
|
|
5
|
-
useAutocompleteDropdownContext,
|
|
6
6
|
} from "./AutocompleteDropdown/AutocompleteDropdown";
|
|
7
7
|
export { useAutocompletePosition } from "./AutocompleteDropdown/useAutocompletePosition";
|
|
8
8
|
export { BasicCard, type BasicCardProps } from "./BasicCard/BasicCard";
|
|
@@ -12,19 +12,19 @@ export {
|
|
|
12
12
|
type BasicDropdownMenuProps,
|
|
13
13
|
} from "./BasicDropdownMenu/BasicDropdownMenu";
|
|
14
14
|
export {
|
|
15
|
-
BuyerPortalProvider,
|
|
16
|
-
type BuyerPortalProviderProps,
|
|
17
15
|
BuyerPortalContext,
|
|
16
|
+
BuyerPortalProvider,
|
|
18
17
|
type BuyerPortalContextType,
|
|
18
|
+
type BuyerPortalProviderProps,
|
|
19
19
|
} from "./BuyerPortalProvider/BuyerPortalProvider";
|
|
20
20
|
export { Card, CardBody, CardFooter, CardHeader } from "./Card";
|
|
21
21
|
export {
|
|
22
22
|
CustomerSwitchDrawer,
|
|
23
23
|
CustomerSwitchOption,
|
|
24
|
-
type CustomerSwitchOptionProps,
|
|
25
24
|
CustomerSwitchOptionsList,
|
|
26
|
-
type CustomerSwitchOptionsListProps,
|
|
27
25
|
CustomerSwitchSearch,
|
|
26
|
+
type CustomerSwitchOptionProps,
|
|
27
|
+
type CustomerSwitchOptionsListProps,
|
|
28
28
|
type CustomerSwitchSearchProps,
|
|
29
29
|
} from "./CustomerSwitch";
|
|
30
30
|
export { default as DropdownFilter } from "./DropdownFilter/DropdownFilter";
|
|
@@ -32,6 +32,10 @@ export {
|
|
|
32
32
|
ErrorMessage,
|
|
33
33
|
type ErrorMessageProps,
|
|
34
34
|
} from "./ErrorMessage/ErrorMessage";
|
|
35
|
+
export {
|
|
36
|
+
HeaderInside,
|
|
37
|
+
type HeaderInsideProps,
|
|
38
|
+
} from "./HeaderInside/HeaderInside";
|
|
35
39
|
export {
|
|
36
40
|
HierarchyTree,
|
|
37
41
|
type HierarchyTreeLevelProps,
|
|
@@ -42,37 +46,34 @@ export { Icon } from "./Icon";
|
|
|
42
46
|
export { InputText, type InputTextProps } from "./InputText/InputText";
|
|
43
47
|
export { default as InternalSearch } from "./InternalSearch/InternalSearch";
|
|
44
48
|
export { InternalTopBar } from "./InternalTopbar/InternalTopbar";
|
|
49
|
+
export {
|
|
50
|
+
LetterHighlight,
|
|
51
|
+
type LetterHighlightProps,
|
|
52
|
+
} from "./LetterHighlight/LetterHighlight";
|
|
53
|
+
export {
|
|
54
|
+
LevelDivider,
|
|
55
|
+
type LevelDividerProps,
|
|
56
|
+
} from "./LevelDivider/LevelDivider";
|
|
45
57
|
export { Logo } from "./Logo/Logo";
|
|
46
58
|
export { MainLinksDropdownMenu } from "./MainLinksDropdownMenu/MainLinksDropdownMenu";
|
|
59
|
+
export { Pagination } from "./Pagination/Pagination";
|
|
47
60
|
export { default as SortFilter } from "./SortFilter/SortFilter";
|
|
48
|
-
export { Tab, type TabProps
|
|
61
|
+
export { Tab, useTab, type TabProps } from "./Tab/Tab";
|
|
62
|
+
export { Table, type TableProps } from "./Table/Table";
|
|
49
63
|
export { Tag, type TagProps } from "./Tag/Tag";
|
|
50
64
|
export { default as Toast } from "./Toast/Toast";
|
|
51
65
|
export {
|
|
52
66
|
VerticalNav,
|
|
53
67
|
type VerticalNavMenuProps,
|
|
54
68
|
} from "./VerticalNav/VerticalNavMenu";
|
|
55
|
-
export {
|
|
56
|
-
LetterHighlight,
|
|
57
|
-
type LetterHighlightProps,
|
|
58
|
-
} from "./LetterHighlight/LetterHighlight";
|
|
59
|
-
export {
|
|
60
|
-
HeaderInside,
|
|
61
|
-
type HeaderInsideProps,
|
|
62
|
-
} from "./HeaderInside/HeaderInside";
|
|
63
|
-
export { Pagination } from "./Pagination/Pagination";
|
|
64
|
-
export { Table, type TableProps } from "./Table/Table";
|
|
65
|
-
export {
|
|
66
|
-
LevelDivider,
|
|
67
|
-
type LevelDividerProps,
|
|
68
|
-
} from "./LevelDivider/LevelDivider";
|
|
69
69
|
|
|
70
|
-
export { Paginator } from "./Paginator/Paginator";
|
|
71
70
|
export type { CounterProps as PaginatorCounterProps } from "./Paginator/Counter";
|
|
72
71
|
export type { NextPageButtonProps as PaginatorNextPageButtonProps } from "./Paginator/NextPageButton";
|
|
72
|
+
export { Paginator } from "./Paginator/Paginator";
|
|
73
73
|
|
|
74
74
|
export { EmptyState, type EmptyStateProps } from "./EmptyState/EmptyState";
|
|
75
|
-
export { ErrorBoundary } from "./ErrorBoundary/ErrorBoundary";
|
|
76
|
-
export { withErrorBoundary } from "./withErrorBoundary/withErrorBoundary";
|
|
77
75
|
export { default as Error } from "./Error/Error";
|
|
76
|
+
export { ErrorBoundary } from "./ErrorBoundary/ErrorBoundary";
|
|
78
77
|
export { IconBookmarked } from "./IconBookmarked/IconBookmarked";
|
|
78
|
+
export { default as QuantitySelectorWithPercentage } from "./QuantitySelectorWithPercentage/QuantitySelectorWithPercentage";
|
|
79
|
+
export { withErrorBoundary } from "./withErrorBoundary/withErrorBoundary";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
export { type AuthRouteProps } from "./AuthRouteProps";
|
|
1
2
|
export type { BreadcrumbData } from "./BreadcrumbData";
|
|
3
|
+
export type { CurrencyType, LocaleType } from "./CurrencyType";
|
|
2
4
|
export type { LoaderData } from "./LoaderData";
|
|
3
5
|
export type { IPagination } from "./Pagination";
|
|
4
|
-
export type { ScopeInput } from "./ScopeInput";
|
|
5
6
|
export type {
|
|
6
|
-
PaymentMethodsIdArray,
|
|
7
7
|
PaymentMethodResponse,
|
|
8
|
+
PaymentMethodsIdArray,
|
|
8
9
|
PaymentMethodsReqCommonParams,
|
|
9
10
|
} from "./PaymentMethodsClientTypes";
|
|
10
|
-
export {
|
|
11
|
+
export type { ScopeInput } from "./ScopeInput";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const parseAmount = (raw?: string) => {
|
|
2
|
+
if (!raw) return 0;
|
|
3
|
+
const cleaned = raw
|
|
4
|
+
.toString()
|
|
5
|
+
.replace(/[^\d.,-]/g, "")
|
|
6
|
+
.trim();
|
|
7
|
+
|
|
8
|
+
const lastComma = cleaned.lastIndexOf(",");
|
|
9
|
+
const lastDot = cleaned.lastIndexOf(".");
|
|
10
|
+
let normalized = cleaned;
|
|
11
|
+
|
|
12
|
+
if (lastComma > -1 && lastDot > -1) {
|
|
13
|
+
if (lastComma > lastDot) {
|
|
14
|
+
normalized = cleaned.replace(/\./g, "").replace(",", ".");
|
|
15
|
+
} else {
|
|
16
|
+
normalized = cleaned.replace(/,/g, "");
|
|
17
|
+
}
|
|
18
|
+
} else if (lastComma > -1) {
|
|
19
|
+
normalized = cleaned.replace(",", ".");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const n = Number(normalized);
|
|
23
|
+
return Number.isFinite(n) ? n : 0;
|
|
24
|
+
};
|