@vtex/faststore-plugin-buyer-portal 1.3.36 → 1.3.38

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 (27) 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 +80 -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 +15 -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 +33 -13
  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/features/shared/utils/creditCard.ts +1 -1
  26. package/src/pages/payment-methods.tsx +23 -4
  27. 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.38] - 2025-12-08
11
+
12
+ ### Added
13
+
14
+ - Pagination to payment methods
15
+ - Pagination to product assorments
16
+
17
+ ## [1.3.37] - 2025-12-04
18
+
19
+ ## Changed
20
+
21
+ - Allow Credit Card CVV to accept 4 digits
22
+
10
23
  ## [1.3.36] - 2025-12-04
11
24
 
12
25
  ### Added
@@ -333,7 +346,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
333
346
  - Add CHANGELOG file
334
347
  - Add README file
335
348
 
336
- [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.36...HEAD
349
+ [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.38...HEAD
337
350
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.2.2...1.2.3
338
351
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.3
339
352
  [1.2.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.4
@@ -375,5 +388,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
375
388
  > > > > > > > main
376
389
  > > > > > > > [1.3.11]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.11
377
390
 
391
+ [1.3.38]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.37...v1.3.38
392
+ [1.3.37]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.36...v1.3.37
378
393
  [1.3.36]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.35...v1.3.36
379
394
  [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.36",
3
+ "version": "1.3.38",
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;
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,54 @@ 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
+ <div data-fs-bp-payment-methods-paginator>
184
+ {data.paging.page > 1 ? (
185
+ <Paginator.NextPageButton
186
+ onClick={decreasePage}
187
+ disabled={isLoading}
188
+ >
189
+ {isLoading ? "Loading" : "Previous Page"}
190
+ </Paginator.NextPageButton>
191
+ ) : (
192
+ <></>
193
+ )}
194
+ {!isLastPage ? (
195
+ <Paginator.NextPageButton
196
+ onClick={increasePage}
197
+ disabled={isLoading}
198
+ >
199
+ {isLoading ? "Loading" : "Next Page"}
200
+ </Paginator.NextPageButton>
201
+ ) : (
202
+ <></>
203
+ )}
204
+
205
+ <Paginator.Counter
206
+ total={data.paging.total}
207
+ itemsLength={
208
+ isLastPage ? data.paging.total : page * data.paging.perPage
209
+ }
210
+ />
211
+ </div>
212
+
213
+ {!isLoading && (
191
214
  <p data-fs-bp-payment-methods-search-container-counter-bottom>
192
- {`${currentPaymentMethods.length} of ${
193
- paymentMethodsData?.length ?? 0
194
- }`}
215
+ {`${paymentMethods.length} of ${paymentMethods?.length ?? 0}`}
195
216
  </p>
196
217
  )}
197
218