@vtex/faststore-plugin-buyer-portal 1.3.16 → 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/.github/workflows/betarelease.yaml +69 -0
- package/CHANGELOG.md +24 -6
- package/beta-release.sh +23 -0
- package/package.json +3 -2
- 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,37 @@
|
|
|
1
|
+
import { useDebounce } from "../../shared/hooks";
|
|
2
|
+
import { DEBOUNCE_TIMEOUT } from "../../shared/utils";
|
|
3
|
+
|
|
4
|
+
import { useListUsers } from "./useListUsers";
|
|
5
|
+
|
|
6
|
+
type Params = {
|
|
7
|
+
customerId: string;
|
|
8
|
+
unitId: string;
|
|
9
|
+
search?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const useDebouncedSearchBudgetNotification = ({
|
|
13
|
+
customerId,
|
|
14
|
+
unitId,
|
|
15
|
+
search = "",
|
|
16
|
+
}: Params) => {
|
|
17
|
+
const debouncedSearchTerm = useDebounce(search, DEBOUNCE_TIMEOUT);
|
|
18
|
+
const isDebouncing = search !== debouncedSearchTerm;
|
|
19
|
+
|
|
20
|
+
const { listUsers, isListUsersLoading } = useListUsers({
|
|
21
|
+
keys: `${debouncedSearchTerm}`,
|
|
22
|
+
data: {
|
|
23
|
+
customerId,
|
|
24
|
+
unitId,
|
|
25
|
+
params: {
|
|
26
|
+
search: debouncedSearchTerm,
|
|
27
|
+
page: 1,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
usersResponse: listUsers,
|
|
34
|
+
isLoadingUsers: isListUsersLoading,
|
|
35
|
+
isDebouncing,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
@@ -16,11 +16,12 @@ import { ContractTabsLayout, GlobalLayout } from "../../../shared/layouts";
|
|
|
16
16
|
import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
17
17
|
import { BudgetAllocationsTable } from "../../components/BudgetAllocationsTable/BudgetAllocationsTable";
|
|
18
18
|
import { BudgetDeleteDrawer } from "../../components/BudgetDeleteDrawer/BudgetDeleteDrawer";
|
|
19
|
+
import { BudgetNotificationsInfo } from "../../components/BudgetNotificationsInfo/BudgetNotificationsInfo";
|
|
19
20
|
import { BudgetRemainingBalance } from "../../components/BudgetRemainingBalance/BudgetRemainingBalance";
|
|
20
21
|
import { BudgetSettingsInfoBalance } from "../../components/BudgetSettingsInfo/BudgetSettingsInfo";
|
|
21
22
|
import { CreateBudgetAllocationDrawer } from "../../components/CreateBudgetAllocationDrawer/CreateBudgetAllocationDrawer";
|
|
22
23
|
import { EditBudgetDrawer } from "../../components/EditBudgetDrawer/EditBudgetDrawer";
|
|
23
|
-
import { useListAllocations } from "../../hooks";
|
|
24
|
+
import { useListAllocations } from "../../hooks/useListAllocations";
|
|
24
25
|
|
|
25
26
|
import type { Budget, BudgetAllocationListResponse } from "../../types";
|
|
26
27
|
|
|
@@ -203,6 +204,13 @@ export const BudgetsDetailsLayout = ({ budget }: BudgetsDetailsLayoutProps) => {
|
|
|
203
204
|
contractId={contract?.id ?? ""}
|
|
204
205
|
/>
|
|
205
206
|
|
|
207
|
+
<BudgetNotificationsInfo
|
|
208
|
+
initialBudget={budget}
|
|
209
|
+
budgetId={budget.id}
|
|
210
|
+
orgUnitId={orgUnit?.id ?? ""}
|
|
211
|
+
contractId={contract?.id ?? ""}
|
|
212
|
+
/>
|
|
213
|
+
|
|
206
214
|
{hasMounted && (
|
|
207
215
|
<BudgetAllocationsTable
|
|
208
216
|
loading={isAllocationsLoading && !loadingLoadMore}
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
@import "../../components/BudgetAllocationsSelection/budget-allocations-selection.scss";
|
|
5
5
|
@import "../../../budgets/components/DeleteBudgetAllocationDrawer/delete-budget-allocations.scss";
|
|
6
6
|
@import "../../components/CreateBudgetAllocationDrawer/create-budget-allocation-drawer.scss";
|
|
7
|
+
@import "../../components/BudgetEditNotificationDrawer/budget-edit-notification-drawer.scss";
|
|
7
8
|
|
|
8
9
|
[data-fs-bp-budgets-details-layout] {
|
|
9
10
|
@import "../../components/CreateBudgetDrawer/create-budget-drawer.scss";
|
|
10
11
|
@import "../../components/BudgetAddForm/budget-add-form.scss";
|
|
11
12
|
@import "../../components/BudgetRemainingBalance/budget-remaining-balance.scss";
|
|
12
|
-
|
|
13
|
+
@import "../../components/BudgetNotificationForm/budget-notification-form.scss";
|
|
14
|
+
@import "../../components/BudgetNotificationsInfo/budget-notifications-info.scss";
|
|
13
15
|
@import "../../components/BudgetSettingsInfo/budget-settings-info.scss";
|
|
14
16
|
@import "../../../shared/components/InternalSearch/internal-search.scss";
|
|
15
17
|
@import "../../components/BudgetsTable/budgets-table.scss";
|
|
@@ -37,6 +39,17 @@
|
|
|
37
39
|
cursor: pointer;
|
|
38
40
|
}
|
|
39
41
|
}
|
|
42
|
+
[data-fs-bp-budget-notifications-empty] {
|
|
43
|
+
[data-fs-empty-state-section] {
|
|
44
|
+
padding: 4rem 17rem;
|
|
45
|
+
text-align: center;
|
|
46
|
+
color: #858585;
|
|
47
|
+
|
|
48
|
+
@include media("<=tablet") {
|
|
49
|
+
padding: 4rem clamp(1rem, 5vw, 17rem);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
40
53
|
|
|
41
54
|
[data-fs-bp-budgets-details-section] {
|
|
42
55
|
min-height: calc(100vh - calc(var(--fs-spacing-9) + var(--fs-spacing-0)));
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "../../../shared/hooks";
|
|
11
11
|
import { FinanceTabsLayout, GlobalLayout } from "../../../shared/layouts";
|
|
12
12
|
import { BudgetDeleteDrawer } from "../../components/BudgetDeleteDrawer/BudgetDeleteDrawer";
|
|
13
|
+
import { BudgetEditNotificationDrawer } from "../../components/BudgetEditNotificationDrawer/BudgetEditNotificationDrawer";
|
|
13
14
|
import { BudgetsTable } from "../../components/BudgetsTable/BudgetsTable";
|
|
14
15
|
import { CreateBudgetAllocationDrawer } from "../../components/CreateBudgetAllocationDrawer/CreateBudgetAllocationDrawer";
|
|
15
16
|
import { CreateBudgetDrawer } from "../../components/CreateBudgetDrawer/CreateBudgetDrawer";
|
|
@@ -73,6 +74,12 @@ export const BudgetsLayout = ({ data }: BudgetsLayoutProps) => {
|
|
|
73
74
|
...createBudgetDrawerProps
|
|
74
75
|
} = useDrawerProps();
|
|
75
76
|
|
|
77
|
+
const {
|
|
78
|
+
open: openNotificationDrawer,
|
|
79
|
+
isOpen: isNotificationDrawerOpen,
|
|
80
|
+
...notificationDrawerProps
|
|
81
|
+
} = useDrawerProps();
|
|
82
|
+
|
|
76
83
|
async function loadMore() {
|
|
77
84
|
setPaginationLoading(true);
|
|
78
85
|
setLoading(true);
|
|
@@ -134,6 +141,26 @@ export const BudgetsLayout = ({ data }: BudgetsLayoutProps) => {
|
|
|
134
141
|
[page, contract?.id, orgUnit?.id, cookie]
|
|
135
142
|
);
|
|
136
143
|
|
|
144
|
+
const handleCreateNotification = useCallback(
|
|
145
|
+
async (budgetId: string) => {
|
|
146
|
+
setSelectedBudgetId(budgetId);
|
|
147
|
+
try {
|
|
148
|
+
const budgetsData = await getBudgetByIdService({
|
|
149
|
+
budgetId,
|
|
150
|
+
customerId: contract?.id ?? "",
|
|
151
|
+
unitId: orgUnit?.id ?? "",
|
|
152
|
+
cookie,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
setBudgetToEdit({ ...budgetsData, amount: String(budgetsData.amount) });
|
|
156
|
+
openNotificationDrawer();
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error("Failed to load budget:", error);
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
[page, contract?.id, orgUnit?.id, cookie]
|
|
162
|
+
);
|
|
163
|
+
|
|
137
164
|
return (
|
|
138
165
|
<GlobalLayout>
|
|
139
166
|
<FinanceTabsLayout pageName="Finance and Compliance">
|
|
@@ -164,6 +191,7 @@ export const BudgetsLayout = ({ data }: BudgetsLayoutProps) => {
|
|
|
164
191
|
total={Number(total ?? 0)}
|
|
165
192
|
onClickAllocationPage={handleAddAllocation}
|
|
166
193
|
onClickEditBudget={handleBudgetEditPage}
|
|
194
|
+
onClickCreateNotification={handleCreateNotification}
|
|
167
195
|
openDeleteDrawer={(budgetId) => {
|
|
168
196
|
setSelectedBudgetId(budgetId);
|
|
169
197
|
openDeleteDrawer();
|
|
@@ -232,6 +260,17 @@ export const BudgetsLayout = ({ data }: BudgetsLayoutProps) => {
|
|
|
232
260
|
initialBudget={budgetsToEdit}
|
|
233
261
|
/>
|
|
234
262
|
)}
|
|
263
|
+
|
|
264
|
+
{isNotificationDrawerOpen && budgetsToEdit && (
|
|
265
|
+
<BudgetEditNotificationDrawer
|
|
266
|
+
{...notificationDrawerProps}
|
|
267
|
+
isOpen={isNotificationDrawerOpen}
|
|
268
|
+
budget={budgetsToEdit}
|
|
269
|
+
budgetId={selectedBudgetId}
|
|
270
|
+
orgUnitId={orgUnit?.id ?? ""}
|
|
271
|
+
contractId={contract?.id ?? ""}
|
|
272
|
+
/>
|
|
273
|
+
)}
|
|
235
274
|
</section>
|
|
236
275
|
</FinanceTabsLayout>
|
|
237
276
|
</GlobalLayout>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
@import "@faststore/ui/src/components/molecules/Tooltip/styles.scss";
|
|
5
5
|
@import "@faststore/ui/src/components/molecules/Table/styles.scss";
|
|
6
6
|
@import "@faststore/ui/src/components/atoms/Button/styles.scss";
|
|
7
|
-
|
|
7
|
+
@import "../../components/BudgetNotificationForm/budget-notification-form.scss";
|
|
8
8
|
@import "../../../shared/components/InternalSearch/internal-search.scss";
|
|
9
9
|
@import "../../../shared/components/HeaderInside/header-inside.scss";
|
|
10
10
|
@import "../../components/BudgetsTable/budgets-table.scss";
|
|
@@ -25,6 +25,22 @@ export type BudgetAllocationListResponse = {
|
|
|
25
25
|
data: BudgetAllocation[];
|
|
26
26
|
total: number;
|
|
27
27
|
};
|
|
28
|
+
|
|
29
|
+
export type NotificationThresholds = {
|
|
30
|
+
value: number;
|
|
31
|
+
};
|
|
32
|
+
export type NotificationUsers = {
|
|
33
|
+
userId: string;
|
|
34
|
+
userName: string;
|
|
35
|
+
userEmail: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type BudgetNotification = {
|
|
39
|
+
hasNotification: boolean;
|
|
40
|
+
thresholds: NotificationThresholds[];
|
|
41
|
+
users: NotificationUsers[];
|
|
42
|
+
};
|
|
43
|
+
|
|
28
44
|
export type Budget = {
|
|
29
45
|
id: string;
|
|
30
46
|
name: string;
|
|
@@ -39,6 +55,7 @@ export type Budget = {
|
|
|
39
55
|
preventCheckoutBudgetExceeded?: boolean;
|
|
40
56
|
preventCheckoutBudgetExpired?: boolean;
|
|
41
57
|
allocations?: BudgetAllocation[];
|
|
58
|
+
notifications?: BudgetNotification;
|
|
42
59
|
};
|
|
43
60
|
|
|
44
61
|
export type BudgetInputForm = Omit<BudgetInput, "amount"> & {
|
|
@@ -29,7 +29,11 @@ export const AutocompleteDropdownItem = ({
|
|
|
29
29
|
}
|
|
30
30
|
data-fs-bp-autocomplete-dropdown-option-selected={isSelected}
|
|
31
31
|
onMouseEnter={() => setFocusedItemIndex(index)}
|
|
32
|
+
onMouseDown={(e) => {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
}}
|
|
32
35
|
onClick={(e) => {
|
|
36
|
+
e.stopPropagation();
|
|
33
37
|
if (closeOnClick) {
|
|
34
38
|
close();
|
|
35
39
|
}
|
|
@@ -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
|
+
};
|