@vtex/faststore-plugin-buyer-portal 1.0.45 → 1.0.47
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 +2 -0
- package/package.json +1 -1
- package/src/features/buying-policies/clients/BuyingPoliciesClient.ts +123 -0
- package/src/features/buying-policies/components/BasicBuyingPolicyDrawer/BasicBuyingPolicyDrawer.tsx +5 -4
- package/src/features/buying-policies/components/BuyingPolicyDropdownMenu/BuyingPolicyDropdownMenu.tsx +4 -0
- package/src/features/buying-policies/components/DeleteBuyingPolicyDrawer/DeleteBuyingPolicyDrawer.tsx +2 -0
- package/src/features/buying-policies/components/UpdateBuyingPolicyDrawer/UpdateBuyingPolicyDrawer.tsx +8 -6
- package/src/features/buying-policies/components/UpdateBuyingPolicyDrawer/update-buying-policy-drawer.scss +1 -0
- package/src/features/buying-policies/hooks/useGetBuyingPolicies.ts +32 -0
- package/src/features/buying-policies/layouts/BuyingPoliciesLayout/BuyingPoliciesLayout.tsx +84 -52
- package/src/features/buying-policies/layouts/BuyingPoliciesLayout/buying-policies-layout.scss +7 -0
- package/src/features/buying-policies/layouts/BuyingPolicyDetailsLayout/BuyingPolicyDetailsLayout.tsx +29 -3
- package/src/features/buying-policies/layouts/BuyingPolicyDetailsLayout/buying-policy-details-layout.scss +1 -0
- package/src/features/buying-policies/mocks/buying-policy-data.ts +6 -6
- package/src/features/buying-policies/services/add-buying-policy.service.ts +11 -1
- package/src/features/buying-policies/services/get-buying-policies.service.ts +25 -0
- package/src/features/buying-policies/services/get-buying-policy.service.ts +8 -5
- package/src/features/buying-policies/services/index.ts +5 -0
- package/src/features/buying-policies/services/remove-buying-policy.service.ts +14 -1
- package/src/features/buying-policies/services/update-buying-policy.service.ts +11 -1
- package/src/features/buying-policies/types/BuyingPolicy.ts +9 -4
- package/src/features/buying-policies/utils/buyingPoliciesWorkflowTypes.ts +8 -0
- package/src/features/buying-policies/utils/buyingPolicyDefault.ts +3 -2
- package/src/features/buying-policies/utils/index.ts +4 -0
- package/src/features/shared/components/Paginator/Counter.tsx +10 -0
- package/src/features/shared/components/Paginator/NextPageButton.tsx +15 -0
- package/src/features/shared/components/Paginator/Paginator.tsx +4 -0
- package/src/features/shared/components/Paginator/paginator.scss +22 -0
- package/src/features/shared/components/index.ts +4 -0
- package/src/features/shared/hooks/index.ts +1 -0
- package/src/features/shared/hooks/useDebounce.ts +16 -2
- package/src/features/shared/hooks/usePageItems.ts +88 -0
- package/src/features/shared/hooks/useQueryParams.ts +20 -5
- package/src/features/shared/utils/constants.ts +2 -0
- package/src/features/shared/utils/getValidPage.ts +6 -0
- package/src/features/shared/utils/index.ts +2 -1
- package/src/features/users/hooks/useDebouncedSearchOrgUnit.ts +2 -1
- package/src/pages/buying-policies.tsx +35 -15
- package/src/pages/buying-policy-details.tsx +9 -3
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Client } from "../../shared/clients/Client";
|
|
2
|
+
import { getApiUrl } from "../../shared/utils";
|
|
3
|
+
import type { BuyingPolicy } from "../types"; // ajuste conforme necessário
|
|
4
|
+
|
|
5
|
+
class BuyingPoliciesClient extends Client {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(getApiUrl());
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getBuyingPoliciesByOrgUnitId(
|
|
11
|
+
customerId: string,
|
|
12
|
+
orgUnitId: string,
|
|
13
|
+
cookie: string,
|
|
14
|
+
search?: string,
|
|
15
|
+
page = 1
|
|
16
|
+
) {
|
|
17
|
+
const params = new URLSearchParams();
|
|
18
|
+
if (search) params.append("search", search);
|
|
19
|
+
if (page && page > 1) params.append("page", String(page));
|
|
20
|
+
const queryString = params.toString();
|
|
21
|
+
const query = queryString ? `?${queryString}` : "";
|
|
22
|
+
|
|
23
|
+
return this.get<{
|
|
24
|
+
data: BuyingPolicy[];
|
|
25
|
+
total: number;
|
|
26
|
+
}>(`customers/${customerId}/units/${orgUnitId}/buying-policies${query}`, {
|
|
27
|
+
headers: {
|
|
28
|
+
Cookie: cookie,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getBuyingPolicyById(
|
|
34
|
+
customerId: string,
|
|
35
|
+
orgUnitId: string,
|
|
36
|
+
policyId: string,
|
|
37
|
+
cookie: string
|
|
38
|
+
) {
|
|
39
|
+
return this.get<BuyingPolicy>(
|
|
40
|
+
`customers/${customerId}/units/${orgUnitId}/buying-policies/${policyId}`,
|
|
41
|
+
{
|
|
42
|
+
headers: {
|
|
43
|
+
Cookie: cookie,
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
addBuyingPolicyToOrgUnit(
|
|
50
|
+
props: {
|
|
51
|
+
customerId: string;
|
|
52
|
+
orgUnitId: string;
|
|
53
|
+
name: string;
|
|
54
|
+
description: string;
|
|
55
|
+
criteria: string;
|
|
56
|
+
action: BuyingPolicy["action"];
|
|
57
|
+
},
|
|
58
|
+
cookie: string
|
|
59
|
+
) {
|
|
60
|
+
const { customerId, orgUnitId, ...data } = props;
|
|
61
|
+
|
|
62
|
+
return this.post<BuyingPolicy, Omit<BuyingPolicy, "id">>(
|
|
63
|
+
`customers/${customerId}/units/${orgUnitId}/buying-policies`,
|
|
64
|
+
data,
|
|
65
|
+
{
|
|
66
|
+
headers: {
|
|
67
|
+
Cookie: cookie,
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
updateBuyingPolicy(
|
|
75
|
+
props: {
|
|
76
|
+
customerId: string;
|
|
77
|
+
orgUnitId: string;
|
|
78
|
+
policyId: string;
|
|
79
|
+
name: string;
|
|
80
|
+
description: string;
|
|
81
|
+
criteria: string;
|
|
82
|
+
action: BuyingPolicy["action"];
|
|
83
|
+
},
|
|
84
|
+
cookie: string
|
|
85
|
+
) {
|
|
86
|
+
const { customerId, orgUnitId, policyId, ...data } = props;
|
|
87
|
+
|
|
88
|
+
return this.patch<BuyingPolicy, Omit<BuyingPolicy, "id">>(
|
|
89
|
+
`customers/${customerId}/units/${orgUnitId}/buying-policies/${policyId}`,
|
|
90
|
+
data,
|
|
91
|
+
{
|
|
92
|
+
headers: {
|
|
93
|
+
Cookie: cookie,
|
|
94
|
+
"Content-Type": "application/json",
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
removeBuyingPolicyFromOrgUnit(
|
|
101
|
+
props: {
|
|
102
|
+
customerId: string;
|
|
103
|
+
orgUnitId: string;
|
|
104
|
+
policyId: string;
|
|
105
|
+
},
|
|
106
|
+
cookie: string
|
|
107
|
+
) {
|
|
108
|
+
const { customerId, orgUnitId, policyId } = props;
|
|
109
|
+
|
|
110
|
+
return this.delete<unknown, unknown>(
|
|
111
|
+
`customers/${customerId}/units/${orgUnitId}/buying-policies/${policyId}`,
|
|
112
|
+
undefined,
|
|
113
|
+
{
|
|
114
|
+
headers: {
|
|
115
|
+
Cookie: cookie,
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const buyingPoliciesClient = new BuyingPoliciesClient();
|
|
123
|
+
export { buyingPoliciesClient };
|
package/src/features/buying-policies/components/BasicBuyingPolicyDrawer/BasicBuyingPolicyDrawer.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import { buyingPolicyOptions, type BuyingPolicy } from "../../types";
|
|
20
20
|
import { OrgUnitInputSearch } from "../../../shared/components/OrgUnitInputSearch/OrgUnitInputSearch";
|
|
21
21
|
import {
|
|
22
|
+
BUYING_POLICIES_WORKFLOW_TYPES,
|
|
22
23
|
buyingPolicyDefault,
|
|
23
24
|
orderFieldsCriteriaOptions,
|
|
24
25
|
spendingLimitsCriteriaOptions,
|
|
@@ -31,7 +32,7 @@ export type BuyingPolicyForm = Omit<BuyingPolicy, "id"> & {
|
|
|
31
32
|
id?: string;
|
|
32
33
|
};
|
|
33
34
|
|
|
34
|
-
const defaultBuyingPolicyValues
|
|
35
|
+
const { id, ...defaultBuyingPolicyValues } = buyingPolicyDefault;
|
|
35
36
|
|
|
36
37
|
export type BasicBuyingPolicyDrawerProps = Omit<
|
|
37
38
|
BasicDrawerProps,
|
|
@@ -91,7 +92,7 @@ export const BasicBuyingPolicyDrawer = ({
|
|
|
91
92
|
return false;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
if (valueAction.type ===
|
|
95
|
+
if (valueAction.type === BUYING_POLICIES_WORKFLOW_TYPES.SEQUENTIAL) {
|
|
95
96
|
return (
|
|
96
97
|
valueAction.levels?.length &&
|
|
97
98
|
valueAction.levels.every((level) => {
|
|
@@ -312,7 +313,7 @@ export const BasicBuyingPolicyDrawer = ({
|
|
|
312
313
|
)}
|
|
313
314
|
/>
|
|
314
315
|
|
|
315
|
-
{action.type ===
|
|
316
|
+
{action.type === BUYING_POLICIES_WORKFLOW_TYPES.BYPASS && (
|
|
316
317
|
<InputText.Legend>
|
|
317
318
|
When this policy is applied, all opther buying policy will be
|
|
318
319
|
ignored
|
|
@@ -324,7 +325,7 @@ export const BasicBuyingPolicyDrawer = ({
|
|
|
324
325
|
message="Action is required"
|
|
325
326
|
/>
|
|
326
327
|
|
|
327
|
-
{form.action.type ===
|
|
328
|
+
{form.action.type === BUYING_POLICIES_WORKFLOW_TYPES.SEQUENTIAL && (
|
|
328
329
|
<>
|
|
329
330
|
{form.action.levels?.map((level, index) => (
|
|
330
331
|
<Fragment key={`${level.id}-${index}`}>
|
|
@@ -5,17 +5,20 @@ import { useBuyerPortal, useDrawerProps } from "../../../shared/hooks";
|
|
|
5
5
|
import { DeleteBuyingPolicyDrawer, UpdateBuyingPolicyDrawer } from "..";
|
|
6
6
|
import { useRouter } from "next/router";
|
|
7
7
|
import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
8
|
+
import type { BuyingPolicy } from "../../types";
|
|
8
9
|
|
|
9
10
|
export type BuyingPolicyDropdownMenuProps = {
|
|
10
11
|
id: string;
|
|
11
12
|
name: string;
|
|
12
13
|
onDelete?: () => void;
|
|
14
|
+
onUpdate?: (buyingPolicy: BuyingPolicy) => void;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export const BuyingPolicyDropdownMenu = ({
|
|
16
18
|
id,
|
|
17
19
|
name,
|
|
18
20
|
onDelete,
|
|
21
|
+
onUpdate,
|
|
19
22
|
}: BuyingPolicyDropdownMenuProps) => {
|
|
20
23
|
const { currentContract, currentOrgUnit } = useBuyerPortal();
|
|
21
24
|
|
|
@@ -76,6 +79,7 @@ export const BuyingPolicyDropdownMenu = ({
|
|
|
76
79
|
orgUnitId={currentOrgUnit?.id ?? ""}
|
|
77
80
|
contractId={currentContract?.id ?? ""}
|
|
78
81
|
buyingPolicyId={id}
|
|
82
|
+
onUpdate={onUpdate}
|
|
79
83
|
{...updateBuyingPolicyDrawerProps}
|
|
80
84
|
isOpen={isUpdateBuyingPolicyDrawerOpen}
|
|
81
85
|
/>
|
|
@@ -4,12 +4,13 @@ import { useGetBuyingPolicy, useUpdateBuyingPolicy } from "../../hooks";
|
|
|
4
4
|
import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
5
5
|
import { BasicBuyingPolicyDrawer, type BasicBuyingPolicyDrawerProps } from "..";
|
|
6
6
|
import { buyingPolicyDefault } from "../../utils";
|
|
7
|
+
import type { BuyingPolicy } from "../../types";
|
|
7
8
|
|
|
8
9
|
export type UpdateBuyingPolicyDrawerProps = Omit<
|
|
9
10
|
BasicBuyingPolicyDrawerProps,
|
|
10
11
|
"children" | "initialValues"
|
|
11
12
|
> & {
|
|
12
|
-
|
|
13
|
+
onUpdate?: (buyingPolicy: BuyingPolicy) => void;
|
|
13
14
|
orgUnitId: string;
|
|
14
15
|
contractId: string;
|
|
15
16
|
buyingPolicyId: string;
|
|
@@ -17,7 +18,7 @@ export type UpdateBuyingPolicyDrawerProps = Omit<
|
|
|
17
18
|
|
|
18
19
|
export const UpdateBuyingPolicyDrawer = ({
|
|
19
20
|
close,
|
|
20
|
-
|
|
21
|
+
onUpdate,
|
|
21
22
|
orgUnitId,
|
|
22
23
|
contractId,
|
|
23
24
|
buyingPolicyId,
|
|
@@ -32,7 +33,7 @@ export const UpdateBuyingPolicyDrawer = ({
|
|
|
32
33
|
const { pushToast } = useUI();
|
|
33
34
|
const router = useRouter();
|
|
34
35
|
|
|
35
|
-
const handleUpdateBuyingPolicySuccess = () => {
|
|
36
|
+
const handleUpdateBuyingPolicySuccess = (buyingPolicy: BuyingPolicy) => {
|
|
36
37
|
pushToast({
|
|
37
38
|
message: "Buying policy updated successfully",
|
|
38
39
|
status: "INFO",
|
|
@@ -55,16 +56,17 @@ export const UpdateBuyingPolicyDrawer = ({
|
|
|
55
56
|
</button>
|
|
56
57
|
),
|
|
57
58
|
});
|
|
58
|
-
|
|
59
|
+
onUpdate?.(buyingPolicy);
|
|
59
60
|
close();
|
|
60
61
|
};
|
|
61
62
|
|
|
62
63
|
const { updateBuyingPolicy, isUpdateBuyingPolicyLoading } =
|
|
63
64
|
useUpdateBuyingPolicy({
|
|
64
65
|
onSuccess: handleUpdateBuyingPolicySuccess,
|
|
65
|
-
onError: () => {
|
|
66
|
+
onError: (err) => {
|
|
67
|
+
// TODO: Handle error
|
|
66
68
|
pushToast({
|
|
67
|
-
message: "Error
|
|
69
|
+
message: "Error adding buying policy",
|
|
68
70
|
status: "ERROR",
|
|
69
71
|
});
|
|
70
72
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "../BasicBuyingPolicyDrawer/basic-buying-policy-drawer.scss";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type QueryOptions, useQuery } from "../../shared/hooks";
|
|
2
|
+
import { getBuyingPoliciesService } from "../services";
|
|
3
|
+
|
|
4
|
+
export const useGetBuyingPolicies = (
|
|
5
|
+
{
|
|
6
|
+
contractId,
|
|
7
|
+
orgUnitId,
|
|
8
|
+
search,
|
|
9
|
+
page,
|
|
10
|
+
}: { orgUnitId: string; contractId: string; search?: string; page: number },
|
|
11
|
+
options?: QueryOptions<AwaitedType<typeof getBuyingPoliciesService>>
|
|
12
|
+
) => {
|
|
13
|
+
const { data, error, isLoading, refetch } = useQuery(
|
|
14
|
+
["api/buying-policies", orgUnitId, contractId, page].join("/"),
|
|
15
|
+
({ cookie }) =>
|
|
16
|
+
getBuyingPoliciesService({
|
|
17
|
+
orgUnitId,
|
|
18
|
+
contractId,
|
|
19
|
+
cookie,
|
|
20
|
+
page,
|
|
21
|
+
search,
|
|
22
|
+
}),
|
|
23
|
+
options
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
buyingPolicies: data,
|
|
28
|
+
hasBuyingPoliciesError: error,
|
|
29
|
+
isBuyingPoliciesLoading: isLoading,
|
|
30
|
+
refetchBuyingPolicies: refetch,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -2,7 +2,9 @@ import { FinanceTabsLayout, GlobalLayout } from "../../../shared/layouts";
|
|
|
2
2
|
import { Table } from "../../../shared/components/Table/Table";
|
|
3
3
|
import { getTableColumns } from "../../../shared/components/Table/utils/tableColumns";
|
|
4
4
|
import { HeaderInside, InternalSearch } from "../../../shared/components";
|
|
5
|
+
import { Paginator } from "../../../shared/components";
|
|
5
6
|
import {
|
|
7
|
+
usePageItems,
|
|
6
8
|
useBuyerPortal,
|
|
7
9
|
useDrawerProps,
|
|
8
10
|
useQueryParams,
|
|
@@ -13,18 +15,33 @@ import { BuyingPolicyDropdownMenu } from "../../components";
|
|
|
13
15
|
import { AddBuyingPolicyDrawer } from "../../components/AddBuyingPolicyDrawer/AddBuyingPolicyDrawer";
|
|
14
16
|
|
|
15
17
|
export type BuyingPoliciesLayoutProps = {
|
|
16
|
-
|
|
18
|
+
buyingPolicies: BuyingPolicy[];
|
|
19
|
+
total: number;
|
|
17
20
|
search: string;
|
|
21
|
+
page: number;
|
|
18
22
|
};
|
|
19
23
|
|
|
20
24
|
export const BuyingPoliciesLayout = ({
|
|
21
|
-
|
|
25
|
+
buyingPolicies: initialBuyingPolicies,
|
|
22
26
|
search,
|
|
27
|
+
total,
|
|
28
|
+
page,
|
|
23
29
|
}: BuyingPoliciesLayoutProps) => {
|
|
24
|
-
const { setQueryString, removeQueryString } = useQueryParams();
|
|
25
|
-
|
|
26
30
|
const { currentContract, currentOrgUnit } = useBuyerPortal();
|
|
27
31
|
|
|
32
|
+
const {
|
|
33
|
+
isLoading,
|
|
34
|
+
items: buyingPolicies,
|
|
35
|
+
searchTerm,
|
|
36
|
+
setSearchTerm,
|
|
37
|
+
increasePage,
|
|
38
|
+
setItems: setBuyingPolicies,
|
|
39
|
+
} = usePageItems<BuyingPolicy>({
|
|
40
|
+
initialItems: initialBuyingPolicies,
|
|
41
|
+
search,
|
|
42
|
+
page,
|
|
43
|
+
});
|
|
44
|
+
|
|
28
45
|
const {
|
|
29
46
|
isOpen: isAddBuyingPolicyDrawerOpen,
|
|
30
47
|
open: openAddBuyingPolicyDrawer,
|
|
@@ -34,58 +51,73 @@ export const BuyingPoliciesLayout = ({
|
|
|
34
51
|
return (
|
|
35
52
|
<GlobalLayout>
|
|
36
53
|
<FinanceTabsLayout pageName="Finance and Compliance">
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
<HeaderInside
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
<section data-fs-buying-policies-section>
|
|
55
|
+
<HeaderInside title="Buying Policies">
|
|
56
|
+
<HeaderInside.Button onClick={openAddBuyingPolicyDrawer} />
|
|
57
|
+
</HeaderInside>
|
|
58
|
+
|
|
59
|
+
<div data-fs-buying-policies-filter>
|
|
60
|
+
<InternalSearch
|
|
61
|
+
defaultValue={searchTerm}
|
|
62
|
+
textSearch={setSearchTerm}
|
|
63
|
+
/>
|
|
64
|
+
<Paginator.Counter
|
|
65
|
+
total={total}
|
|
66
|
+
itemsLength={buyingPolicies.length}
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
42
69
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
<Table>
|
|
71
|
+
<Table.Head columns={getTableColumns({ withType: false })} />
|
|
72
|
+
<Table.Body>
|
|
73
|
+
{buyingPolicies.map((buyingPolicy) => (
|
|
74
|
+
<Table.Row
|
|
75
|
+
key={buyingPolicy.id}
|
|
76
|
+
title={buyingPolicy.name}
|
|
77
|
+
iconName="Rebase"
|
|
78
|
+
iconSize={20}
|
|
79
|
+
href={buyerPortalRoutes.buyingPolicyDetails({
|
|
80
|
+
contractId: currentContract?.id ?? "",
|
|
81
|
+
orgUnitId: currentOrgUnit?.id ?? "",
|
|
82
|
+
buyingPolicyId: buyingPolicy.id,
|
|
83
|
+
})}
|
|
84
|
+
dropdownMenu={
|
|
85
|
+
<BuyingPolicyDropdownMenu
|
|
86
|
+
id={buyingPolicy.id}
|
|
87
|
+
name={buyingPolicy.name}
|
|
88
|
+
/>
|
|
89
|
+
}
|
|
90
|
+
/>
|
|
91
|
+
))}
|
|
92
|
+
</Table.Body>
|
|
93
|
+
</Table>
|
|
53
94
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
orgUnitId: currentOrgUnit?.id ?? "",
|
|
66
|
-
buyingPolicyId: buyingPolicy.id,
|
|
67
|
-
})}
|
|
68
|
-
dropdownMenu={
|
|
69
|
-
<BuyingPolicyDropdownMenu
|
|
70
|
-
id={buyingPolicy.id}
|
|
71
|
-
name={buyingPolicy.name}
|
|
72
|
-
/>
|
|
73
|
-
}
|
|
74
|
-
/>
|
|
75
|
-
))}
|
|
76
|
-
</Table.Body>
|
|
77
|
-
</Table>
|
|
78
|
-
</section>
|
|
95
|
+
<div data-fs-bp-buying-policies-paginator>
|
|
96
|
+
{total > buyingPolicies.length ? (
|
|
97
|
+
<Paginator.NextPageButton
|
|
98
|
+
onClick={increasePage}
|
|
99
|
+
disabled={isLoading}
|
|
100
|
+
>
|
|
101
|
+
{isLoading ? "Loading" : "Load More"}
|
|
102
|
+
</Paginator.NextPageButton>
|
|
103
|
+
) : (
|
|
104
|
+
<span />
|
|
105
|
+
)}
|
|
79
106
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
contractId={currentContract?.id ?? ""}
|
|
84
|
-
isOpen={isAddBuyingPolicyDrawerOpen}
|
|
85
|
-
{...addBuyingPolicyDrawerProps}
|
|
107
|
+
<Paginator.Counter
|
|
108
|
+
total={total}
|
|
109
|
+
itemsLength={buyingPolicies.length}
|
|
86
110
|
/>
|
|
87
|
-
|
|
88
|
-
|
|
111
|
+
</div>
|
|
112
|
+
</section>
|
|
113
|
+
{isAddBuyingPolicyDrawerOpen && (
|
|
114
|
+
<AddBuyingPolicyDrawer
|
|
115
|
+
orgUnitId={currentOrgUnit?.id ?? ""}
|
|
116
|
+
contractId={currentContract?.id ?? ""}
|
|
117
|
+
isOpen={isAddBuyingPolicyDrawerOpen}
|
|
118
|
+
{...addBuyingPolicyDrawerProps}
|
|
119
|
+
/>
|
|
120
|
+
)}
|
|
89
121
|
</FinanceTabsLayout>
|
|
90
122
|
</GlobalLayout>
|
|
91
123
|
);
|
package/src/features/buying-policies/layouts/BuyingPolicyDetailsLayout/BuyingPolicyDetailsLayout.tsx
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { FinanceTabsLayout, GlobalLayout } from "../../../shared/layouts";
|
|
2
2
|
import { BasicDropdownMenu, HeaderInside } from "../../../shared/components";
|
|
3
|
-
import { useBuyerPortal } from "../../../shared/hooks";
|
|
3
|
+
import { useBuyerPortal, useDrawerProps } from "../../../shared/hooks";
|
|
4
4
|
import type { BuyingPolicy } from "../../types";
|
|
5
5
|
import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
6
6
|
import { Dropdown } from "@faststore/ui";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
BuyingPolicyDropdownMenu,
|
|
9
|
+
UpdateBuyingPolicyDrawer,
|
|
10
|
+
} from "../../components";
|
|
11
|
+
import { useRouter } from "next/router";
|
|
8
12
|
|
|
9
13
|
export type BuyingPolicyDetailsLayoutProps = {
|
|
10
14
|
data: { buyingPolicy: BuyingPolicy | null };
|
|
@@ -15,6 +19,14 @@ export const BuyingPolicyDetailsLayout = ({
|
|
|
15
19
|
}: BuyingPolicyDetailsLayoutProps) => {
|
|
16
20
|
const { currentContract, currentOrgUnit } = useBuyerPortal();
|
|
17
21
|
|
|
22
|
+
const { reload } = useRouter();
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
open: openUpdateBuyingPolicyDrawerProps,
|
|
26
|
+
isOpen: isUpdateBuyingPolicyDrawerOpen,
|
|
27
|
+
...updateBuyingPolicyDrawerProps
|
|
28
|
+
} = useDrawerProps();
|
|
29
|
+
|
|
18
30
|
return (
|
|
19
31
|
<GlobalLayout>
|
|
20
32
|
<FinanceTabsLayout pageName="Finance and Compliance">
|
|
@@ -37,7 +49,11 @@ export const BuyingPolicyDetailsLayout = ({
|
|
|
37
49
|
|
|
38
50
|
<div data-fs-buying-policy-details-line>
|
|
39
51
|
<span data-fs-buying-policy-details-title>Settings</span>
|
|
40
|
-
<button
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
data-fs-buying-policy-details-edit-button
|
|
55
|
+
onClick={openUpdateBuyingPolicyDrawerProps}
|
|
56
|
+
>
|
|
41
57
|
Edit
|
|
42
58
|
</button>
|
|
43
59
|
</div>
|
|
@@ -82,6 +98,16 @@ export const BuyingPolicyDetailsLayout = ({
|
|
|
82
98
|
</span>
|
|
83
99
|
</div>
|
|
84
100
|
</section>
|
|
101
|
+
{isUpdateBuyingPolicyDrawerOpen && (
|
|
102
|
+
<UpdateBuyingPolicyDrawer
|
|
103
|
+
orgUnitId={currentOrgUnit?.id ?? ""}
|
|
104
|
+
contractId={currentContract?.id ?? ""}
|
|
105
|
+
buyingPolicyId={buyingPolicy?.id ?? ""}
|
|
106
|
+
onUpdate={reload}
|
|
107
|
+
{...updateBuyingPolicyDrawerProps}
|
|
108
|
+
isOpen={isUpdateBuyingPolicyDrawerOpen}
|
|
109
|
+
/>
|
|
110
|
+
)}
|
|
85
111
|
</FinanceTabsLayout>
|
|
86
112
|
</GlobalLayout>
|
|
87
113
|
);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
@import "../../../shared/layouts/FinanceTabsLayout/finance-tabs-layout.scss";
|
|
4
4
|
@import "../../components/BuyingPolicyDropdownMenu/buying-policy-dropdown-menu.scss";
|
|
5
|
+
@import "../../components/UpdateBuyingPolicyDrawer/update-buying-policy-drawer.scss";
|
|
5
6
|
|
|
6
7
|
[data-fs-buying-policy-details-section] {
|
|
7
8
|
@import "../../../shared/components/HeaderInside/header-inside.scss";
|
|
@@ -16,7 +16,7 @@ export const buyingPoliciesData: BuyingPolicy[] = [
|
|
|
16
16
|
description: "Policy for handling bulk orders with discounts.",
|
|
17
17
|
criteria: "quantity > 100",
|
|
18
18
|
action: {
|
|
19
|
-
type: "Sequential
|
|
19
|
+
type: "Sequential workflow",
|
|
20
20
|
levels: [
|
|
21
21
|
{ id: "1", name: "Manager" },
|
|
22
22
|
{ id: "2", name: "Director" },
|
|
@@ -48,7 +48,7 @@ export const buyingPoliciesData: BuyingPolicy[] = [
|
|
|
48
48
|
description: "Handles seasonal promotions and discounts.",
|
|
49
49
|
criteria: "season == 'holiday'",
|
|
50
50
|
action: {
|
|
51
|
-
type: "Sequential
|
|
51
|
+
type: "Sequential workflow",
|
|
52
52
|
levels: [
|
|
53
53
|
{ id: "1", name: "Marketing Manager" },
|
|
54
54
|
{ id: "2", name: "Finance Director" },
|
|
@@ -61,7 +61,7 @@ export const buyingPoliciesData: BuyingPolicy[] = [
|
|
|
61
61
|
description: "Requires approval for high-value purchases.",
|
|
62
62
|
criteria: "purchaseAmount > 10000",
|
|
63
63
|
action: {
|
|
64
|
-
type: "Sequential
|
|
64
|
+
type: "Sequential workflow",
|
|
65
65
|
levels: [
|
|
66
66
|
{ id: "1", name: "Manager" },
|
|
67
67
|
{ id: "2", name: "Finance Director" },
|
|
@@ -93,7 +93,7 @@ export const buyingPoliciesData: BuyingPolicy[] = [
|
|
|
93
93
|
description: "Custom rules for corporate accounts.",
|
|
94
94
|
criteria: "accountType == 'corporate'",
|
|
95
95
|
action: {
|
|
96
|
-
type: "Sequential
|
|
96
|
+
type: "Sequential workflow",
|
|
97
97
|
levels: [
|
|
98
98
|
{ id: "1", name: "Account Manager" },
|
|
99
99
|
{ id: "2", name: "Finance Director" },
|
|
@@ -115,7 +115,7 @@ export const buyingPoliciesData: BuyingPolicy[] = [
|
|
|
115
115
|
description: "Rules for international orders.",
|
|
116
116
|
criteria: "shippingCountry != 'domestic'",
|
|
117
117
|
action: {
|
|
118
|
-
type: "Sequential
|
|
118
|
+
type: "Sequential workflow",
|
|
119
119
|
levels: [
|
|
120
120
|
{ id: "1", name: "Logistics Manager" },
|
|
121
121
|
{ id: "2", name: "Finance Director" },
|
|
@@ -155,7 +155,7 @@ export const buyingPoliciesData: BuyingPolicy[] = [
|
|
|
155
155
|
description: "Encourages eco-friendly purchases.",
|
|
156
156
|
criteria: "productType == 'eco-friendly'",
|
|
157
157
|
action: {
|
|
158
|
-
type: "Sequential
|
|
158
|
+
type: "Sequential workflow",
|
|
159
159
|
levels: [
|
|
160
160
|
{ id: "1", name: "Sustainability Manager" },
|
|
161
161
|
{ id: "2", name: "CEO" },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buyingPoliciesClient } from "../clients/BuyingPoliciesClient";
|
|
1
2
|
import type { BuyingPolicy } from "../types";
|
|
2
3
|
|
|
3
4
|
export type AddBuyingPolicyServiceProps = {
|
|
@@ -9,7 +10,16 @@ export type AddBuyingPolicyServiceProps = {
|
|
|
9
10
|
|
|
10
11
|
export const addBuyingPolicyService = async ({
|
|
11
12
|
buyingPolicy,
|
|
13
|
+
contractId,
|
|
14
|
+
orgUnitId,
|
|
12
15
|
cookie,
|
|
13
16
|
}: AddBuyingPolicyServiceProps) => {
|
|
14
|
-
return
|
|
17
|
+
return buyingPoliciesClient.addBuyingPolicyToOrgUnit(
|
|
18
|
+
{
|
|
19
|
+
...buyingPolicy,
|
|
20
|
+
customerId: contractId,
|
|
21
|
+
orgUnitId,
|
|
22
|
+
},
|
|
23
|
+
cookie
|
|
24
|
+
);
|
|
15
25
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { buyingPoliciesClient } from "../clients/BuyingPoliciesClient";
|
|
2
|
+
|
|
3
|
+
export type GetBuyingPoliciesServiceProps = {
|
|
4
|
+
orgUnitId: string;
|
|
5
|
+
contractId: string;
|
|
6
|
+
cookie: string;
|
|
7
|
+
search?: string;
|
|
8
|
+
page?: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const getBuyingPoliciesService = async ({
|
|
12
|
+
search,
|
|
13
|
+
page = 1,
|
|
14
|
+
contractId,
|
|
15
|
+
cookie,
|
|
16
|
+
orgUnitId,
|
|
17
|
+
}: GetBuyingPoliciesServiceProps) => {
|
|
18
|
+
return buyingPoliciesClient.getBuyingPoliciesByOrgUnitId(
|
|
19
|
+
contractId,
|
|
20
|
+
orgUnitId,
|
|
21
|
+
cookie,
|
|
22
|
+
search,
|
|
23
|
+
page
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buyingPoliciesClient } from "../clients/BuyingPoliciesClient";
|
|
2
2
|
|
|
3
3
|
export type GetBuyingPolicyServiceProps = {
|
|
4
4
|
orgUnitId: string;
|
|
@@ -11,9 +11,12 @@ export const getBuyingPolicyService = async ({
|
|
|
11
11
|
orgUnitId,
|
|
12
12
|
contractId,
|
|
13
13
|
buyingPolicyId,
|
|
14
|
+
cookie,
|
|
14
15
|
}: GetBuyingPolicyServiceProps) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
return buyingPoliciesClient.getBuyingPolicyById(
|
|
17
|
+
contractId,
|
|
18
|
+
orgUnitId,
|
|
19
|
+
buyingPolicyId,
|
|
20
|
+
cookie
|
|
21
|
+
);
|
|
19
22
|
};
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
+
import { buyingPoliciesClient } from "../clients/BuyingPoliciesClient";
|
|
2
|
+
|
|
1
3
|
export type RemoveBuyingPolicyServiceProps = {
|
|
4
|
+
contractId: string;
|
|
5
|
+
orgUnitId: string;
|
|
2
6
|
buyingPolicyId: string;
|
|
3
7
|
cookie: string;
|
|
4
8
|
};
|
|
5
9
|
|
|
6
10
|
export const removeBuyingPolicyService = async ({
|
|
11
|
+
contractId,
|
|
12
|
+
orgUnitId,
|
|
7
13
|
buyingPolicyId,
|
|
8
14
|
cookie,
|
|
9
15
|
}: RemoveBuyingPolicyServiceProps) => {
|
|
10
|
-
return
|
|
16
|
+
return buyingPoliciesClient.removeBuyingPolicyFromOrgUnit(
|
|
17
|
+
{
|
|
18
|
+
customerId: contractId,
|
|
19
|
+
orgUnitId: orgUnitId,
|
|
20
|
+
policyId: buyingPolicyId,
|
|
21
|
+
},
|
|
22
|
+
cookie
|
|
23
|
+
);
|
|
11
24
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buyingPoliciesClient } from "../clients/BuyingPoliciesClient";
|
|
1
2
|
import type { BuyingPolicy } from "../types";
|
|
2
3
|
|
|
3
4
|
export type UpdateBuyingPolicyServiceProps = {
|
|
@@ -13,6 +14,15 @@ export const updateBuyingPolicyService = async ({
|
|
|
13
14
|
buyingPolicy,
|
|
14
15
|
contractId,
|
|
15
16
|
orgUnitId,
|
|
17
|
+
cookie,
|
|
16
18
|
}: UpdateBuyingPolicyServiceProps) => {
|
|
17
|
-
return
|
|
19
|
+
return buyingPoliciesClient.updateBuyingPolicy(
|
|
20
|
+
{
|
|
21
|
+
...buyingPolicy,
|
|
22
|
+
customerId: contractId,
|
|
23
|
+
orgUnitId,
|
|
24
|
+
policyId: buyingPolicyId,
|
|
25
|
+
},
|
|
26
|
+
cookie
|
|
27
|
+
);
|
|
18
28
|
};
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BUYING_POLICIES_WORKFLOW_TYPES,
|
|
3
|
+
type BuyingPoliciesWorkflowType,
|
|
4
|
+
} from "../utils";
|
|
5
|
+
|
|
1
6
|
export const buyingPolicyOptions = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
7
|
+
BUYING_POLICIES_WORKFLOW_TYPES.DENY,
|
|
8
|
+
BUYING_POLICIES_WORKFLOW_TYPES.SEQUENTIAL,
|
|
9
|
+
BUYING_POLICIES_WORKFLOW_TYPES.BYPASS,
|
|
5
10
|
] as const;
|
|
6
11
|
|
|
7
12
|
export type BuyingPolicy = {
|
|
@@ -10,7 +15,7 @@ export type BuyingPolicy = {
|
|
|
10
15
|
description: string;
|
|
11
16
|
criteria: string;
|
|
12
17
|
action: {
|
|
13
|
-
type:
|
|
18
|
+
type: BuyingPoliciesWorkflowType;
|
|
14
19
|
levels?: {
|
|
15
20
|
id: string;
|
|
16
21
|
name?: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const BUYING_POLICIES_WORKFLOW_TYPES = {
|
|
2
|
+
SEQUENTIAL: "Sequential workflow",
|
|
3
|
+
DENY: "Deny order",
|
|
4
|
+
BYPASS: "Bypass all buying policies",
|
|
5
|
+
} as const;
|
|
6
|
+
|
|
7
|
+
export type BuyingPoliciesWorkflowType =
|
|
8
|
+
(typeof BUYING_POLICIES_WORKFLOW_TYPES)[keyof typeof BUYING_POLICIES_WORKFLOW_TYPES];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BuyingPolicy } from "../types";
|
|
2
|
+
import { BUYING_POLICIES_WORKFLOW_TYPES } from "./buyingPoliciesWorkflowTypes";
|
|
2
3
|
|
|
3
4
|
export const buyingPolicyDefault: BuyingPolicy = {
|
|
4
5
|
id: "",
|
|
@@ -6,7 +7,7 @@ export const buyingPolicyDefault: BuyingPolicy = {
|
|
|
6
7
|
description: "",
|
|
7
8
|
criteria: "",
|
|
8
9
|
action: {
|
|
9
|
-
type:
|
|
10
|
+
type: BUYING_POLICIES_WORKFLOW_TYPES.DENY,
|
|
10
11
|
levels: [
|
|
11
12
|
{
|
|
12
13
|
id: "",
|
|
@@ -14,4 +15,4 @@ export const buyingPolicyDefault: BuyingPolicy = {
|
|
|
14
15
|
},
|
|
15
16
|
],
|
|
16
17
|
},
|
|
17
|
-
};
|
|
18
|
+
} as const;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export { buyingPolicyDefault } from "./buyingPolicyDefault";
|
|
2
2
|
export { orderFieldsCriteriaOptions } from "./orderFieldsCriteriaOptions";
|
|
3
3
|
export { spendingLimitsCriteriaOptions } from "./spendingLimitsCriteriaOptions";
|
|
4
|
+
export {
|
|
5
|
+
BUYING_POLICIES_WORKFLOW_TYPES,
|
|
6
|
+
BuyingPoliciesWorkflowType,
|
|
7
|
+
} from "./buyingPoliciesWorkflowTypes";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
|
|
3
|
+
export type NextPageButtonProps = ComponentProps<"button">;
|
|
4
|
+
|
|
5
|
+
export const NextPageButton = ({
|
|
6
|
+
type = "button",
|
|
7
|
+
children = "Next Page",
|
|
8
|
+
...otherProps
|
|
9
|
+
}: NextPageButtonProps) => {
|
|
10
|
+
return (
|
|
11
|
+
<button data-fs-paginator-next-page-button type="button" {...otherProps}>
|
|
12
|
+
{children}
|
|
13
|
+
</button>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[data-fs-bp-paginator-counter] {
|
|
2
|
+
color: #5c5c5c;
|
|
3
|
+
font-weight: var(--fs-text-weight-medium);
|
|
4
|
+
font-size: var(--fs-text-size-1);
|
|
5
|
+
line-height: calc(var(--fs-spacing-3) - var(--fs-spacing-0));
|
|
6
|
+
text-align: right;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
[data-fs-paginator-next-page-button] {
|
|
10
|
+
color: #0366dd;
|
|
11
|
+
font-weight: var(--fs-text-weight-semibold);
|
|
12
|
+
font-size: var(--fs-text-size-1);
|
|
13
|
+
line-height: var(--fs-spacing-3);
|
|
14
|
+
text-align: center;
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
padding: var(--fs-spacing-0);
|
|
17
|
+
border-radius: var(--fs-spacing-0);
|
|
18
|
+
|
|
19
|
+
&:hover {
|
|
20
|
+
background-color: #f5f5f5;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -71,3 +71,7 @@ export {
|
|
|
71
71
|
LevelDivider,
|
|
72
72
|
type LevelDividerProps,
|
|
73
73
|
} from "./LevelDivider/LevelDivider";
|
|
74
|
+
|
|
75
|
+
export { Paginator } from "./Paginator/Paginator";
|
|
76
|
+
export { CounterProps as PaginatorCounterProps } from "./Paginator/Counter";
|
|
77
|
+
export { NextPageButtonProps as PaginatorNextPageButtonProps } from "./Paginator/NextPageButton";
|
|
@@ -10,3 +10,4 @@ export { useQueryParams } from "./useQueryParams";
|
|
|
10
10
|
export { useDebounce } from "./useDebounce";
|
|
11
11
|
export { useAddToScope } from "./useAddToScope";
|
|
12
12
|
export { useRemoveFromScope } from "./useRemoveFromScope";
|
|
13
|
+
export { usePageItems, type UsePageItemsProps } from "./usePageItems";
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
export function useDebounce<T>(
|
|
3
|
+
export function useDebounce<T>(
|
|
4
|
+
value: T,
|
|
5
|
+
delay: number,
|
|
6
|
+
options?: { onDebounce?: (value: T) => void }
|
|
7
|
+
): T {
|
|
8
|
+
const { onDebounce } = options || {};
|
|
4
9
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
5
10
|
|
|
11
|
+
const firstChangeRef = useRef(true);
|
|
12
|
+
|
|
6
13
|
useEffect(() => {
|
|
7
14
|
const handler = setTimeout(() => {
|
|
15
|
+
if (firstChangeRef.current) {
|
|
16
|
+
firstChangeRef.current = false;
|
|
17
|
+
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
8
20
|
setDebouncedValue(value);
|
|
21
|
+
|
|
22
|
+
onDebounce?.(value);
|
|
9
23
|
}, delay);
|
|
10
24
|
|
|
11
25
|
return () => {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useState, useRef, useEffect, useCallback } from "react";
|
|
2
|
+
import { useDebounce } from "./useDebounce";
|
|
3
|
+
import { useQueryParams } from "./useQueryParams";
|
|
4
|
+
import { DEBOUNCE_TIMEOUT } from "../utils";
|
|
5
|
+
|
|
6
|
+
export type UsePageItemsProps<T> = {
|
|
7
|
+
initialItems: T[];
|
|
8
|
+
search?: string;
|
|
9
|
+
page?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const usePageItems = <T>({
|
|
13
|
+
initialItems,
|
|
14
|
+
search,
|
|
15
|
+
page = 1,
|
|
16
|
+
}: UsePageItemsProps<T>) => {
|
|
17
|
+
const [items, setItems] = useState<T[]>(initialItems ?? []);
|
|
18
|
+
|
|
19
|
+
const [searchTerm, setSearchTerm] = useState(search ?? "");
|
|
20
|
+
|
|
21
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
22
|
+
|
|
23
|
+
useDebounce(searchTerm, DEBOUNCE_TIMEOUT, {
|
|
24
|
+
onDebounce: (value) => {
|
|
25
|
+
setIsLoading(true);
|
|
26
|
+
if (value) {
|
|
27
|
+
setQueryStrings({
|
|
28
|
+
search: value,
|
|
29
|
+
page: "1",
|
|
30
|
+
});
|
|
31
|
+
} else {
|
|
32
|
+
setQueryStrings({
|
|
33
|
+
search: null,
|
|
34
|
+
page: "1",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const { setQueryStrings, setQueryString } = useQueryParams();
|
|
41
|
+
|
|
42
|
+
const firstSearchRef = useRef(true);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
setItems([]);
|
|
46
|
+
}, [search]);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (firstSearchRef.current) {
|
|
50
|
+
setItems(initialItems);
|
|
51
|
+
firstSearchRef.current = false;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
setItems((prev) => [...prev, ...initialItems]);
|
|
56
|
+
setIsLoading(false);
|
|
57
|
+
}, [initialItems]);
|
|
58
|
+
|
|
59
|
+
const increasePage = useCallback(() => {
|
|
60
|
+
setQueryString("page", (page + 1).toString());
|
|
61
|
+
setIsLoading(true);
|
|
62
|
+
}, [page, setQueryString]);
|
|
63
|
+
|
|
64
|
+
const decreasePage = useCallback(() => {
|
|
65
|
+
setQueryString("page", (page - 1).toString());
|
|
66
|
+
setIsLoading(true);
|
|
67
|
+
}, [setQueryString, page]);
|
|
68
|
+
|
|
69
|
+
const setPage = useCallback(
|
|
70
|
+
(newPage: number) => {
|
|
71
|
+
setQueryString("page", newPage.toString());
|
|
72
|
+
setIsLoading(true);
|
|
73
|
+
},
|
|
74
|
+
[setQueryString]
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
items,
|
|
79
|
+
setItems,
|
|
80
|
+
searchTerm,
|
|
81
|
+
setSearchTerm,
|
|
82
|
+
isLoading,
|
|
83
|
+
setIsLoading,
|
|
84
|
+
increasePage,
|
|
85
|
+
decreasePage,
|
|
86
|
+
setPage,
|
|
87
|
+
};
|
|
88
|
+
};
|
|
@@ -19,9 +19,24 @@ export const useQueryParams = () => {
|
|
|
19
19
|
|
|
20
20
|
const setQueryString = useCallback(
|
|
21
21
|
(prop: string, value: string) => {
|
|
22
|
-
router.push(pathname
|
|
22
|
+
router.push(`${pathname}?${createQueryString(prop, value)}`);
|
|
23
23
|
},
|
|
24
|
-
[router]
|
|
24
|
+
[router, pathname, createQueryString]
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const setQueryStrings = useCallback(
|
|
28
|
+
(props: Record<string, string | null>) => {
|
|
29
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
30
|
+
for (const [key, value] of Object.entries(props)) {
|
|
31
|
+
if (value === null) {
|
|
32
|
+
params.delete(key);
|
|
33
|
+
} else {
|
|
34
|
+
params.set(key, value);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
router.push(`${pathname}?${params.toString()}`);
|
|
38
|
+
},
|
|
39
|
+
[router, pathname, searchParams]
|
|
25
40
|
);
|
|
26
41
|
|
|
27
42
|
const removeQueryString = useCallback(
|
|
@@ -29,10 +44,10 @@ export const useQueryParams = () => {
|
|
|
29
44
|
const params = new URLSearchParams(searchParams.toString());
|
|
30
45
|
params.delete(prop);
|
|
31
46
|
|
|
32
|
-
router.push(pathname
|
|
47
|
+
router.push(`${pathname}?${params.toString()}`);
|
|
33
48
|
},
|
|
34
|
-
[router]
|
|
49
|
+
[router, pathname, searchParams]
|
|
35
50
|
);
|
|
36
51
|
|
|
37
|
-
return { setQueryString, removeQueryString };
|
|
52
|
+
return { setQueryString, removeQueryString, setQueryStrings };
|
|
38
53
|
};
|
|
@@ -5,3 +5,5 @@ export const API_URL = (checkoutUrl: string, operation?: string) =>
|
|
|
5
5
|
// DEV URL - CHANGE BEFORE MERGE
|
|
6
6
|
// export const API_URL = (checkoutUrl?: string, operation?: string) =>
|
|
7
7
|
// `https://everton--b2bfaststoredev.myvtex.com/_v/buyer-portal/${operation}`;
|
|
8
|
+
|
|
9
|
+
export const DEBOUNCE_TIMEOUT = 500;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { addressLabelToPropMapping } from "./addresLabelToPropMapping";
|
|
2
2
|
export { getApiUrl, getPostalCodeApiUrl } from "./api";
|
|
3
3
|
export { compareItems } from "./compareItems";
|
|
4
|
-
export { API_URL, AUT_COOKIE_KEY } from "./constants";
|
|
4
|
+
export { API_URL, AUT_COOKIE_KEY, DEBOUNCE_TIMEOUT } from "./constants";
|
|
5
5
|
export {
|
|
6
6
|
getAuthCookie,
|
|
7
7
|
getCookieAsString,
|
|
@@ -26,3 +26,4 @@ export { getContractSettingsLinks } from "./getContractSettingsLinks";
|
|
|
26
26
|
export { getOrganizationSettingsLinks } from "./getOrganizationSettingsLinks";
|
|
27
27
|
export { maskCardNumber, maskCVV, maskExpirationDate } from "./creditCard";
|
|
28
28
|
export { getFinanceSettingsLinks } from "./getFinanceSettingsLinks";
|
|
29
|
+
export { getValidPage } from "./getValidPage";
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { useSearchOrgUnits } from "../../org-units/hooks";
|
|
2
2
|
import { useDebounce } from "../../shared/hooks";
|
|
3
|
+
import { DEBOUNCE_TIMEOUT } from "../../shared/utils";
|
|
3
4
|
|
|
4
5
|
export const useDebouncedSearchOrgUnit = (searchTerm = "") => {
|
|
5
|
-
const debouncedSearchTerm = useDebounce(searchTerm,
|
|
6
|
+
const debouncedSearchTerm = useDebounce(searchTerm, DEBOUNCE_TIMEOUT);
|
|
6
7
|
const { searchedOrgUnits } = useSearchOrgUnits(debouncedSearchTerm);
|
|
7
8
|
|
|
8
9
|
if (searchTerm === "") {
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getAddressesService,
|
|
3
|
-
type GetAddressesServiceProps,
|
|
4
|
-
} from "../features/addresses/services";
|
|
1
|
+
import type { GetAddressesServiceProps } from "../features/addresses/services";
|
|
5
2
|
|
|
6
3
|
import {
|
|
7
4
|
type ClientContext,
|
|
8
5
|
getClientContext,
|
|
9
|
-
|
|
6
|
+
getValidPage,
|
|
10
7
|
} from "../features/shared/utils";
|
|
11
|
-
import type { AddressData } from "../features/addresses/types";
|
|
12
|
-
import { AddressLayout } from "../features/addresses/layouts";
|
|
13
8
|
import { BuyerPortalProvider } from "../features/shared/components";
|
|
14
9
|
import type { LoaderData } from "../features/shared/types";
|
|
15
10
|
import type { OrgUnitBasicData } from "../features/org-units/types";
|
|
@@ -19,12 +14,14 @@ import type { UserData } from "../features/users/types";
|
|
|
19
14
|
import type { ContractData } from "../features/contracts/types";
|
|
20
15
|
import { getContractDetailsService } from "../features/contracts/services";
|
|
21
16
|
import type { BuyingPolicy } from "../features/buying-policies/types";
|
|
22
|
-
import { buyingPoliciesData } from "../features/buying-policies/mocks";
|
|
23
17
|
import { BuyingPoliciesLayout } from "../features/buying-policies/layouts";
|
|
18
|
+
import { getBuyingPoliciesService } from "../features/buying-policies/services";
|
|
24
19
|
|
|
25
20
|
export type BuyingPoliciesPageData = {
|
|
26
|
-
|
|
21
|
+
buyingPolicies: BuyingPolicy[];
|
|
22
|
+
total: number;
|
|
27
23
|
search: string;
|
|
24
|
+
page: number;
|
|
28
25
|
context: {
|
|
29
26
|
currentContract: ContractData | null;
|
|
30
27
|
clientContext: ClientContext;
|
|
@@ -37,12 +34,15 @@ export type BuyingPoliciesPageQuery = GetAddressesServiceProps & {
|
|
|
37
34
|
orgUnitId: string;
|
|
38
35
|
contractId: string;
|
|
39
36
|
search?: string;
|
|
37
|
+
page?: string;
|
|
40
38
|
};
|
|
41
39
|
|
|
42
40
|
export async function loader(
|
|
43
41
|
data: LoaderData<BuyingPoliciesPageQuery>
|
|
44
42
|
): Promise<BuyingPoliciesPageData> {
|
|
45
|
-
const { contractId, orgUnitId, search = "" } = data.query;
|
|
43
|
+
const { contractId, orgUnitId, search = "", page: pageString } = data.query;
|
|
44
|
+
|
|
45
|
+
const page = getValidPage(pageString);
|
|
46
46
|
|
|
47
47
|
const { cookie, userId, ...clientContext } = await getClientContext(data);
|
|
48
48
|
|
|
@@ -58,11 +58,20 @@ export async function loader(
|
|
|
58
58
|
cookie,
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
const { data: buyingPolicies = [], total = 0 } =
|
|
62
|
+
await getBuyingPoliciesService({
|
|
63
|
+
orgUnitId,
|
|
64
|
+
contractId,
|
|
65
|
+
cookie,
|
|
66
|
+
page,
|
|
67
|
+
search,
|
|
68
|
+
});
|
|
69
|
+
|
|
61
70
|
return {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
},
|
|
71
|
+
buyingPolicies,
|
|
72
|
+
total,
|
|
65
73
|
search,
|
|
74
|
+
page,
|
|
66
75
|
context: {
|
|
67
76
|
clientContext: { cookie, userId, ...clientContext },
|
|
68
77
|
currentOrgUnit,
|
|
@@ -72,9 +81,20 @@ export async function loader(
|
|
|
72
81
|
};
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
const AddressPage = ({
|
|
84
|
+
const AddressPage = ({
|
|
85
|
+
buyingPolicies,
|
|
86
|
+
search,
|
|
87
|
+
page,
|
|
88
|
+
context,
|
|
89
|
+
total,
|
|
90
|
+
}: BuyingPoliciesPageData) => (
|
|
76
91
|
<BuyerPortalProvider {...context}>
|
|
77
|
-
<BuyingPoliciesLayout
|
|
92
|
+
<BuyingPoliciesLayout
|
|
93
|
+
buyingPolicies={buyingPolicies}
|
|
94
|
+
search={search}
|
|
95
|
+
page={page}
|
|
96
|
+
total={total}
|
|
97
|
+
/>
|
|
78
98
|
</BuyerPortalProvider>
|
|
79
99
|
);
|
|
80
100
|
|
|
@@ -12,6 +12,7 @@ import { getContractDetailsService } from "../features/contracts/services";
|
|
|
12
12
|
import type { BuyingPolicy } from "../features/buying-policies/types";
|
|
13
13
|
import { buyingPoliciesData } from "../features/buying-policies/mocks";
|
|
14
14
|
import { BuyingPolicyDetailsLayout } from "../features/buying-policies/layouts/BuyingPolicyDetailsLayout/BuyingPolicyDetailsLayout";
|
|
15
|
+
import { getBuyingPolicyService } from "../features/buying-policies/services";
|
|
15
16
|
|
|
16
17
|
export type BuyingPolicyDetailsPageData = {
|
|
17
18
|
data: { buyingPolicy: BuyingPolicy | null };
|
|
@@ -48,11 +49,16 @@ export async function loader(
|
|
|
48
49
|
cookie,
|
|
49
50
|
});
|
|
50
51
|
|
|
52
|
+
const buyingPolicy = await getBuyingPolicyService({
|
|
53
|
+
orgUnitId,
|
|
54
|
+
contractId,
|
|
55
|
+
buyingPolicyId,
|
|
56
|
+
cookie,
|
|
57
|
+
});
|
|
58
|
+
|
|
51
59
|
return {
|
|
52
60
|
data: {
|
|
53
|
-
buyingPolicy
|
|
54
|
-
buyingPoliciesData.find((policy) => policy.id === buyingPolicyId) ??
|
|
55
|
-
null,
|
|
61
|
+
buyingPolicy,
|
|
56
62
|
},
|
|
57
63
|
context: {
|
|
58
64
|
clientContext: { cookie, userId, ...clientContext },
|