@vtex/faststore-plugin-buyer-portal 1.3.77 → 1.3.78
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 +10 -1
- package/package.json +1 -1
- package/src/features/addresses/clients/AddressesClient.ts +8 -26
- package/src/features/addresses/hooks/index.ts +1 -0
- package/src/features/addresses/hooks/useDebouncedSearchAddressByUnitId.ts +8 -9
- package/src/features/addresses/hooks/useListAddresses.ts +36 -0
- package/src/features/addresses/layouts/AddressesLayout/AddressesLayout.tsx +133 -151
- package/src/features/addresses/layouts/AddressesLayout/addresses-layout.scss +17 -2
- package/src/features/addresses/layouts/index.ts +1 -4
- package/src/features/shared/hooks/index.ts +1 -0
- package/src/features/shared/hooks/useUrlPaginatedSearch.ts +79 -0
- package/src/features/shared/utils/constants.ts +1 -1
- package/src/pages/addresses.tsx +16 -62
- package/src/features/addresses/hooks/useSearchAddressByUnitId.ts +0 -27
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.78] - 2026-04-23
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Move addresses list requests from server to client side
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Remove `for` loop from `AddressesClient.getAddressesByUnitId` to prevent failed requests
|
|
17
|
+
|
|
10
18
|
## [1.3.77] - 2026-04-14
|
|
11
19
|
|
|
12
20
|
### Changed
|
|
@@ -573,7 +581,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
573
581
|
- Add CHANGELOG file
|
|
574
582
|
- Add README file
|
|
575
583
|
|
|
576
|
-
[unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.
|
|
584
|
+
[unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.78...HEAD
|
|
577
585
|
[1.3.55]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.54...v1.3.55
|
|
578
586
|
[1.3.54]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.53...v1.3.54
|
|
579
587
|
[1.3.53]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.52...v1.3.53
|
|
@@ -641,6 +649,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
641
649
|
[1.3.65]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.64...v1.3.65
|
|
642
650
|
[1.3.64]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.64
|
|
643
651
|
[1.3.69]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.69
|
|
652
|
+
[1.3.78]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.77...v1.3.78
|
|
644
653
|
[1.3.77]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.76...v1.3.77
|
|
645
654
|
[1.3.76]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.75...v1.3.76
|
|
646
655
|
[1.3.75]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.74...v1.3.75
|
package/package.json
CHANGED
|
@@ -32,32 +32,14 @@ export default class AddressesClient extends Client {
|
|
|
32
32
|
addressType,
|
|
33
33
|
page = 1,
|
|
34
34
|
}: GetAddressesByUnitIdParams) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const queryString = params.toString();
|
|
44
|
-
const url = queryString
|
|
45
|
-
? `units/${orgUnitId}/addresses?${queryString}`
|
|
46
|
-
: `units/${orgUnitId}/addresses`;
|
|
47
|
-
|
|
48
|
-
const response = await this.get<AddressResponse>(url, {
|
|
49
|
-
headers: {
|
|
50
|
-
Cookie: cookie,
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
if (response?.addresses) {
|
|
55
|
-
allAddresses = allAddresses.concat(response.addresses);
|
|
56
|
-
total = response.total;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return { addresses: allAddresses, total };
|
|
35
|
+
return this.get<AddressResponse>(`units/${orgUnitId}/addresses`, {
|
|
36
|
+
headers: { Cookie: cookie },
|
|
37
|
+
params: {
|
|
38
|
+
page,
|
|
39
|
+
name: name ?? "",
|
|
40
|
+
addressType: addressType ?? "",
|
|
41
|
+
},
|
|
42
|
+
});
|
|
61
43
|
}
|
|
62
44
|
|
|
63
45
|
getAddressById(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useDebounce } from "../../shared/hooks";
|
|
2
2
|
import { DEBOUNCE_TIMEOUT } from "../../shared/utils/constants";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { useListAddresses } from "./useListAddresses";
|
|
5
5
|
|
|
6
6
|
import type { AddressData } from "../types";
|
|
7
7
|
import type { UseDebouncedSearchAddressByUnitIdProps } from "../types/AddressData";
|
|
@@ -12,12 +12,11 @@ export const useDebouncedSearchAddressByUnitId = ({
|
|
|
12
12
|
addressType,
|
|
13
13
|
}: UseDebouncedSearchAddressByUnitIdProps) => {
|
|
14
14
|
const debouncedSearchTerm = useDebounce(search, DEBOUNCE_TIMEOUT);
|
|
15
|
-
const {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
});
|
|
15
|
+
const { listAddressesData, listAddressesLoading } = useListAddresses({
|
|
16
|
+
orgUnitId,
|
|
17
|
+
search: debouncedSearchTerm,
|
|
18
|
+
addressType,
|
|
19
|
+
});
|
|
21
20
|
|
|
22
21
|
if (search === "") {
|
|
23
22
|
return {
|
|
@@ -27,7 +26,7 @@ export const useDebouncedSearchAddressByUnitId = ({
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
return {
|
|
30
|
-
searchedAddresses:
|
|
31
|
-
isDebouncedSearchAddressesLoading:
|
|
29
|
+
searchedAddresses: listAddressesData?.data ?? [],
|
|
30
|
+
isDebouncedSearchAddressesLoading: listAddressesLoading,
|
|
32
31
|
};
|
|
33
32
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type QueryOptions, useQuery } from "../../shared/hooks";
|
|
2
|
+
import { getAddressesByUnitIdService } from "../services";
|
|
3
|
+
|
|
4
|
+
type UseListAddressesProps = {
|
|
5
|
+
orgUnitId: string;
|
|
6
|
+
search: string;
|
|
7
|
+
page?: number;
|
|
8
|
+
addressType?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const useListAddresses = (
|
|
12
|
+
{ orgUnitId, search, page = 1, addressType }: UseListAddressesProps,
|
|
13
|
+
options?: QueryOptions<AwaitedType<typeof getAddressesByUnitIdService>>
|
|
14
|
+
) => {
|
|
15
|
+
const { data, error, isLoading, refetch } = useQuery(
|
|
16
|
+
`api/addresses/unitId=${orgUnitId}/page=${page}/search=${
|
|
17
|
+
search ?? ""
|
|
18
|
+
}/addressType=${addressType ?? ""}`,
|
|
19
|
+
({ cookie }) =>
|
|
20
|
+
getAddressesByUnitIdService({
|
|
21
|
+
orgUnitId,
|
|
22
|
+
search,
|
|
23
|
+
cookie,
|
|
24
|
+
addressType,
|
|
25
|
+
page,
|
|
26
|
+
}),
|
|
27
|
+
options
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
listAddressesData: data,
|
|
32
|
+
listAddressesError: error,
|
|
33
|
+
listAddressesLoading: isLoading,
|
|
34
|
+
listAddressesRefresh: refetch,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -3,85 +3,42 @@ import { useEffect } from "react";
|
|
|
3
3
|
import { useRouter } from "next/router";
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
EmptyState,
|
|
6
7
|
HeaderInside,
|
|
7
8
|
IconBookmarked,
|
|
8
9
|
InternalSearch,
|
|
9
10
|
Paginator,
|
|
11
|
+
Table,
|
|
10
12
|
} from "../../../shared/components";
|
|
11
|
-
import { EmptyState } from "../../../shared/components/EmptyState/EmptyState";
|
|
12
|
-
import { Table } from "../../../shared/components/Table/Table";
|
|
13
13
|
import { getTableColumns } from "../../../shared/components/Table/utils/tableColumns";
|
|
14
14
|
import {
|
|
15
15
|
useBuyerPortal,
|
|
16
16
|
useDrawerProps,
|
|
17
17
|
useGetScopeConfig,
|
|
18
|
+
useUrlPaginatedSearch,
|
|
18
19
|
SCOPE_KEYS,
|
|
19
20
|
} from "../../../shared/hooks";
|
|
20
|
-
import { usePageItems } from "../../../shared/hooks/usePageItems";
|
|
21
21
|
import { ContractTabsLayout, GlobalLayout } from "../../../shared/layouts";
|
|
22
22
|
import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
23
23
|
import { AddressDropdownMenu, CreateAddressDrawer } from "../../components";
|
|
24
24
|
import { CreateAddressSettingsDrawer } from "../../components/CreateAddressSettingsDrawer/CreateAddressSettingsDrawer";
|
|
25
|
-
import {
|
|
25
|
+
import { useListAddresses } from "../../hooks/useListAddresses";
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
const PAGE_SIZE = 25;
|
|
28
28
|
|
|
29
|
-
export
|
|
30
|
-
addresses: AddressData[];
|
|
31
|
-
total: number;
|
|
32
|
-
search: string;
|
|
33
|
-
page: number;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export const AddressLayout = ({
|
|
37
|
-
addresses: initialAddresses,
|
|
38
|
-
search,
|
|
39
|
-
total,
|
|
40
|
-
page,
|
|
41
|
-
}: AddressLayoutProps) => {
|
|
29
|
+
export const AddressLayout = () => {
|
|
42
30
|
const router = useRouter();
|
|
43
31
|
|
|
44
32
|
const {
|
|
45
|
-
isLoading,
|
|
46
|
-
items: addresses,
|
|
47
|
-
searchTerm,
|
|
48
|
-
setSearchTerm,
|
|
49
|
-
increasePage,
|
|
50
|
-
updateItemsFromRefetch,
|
|
51
|
-
setIsLoading,
|
|
52
|
-
} = usePageItems<AddressData>({
|
|
53
|
-
initialItems: initialAddresses,
|
|
54
|
-
search,
|
|
55
33
|
page,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
{ lazy: true }
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
if (addresses.length === 0) {
|
|
69
|
-
setIsLoading(true);
|
|
70
|
-
refetchSearchedAddresses();
|
|
71
|
-
}
|
|
72
|
-
}, []);
|
|
73
|
-
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
if (addressesData?.data) {
|
|
76
|
-
updateItemsFromRefetch(addressesData.data);
|
|
77
|
-
}
|
|
78
|
-
}, [addressesData]);
|
|
79
|
-
|
|
80
|
-
const handleRefetchAddresses = () => {
|
|
81
|
-
setIsLoading(true);
|
|
82
|
-
refetchScopeConfig();
|
|
83
|
-
refetchSearchedAddresses();
|
|
84
|
-
};
|
|
34
|
+
search,
|
|
35
|
+
committedSearch,
|
|
36
|
+
isRouterSynced,
|
|
37
|
+
setSearch,
|
|
38
|
+
goToPage,
|
|
39
|
+
nextPage,
|
|
40
|
+
previousPage,
|
|
41
|
+
} = useUrlPaginatedSearch();
|
|
85
42
|
|
|
86
43
|
const {
|
|
87
44
|
open: openCreateDrawer,
|
|
@@ -109,6 +66,21 @@ export const AddressLayout = ({
|
|
|
109
66
|
}
|
|
110
67
|
);
|
|
111
68
|
|
|
69
|
+
const { listAddressesData, listAddressesLoading, listAddressesRefresh } =
|
|
70
|
+
useListAddresses(
|
|
71
|
+
{
|
|
72
|
+
orgUnitId: (router.query.orgUnitId as string) ?? "",
|
|
73
|
+
page: isRouterSynced ? page : 0,
|
|
74
|
+
search: committedSearch,
|
|
75
|
+
},
|
|
76
|
+
{ lazy: !isRouterSynced }
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const handleRefetchAddresses = () => {
|
|
80
|
+
refetchScopeConfig();
|
|
81
|
+
listAddressesRefresh();
|
|
82
|
+
};
|
|
83
|
+
|
|
112
84
|
const columns = getTableColumns({
|
|
113
85
|
nameColumnSize: "18.125rem",
|
|
114
86
|
actionsLength: 2,
|
|
@@ -123,6 +95,23 @@ export const AddressLayout = ({
|
|
|
123
95
|
],
|
|
124
96
|
});
|
|
125
97
|
|
|
98
|
+
const intervalData = {
|
|
99
|
+
page,
|
|
100
|
+
itemsLength: listAddressesData?.data.length ?? 0,
|
|
101
|
+
total: listAddressesData?.total ?? 0,
|
|
102
|
+
isLoading: listAddressesLoading,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const totalPages = Math.ceil(intervalData.total / PAGE_SIZE);
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (listAddressesLoading || intervalData.total === 0) return;
|
|
109
|
+
|
|
110
|
+
if (page > totalPages) {
|
|
111
|
+
goToPage(Math.max(totalPages, 1));
|
|
112
|
+
}
|
|
113
|
+
}, [page, totalPages, intervalData.total, listAddressesLoading]);
|
|
114
|
+
|
|
126
115
|
return (
|
|
127
116
|
<GlobalLayout>
|
|
128
117
|
<ContractTabsLayout
|
|
@@ -142,103 +131,96 @@ export const AddressLayout = ({
|
|
|
142
131
|
<HeaderInside.Button onClick={openCreateDrawer} />
|
|
143
132
|
</HeaderInside>
|
|
144
133
|
|
|
145
|
-
|
|
146
|
-
<
|
|
134
|
+
<div data-fs-buyer-portal-address-filter>
|
|
135
|
+
<InternalSearch defaultValue={search} textSearch={setSearch} />
|
|
136
|
+
<Paginator.Interval {...intervalData} />
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{!listAddressesLoading && intervalData.total === 0 ? (
|
|
140
|
+
<EmptyState
|
|
141
|
+
title={
|
|
142
|
+
committedSearch.length > 0
|
|
143
|
+
? "No results found"
|
|
144
|
+
: "No addresses yet"
|
|
145
|
+
}
|
|
146
|
+
description={
|
|
147
|
+
committedSearch.length > 0
|
|
148
|
+
? "Try using different terms or filters"
|
|
149
|
+
: undefined
|
|
150
|
+
}
|
|
151
|
+
iconName={
|
|
152
|
+
committedSearch.length > 0 ? undefined : "LocalPostOffice"
|
|
153
|
+
}
|
|
154
|
+
/>
|
|
147
155
|
) : (
|
|
148
|
-
|
|
149
|
-
<
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
{Array.isArray(address.types) &&
|
|
185
|
-
address.types.length > 0 ? (
|
|
186
|
-
<span>{address.types.join(", ")}</span>
|
|
187
|
-
) : null}
|
|
188
|
-
</Table.Cell>
|
|
189
|
-
}
|
|
190
|
-
href={buyerPortalRoutes.addressDetails({
|
|
191
|
-
orgUnitId: orgUnit?.id ?? "",
|
|
192
|
-
contractId: contract?.id ?? "",
|
|
193
|
-
addressId: id,
|
|
194
|
-
})}
|
|
195
|
-
actionIcons={
|
|
196
|
-
address.isDefault ? <IconBookmarked /> : null
|
|
197
|
-
}
|
|
198
|
-
dropdownMenu={
|
|
199
|
-
<AddressDropdownMenu
|
|
200
|
-
currentAddress={{
|
|
201
|
-
id,
|
|
202
|
-
...address,
|
|
203
|
-
}}
|
|
204
|
-
isSyncMode={isSyncMode}
|
|
205
|
-
onUpdate={handleRefetchAddresses}
|
|
206
|
-
onOpen={() => {
|
|
207
|
-
const url = buyerPortalRoutes.addressDetails({
|
|
208
|
-
orgUnitId: orgUnit?.id ?? "",
|
|
209
|
-
contractId: contract?.id ?? "",
|
|
210
|
-
addressId: id,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
router.push(url);
|
|
214
|
-
}}
|
|
215
|
-
/>
|
|
216
|
-
}
|
|
156
|
+
<div data-fs-addresses-table>
|
|
157
|
+
<Table layoutFixed>
|
|
158
|
+
<Table.Head columns={columns} />
|
|
159
|
+
<Table.Body>
|
|
160
|
+
{listAddressesLoading ? (
|
|
161
|
+
<Table.Loading columns={columns.length} />
|
|
162
|
+
) : (
|
|
163
|
+
listAddressesData?.data.map(({ id, ...address }) => (
|
|
164
|
+
<Table.Row
|
|
165
|
+
key={id}
|
|
166
|
+
title={address.name}
|
|
167
|
+
searchTerm={committedSearch}
|
|
168
|
+
iconName="LocalPostOffice"
|
|
169
|
+
iconSize={20}
|
|
170
|
+
href={buyerPortalRoutes.addressDetails({
|
|
171
|
+
orgUnitId: orgUnit?.id ?? "",
|
|
172
|
+
contractId: contract?.id ?? "",
|
|
173
|
+
addressId: id,
|
|
174
|
+
})}
|
|
175
|
+
actionIcons={
|
|
176
|
+
address.isDefault ? <IconBookmarked /> : null
|
|
177
|
+
}
|
|
178
|
+
dropdownMenu={
|
|
179
|
+
<AddressDropdownMenu
|
|
180
|
+
currentAddress={{ id, ...address }}
|
|
181
|
+
isSyncMode={isSyncMode}
|
|
182
|
+
onUpdate={handleRefetchAddresses}
|
|
183
|
+
onOpen={() => {
|
|
184
|
+
const url = buyerPortalRoutes.addressDetails({
|
|
185
|
+
orgUnitId: orgUnit?.id ?? "",
|
|
186
|
+
contractId: contract?.id ?? "",
|
|
187
|
+
addressId: id,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
router.push(url);
|
|
191
|
+
}}
|
|
217
192
|
/>
|
|
218
|
-
|
|
219
|
-
)}
|
|
220
|
-
</Table.Body>
|
|
221
|
-
</Table>
|
|
222
|
-
|
|
223
|
-
{!isLoading && (
|
|
224
|
-
<p
|
|
225
|
-
data-fs-bp-addresses-paginator-last
|
|
226
|
-
>{`${addresses.length} of ${total}`}</p>
|
|
227
|
-
)}
|
|
228
|
-
|
|
229
|
-
<div data-fs-bp-addresses-paginator>
|
|
230
|
-
{total > addresses.length && (
|
|
231
|
-
<Paginator.NextPageButton
|
|
232
|
-
onClick={increasePage}
|
|
233
|
-
disabled={isLoading}
|
|
193
|
+
}
|
|
234
194
|
>
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
195
|
+
<Table.Cell hideOnScreenSize="phonemid">
|
|
196
|
+
{Array.isArray(address.types) &&
|
|
197
|
+
address.types.length > 0 ? (
|
|
198
|
+
<span>{address.types.join(", ")}</span>
|
|
199
|
+
) : null}
|
|
200
|
+
</Table.Cell>
|
|
201
|
+
</Table.Row>
|
|
202
|
+
))
|
|
203
|
+
)}
|
|
204
|
+
</Table.Body>
|
|
205
|
+
</Table>
|
|
206
|
+
|
|
207
|
+
<div data-fs-bp-addresses-paginator>
|
|
208
|
+
<Paginator.NextPageButton
|
|
209
|
+
onClick={previousPage}
|
|
210
|
+
disabled={page <= 1}
|
|
211
|
+
>
|
|
212
|
+
Previous Page
|
|
213
|
+
</Paginator.NextPageButton>
|
|
214
|
+
<Paginator.NextPageButton
|
|
215
|
+
data-fs-bp-addresses-next-page
|
|
216
|
+
onClick={nextPage}
|
|
217
|
+
disabled={page >= totalPages}
|
|
218
|
+
>
|
|
219
|
+
Next Page
|
|
220
|
+
</Paginator.NextPageButton>
|
|
221
|
+
<Paginator.Interval {...intervalData} />
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
242
224
|
)}
|
|
243
225
|
|
|
244
226
|
{isCreateAddressDrawerOpen && (
|
|
@@ -146,8 +146,23 @@
|
|
|
146
146
|
display: flex;
|
|
147
147
|
align-items: center;
|
|
148
148
|
justify-content: flex-start;
|
|
149
|
-
margin-top: var(--fs-
|
|
150
|
-
padding: var(--fs-
|
|
149
|
+
margin-top: var(--fs-bp-margin-3);
|
|
150
|
+
padding: var(--fs-bp-padding-3) 0;
|
|
151
|
+
gap: var(--fs-bp-gap-4);
|
|
152
|
+
|
|
153
|
+
[data-fs-bp-paginator-counter] {
|
|
154
|
+
margin-left: var(--fs-bp-margin-auto)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
[data-fs-paginator-next-page-button]:disabled {
|
|
158
|
+
cursor: not-allowed;
|
|
159
|
+
color: var(--fs-bp-color-neutral-6);
|
|
160
|
+
|
|
161
|
+
&:hover {
|
|
162
|
+
color: var(--fs-bp-color-neutral-6);
|
|
163
|
+
background-color: transparent;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
151
166
|
|
|
152
167
|
@include media("<=phonemid") {
|
|
153
168
|
padding: var(--fs-spacing-3) 0;
|
|
@@ -2,7 +2,4 @@ export {
|
|
|
2
2
|
AddressDetailsLayout,
|
|
3
3
|
type AddressDetailsLayoutProps,
|
|
4
4
|
} from "./AddressDetailsLayout/AddressDetailsLayout";
|
|
5
|
-
export {
|
|
6
|
-
AddressLayout,
|
|
7
|
-
type AddressLayoutProps,
|
|
8
|
-
} from "./AddressesLayout/AddressesLayout";
|
|
5
|
+
export { AddressLayout } from "./AddressesLayout/AddressesLayout";
|
|
@@ -18,3 +18,4 @@ export { useAnalytics } from "./analytics/useAnalytics";
|
|
|
18
18
|
export { ANALYTICS_FALLBACK_CORRELATION_ID } from "./analytics/constants";
|
|
19
19
|
export { useGetScopeConfig, SCOPE_KEYS } from "./useGetScopeConfig";
|
|
20
20
|
export { useSetScopeConfig } from "./useSetScopeConfig";
|
|
21
|
+
export { useUrlPaginatedSearch } from "./useUrlPaginatedSearch";
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { useRouter } from "next/router";
|
|
4
|
+
|
|
5
|
+
import { getValidPage } from "../utils";
|
|
6
|
+
|
|
7
|
+
import { useDebounce } from "./useDebounce";
|
|
8
|
+
import { useQueryParams } from "./useQueryParams";
|
|
9
|
+
|
|
10
|
+
type UseUrlPaginatedSearchOptions = {
|
|
11
|
+
pageParam?: string;
|
|
12
|
+
searchParam?: string;
|
|
13
|
+
debounceMs?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function useUrlPaginatedSearch(options?: UseUrlPaginatedSearchOptions) {
|
|
17
|
+
const {
|
|
18
|
+
pageParam = "page",
|
|
19
|
+
searchParam = "search",
|
|
20
|
+
debounceMs = 300,
|
|
21
|
+
} = options ?? {};
|
|
22
|
+
|
|
23
|
+
const router = useRouter();
|
|
24
|
+
const { setQueryStrings } = useQueryParams();
|
|
25
|
+
|
|
26
|
+
const [page, setPageState] = useState(1);
|
|
27
|
+
const [search, setSearch] = useState("");
|
|
28
|
+
const [committedSearch, setCommittedSearch] = useState("");
|
|
29
|
+
const [isRouterSynced, setIsRouterSynced] = useState(false);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!router.isReady) return;
|
|
33
|
+
|
|
34
|
+
const urlPage = getValidPage(router.query[pageParam]);
|
|
35
|
+
const urlSearch = (router.query[searchParam] ?? "").toString();
|
|
36
|
+
|
|
37
|
+
setPageState(urlPage);
|
|
38
|
+
setSearch(urlSearch);
|
|
39
|
+
setCommittedSearch(urlSearch);
|
|
40
|
+
setIsRouterSynced(true);
|
|
41
|
+
}, [router.isReady, router.query[pageParam], router.query[searchParam]]);
|
|
42
|
+
|
|
43
|
+
useDebounce(search, debounceMs, {
|
|
44
|
+
onDebounce: (value) => {
|
|
45
|
+
setCommittedSearch((prev) => {
|
|
46
|
+
if (value === prev) return prev;
|
|
47
|
+
|
|
48
|
+
setPageState(1);
|
|
49
|
+
setQueryStrings({
|
|
50
|
+
[pageParam]: "1",
|
|
51
|
+
[searchParam]: value || null,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return value;
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const goToPage = (newPage: number) => {
|
|
60
|
+
setPageState(() => {
|
|
61
|
+
setQueryStrings({ [pageParam]: `${newPage}` });
|
|
62
|
+
return newPage;
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const nextPage = () => goToPage(page + 1);
|
|
67
|
+
const previousPage = () => goToPage(page - 1);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
page,
|
|
71
|
+
search,
|
|
72
|
+
committedSearch,
|
|
73
|
+
isRouterSynced,
|
|
74
|
+
setSearch,
|
|
75
|
+
goToPage,
|
|
76
|
+
nextPage,
|
|
77
|
+
previousPage,
|
|
78
|
+
};
|
|
79
|
+
}
|
package/src/pages/addresses.tsx
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { AddressLayout } from "../features/addresses/layouts";
|
|
2
|
-
import {
|
|
3
|
-
getAddressesByUnitIdService,
|
|
4
|
-
type GetAddressesServiceProps,
|
|
5
|
-
} from "../features/addresses/services";
|
|
6
2
|
import { getContractDetailsService } from "../features/contracts/services";
|
|
7
3
|
import { getOrgUnitBasicDataService } from "../features/org-units/services";
|
|
8
4
|
import { withErrorBoundary } from "../features/shared/components";
|
|
@@ -10,24 +6,18 @@ import { ErrorBoundaryProps } from "../features/shared/components/ErrorBoundary/
|
|
|
10
6
|
import { ErrorTabsLayout } from "../features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout";
|
|
11
7
|
import {
|
|
12
8
|
type ClientContext,
|
|
13
|
-
getValidPage,
|
|
14
9
|
withLoaderErrorBoundary,
|
|
15
10
|
withAuthLoader,
|
|
16
11
|
withProviders,
|
|
17
12
|
} from "../features/shared/utils";
|
|
18
13
|
import { getUserByIdService } from "../features/users/services";
|
|
19
14
|
|
|
20
|
-
import type { AddressData } from "../features/addresses/types";
|
|
21
15
|
import type { ContractData } from "../features/contracts/types";
|
|
22
16
|
import type { OrgUnitBasicData } from "../features/org-units/types";
|
|
23
17
|
import type { AuthRouteProps, LoaderData } from "../features/shared/types";
|
|
24
18
|
import type { UserData } from "../features/users/types";
|
|
25
19
|
|
|
26
20
|
export type AddressesPageData = {
|
|
27
|
-
data: AddressData[];
|
|
28
|
-
search: string;
|
|
29
|
-
total: number;
|
|
30
|
-
page: number;
|
|
31
21
|
context: {
|
|
32
22
|
currentContract: ContractData | null;
|
|
33
23
|
clientContext: ClientContext;
|
|
@@ -38,28 +28,21 @@ export type AddressesPageData = {
|
|
|
38
28
|
error?: ErrorBoundaryProps;
|
|
39
29
|
};
|
|
40
30
|
|
|
41
|
-
export type AddressesPageQuery =
|
|
31
|
+
export type AddressesPageQuery = {
|
|
42
32
|
orgUnitId: string;
|
|
43
33
|
contractId: string;
|
|
44
|
-
page?: string;
|
|
45
34
|
};
|
|
46
35
|
|
|
47
36
|
const loaderFunction = async (
|
|
48
37
|
data: LoaderData<AddressesPageQuery>
|
|
49
38
|
): Promise<AuthRouteProps<AddressesPageData>> => {
|
|
50
|
-
const { contractId, orgUnitId
|
|
51
|
-
|
|
52
|
-
const page = getValidPage(pageString);
|
|
39
|
+
const { contractId, orgUnitId } = data.query;
|
|
53
40
|
|
|
54
41
|
return withAuthLoader(
|
|
55
42
|
data,
|
|
56
43
|
async ({ cookie, userId, customerId, ...clientContext }) => {
|
|
57
44
|
if (!contractId || !orgUnitId) {
|
|
58
45
|
return {
|
|
59
|
-
data: [],
|
|
60
|
-
search: search ?? "",
|
|
61
|
-
total: 0,
|
|
62
|
-
page: page ?? 1,
|
|
63
46
|
context: {
|
|
64
47
|
clientContext: { customerId, cookie, userId, ...clientContext },
|
|
65
48
|
currentOrgUnit: null,
|
|
@@ -69,31 +52,20 @@ const loaderFunction = async (
|
|
|
69
52
|
};
|
|
70
53
|
}
|
|
71
54
|
|
|
72
|
-
const [currentOrgUnit, user, contract
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
getAddressesByUnitIdService({
|
|
85
|
-
orgUnitId,
|
|
86
|
-
search,
|
|
87
|
-
page,
|
|
88
|
-
cookie,
|
|
89
|
-
}),
|
|
90
|
-
]);
|
|
55
|
+
const [currentOrgUnit, user, contract] = await Promise.all([
|
|
56
|
+
getOrgUnitBasicDataService({
|
|
57
|
+
id: orgUnitId,
|
|
58
|
+
cookie,
|
|
59
|
+
}),
|
|
60
|
+
getUserByIdService({ orgUnitId, userId, cookie }),
|
|
61
|
+
getContractDetailsService({
|
|
62
|
+
contractId,
|
|
63
|
+
cookie,
|
|
64
|
+
unitId: orgUnitId,
|
|
65
|
+
}),
|
|
66
|
+
]);
|
|
91
67
|
|
|
92
68
|
return {
|
|
93
|
-
data: addressesResponse.data,
|
|
94
|
-
total: addressesResponse.total,
|
|
95
|
-
page,
|
|
96
|
-
search: search ?? "",
|
|
97
69
|
context: {
|
|
98
70
|
clientContext: { cookie, userId, customerId, ...clientContext },
|
|
99
71
|
currentOrgUnit,
|
|
@@ -110,26 +82,8 @@ export const loader = withLoaderErrorBoundary(loaderFunction, {
|
|
|
110
82
|
redirectToError: true,
|
|
111
83
|
});
|
|
112
84
|
|
|
113
|
-
const AddressPage = ({
|
|
114
|
-
|
|
115
|
-
search,
|
|
116
|
-
page,
|
|
117
|
-
total,
|
|
118
|
-
hasError,
|
|
119
|
-
error,
|
|
120
|
-
}: AddressesPageData) => (
|
|
121
|
-
<>
|
|
122
|
-
{hasError ? (
|
|
123
|
-
<ErrorTabsLayout error={error} />
|
|
124
|
-
) : (
|
|
125
|
-
<AddressLayout
|
|
126
|
-
addresses={data}
|
|
127
|
-
search={search}
|
|
128
|
-
page={page}
|
|
129
|
-
total={total}
|
|
130
|
-
/>
|
|
131
|
-
)}
|
|
132
|
-
</>
|
|
85
|
+
const AddressPage = ({ hasError, error }: AddressesPageData) => (
|
|
86
|
+
<>{hasError ? <ErrorTabsLayout error={error} /> : <AddressLayout />}</>
|
|
133
87
|
);
|
|
134
88
|
|
|
135
89
|
export default withProviders(
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { type QueryOptions, useQuery } from "../../shared/hooks";
|
|
2
|
-
import { getAddressesByUnitIdService } from "../services";
|
|
3
|
-
|
|
4
|
-
import type { UseDebouncedSearchAddressByUnitIdProps } from "../types/AddressData";
|
|
5
|
-
|
|
6
|
-
export const useSearchAddressByUnitId = (
|
|
7
|
-
{ orgUnitId, search, addressType }: UseDebouncedSearchAddressByUnitIdProps,
|
|
8
|
-
options?: QueryOptions<AwaitedType<typeof getAddressesByUnitIdService>>
|
|
9
|
-
) => {
|
|
10
|
-
const { data, error, isLoading, refetch } = useQuery(
|
|
11
|
-
`api/search/addresses/${search}`,
|
|
12
|
-
({ cookie }) =>
|
|
13
|
-
getAddressesByUnitIdService({
|
|
14
|
-
orgUnitId,
|
|
15
|
-
search,
|
|
16
|
-
cookie,
|
|
17
|
-
addressType,
|
|
18
|
-
}),
|
|
19
|
-
options
|
|
20
|
-
);
|
|
21
|
-
return {
|
|
22
|
-
searchedAddresses: data,
|
|
23
|
-
hasSearchedAddressesError: error,
|
|
24
|
-
isSearchedAddressesLoading: isLoading,
|
|
25
|
-
refetchSearchedAddresses: refetch,
|
|
26
|
-
};
|
|
27
|
-
};
|