@vtex/faststore-plugin-buyer-portal 1.1.1 → 1.1.3

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 (70) hide show
  1. package/.github/workflows/ci.yml +1 -1
  2. package/.github/workflows/release.yaml +1 -1
  3. package/CHANGELOG.md +3 -0
  4. package/package.json +4 -4
  5. package/plugin.config.js +4 -6
  6. package/public/buyer-portal-icons.svg +15 -0
  7. package/src/features/addresses/clients/AddressesClient.ts +24 -11
  8. package/src/features/addresses/clients/RecipientsClient.ts +110 -0
  9. package/src/features/addresses/components/AddRecipientsDrawer/AddRecipientsDrawer.tsx +87 -0
  10. package/src/features/addresses/components/AddressDropdownMenu/AddressDropdownMenu.tsx +7 -3
  11. package/src/features/addresses/components/AddressRecipientsList/AddressRecipientsList.tsx +238 -0
  12. package/src/features/addresses/components/AddressRecipientsList/address-recipients-list.scss +57 -0
  13. package/src/features/addresses/components/CreateAddressDrawer/CreateAddressDrawer.tsx +1 -3
  14. package/src/features/addresses/components/DeleteRecipientAddressDrawer/DeleteRecipientAddressDrawer.tsx +112 -0
  15. package/src/features/addresses/components/EditRecipientAddressDrawer/EditRecipientAddressDrawer.tsx +174 -0
  16. package/src/features/addresses/components/EditRecipientAddressDrawer/edit-recipient-address-drawer.scss +30 -0
  17. package/src/features/addresses/components/RecipientsForm/RecipientItem/RecipientItem.tsx +64 -0
  18. package/src/features/addresses/components/RecipientsForm/RecipientItem/recipient-item.scss +32 -0
  19. package/src/features/addresses/components/RecipientsForm/RecipientsForm.tsx +39 -54
  20. package/src/features/addresses/components/RecipientsForm/recipients-form.scss +8 -9
  21. package/src/features/addresses/components/index.ts +1 -1
  22. package/src/features/addresses/hooks/useAddRecipientsToAddress.ts +27 -0
  23. package/src/features/addresses/hooks/useDeleteRecipientAddress.ts +30 -0
  24. package/src/features/addresses/hooks/useEditRecipientsToAddress.ts +27 -0
  25. package/src/features/addresses/hooks/useSearchAddressRecipients.ts +26 -0
  26. package/src/features/addresses/layouts/AddressDetailsLayout/AddressDetailsLayout.tsx +90 -16
  27. package/src/features/addresses/layouts/AddressDetailsLayout/address-details-layout.scss +10 -0
  28. package/src/features/addresses/layouts/AddressesLayout/AddressesLayout.tsx +3 -1
  29. package/src/features/addresses/layouts/AddressesLayout/addresses-layout.scss +1 -0
  30. package/src/features/addresses/services/recipients/add-address-recipients.service.ts +29 -0
  31. package/src/features/addresses/services/recipients/delete-address-recipients.service.ts +24 -0
  32. package/src/features/addresses/services/recipients/edit-address-recipients.service.ts +28 -0
  33. package/src/features/addresses/services/recipients/get-address-recipients.service.ts +29 -0
  34. package/src/features/addresses/types/AddressData.ts +30 -0
  35. package/src/features/addresses/types/index.ts +1 -0
  36. package/src/features/buying-policies/clients/BuyingPoliciesClient.ts +26 -14
  37. package/src/features/collections/clients/CollectionsClient.ts +101 -0
  38. package/src/features/collections/components/AddCollectionsDrawer/AddCollectionsDrawer.tsx +143 -0
  39. package/src/features/collections/components/AddCollectionsDrawer/add-collections-drawer.scss +58 -0
  40. package/src/features/collections/components/CollectionsTable/CollectionsTable.tsx +94 -0
  41. package/src/features/collections/components/CollectionsTable/collections-table.scss +101 -0
  42. package/src/features/collections/components/RemoveCollectionDrawer/RemoveCollectionDrawer.tsx +75 -0
  43. package/src/features/collections/components/RemoveCollectionDrawer/remove-collection-drawer.scss +15 -0
  44. package/src/features/collections/components/index.tsx +5 -0
  45. package/src/features/collections/components/table/AddCollectionsDrawerTable.tsx +110 -0
  46. package/src/features/collections/components/table/add-collections-drawer-table.scss +66 -0
  47. package/src/features/collections/hooks/useAddCollectionsToScope.ts +23 -0
  48. package/src/features/collections/hooks/useGetCollectionsFromContract.ts +24 -0
  49. package/src/features/collections/hooks/useGetCollectionsFromScope.ts +27 -0
  50. package/src/features/collections/hooks/useRemoveCollectionsFromScope.ts +26 -0
  51. package/src/features/collections/layouts/CollectionsLayout/CollectionsLayout.tsx +153 -0
  52. package/src/features/collections/layouts/CollectionsLayout/collections-layout.scss +92 -0
  53. package/src/features/collections/layouts/index.ts +4 -0
  54. package/src/features/collections/services/add-collections-to-scope.service.ts +15 -0
  55. package/src/features/collections/services/get-collections-from-contract.service.ts +9 -0
  56. package/src/features/collections/services/get-collections-from-scope.service.ts +32 -0
  57. package/src/features/collections/services/remove-collections-from-scope.ts +7 -0
  58. package/src/features/collections/types/index.ts +20 -0
  59. package/src/features/shared/components/BasicDrawer/BasicDrawer.tsx +3 -0
  60. package/src/features/shared/components/BasicDrawer/BasicDrawerButton.tsx +4 -3
  61. package/src/features/shared/components/BasicDrawer/basic-drawer.scss +7 -5
  62. package/src/features/shared/components/Tab/Tab.tsx +13 -3
  63. package/src/features/shared/components/Table/TableRow/TableRow.tsx +7 -4
  64. package/src/features/shared/components/TextHighlight/TextHighlight.tsx +25 -0
  65. package/src/features/shared/hooks/usePageItems.ts +1 -7
  66. package/src/features/shared/utils/phoneNumber.ts +18 -0
  67. package/src/pages/address-details.tsx +51 -6
  68. package/src/pages/collections.tsx +107 -0
  69. package/src/themes/layouts.scss +3 -0
  70. package/src/features/addresses/components/RecipientsDrawer/RecipientsDrawer.tsx +0 -39
@@ -24,7 +24,7 @@ jobs:
24
24
  - name: Set up Node.js
25
25
  uses: actions/setup-node@v4
26
26
  with:
27
- node-version: 18
27
+ node-version: 20
28
28
 
29
29
  - name: Install dependencies in plugin
30
30
  run: yarn install
@@ -21,7 +21,7 @@ jobs:
21
21
  - name: Setup Node.js environment
22
22
  uses: actions/setup-node@v4
23
23
  with:
24
- node-version: 18
24
+ node-version: 20
25
25
  cache: "yarn"
26
26
  registry-url: "https://registry.npmjs.org"
27
27
 
package/CHANGELOG.md CHANGED
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ - Address Recipients Add & Delete Feature
11
+ - Address Recipients Edit Feature
12
+
10
13
  ### Fixed
11
14
 
12
15
  - UserData accepts undefined e-mail
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "dependencies": {
7
7
  "@types/react-dom": "^19.0.3",
8
- "cypress": "13",
9
8
  "react-dom": "^19.0.0"
10
9
  },
11
10
  "devDependencies": {
12
- "@faststore/core": "^3.0.147",
13
- "@faststore/ui": "^3.0.147",
11
+ "@faststore/core": "^3.41.5",
12
+ "@faststore/ui": "^3.41.5",
14
13
  "@types/react": "^18.2.42",
14
+ "cypress": "13",
15
15
  "next": "13.5.7",
16
16
  "typescript": "4.7.3"
17
17
  },
package/plugin.config.js CHANGED
@@ -5,7 +5,6 @@ module.exports = {
5
5
  path: "/buyer-portal/[[...contractMode]]",
6
6
  appLayout: false,
7
7
  },
8
-
9
8
  "address-details": {
10
9
  path: "/buyer-portal/address/[orgUnitId]/[contractId]/[addressId]",
11
10
  appLayout: false,
@@ -18,7 +17,6 @@ module.exports = {
18
17
  path: "/buyer-portal/addresses/[orgUnitId]/[contractId]",
19
18
  appLayout: false,
20
19
  },
21
-
22
20
  // Theses Routes will be added in the future
23
21
  // "payment-methods": {
24
22
  // path: "/buyer-portal/payment-methods/[orgUnitId]/[contractId]",
@@ -28,10 +26,10 @@ module.exports = {
28
26
  path: "/buyer-portal/credit-cards/[orgUnitId]/[contractId]",
29
27
  appLayout: false,
30
28
  },
31
- // collections: {
32
- // path: "/buyer-portal/collections/[orgUnitId]/[contractId]",
33
- // appLayout: false,
34
- // },
29
+ collections: {
30
+ path: "/buyer-portal/collections/[orgUnitId]/[contractId]",
31
+ appLayout: false,
32
+ },
35
33
  // "po-numbers": {
36
34
  // path: "/buyer-portal/po-numbers/[orgUnitId]/[contractId]",
37
35
  // appLayout: false,
@@ -1,4 +1,14 @@
1
+
1
2
  <svg style="display:none">
3
+ <symbol
4
+ id="Shapes"
5
+ viewBox="0 0 19 20"
6
+ fill="currentColor"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ >
9
+ <path fill="currentColor" d="M3.5 9L9 0L14.5 9H3.5ZM14.5 20C13.25 20 12.1875 19.5625 11.3125 18.6875C10.4375 17.8125 10 16.75 10 15.5C10 14.25 10.4375 13.1875 11.3125 12.3125C12.1875 11.4375 13.25 11 14.5 11C15.75 11 16.8125 11.4375 17.6875 12.3125C18.5625 13.1875 19 14.25 19 15.5C19 16.75 18.5625 17.8125 17.6875 18.6875C16.8125 19.5625 15.75 20 14.5 20ZM0 19.5V11.5H8V19.5H0ZM14.5 18C15.2 18 15.7917 17.7583 16.275 17.275C16.7583 16.7917 17 16.2 17 15.5C17 14.8 16.7583 14.2083 16.275 13.725C15.7917 13.2417 15.2 13 14.5 13C13.8 13 13.2083 13.2417 12.725 13.725C12.2417 14.2083 12 14.8 12 15.5C12 16.2 12.2417 16.7917 12.725 17.275C13.2083 17.7583 13.8 18 14.5 18ZM2 17.5H6V13.5H2V17.5ZM7.05 7H10.95L9 3.85L7.05 7Z" />
10
+ </symbol>
11
+
2
12
  <symbol id="Active" xmlns="http://www.w3.org/2000/svg" fill="currentColor"
3
13
  viewBox="0 -960 960 960">
4
14
  <path
@@ -14,6 +24,11 @@
14
24
  <path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" />
15
25
  </symbol>
16
26
 
27
+
28
+ <symbol id="DoNotDisturb" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
29
+ <path d="M5 11H15V9H5V11ZM10 20C8.61667 20 7.31667 19.7375 6.1 19.2125C4.88333 18.6875 3.825 17.975 2.925 17.075C2.025 16.175 1.3125 15.1167 0.7875 13.9C0.2625 12.6833 0 11.3833 0 10C0 8.61667 0.2625 7.31667 0.7875 6.1C1.3125 4.88333 2.025 3.825 2.925 2.925C3.825 2.025 4.88333 1.3125 6.1 0.7875C7.31667 0.2625 8.61667 0 10 0C11.3833 0 12.6833 0.2625 13.9 0.7875C15.1167 1.3125 16.175 2.025 17.075 2.925C17.975 3.825 18.6875 4.88333 19.2125 6.1C19.7375 7.31667 20 8.61667 20 10C20 11.3833 19.7375 12.6833 19.2125 13.9C18.6875 15.1167 17.975 16.175 17.075 17.075C16.175 17.975 15.1167 18.6875 13.9 19.2125C12.6833 19.7375 11.3833 20 10 20ZM10 18C12.2333 18 14.125 17.225 15.675 15.675C17.225 14.125 18 12.2333 18 10C18 7.76667 17.225 5.875 15.675 4.325C14.125 2.775 12.2333 2 10 2C7.76667 2 5.875 2.775 4.325 4.325C2.775 5.875 2 7.76667 2 10C2 12.2333 2.775 14.125 4.325 15.675C5.875 17.225 7.76667 18 10 18Z"/>
30
+ </symbol>
31
+
17
32
  <symbol
18
33
  id="Address" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"
19
34
  fill="currentColor">
@@ -20,24 +20,37 @@ export default class AddressesClient extends Client {
20
20
  });
21
21
  }
22
22
 
23
- getAddressesByUnitId(
23
+ async getAddressesByUnitId(
24
24
  orgUnitId: string,
25
25
  cookie: string,
26
26
  name?: string,
27
27
  page = 1
28
28
  ) {
29
- const params = new URLSearchParams();
30
- if (name) params.append("name", name);
31
- if (page && page > 1) params.append("page", String(page));
32
- const queryString = params.toString();
29
+ let allAddresses: AddressData[] = [];
30
+ let total = 0;
33
31
 
34
- const url = `unit/addresses/${orgUnitId}?${queryString}`;
32
+ for (let currentPage = 1; currentPage <= page; currentPage++) {
33
+ const params = new URLSearchParams();
34
+ if (name) params.append("name", name);
35
+ if (currentPage > 1) params.append("page", String(currentPage));
36
+ const queryString = params.toString();
37
+ const url = queryString
38
+ ? `unit/addresses/${orgUnitId}?${queryString}`
39
+ : `unit/addresses/${orgUnitId}`;
35
40
 
36
- return this.get<AddressResponse>(url, {
37
- headers: {
38
- Cookie: cookie,
39
- },
40
- });
41
+ const response = await this.get<AddressResponse>(url, {
42
+ headers: {
43
+ Cookie: cookie,
44
+ },
45
+ });
46
+
47
+ if (response?.addresses) {
48
+ allAddresses = allAddresses.concat(response.addresses);
49
+ total = response.total;
50
+ }
51
+ }
52
+
53
+ return { addresses: allAddresses, total };
41
54
  }
42
55
 
43
56
  getAddressById(addressId: string, cookie: string) {
@@ -0,0 +1,110 @@
1
+ import { Client } from "../../shared/clients/Client";
2
+ import { getApiUrl } from "../../shared/utils";
3
+ import type { RecipientData, RecipientInput } from "../types";
4
+ import type {
5
+ AddRecipientsResponse,
6
+ RecipientsPayload,
7
+ RecipientsResponse,
8
+ } from "../types/AddressData";
9
+
10
+ export default class RecipientsClient extends Client {
11
+ constructor() {
12
+ super(getApiUrl());
13
+ }
14
+
15
+ async getRecipientsByCustomerIdAndAddressId(
16
+ customerId: string,
17
+ addressId: string,
18
+ cookie: string,
19
+ name?: string,
20
+ page = 1
21
+ ) {
22
+ let recipientsData: RecipientData[] = [];
23
+ let total = 0;
24
+
25
+ for (let currentPage = 1; currentPage <= page; currentPage++) {
26
+ const params = new URLSearchParams();
27
+ if (name) params.append("name", name);
28
+ if (currentPage > 1) params.append("page", String(currentPage));
29
+ const queryString = params.toString();
30
+
31
+ const response = await this.get<RecipientsResponse>(
32
+ `customers/${customerId}/address/${addressId}/recipients?${queryString}`,
33
+ {
34
+ headers: {
35
+ Cookie: cookie,
36
+ },
37
+ }
38
+ );
39
+
40
+ if (response?.data) {
41
+ recipientsData = recipientsData.concat(response.data);
42
+ total = response.total;
43
+ }
44
+ }
45
+
46
+ return { data: recipientsData, total };
47
+ }
48
+
49
+ addRecipientsToAddress(
50
+ customerId: string,
51
+ addressId: string,
52
+ recipients: RecipientInput[],
53
+ cookie: string
54
+ ) {
55
+ return this.post<AddRecipientsResponse, RecipientsPayload[]>(
56
+ `customers/${customerId}/address/${addressId}/recipients`,
57
+ recipients.map((recipient) => ({
58
+ name: recipient.recipientName,
59
+ phone: recipient.recipientPhone,
60
+ })),
61
+ {
62
+ headers: {
63
+ Cookie: cookie,
64
+ },
65
+ }
66
+ );
67
+ }
68
+
69
+ editRecipientsToAddress(
70
+ customerId: string,
71
+ addressId: string,
72
+ recipientId: string,
73
+ recipientData: RecipientInput,
74
+ cookie: string
75
+ ) {
76
+ return this.put<Response, RecipientsPayload>(
77
+ `customers/${customerId}/address/${addressId}/recipients/${recipientId}`,
78
+ {
79
+ name: recipientData.recipientName,
80
+ phone: recipientData.recipientPhone,
81
+ },
82
+ {
83
+ headers: {
84
+ Cookie: cookie,
85
+ },
86
+ }
87
+ );
88
+ }
89
+
90
+ deleteRecipientFromAddress(
91
+ customerId: string,
92
+ addressId: string,
93
+ recipientId: string,
94
+ cookie: string
95
+ ) {
96
+ return this.delete(
97
+ `customers/${customerId}/address/${addressId}/recipients/${recipientId}`,
98
+ null,
99
+ {
100
+ headers: {
101
+ Cookie: cookie,
102
+ },
103
+ }
104
+ );
105
+ }
106
+ }
107
+
108
+ const recipientsClient = new RecipientsClient();
109
+
110
+ export { recipientsClient };
@@ -0,0 +1,87 @@
1
+ import { useState } from "react";
2
+
3
+ import { type BasicDrawerProps, BasicDrawer } from "../../../shared/components";
4
+ import { RecipientsForm } from "..";
5
+ import type { RecipientInput } from "../../types";
6
+ import { useUI } from "@faststore/ui";
7
+ import { useAddRecipientsToAddress } from "../../hooks/useAddRecipientsToAddress";
8
+ import { useRouter } from "next/router";
9
+
10
+ export type AddRecipientsDrawerProps = Omit<BasicDrawerProps, "children"> & {
11
+ readonly?: boolean;
12
+ addressId?: string;
13
+ refetchRecipients: () => void;
14
+ };
15
+
16
+ export const AddRecipientsDrawer = ({
17
+ close,
18
+ refetchRecipients,
19
+ addressId,
20
+ readonly,
21
+ ...props
22
+ }: AddRecipientsDrawerProps) => {
23
+ const [recipients, setRecipients] = useState<RecipientInput[]>([]);
24
+ const router = useRouter();
25
+ const { pushToast } = useUI();
26
+
27
+ const handleAddRecipientToAddressSuccess = () => {
28
+ pushToast({
29
+ message: "Recipients added successfully ",
30
+ status: "INFO",
31
+ });
32
+ refetchRecipients();
33
+ close();
34
+ };
35
+
36
+ const { addRecipientsToAddress, isAddRecipientToAddressLoading } =
37
+ useAddRecipientsToAddress({
38
+ onSuccess: handleAddRecipientToAddressSuccess,
39
+ onError: (err) => {
40
+ pushToast({
41
+ message: "An error occurred while adding recipients",
42
+ status: "ERROR",
43
+ });
44
+ },
45
+ });
46
+
47
+ const handleConfirmClick = () => {
48
+ if (!isAddRecipientToAddressLoading) {
49
+ addRecipientsToAddress({
50
+ recipients,
51
+ contractId: router.query.contractId as string,
52
+ addressId: addressId ?? (router.query.addressId as string),
53
+ });
54
+ }
55
+ };
56
+
57
+ const isConfirmButtonEnabled =
58
+ recipients.length > 0 &&
59
+ recipients.every(
60
+ (recipient) =>
61
+ recipient.recipientName.trim() !== "" &&
62
+ recipient.recipientPhone.trim() !== ""
63
+ );
64
+
65
+ return (
66
+ <BasicDrawer data-fs-bp-create-address-drawer close={close} {...props}>
67
+ <BasicDrawer.Heading title="Add Recipients" onClose={close} />
68
+ <BasicDrawer.Body>
69
+ <RecipientsForm recipients={recipients} setRecipients={setRecipients} />
70
+ </BasicDrawer.Body>
71
+
72
+ <BasicDrawer.Footer>
73
+ <BasicDrawer.Button variant="ghost" onClick={close}>
74
+ Cancel
75
+ </BasicDrawer.Button>
76
+ <BasicDrawer.Button
77
+ variant="confirm"
78
+ isLoading={isAddRecipientToAddressLoading}
79
+ onClick={handleConfirmClick}
80
+ disabled={!isConfirmButtonEnabled}
81
+ >
82
+ Save
83
+ </BasicDrawer.Button>
84
+ </BasicDrawer.Footer>
85
+ </BasicDrawer>
86
+ );
87
+ };
@@ -3,13 +3,14 @@ import { DropdownItem, Icon as UIIcon } from "@faststore/ui";
3
3
  import {
4
4
  EditAddressDrawer,
5
5
  LocationDrawer,
6
- RecipientsDrawer,
6
+ AddRecipientsDrawer,
7
7
  RemoveAddressDrawer,
8
8
  DeleteAddressDrawer,
9
9
  } from "..";
10
10
  import { useDrawerProps, useBuyerPortal } from "../../../shared/hooks";
11
11
  import { BasicDropdownMenu, Icon } from "../../../shared/components";
12
12
  import type { AddressData } from "../../types";
13
+ import { useRouter } from "next/router";
13
14
 
14
15
  export type AddressDropdownMenuProps = {
15
16
  currentAddress: AddressData;
@@ -22,10 +23,11 @@ export const AddressDropdownMenu = ({
22
23
  currentAddress,
23
24
  }: AddressDropdownMenuProps) => {
24
25
  const { currentOrgUnit } = useBuyerPortal();
26
+ const router = useRouter();
25
27
 
26
28
  const idsPath = currentOrgUnit?.path?.ids?.split("/") ?? [];
27
29
 
28
- const isRootLevelOrgUnit = idsPath.length === 2 ?? false;
30
+ const isRootLevelOrgUnit = idsPath.length === 2;
29
31
 
30
32
  const {
31
33
  open: openEditDrawerProps,
@@ -110,10 +112,12 @@ export const AddressDropdownMenu = ({
110
112
  />
111
113
  )}
112
114
  {isOpenRecipientsDrawer && (
113
- <RecipientsDrawer
115
+ <AddRecipientsDrawer
114
116
  readonly
115
117
  {...recipientDrawerProps}
116
118
  isOpen={isOpenRecipientsDrawer}
119
+ refetchRecipients={() => router.reload()}
120
+ addressId={currentAddress.id}
117
121
  />
118
122
  )}
119
123
  {isOpenRemoveAddressDrawer && (
@@ -0,0 +1,238 @@
1
+ import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
2
+
3
+ import { IconButton } from "@faststore/ui";
4
+ import {
5
+ Icon,
6
+ InternalSearch,
7
+ Paginator,
8
+ Table,
9
+ } from "../../../shared/components";
10
+ import { EmptyState } from "../../../shared/components/EmptyState/EmptyState";
11
+ import { usePageItems } from "../../../shared/hooks/usePageItems";
12
+ import { RecipientData } from "../../types";
13
+ import { maskPhoneNumber } from "../../../shared/utils/phoneNumber";
14
+ import { useDrawerProps } from "../../../shared/hooks";
15
+ import { DeleteRecipientAddressDrawer } from "../DeleteRecipientAddressDrawer/DeleteRecipientAddressDrawer";
16
+ import { EditRecipientAddressDrawer } from "../EditRecipientAddressDrawer/EditRecipientAddressDrawer";
17
+ import { useSearchAddressRecipients } from "../../hooks/useSearchAddressRecipients";
18
+ import { useRouter } from "next/router";
19
+
20
+ export type AddressRecipientsListRef = {
21
+ refetchRecipients: () => void;
22
+ };
23
+
24
+ export type AddressRecipientsListProps = {
25
+ recipients: RecipientData[];
26
+ total: number;
27
+ search: string;
28
+ page: number;
29
+ countryCode?: string;
30
+ };
31
+
32
+ export const AddressRecipientsList = forwardRef<
33
+ AddressRecipientsListRef,
34
+ AddressRecipientsListProps
35
+ >(
36
+ (
37
+ {
38
+ recipients: initialRecipients,
39
+ search,
40
+ total,
41
+ page = 1,
42
+ countryCode = "",
43
+ }: AddressRecipientsListProps,
44
+ ref
45
+ ) => {
46
+ const router = useRouter();
47
+
48
+ const [totalRecipients, setTotalRecipients] = useState(total);
49
+
50
+ const {
51
+ isLoading,
52
+ items: recipients,
53
+ searchTerm,
54
+ setSearchTerm,
55
+ increasePage,
56
+ setIsLoading,
57
+ setItems,
58
+ } = usePageItems<RecipientData>({
59
+ initialItems: initialRecipients,
60
+ search,
61
+ page,
62
+ });
63
+
64
+ const [currentRecipient, setCurrentRecipient] = useState<RecipientData>();
65
+
66
+ const { searchAddressRecipients, refetchSearchAddressRecipients } =
67
+ useSearchAddressRecipients({
68
+ addressId: router.query.addressId as string,
69
+ contractId: router.query.contractId as string,
70
+ page: 1,
71
+ search: search ?? "",
72
+ });
73
+
74
+ useEffect(() => {
75
+ if (searchAddressRecipients?.data) {
76
+ setItems(searchAddressRecipients.data);
77
+ setTotalRecipients(searchAddressRecipients.total);
78
+ setIsLoading(false);
79
+ }
80
+ }, [searchAddressRecipients]);
81
+
82
+ const handleRefetchRecipients = () => {
83
+ setIsLoading(true);
84
+ refetchSearchAddressRecipients();
85
+ };
86
+
87
+ useImperativeHandle(ref, () => ({
88
+ refetchRecipients: handleRefetchRecipients,
89
+ }));
90
+
91
+ const {
92
+ open: openDeleteRecipientsAddressDrawer,
93
+ isOpen: isOpenDeleteRecipientAddressDrawer,
94
+ ...deleteRecipientAddressDrawerProps
95
+ } = useDrawerProps();
96
+
97
+ const {
98
+ open: openEditRecipientsAddressDrawer,
99
+ isOpen: isOpenEditRecipientAddressDrawer,
100
+ ...editRecipientAddressDrawerProps
101
+ } = useDrawerProps();
102
+
103
+ const renderActions = (recipient: RecipientData) => {
104
+ return (
105
+ <div data-fs-buyer-portal-recipients-actions>
106
+ <IconButton
107
+ data-fs-buyer-portal-recipients-actions-icon
108
+ icon={<Icon name="Trash" width={20} height={20} />}
109
+ aria-label={"remove recipient action"}
110
+ onClick={() => {
111
+ setCurrentRecipient(recipient);
112
+ openDeleteRecipientsAddressDrawer();
113
+ }}
114
+ />
115
+ <IconButton
116
+ data-fs-buyer-portal-recipients-actions-icon
117
+ icon={<Icon name="Edit" width={20} height={20} />}
118
+ aria-label={"Edit recipient action"}
119
+ onClick={() => {
120
+ setCurrentRecipient(recipient);
121
+ openEditRecipientsAddressDrawer();
122
+ }}
123
+ />
124
+ </div>
125
+ );
126
+ };
127
+
128
+ return (
129
+ <div data-fs-buyer-portal-address-recipients-list>
130
+ <div data-fs-buyer-portal-address-recipients-filter>
131
+ <InternalSearch
132
+ defaultValue={searchTerm}
133
+ textSearch={setSearchTerm}
134
+ />
135
+
136
+ <Paginator.Counter
137
+ total={totalRecipients}
138
+ itemsLength={recipients.length}
139
+ />
140
+ </div>
141
+ {!isLoading && recipients.length === 0 && searchTerm.length > 0 ? (
142
+ <EmptyState
143
+ title="No results found"
144
+ description="Try using different terms or filters"
145
+ />
146
+ ) : (
147
+ <div data-fs-address-recipients-table>
148
+ <Table>
149
+ <Table.Head
150
+ columns={[
151
+ {
152
+ key: "name",
153
+ label: "Name",
154
+ align: "left",
155
+ },
156
+ {
157
+ key: "phone",
158
+ label: "Phone",
159
+ align: "left",
160
+ },
161
+ {
162
+ key: "actions",
163
+ label: "",
164
+ align: "right",
165
+ size: "large",
166
+ },
167
+ ]}
168
+ />
169
+
170
+ <Table.Body>
171
+ {isLoading ? (
172
+ <Table.Loading />
173
+ ) : (
174
+ recipients.map((recipient) => (
175
+ <Table.Row
176
+ key={recipient.id}
177
+ title={recipient.firstName + " " + recipient.lastName}
178
+ searchTerm={search}
179
+ children={
180
+ recipient.phone ? (
181
+ <td>
182
+ <span>
183
+ {maskPhoneNumber(recipient.phone, countryCode)}
184
+ </span>
185
+ </td>
186
+ ) : null
187
+ }
188
+ actionIcons={renderActions(recipient as RecipientData)}
189
+ />
190
+ ))
191
+ )}
192
+ </Table.Body>
193
+ </Table>
194
+
195
+ {isOpenDeleteRecipientAddressDrawer && currentRecipient && (
196
+ <DeleteRecipientAddressDrawer
197
+ readonly
198
+ recipientAddressName={
199
+ currentRecipient.firstName + " " + currentRecipient.lastName
200
+ }
201
+ recipientAddressId={currentRecipient.id}
202
+ isOpen={isOpenDeleteRecipientAddressDrawer}
203
+ refetchRecipients={handleRefetchRecipients}
204
+ {...deleteRecipientAddressDrawerProps}
205
+ />
206
+ )}
207
+
208
+ {isOpenEditRecipientAddressDrawer && currentRecipient && (
209
+ <EditRecipientAddressDrawer
210
+ readonly
211
+ recipient={currentRecipient}
212
+ isOpen={isOpenEditRecipientAddressDrawer}
213
+ refetchRecipients={handleRefetchRecipients}
214
+ {...editRecipientAddressDrawerProps}
215
+ />
216
+ )}
217
+
218
+ <div data-fs-bp-address-recipients-paginator>
219
+ {totalRecipients > recipients.length && (
220
+ <Paginator.NextPageButton
221
+ onClick={increasePage}
222
+ disabled={isLoading}
223
+ >
224
+ {isLoading ? "Loading" : "Load More"}
225
+ </Paginator.NextPageButton>
226
+ )}
227
+
228
+ <Paginator.Counter
229
+ total={totalRecipients}
230
+ itemsLength={recipients.length}
231
+ />
232
+ </div>
233
+ </div>
234
+ )}
235
+ </div>
236
+ );
237
+ }
238
+ );