@vtex/faststore-plugin-buyer-portal 1.3.37 → 1.3.39

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.
Files changed (26) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/package.json +1 -1
  3. package/src/features/payment-methods/clients/PaymentMethodsClient.ts +9 -4
  4. package/src/features/payment-methods/components/AddPaymentMethodsDrawer/AddPaymentMethodsDrawer.tsx +75 -10
  5. package/src/features/payment-methods/components/AddPaymentMethodsDrawer/add-payment-methods-drawer.scss +17 -0
  6. package/src/features/payment-methods/hooks/useDebouncedSearchPaymentMethods.ts +4 -4
  7. package/src/features/payment-methods/hooks/useGetPaymentMethodsByUnitId.ts +2 -3
  8. package/src/features/payment-methods/layouts/PaymentMethodsLayout/PaymentMethodsLayout.tsx +82 -59
  9. package/src/features/payment-methods/layouts/PaymentMethodsLayout/payment-methods-layout.scss +147 -122
  10. package/src/features/payment-methods/services/get-payment-methods-by-unit-id.service.ts +7 -5
  11. package/src/features/payment-methods/types/PaymentMethod.ts +7 -0
  12. package/src/features/payment-methods/types/index.ts +1 -1
  13. package/src/features/product-assortment/clients/ProductAssortmentClient.ts +14 -9
  14. package/src/features/product-assortment/components/AddProductAssortmentDrawer/AddProductAssortmentDrawer.tsx +73 -28
  15. package/src/features/product-assortment/components/AddProductAssortmentDrawer/add-product-assortment-drawer.scss +4 -0
  16. package/src/features/product-assortment/components/ProductAssortmentTable/ProductAssortmentTable.tsx +2 -2
  17. package/src/features/product-assortment/hooks/index.ts +3 -0
  18. package/src/features/product-assortment/hooks/useGetProductAssortments.ts +35 -0
  19. package/src/features/product-assortment/layouts/ProductAssortmentLayout/ProductAssortmentLayout.tsx +67 -27
  20. package/src/features/product-assortment/services/get-product-assortment-from-contract.service.ts +4 -5
  21. package/src/features/product-assortment/services/get-product-assortment-from-scope.service.ts +9 -4
  22. package/src/features/product-assortment/services/index.ts +4 -0
  23. package/src/features/product-assortment/types/index.ts +19 -14
  24. package/src/features/shared/utils/constants.ts +1 -1
  25. package/src/pages/payment-methods.tsx +23 -4
  26. package/src/pages/productAssortment.tsx +18 -17
package/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.39] - 2025-12-09
11
+
12
+ ### Added
13
+
14
+ - Add empty state to Payment Methods
15
+
16
+ ## [1.3.38] - 2025-12-08
17
+
18
+ ### Added
19
+
20
+ - Pagination to payment methods
21
+ - Pagination to product assorments
22
+
10
23
  ## [1.3.37] - 2025-12-04
11
24
 
12
25
  ## Changed
@@ -339,7 +352,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
339
352
  - Add CHANGELOG file
340
353
  - Add README file
341
354
 
342
- [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.37...HEAD
355
+ [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.39...HEAD
343
356
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.2.2...1.2.3
344
357
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.3
345
358
  [1.2.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.4
@@ -381,6 +394,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
381
394
  > > > > > > > main
382
395
  > > > > > > > [1.3.11]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.11
383
396
 
397
+ [1.3.39]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.38...v1.3.39
398
+ [1.3.38]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.37...v1.3.38
384
399
  [1.3.37]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.36...v1.3.37
385
400
  [1.3.36]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.35...v1.3.36
386
401
  [1.3.35]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.35
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.3.37",
3
+ "version": "1.3.39",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -6,7 +6,7 @@ import type {
6
6
  PaymentMethodsIdArray,
7
7
  PaymentMethodsReqCommonParams,
8
8
  } from "../../shared/types";
9
- import type { PaymentMethodData } from "../types";
9
+ import type { PaginatedPaymentMethod } from "../types";
10
10
 
11
11
  export default class PaymentMethodsClient extends Client {
12
12
  constructor() {
@@ -18,6 +18,7 @@ export default class PaymentMethodsClient extends Client {
18
18
  options?: {
19
19
  filterByScope?: boolean;
20
20
  name?: string;
21
+ page?: number;
21
22
  };
22
23
  }
23
24
  ) {
@@ -25,7 +26,7 @@ export default class PaymentMethodsClient extends Client {
25
26
 
26
27
  const queryParams = new URLSearchParams();
27
28
 
28
- if (options?.filterByScope) {
29
+ if (options?.filterByScope !== undefined) {
29
30
  queryParams.append("filterByScope", options.filterByScope.toString());
30
31
  }
31
32
 
@@ -33,12 +34,16 @@ export default class PaymentMethodsClient extends Client {
33
34
  queryParams.append("name", options.name);
34
35
  }
35
36
 
37
+ if (options?.page) {
38
+ queryParams.append("page", options.page.toString());
39
+ }
40
+
36
41
  const queryString = queryParams.toString();
37
- const url = `customers/${customerId}/units/${unitId}/payment-methods${
42
+ const url = `customers/${customerId}/units/${unitId}/payment-methods/paginated${
38
43
  queryString ? `?${queryString}` : ""
39
44
  }`;
40
45
 
41
- return await this.get<PaymentMethodData[]>(url, {
46
+ return await this.get<PaginatedPaymentMethod>(url, {
42
47
  headers: {
43
48
  Cookie: cookie,
44
49
  },
@@ -8,12 +8,14 @@ import {
8
8
  type BasicDrawerProps,
9
9
  BasicDrawer,
10
10
  EmptyState,
11
+ InternalSearch,
12
+ Paginator,
11
13
  Table,
12
14
  } from "../../../shared/components";
13
- import { useBuyerPortal } from "../../../shared/hooks";
15
+ import { useBuyerPortal, useDebounce } from "../../../shared/hooks";
16
+ import { DEBOUNCE_TIMEOUT } from "../../../shared/utils";
14
17
  import { useAddPaymentMethodsToUnit } from "../../hooks";
15
18
  import { useDebouncedSearchPaymentMethods } from "../../hooks/useDebouncedSearchPaymentMethods";
16
- import { SearchPaymentMethods } from "../SearchPaymentMethods/SearchPaymentMethods";
17
19
 
18
20
  import type { TableColumn } from "../../../shared/components/Table/TableHead/TableHead";
19
21
 
@@ -30,6 +32,8 @@ export const AddPaymentMethodsDrawer = ({
30
32
  ...props
31
33
  }: AddPaymentMethodsDrawerProps) => {
32
34
  const [querySearch, setQuerySearch] = useState("");
35
+ const [drawerPage, setDrawerPage] = useState(1);
36
+ const [isLastPage, setIsLastPage] = useState(true);
33
37
  const [selectedMethods, setSelectedMethods] = useState<Set<number>>(
34
38
  new Set()
35
39
  );
@@ -44,19 +48,33 @@ export const AddPaymentMethodsDrawer = ({
44
48
  const customerId = contract?.id ?? "";
45
49
  const unitId = orgUnit?.id ?? "";
46
50
 
51
+ useDebounce(querySearch, DEBOUNCE_TIMEOUT, {
52
+ onDebounce: (value) => {
53
+ setQuerySearch(value);
54
+ setDrawerPage(1);
55
+ },
56
+ });
57
+
47
58
  const { paymentMethods, isLoadingPaymentMethods } =
48
59
  useDebouncedSearchPaymentMethods({
49
60
  customerId,
50
61
  unitId,
51
62
  filterByScope: false,
52
63
  search: querySearch,
64
+ page: drawerPage,
53
65
  });
54
66
 
55
67
  const availableMethods = useMemo(
56
- () => (paymentMethods ?? []).filter((method) => !method?.isEnabled),
68
+ () => paymentMethods?.items ?? [],
57
69
  [paymentMethods]
58
70
  );
59
71
 
72
+ useEffect(() => {
73
+ if (paymentMethods?.paging) {
74
+ setIsLastPage(paymentMethods.paging.pages === drawerPage);
75
+ }
76
+ }, [paymentMethods]);
77
+
60
78
  const handleSuccess = () => {
61
79
  pushToast({
62
80
  message: "Payment methods added successfully",
@@ -192,7 +210,7 @@ export const AddPaymentMethodsDrawer = ({
192
210
  const renderBodyContent = () => {
193
211
  if (isLoadingPaymentMethods) return renderLoading();
194
212
 
195
- if ((paymentMethods?.length ?? 0) === 0) return renderEmpty();
213
+ if ((paymentMethods?.items.length ?? 0) === 0) return renderEmpty();
196
214
 
197
215
  if (availableMethods.length > 0) return renderList();
198
216
 
@@ -220,14 +238,61 @@ export const AddPaymentMethodsDrawer = ({
220
238
  {orgUnit?.name && <span>{` to ${orgUnit.name}`}</span>}
221
239
  </p>
222
240
 
223
- <SearchPaymentMethods
224
- data={selectedMethods.size}
225
- paymentMethodsData={availableMethods}
226
- defaultValue={querySearch}
227
- textSearch={setQuerySearch}
228
- />
241
+ {paymentMethods && (
242
+ <div data-fs-payment-methods-filter>
243
+ <InternalSearch
244
+ textSearch={setQuerySearch}
245
+ defaultValue={querySearch}
246
+ />
247
+
248
+ {paymentMethods?.paging && paymentMethods.items.length > 0 && (
249
+ <Paginator.Counter
250
+ total={paymentMethods.paging.total}
251
+ itemsLength={
252
+ isLastPage
253
+ ? paymentMethods.paging.total
254
+ : drawerPage * paymentMethods.paging.perPage
255
+ }
256
+ />
257
+ )}
258
+ </div>
259
+ )}
229
260
 
230
261
  {renderBodyContent()}
262
+
263
+ {paymentMethods.items && paymentMethods.paging && (
264
+ <div data-fs-bp-drawer-payment-methods-paginator>
265
+ {availableMethods.length > 0 &&
266
+ paymentMethods?.paging?.page > 1 ? (
267
+ <Paginator.NextPageButton
268
+ onClick={() => setDrawerPage(drawerPage - 1)}
269
+ disabled={isLoadingPaymentMethods}
270
+ >
271
+ {isLoadingPaymentMethods ? "Loading" : "Previous page"}
272
+ </Paginator.NextPageButton>
273
+ ) : (
274
+ <></>
275
+ )}
276
+
277
+ {!isLastPage && (
278
+ <Paginator.NextPageButton
279
+ onClick={() => setDrawerPage(drawerPage + 1)}
280
+ disabled={isLoadingPaymentMethods}
281
+ >
282
+ {isLoadingPaymentMethods ? "Loading" : "Next page"}
283
+ </Paginator.NextPageButton>
284
+ )}
285
+
286
+ <Paginator.Counter
287
+ total={paymentMethods.paging.total}
288
+ itemsLength={
289
+ isLastPage
290
+ ? paymentMethods.paging.total
291
+ : drawerPage * paymentMethods.paging.perPage
292
+ }
293
+ />
294
+ </div>
295
+ )}
231
296
  </section>
232
297
  </BasicDrawer.Body>
233
298
 
@@ -16,6 +16,23 @@
16
16
  }
17
17
  }
18
18
 
19
+ [data-fs-payment-methods-filter] {
20
+ display: flex;
21
+ justify-content: space-between;
22
+ align-items: center;
23
+ height: 2.5rem;
24
+
25
+ @include media("<=tablet") {
26
+ [data-fs-buyer-portal-internal-search] {
27
+ width: 100%;
28
+ }
29
+
30
+ [data-fs-bp-paginator-counter] {
31
+ display: none;
32
+ }
33
+ }
34
+ }
35
+
19
36
  [data-fs-bp-payment-methods-checkbox] {
20
37
  border-color: #e5e5e5;
21
38
  margin: 0 auto;
@@ -3,22 +3,22 @@ import { GetPaymentMethodsByUnitIdServiceProps } from "../services";
3
3
 
4
4
  import { useGetPaymentMethodsByUnitId } from "./useGetPaymentMethodsByUnitId";
5
5
 
6
- import type { PaymentMethodData } from "../types";
7
-
8
6
  export const useDebouncedSearchPaymentMethods = (
9
7
  data: Omit<GetPaymentMethodsByUnitIdServiceProps, "cookie">
10
8
  ) => {
11
- const { customerId, unitId, search = "" } = data;
9
+ const { customerId, unitId, search = "", page, filterByScope } = data;
12
10
  const debouncedSearchTerm = useDebounce(search, 500);
13
11
  const { paymentMethods, isLoadingPaymentMethods } =
14
12
  useGetPaymentMethodsByUnitId({
15
13
  customerId,
16
14
  unitId,
17
15
  search: debouncedSearchTerm,
16
+ filterByScope,
17
+ page: page ?? 1,
18
18
  });
19
19
 
20
20
  return {
21
- paymentMethods: (paymentMethods ?? []) as PaymentMethodData[],
21
+ paymentMethods: paymentMethods,
22
22
  isLoadingPaymentMethods,
23
23
  };
24
24
  };
@@ -3,8 +3,7 @@ import {
3
3
  getPaymentMethodsByUnitIdService,
4
4
  type GetPaymentMethodsByUnitIdServiceProps,
5
5
  } from "../services";
6
-
7
- import type { PaymentMethodData } from "../types";
6
+ import { PaginatedPaymentMethod } from "../types";
8
7
 
9
8
  export const useGetPaymentMethodsByUnitId = (
10
9
  params: Omit<GetPaymentMethodsByUnitIdServiceProps, "cookie">,
@@ -23,7 +22,7 @@ export const useGetPaymentMethodsByUnitId = (
23
22
  );
24
23
 
25
24
  return {
26
- paymentMethods: (data ?? []) as PaymentMethodData[],
25
+ paymentMethods: data ?? ({} as PaginatedPaymentMethod),
27
26
  isLoadingPaymentMethods: isLoading,
28
27
  hasPaymentMethodsError: error,
29
28
  refetchPaymentMethods: refetch,
@@ -1,47 +1,65 @@
1
- import { useEffect, useMemo, useState } from "react";
1
+ import { useState } from "react";
2
2
 
3
- import { HeaderInside, Table } from "../../../shared/components";
3
+ import {
4
+ HeaderInside,
5
+ InternalSearch,
6
+ Paginator,
7
+ Table,
8
+ } from "../../../shared/components";
4
9
  import ConditionalTooltip from "../../../shared/components/ConditionalTooltip/ConditionalTooltip";
5
10
  import { EmptyState } from "../../../shared/components/EmptyState/EmptyState";
6
11
  import { getTableColumns } from "../../../shared/components/Table/utils/tableColumns";
7
12
  import {
8
13
  useBuyerPortal,
9
14
  useDrawerProps,
10
- useQueryParams,
15
+ usePageItems,
11
16
  } from "../../../shared/hooks";
12
17
  import { ContractTabsLayout, GlobalLayout } from "../../../shared/layouts";
13
18
  import {
14
19
  AddPaymentMethodsDrawer,
15
20
  RemoveMethodButton,
16
21
  RemovePaymentMethodsDrawer,
17
- SearchPaymentMethods,
18
22
  } from "../../components";
19
- import { useDebouncedSearchPaymentMethods } from "../../hooks/useDebouncedSearchPaymentMethods";
20
23
  import { useRemovePaymentMethod } from "../../hooks/useRemovePaymentMethodSubmit";
21
24
 
22
- import type { PaymentMethodData } from "../../types";
25
+ import type { PaginatedPaymentMethod, PaymentMethodData } from "../../types";
23
26
 
24
27
  export type PaymentMethodsLayoutProps = {
25
- data: PaymentMethodData[];
28
+ data: PaginatedPaymentMethod;
29
+ totalPaymentMethods: number;
26
30
  search?: string;
31
+ page?: number;
27
32
  };
28
33
 
29
34
  export const PaymentMethodsLayout = ({
30
35
  data,
31
36
  search,
37
+ totalPaymentMethods,
38
+ page = 1,
32
39
  }: PaymentMethodsLayoutProps) => {
33
- const [paymentMethodsData, setPaymentMethodsData] =
34
- useState<PaymentMethodData[]>(data);
35
40
  const [selectedMethod, setSelectedMethod] = useState<
36
41
  PaymentMethodData | undefined
37
42
  >(undefined);
38
- const [querySearch, setQuerySearch] = useState(search ?? "");
39
43
 
40
- const { setQueryString, removeQueryString } = useQueryParams();
41
44
  const { isRemovingPaymentMethod } = useRemovePaymentMethod(() => {
42
45
  setSelectedMethod(undefined);
43
46
  });
44
47
 
48
+ const isLastPage = data.paging.pages === page || data.paging.pages === 0;
49
+
50
+ const {
51
+ isLoading,
52
+ items: paymentMethods,
53
+ searchTerm,
54
+ setSearchTerm,
55
+ increasePage,
56
+ decreasePage,
57
+ } = usePageItems<PaymentMethodData>({
58
+ initialItems: data.items,
59
+ search,
60
+ page,
61
+ });
62
+
45
63
  const {
46
64
  open: openAddDrawer,
47
65
  isOpen: isAddPaymentMethodDrawerOpen,
@@ -60,39 +78,9 @@ export const PaymentMethodsLayout = ({
60
78
  currentContract: contract,
61
79
  } = useBuyerPortal();
62
80
 
63
- const { paymentMethods, isLoadingPaymentMethods } =
64
- useDebouncedSearchPaymentMethods({
65
- customerId: contract?.id ?? "",
66
- unitId: orgUnit?.id ?? "",
67
- search: querySearch,
68
- });
69
-
70
- const fetchedMethods = paymentMethods ?? [];
71
- const currentPaymentMethods = useMemo(
72
- () => (paymentMethodsData ?? []).filter((m) => m?.isEnabled),
73
- [paymentMethodsData]
74
- );
75
-
76
- const isSinglePaymentMethod = currentPaymentMethods.length <= 1;
81
+ const isSinglePaymentMethod = paymentMethods.length <= 1;
77
82
  const allPaymentMethodsSelected =
78
- currentPaymentMethods.length === (paymentMethodsData?.length ?? 0);
79
-
80
- useEffect(() => {
81
- if (!isLoadingPaymentMethods && querySearch.length > 0) {
82
- setPaymentMethodsData(fetchedMethods);
83
- }
84
- }, [fetchedMethods, isLoadingPaymentMethods, querySearch]);
85
-
86
- const handleSearch = (searchTerm: string) => {
87
- setQuerySearch(searchTerm);
88
-
89
- if (searchTerm) {
90
- setQueryString("search", searchTerm);
91
- } else {
92
- removeQueryString("search");
93
- setPaymentMethodsData(data);
94
- }
95
- };
83
+ paymentMethods.length === totalPaymentMethods;
96
84
 
97
85
  const onRemoveClick = (method: PaymentMethodData) => {
98
86
  setSelectedMethod(method);
@@ -106,7 +94,7 @@ export const PaymentMethodsLayout = ({
106
94
  <EmptyState
107
95
  title="No results found"
108
96
  description={
109
- querySearch.length > 0
97
+ searchTerm.length > 0
110
98
  ? "Try using different terms"
111
99
  : "No payment methods found"
112
100
  }
@@ -114,10 +102,10 @@ export const PaymentMethodsLayout = ({
114
102
  );
115
103
 
116
104
  const renderTableRows = () =>
117
- currentPaymentMethods.map((method) => (
105
+ paymentMethods.map((method) => (
118
106
  <Table.Row
119
107
  key={method?.id}
120
- searchTerm={querySearch}
108
+ searchTerm={searchTerm}
121
109
  title={method?.name}
122
110
  iconName="AccountBalanceWallet"
123
111
  iconSize={24}
@@ -135,7 +123,7 @@ export const PaymentMethodsLayout = ({
135
123
  <Table layoutFixed>
136
124
  <Table.Head columns={columns} />
137
125
  <Table.Body>
138
- {isLoadingPaymentMethods ? (
126
+ {isLoading ? (
139
127
  <Table.Loading columns={columns.length} />
140
128
  ) : (
141
129
  renderTableRows()
@@ -145,8 +133,7 @@ export const PaymentMethodsLayout = ({
145
133
  );
146
134
 
147
135
  const renderContent = () => {
148
- if (!isLoadingPaymentMethods && fetchedMethods.length === 0)
149
- return renderEmpty();
136
+ if (!isLoading && paymentMethods.length === 0) return renderEmpty();
150
137
  return renderTable();
151
138
  };
152
139
 
@@ -178,20 +165,56 @@ export const PaymentMethodsLayout = ({
178
165
  </ConditionalTooltip>
179
166
  </HeaderInside>
180
167
 
181
- <SearchPaymentMethods
182
- data={currentPaymentMethods}
183
- paymentMethodsData={paymentMethodsData}
184
- defaultValue={querySearch}
185
- textSearch={handleSearch}
186
- />
168
+ <div data-fs-payment-methods-filter>
169
+ <InternalSearch
170
+ defaultValue={searchTerm}
171
+ textSearch={setSearchTerm}
172
+ />
173
+ <Paginator.Counter
174
+ total={data.paging.total}
175
+ itemsLength={
176
+ isLastPage ? data.paging.total : page * data.paging.perPage
177
+ }
178
+ />
179
+ </div>
187
180
 
188
181
  {renderContent()}
189
182
 
190
- {!isLoadingPaymentMethods && (
183
+ {!isLoading && paymentMethods.length > 0 && (
184
+ <div data-fs-bp-payment-methods-paginator>
185
+ {data.paging.page > 1 ? (
186
+ <Paginator.NextPageButton
187
+ onClick={decreasePage}
188
+ disabled={isLoading}
189
+ >
190
+ {isLoading ? "Loading" : "Previous Page"}
191
+ </Paginator.NextPageButton>
192
+ ) : (
193
+ <></>
194
+ )}
195
+ {!isLastPage ? (
196
+ <Paginator.NextPageButton
197
+ onClick={increasePage}
198
+ disabled={isLoading}
199
+ >
200
+ {isLoading ? "Loading" : "Next Page"}
201
+ </Paginator.NextPageButton>
202
+ ) : (
203
+ <></>
204
+ )}
205
+
206
+ <Paginator.Counter
207
+ total={data.paging.total}
208
+ itemsLength={
209
+ isLastPage ? data.paging.total : page * data.paging.perPage
210
+ }
211
+ />
212
+ </div>
213
+ )}
214
+
215
+ {!isLoading && (
191
216
  <p data-fs-bp-payment-methods-search-container-counter-bottom>
192
- {`${currentPaymentMethods.length} of ${
193
- paymentMethodsData?.length ?? 0
194
- }`}
217
+ {`${paymentMethods.length} of ${paymentMethods?.length ?? 0}`}
195
218
  </p>
196
219
  )}
197
220