@vtex/faststore-plugin-buyer-portal 1.3.5 → 1.3.7

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 (64) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/cypress/constants.ts +2 -0
  3. package/cypress/integration/organizational-units.test.ts +1 -1
  4. package/cypress/integration/{collections.test.ts → product-assortment.test.ts} +54 -50
  5. package/cypress/integration/profile.test.ts +1 -1
  6. package/cypress/integration/users.test.ts +9 -0
  7. package/package.json +1 -1
  8. package/plugin.config.js +2 -2
  9. package/src/features/{collections/clients/CollectionsClient.ts → product-assortment/clients/ProductAssortmentClient.ts} +18 -20
  10. package/src/features/product-assortment/components/AddProductAssortmentDrawer/AddProductAssortmentDrawer.tsx +144 -0
  11. package/src/features/{collections/components/AddCollectionsDrawer/add-collections-drawer.scss → product-assortment/components/AddProductAssortmentDrawer/add-product-assortment-drawer.scss} +9 -10
  12. package/src/features/product-assortment/components/ProductAssortmentTable/ProductAssortmentTable.tsx +99 -0
  13. package/src/features/product-assortment/components/ProductAssortmentTable/product-assortment-table.scss +45 -0
  14. package/src/features/{collections/components/RemoveCollectionDrawer/RemoveCollectionDrawer.tsx → product-assortment/components/RemoveProductAssortmentDrawer/RemoveProductAssortmentDrawer.tsx} +21 -20
  15. package/src/features/product-assortment/components/RemoveProductAssortmentDrawer/remove-product-assortment-drawer.scss +9 -0
  16. package/src/features/product-assortment/components/index.tsx +2 -0
  17. package/src/features/{collections/components/table/AddCollectionsDrawerTable.tsx → product-assortment/components/table/AddProductAssortmentDrawerTable.tsx} +9 -9
  18. package/src/features/product-assortment/components/table/add-product-assortment-drawer-table.scss +14 -0
  19. package/src/features/product-assortment/hooks/useAddProductAssortmentToScope.ts +25 -0
  20. package/src/features/product-assortment/hooks/useRemoveProductAssortmentFromScope.ts +26 -0
  21. package/src/features/{collections/layouts/CollectionsLayout/CollectionsLayout.tsx → product-assortment/layouts/ProductAssortmentLayout/ProductAssortmentLayout.tsx} +43 -49
  22. package/src/features/{collections/layouts/CollectionsLayout/collections-layout.scss → product-assortment/layouts/ProductAssortmentLayout/product-assortment-layout.scss} +7 -7
  23. package/src/features/product-assortment/layouts/index.ts +1 -0
  24. package/src/features/product-assortment/services/add-product-assortment-to-scope.service.ts +16 -0
  25. package/src/features/product-assortment/services/get-product-assortment-from-contract.service.ts +13 -0
  26. package/src/features/product-assortment/services/get-product-assortment-from-scope.service.ts +28 -0
  27. package/src/features/product-assortment/services/remove-product-assortment-from-scope.ts +9 -0
  28. package/src/features/product-assortment/types/index.ts +80 -0
  29. package/src/features/profile/layouts/ProfileLayout/profile-layout.scss +8 -5
  30. package/src/features/roles/layout/RoleDetailsLayout/RoleDetailsLayout.tsx +17 -36
  31. package/src/features/roles/layout/RoleDetailsLayout/role-details-layout.scss +40 -0
  32. package/src/features/roles/layout/RolesLayout/roles-layout.scss +8 -8
  33. package/src/features/shared/utils/buyerPortalRoutes.ts +5 -2
  34. package/src/features/shared/utils/constants.ts +1 -1
  35. package/src/features/shared/utils/getContractSettingsLinks.ts +2 -2
  36. package/src/features/shared/utils/routeLayoutMapping.ts +2 -2
  37. package/src/features/users/clients/UsersClient.ts +2 -0
  38. package/src/features/users/components/CreateUserDrawer/CreateUserDrawer.tsx +19 -2
  39. package/src/features/users/components/UpdateUserDrawer/UpdateUserDrawer.tsx +31 -5
  40. package/src/features/users/components/UserDropdownMenu/UserDropdownMenu.tsx +1 -1
  41. package/src/features/users/layouts/UserDetailsLayout/UserDetailsLayout.tsx +8 -0
  42. package/src/features/users/services/add-user-to-org-unit.service.ts +1 -0
  43. package/src/features/users/services/get-user-by-id.service.ts +2 -1
  44. package/src/features/users/services/update-user.service.ts +3 -0
  45. package/src/features/users/types/UserData.ts +1 -0
  46. package/src/features/users/types/UserDataService.ts +1 -0
  47. package/src/pages/{collections.tsx → productAssortment.tsx} +30 -51
  48. package/src/themes/layouts.scss +2 -2
  49. package/src/features/collections/components/AddCollectionsDrawer/AddCollectionsDrawer.tsx +0 -146
  50. package/src/features/collections/components/CollectionsTable/CollectionsTable.tsx +0 -94
  51. package/src/features/collections/components/CollectionsTable/collections-table.scss +0 -140
  52. package/src/features/collections/components/RemoveCollectionDrawer/remove-collection-drawer.scss +0 -15
  53. package/src/features/collections/components/index.tsx +0 -5
  54. package/src/features/collections/components/table/add-collections-drawer-table.scss +0 -71
  55. package/src/features/collections/hooks/useAddCollectionsToScope.ts +0 -23
  56. package/src/features/collections/hooks/useGetCollectionsFromContract.ts +0 -26
  57. package/src/features/collections/hooks/useGetCollectionsFromScope.ts +0 -27
  58. package/src/features/collections/hooks/useRemoveCollectionsFromScope.ts +0 -26
  59. package/src/features/collections/layouts/index.ts +0 -4
  60. package/src/features/collections/services/add-collections-to-scope.service.ts +0 -15
  61. package/src/features/collections/services/get-collections-from-contract.service.ts +0 -9
  62. package/src/features/collections/services/get-collections-from-scope.service.ts +0 -24
  63. package/src/features/collections/services/remove-collections-from-scope.ts +0 -7
  64. package/src/features/collections/types/index.ts +0 -20
package/CHANGELOG.md CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.7] - 2025-10-17
11
+
12
+ ### Added
13
+
14
+ - Responsiviness to Profile page
15
+
16
+ ## [1.3.6] - 2025-10-17
17
+
18
+ ### Changed
19
+
20
+ - Collections to Product assortment
21
+ - Rename CSS files and data attributes
22
+ - Rename labels, titles and messages
23
+ - Rename files and folders
24
+ - Rename code
25
+ - Rename routes
26
+
10
27
  ## [1.3.5] - 2025-10-17
11
28
 
12
29
  ### Fixed
@@ -36,6 +53,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
36
53
 
37
54
  ### Added
38
55
 
56
+ - Responsiviness to Roles and Roles Details page
57
+
58
+ ### Added
39
59
  - Responsiviness adjustment to to Buying policies page
40
60
 
41
61
  ### Added
@@ -126,7 +146,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
126
146
  - Add CHANGELOG file
127
147
  - Add README file
128
148
 
129
- [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/1.3.5...HEAD
149
+ [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/1.3.7...HEAD
130
150
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.2.2...1.2.3
131
151
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.3
132
152
  [1.2.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.4
@@ -135,3 +155,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
135
155
  [1.3.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.4
136
156
 
137
157
  [1.3.5]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.5
158
+
159
+ [1.3.6]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.6
160
+
161
+ [1.3.7]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.7
@@ -50,11 +50,13 @@ export const TEST_DATA = {
50
50
  NEW_USER: {
51
51
  NAME: "New User",
52
52
  EMAIL: "new_user+e2e_test@vtex.com",
53
+ PHONE: "(555) 555-5555",
53
54
  ROLE: "Buyer",
54
55
  },
55
56
  USER_FROM_OTHER_ORG_UNIT: {
56
57
  NAME: "User from other unit",
57
58
  EMAIL: "rodrigo.tavares+devb2b@vtex.com",
59
+ PHONE: "(000) 000-0000",
58
60
  ROLE: "Order Approver",
59
61
  },
60
62
  },
@@ -155,7 +155,7 @@ describe("Organizational Units", { retries: 2 }, () => {
155
155
  cy.contains("Addresses").should("be.visible");
156
156
  cy.contains("Payment methods").should("be.visible");
157
157
  cy.contains("Credit cards").should("be.visible");
158
- cy.contains("Collections").should("be.visible");
158
+ cy.contains("Product assortment").should("be.visible");
159
159
  cy.contains("PO numbers").should("be.visible");
160
160
  cy.contains("Cost centers").should("be.visible");
161
161
  cy.contains("Releases").should("be.visible");
@@ -1,12 +1,12 @@
1
1
  import { TEST_CONFIG } from "../constants";
2
2
 
3
- function visitCollectionsPage() {
3
+ function visitProductAssortmentPage() {
4
4
  // Visit the homepage
5
5
  cy.visit(TEST_CONFIG.ROUTES.BUYER_PORTAL);
6
6
 
7
- // Navigate to collections page
7
+ // Navigate to product assortment page
8
8
  cy.get("[data-fs-vertical-nav-menu-item]")
9
- .contains("Collections")
9
+ .contains("Product assortment")
10
10
  .click({ timeout: TEST_CONFIG.TIMEOUTS.PAGE_LOAD });
11
11
 
12
12
  cy.wait(1000);
@@ -15,25 +15,27 @@ function visitCollectionsPage() {
15
15
  timeout: TEST_CONFIG.TIMEOUTS.LONG_DELAY,
16
16
  }).should("not.exist");
17
17
 
18
- cy.get("[data-fs-bp-collections-layout]").as("collectionLayout");
18
+ cy.get("[data-fs-bp-product-assortment-layout]").as(
19
+ "productAssortmentLayout"
20
+ );
19
21
  }
20
22
 
21
23
  describe(
22
- "Collections",
24
+ "Product assortment",
23
25
  {
24
26
  retries: 2,
25
27
  },
26
28
  () => {
27
- const COLLECTION_UNDER_TEST = "Collection Demo";
29
+ const PRODUCT_ASSORTMENT_UNDER_TEST = "Product Assortment Demo";
28
30
 
29
31
  beforeEach(() => {
30
32
  cy.login();
31
33
  });
32
34
 
33
- it("Should show empty state when unit has no collection", () => {
35
+ it("Should show empty state when unit has no assortment", () => {
34
36
  cy.intercept(
35
37
  "GET",
36
- "/_next/data/**/pvt/organization-account/collections/**",
38
+ "/_next/data/**/pvt/organization-account/product-assortment/**",
37
39
  (req) => {
38
40
  req.reply((res) => {
39
41
  res.body.pageProps.data.isContractEmpty = true;
@@ -41,34 +43,34 @@ describe(
41
43
  }
42
44
  );
43
45
 
44
- visitCollectionsPage();
46
+ visitProductAssortmentPage();
45
47
 
46
- cy.get("@collectionLayout")
47
- .find("[data-fs-bp-collection-content]")
48
+ cy.get("@productAssortmentLayout")
49
+ .find("[data-fs-bp-product-assortment-content]")
48
50
  .should("not.exist");
49
- cy.get("@collectionLayout")
51
+ cy.get("@productAssortmentLayout")
50
52
  .find("[data-fs-empty-state-section]")
51
53
  .should("be.visible");
52
54
  });
53
55
 
54
- it("Should list and filter Collections correctly", () => {
55
- visitCollectionsPage();
56
+ it("Should list and filter Product Assortment correctly", () => {
57
+ visitProductAssortmentPage();
56
58
 
57
- // Wait for collections section to be visible
59
+ // Wait for product assortment section to be visible
58
60
  cy.get("[data-fs-bp-header-inside-title]")
59
- .contains("Collections")
61
+ .contains("Product assortment")
60
62
  .should("be.visible");
61
63
 
62
- // Wait for collections table to be loaded
63
- cy.get("[data-fs-bp-collection-table]", {
64
+ // Wait for product assortment table to be loaded
65
+ cy.get("[data-fs-bp-product-assortment-table]", {
64
66
  timeout: TEST_CONFIG.TIMEOUTS.PAGE_LOAD,
65
67
  }).should("exist");
66
68
 
67
69
  cy.get("[data-fs-bp-table-row-title]")
68
70
  .eq(0)
69
71
  .then(($firstTitle) => {
70
- cy.get("@collectionLayout")
71
- .find("[data-fs-collection-filter] input")
72
+ cy.get("@productAssortmentLayout")
73
+ .find("[data-fs-product-assortment-filter] input")
72
74
  .as("searchInput")
73
75
  .should("be.visible")
74
76
  .lazyType($firstTitle.text());
@@ -77,8 +79,8 @@ describe(
77
79
  // Wait for list to update
78
80
  cy.wait(TEST_CONFIG.TIMEOUTS.PAGE_LOAD);
79
81
 
80
- // Check if the resulted collection is visible
81
- cy.get("[data-fs-bp-collection-table]", {
82
+ // Check if the resulted product assortment is visible
83
+ cy.get("[data-fs-bp-product-assortment-table]", {
82
84
  timeout: TEST_CONFIG.TIMEOUTS.PAGE_LOAD,
83
85
  }).should("exist");
84
86
 
@@ -91,34 +93,34 @@ describe(
91
93
  cy.get("[data-fs-bp-table] [data-fs-bp-table-row]").should("not.exist");
92
94
  });
93
95
 
94
- it("Should add a new Collection correctly", () => {
95
- visitCollectionsPage();
96
+ it("Should add a new Product Assortment correctly", () => {
97
+ visitProductAssortmentPage();
96
98
 
97
99
  // Check if drawer is present on screen
98
- cy.get("[data-fs-bp-add-collections-drawer]").should("not.exist");
100
+ cy.get("[data-fs-bp-add-product-assortment-drawer]").should("not.exist");
99
101
 
100
102
  // Click on add button to open drawer
101
- cy.get("@collectionLayout")
102
- .find("[aria-label='Add collection']")
103
+ cy.get("@productAssortmentLayout")
104
+ .find("[aria-label='Add product assortment']")
103
105
  .should("be.visible")
104
106
  .click();
105
107
 
106
108
  // Check if drawer was opened
107
- cy.get("[data-fs-bp-add-collections-drawer]")
109
+ cy.get("[data-fs-bp-add-product-assortment-drawer]")
108
110
  .as("addDrawer")
109
111
  .should("be.visible");
110
112
  cy.get("@addDrawer")
111
113
  .find("[data-fs-bp-basic-drawer-heading]")
112
- .contains("Add collections")
114
+ .contains("Add product assortment")
113
115
  .should("be.visible");
114
116
 
115
- // Check if collections was listed correclty and submit button is disable
117
+ // Check if product assortment was listed correclty and submit button is disable
116
118
  cy.get("@addDrawer")
117
119
  .find("[data-fs-bp-basic-drawer-button-variant='confirm']")
118
120
  .as("submitButton")
119
121
  .should("be.disabled");
120
122
  cy.get("@addDrawer")
121
- .find("[data-fs-bp-add-collections-drawer-table] tbody tr")
123
+ .find("[data-fs-bp-add-product-assortment-drawer-table] tbody tr")
122
124
  .as("results");
123
125
 
124
126
  // Check if empty state is visibile when a search returns no results
@@ -127,20 +129,20 @@ describe(
127
129
  .as("input")
128
130
  .type("foo bar");
129
131
  cy.get("@addDrawer")
130
- .find("[data-fs-bp-add-collections-drawer-empty-state]")
132
+ .find("[data-fs-bp-add-product-assortment-drawer-empty-state]")
131
133
  .as("emptyState")
132
134
  .should("be.visible");
133
135
  cy.get("@results").should("not.exist");
134
136
 
135
137
  // Check if results is updated when search results is not empty
136
- cy.get("@input").clear().type(COLLECTION_UNDER_TEST);
138
+ cy.get("@input").clear().type(PRODUCT_ASSORTMENT_UNDER_TEST);
137
139
  cy.get("@emptyState").should("not.exist");
138
140
  cy.get("@results").should("have.length", 1);
139
141
 
140
- // Select first Collection
142
+ // Select first Product Assortment
141
143
  cy.get("@addDrawer")
142
144
  .find(
143
- "[data-fs-bp-add-collections-drawer-table] tbody td [data-fs-checkbox]"
145
+ "[data-fs-bp-add-product-assortment-drawer-table] tbody td [data-fs-checkbox]"
144
146
  )
145
147
  .first()
146
148
  .check();
@@ -151,38 +153,40 @@ describe(
151
153
  // Wait for page refresh
152
154
  cy.wait(TEST_CONFIG.TIMEOUTS.RETRY_DELAY);
153
155
 
154
- cy.get("[data-fs-bp-collection-table]", {
156
+ cy.get("[data-fs-bp-product-assortment-table]", {
155
157
  timeout: TEST_CONFIG.TIMEOUTS.PAGE_LOAD,
156
158
  }).within(() => {
157
- cy.contains(COLLECTION_UNDER_TEST);
159
+ cy.contains(PRODUCT_ASSORTMENT_UNDER_TEST);
158
160
  });
159
161
  });
160
162
 
161
- it("Should remove Collection correctly", () => {
162
- visitCollectionsPage();
163
+ it("Should remove Product Assortment correctly", () => {
164
+ visitProductAssortmentPage();
163
165
 
164
166
  // Check if delete drawer is not visible
165
- cy.get("[data-fs-bp-remove-collections-drawer]").should("not.exist");
167
+ cy.get("[data-fs-bp-remove-product-assortment-drawer]").should(
168
+ "not.exist"
169
+ );
166
170
 
167
- // Count current collection list before remove one
168
- cy.get("[data-fs-bp-collection-table]", {
171
+ // Count current product assortment list before remove one
172
+ cy.get("[data-fs-bp-product-assortment-table]", {
169
173
  timeout: TEST_CONFIG.TIMEOUTS.PAGE_LOAD,
170
174
  }).should("exist");
171
175
 
172
- // Find remove collection icon button and click it
176
+ // Find remove product assortment icon button and click it
173
177
  cy.get("[data-fs-bp-table] [data-fs-bp-table-row]")
174
- .contains(COLLECTION_UNDER_TEST)
178
+ .contains(PRODUCT_ASSORTMENT_UNDER_TEST)
175
179
  .closest("[data-fs-bp-table-row]")
176
- .find("[aria-label='Remove collections']")
180
+ .find("[aria-label='Remove product assortment']")
177
181
  .click();
178
182
 
179
183
  // Check if delete drawer was opened
180
- cy.get("[data-fs-bp-remove-collections-drawer]")
184
+ cy.get("[data-fs-bp-remove-product-assortment-drawer]")
181
185
  .as("deleteDrawer")
182
186
  .should("be.visible");
183
187
  cy.get("@deleteDrawer")
184
188
  .find("[data-fs-bp-basic-drawer-heading]")
185
- .contains("Remove collection from unit")
189
+ .contains("Remove product assortment from unit")
186
190
  .should("be.visible");
187
191
 
188
192
  // Find remove confirmation button and click it
@@ -194,13 +198,13 @@ describe(
194
198
  // Wait for page refresh
195
199
  cy.wait(TEST_CONFIG.TIMEOUTS.RETRY_DELAY);
196
200
 
197
- // Check if the list was updated without removed collection
201
+ // Check if the list was updated without removed product assortment
198
202
  cy.get("@deleteDrawer").should("not.exist");
199
- cy.get("[data-fs-bp-collection-table]", {
203
+ cy.get("[data-fs-bp-product-assortment-table]", {
200
204
  timeout: TEST_CONFIG.TIMEOUTS.PAGE_LOAD,
201
205
  })
202
206
  .should("exist")
203
- .contains(COLLECTION_UNDER_TEST)
207
+ .contains(PRODUCT_ASSORTMENT_UNDER_TEST)
204
208
  .should("not.exist");
205
209
  });
206
210
  }
@@ -29,7 +29,7 @@ describe("Profile", () => {
29
29
  cy.contains("Addresses").should("be.visible");
30
30
  cy.contains("Payment methods").should("be.visible");
31
31
  cy.contains("Credit cards").should("be.visible");
32
- cy.contains("Collections").should("be.visible");
32
+ cy.contains("Product assortment").should("be.visible");
33
33
  cy.contains("PO numbers").should("be.visible");
34
34
  cy.contains("Cost centers").should("be.visible");
35
35
  cy.contains("Releases").should("be.visible");
@@ -62,6 +62,13 @@ const fillUserForm = (
62
62
  .clear()
63
63
  .type(userData.EMAIL);
64
64
 
65
+ cy.get("label")
66
+ .contains("Phone number")
67
+ .parent()
68
+ .find("[data-fs-bp-input-text-input]")
69
+ .clear()
70
+ .type(userData.PHONE);
71
+
65
72
  cy.wait(500);
66
73
  // Fill the user role
67
74
  cy.get('[data-fs-bp-create-user-roles="true"]')
@@ -189,6 +196,7 @@ describe("Users", { retries: 2 }, () => {
189
196
  // Verify labels and buttons are present
190
197
  cy.contains("Name").should("be.visible");
191
198
  cy.contains("Email").should("be.visible");
199
+ cy.contains("Phone number").should("be.visible");
192
200
  cy.contains("Role").should("be.visible");
193
201
  cy.contains("Organizational Unit").should("be.visible");
194
202
  cy.contains("Edit").should("be.visible");
@@ -261,6 +269,7 @@ describe("Users", { retries: 2 }, () => {
261
269
  const updatedUserData = {
262
270
  NAME: `${USER_NAME} Updated`,
263
271
  EMAIL: USER_EMAIL,
272
+ PHONE: "(888) 888-8888",
264
273
  ROLE: TEST_DATA.USERS.NEW_USER.ROLE,
265
274
  };
266
275
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/plugin.config.js CHANGED
@@ -36,8 +36,8 @@ module.exports = {
36
36
  path: "/pvt/organization-account/credit-cards/[orgUnitId]/[contractId]",
37
37
  appLayout: false,
38
38
  },
39
- collections: {
40
- path: "/pvt/organization-account/collections/[orgUnitId]/[contractId]",
39
+ productAssortment: {
40
+ path: "/pvt/organization-account/product-assortment/[orgUnitId]/[contractId]",
41
41
  appLayout: false,
42
42
  },
43
43
  "po-numbers": {
@@ -2,9 +2,10 @@ import { Client } from "../../shared/clients/Client";
2
2
  import { getApiUrl } from "../../shared/utils";
3
3
 
4
4
  import type {
5
- AddCollectionResponse,
6
- GetCollectionsFromContractResponse,
7
- GetCollectionsFromScopeResponse,
5
+ AddProductAssortmentPayload,
6
+ AddProductAssortmentResponse,
7
+ GetProductAssortmentFromContractResponse,
8
+ GetProductAssortmentFromScopeResponse,
8
9
  } from "../types";
9
10
 
10
11
  type DefaultArgs = {
@@ -13,18 +14,15 @@ type DefaultArgs = {
13
14
  unitId: string;
14
15
  };
15
16
 
16
- export type UpdateCollectionsPayload = Array<{ name: string }>;
17
- export type AddCollectionsPayload = Array<{ name: string }>;
18
-
19
- export class CollectionsClient extends Client {
17
+ export class ProductAssortmentClient extends Client {
20
18
  constructor() {
21
19
  super(getApiUrl());
22
20
  }
23
21
 
24
- getCollectionFromContract(args: DefaultArgs & { name?: string }) {
22
+ getProductAssortmentFromContract(args: DefaultArgs & { name?: string }) {
25
23
  const { contractId, cookie, name, unitId } = args;
26
24
 
27
- return this.get<GetCollectionsFromContractResponse["collections"]>(
25
+ return this.get<GetProductAssortmentFromContractResponse["collections"]>(
28
26
  `customers/${contractId}/units/${unitId}/collections`,
29
27
  {
30
28
  headers: { Cookie: cookie },
@@ -33,7 +31,7 @@ export class CollectionsClient extends Client {
33
31
  );
34
32
  }
35
33
 
36
- getCollectionsListInScope(
34
+ getProductAssortmentListInScope(
37
35
  args: DefaultArgs & {
38
36
  unitId: string;
39
37
  name?: string;
@@ -43,7 +41,7 @@ export class CollectionsClient extends Client {
43
41
  ) {
44
42
  const { contractId, cookie, name, unitId, filterByScope } = args;
45
43
 
46
- return this.get<GetCollectionsFromScopeResponse["collections"]>(
44
+ return this.get<GetProductAssortmentFromScopeResponse["collections"]>(
47
45
  `customers/${contractId}/units/${unitId}/collections`,
48
46
  {
49
47
  headers: { Cookie: cookie },
@@ -56,12 +54,12 @@ export class CollectionsClient extends Client {
56
54
  );
57
55
  }
58
56
 
59
- addCollectionsToScope(
60
- args: DefaultArgs & { unitId: string; data: AddCollectionsPayload }
57
+ addProductAssortmentToScope(
58
+ args: DefaultArgs & { unitId: string; data: AddProductAssortmentPayload }
61
59
  ) {
62
60
  const { contractId, cookie, unitId, data } = args;
63
61
 
64
- return this.post<AddCollectionResponse, AddCollectionsPayload>(
62
+ return this.post<AddProductAssortmentResponse, AddProductAssortmentPayload>(
65
63
  `/customers/${contractId}/units/${unitId}/collections`,
66
64
  data,
67
65
  {
@@ -70,13 +68,13 @@ export class CollectionsClient extends Client {
70
68
  );
71
69
  }
72
70
 
73
- removeCollectionFromScope(
74
- args: DefaultArgs & { unitId: string; collectionId: string }
71
+ removeProductAssortmentFromScope(
72
+ args: DefaultArgs & { unitId: string; productAssortmentId: string }
75
73
  ) {
76
- const { contractId, unitId, cookie, collectionId } = args;
74
+ const { contractId, unitId, cookie, productAssortmentId } = args;
77
75
 
78
76
  return this.delete(
79
- `/customers/${contractId}/units/${unitId}/collections/${collectionId}`,
77
+ `/customers/${contractId}/units/${unitId}/collections/${productAssortmentId}`,
80
78
  undefined,
81
79
  {
82
80
  headers: { Cookie: cookie },
@@ -85,5 +83,5 @@ export class CollectionsClient extends Client {
85
83
  }
86
84
  }
87
85
 
88
- const collectionsClient = new CollectionsClient();
89
- export { collectionsClient };
86
+ const productAssortmentClient = new ProductAssortmentClient();
87
+ export { productAssortmentClient };
@@ -0,0 +1,144 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+
3
+ import { useRouter } from "next/router";
4
+
5
+ import { Link, useUI } from "@faststore/ui";
6
+
7
+ import {
8
+ BasicDrawer,
9
+ Icon,
10
+ InternalSearch,
11
+ Paginator,
12
+ } from "../../../shared/components";
13
+ import { useBuyerPortal } from "../../../shared/hooks";
14
+ import { useAddProductAssortmentToScope } from "../../hooks/useAddProductAssortmentToScope";
15
+ import { AddProductAssortmentDrawerTable } from "../table/AddProductAssortmentDrawerTable";
16
+
17
+ import type {
18
+ AddProductAssortmentDrawerProps,
19
+ ProductAssortmentSummary,
20
+ } from "../../types";
21
+
22
+ export const PAGE_SIZE = 10;
23
+
24
+ export const AddProductAssortmentDrawer = ({
25
+ close,
26
+ productAssortment,
27
+ ...props
28
+ }: AddProductAssortmentDrawerProps) => {
29
+ const { pushToast } = useUI();
30
+ const [drawerSearch, setDrawerSearch] = useState("");
31
+ const { currentOrgUnit, currentContract } = useBuyerPortal();
32
+ const { reload } = useRouter();
33
+
34
+ const unitId = currentOrgUnit?.id;
35
+ const contractId = currentContract?.id;
36
+ const isLoading = false;
37
+
38
+ useEffect(() => {
39
+ setDrawerSearch("");
40
+ }, [props.isOpen]);
41
+
42
+ const drawerProductAssortment = useMemo(() => {
43
+ return productAssortment.filter((p) =>
44
+ p.name.toLowerCase().includes(drawerSearch.toLowerCase())
45
+ );
46
+ }, [productAssortment, drawerSearch]);
47
+
48
+ const addProductAssortmentMutation = useAddProductAssortmentToScope();
49
+
50
+ const [selectedAssortment, setSelectedAssortment] = useState<
51
+ ProductAssortmentSummary[]
52
+ >([]);
53
+
54
+ const handleSuccess = async () => {
55
+ await addProductAssortmentMutation.mutate({
56
+ unitId: unitId ?? "",
57
+ contractId: contractId ?? "",
58
+ data: selectedAssortment.map((s) => ({ name: s.name })),
59
+ });
60
+
61
+ pushToast({
62
+ message: "Product assortment added successfully",
63
+ status: "INFO",
64
+ });
65
+
66
+ close();
67
+ reload();
68
+ };
69
+
70
+ return (
71
+ <BasicDrawer
72
+ data-fs-bp-add-product-assortment-drawer
73
+ id="basicDrawer"
74
+ close={close}
75
+ {...props}
76
+ >
77
+ <BasicDrawer.Heading title="Add product assortment" onClose={close} />
78
+
79
+ <BasicDrawer.Body>
80
+ <span data-fs-bp-add-product-assortment-text-drawer>
81
+ Add product assortment to <Link href="#">{currentOrgUnit?.name}</Link>
82
+ </span>
83
+
84
+ <div data-fs-bp-add-product-assortment-wrapper>
85
+ <InternalSearch
86
+ textSearch={(searchTerm) => {
87
+ setDrawerSearch(searchTerm);
88
+ }}
89
+ />
90
+
91
+ <Paginator.Counter
92
+ total={drawerProductAssortment.length}
93
+ itemsLength={drawerProductAssortment.length}
94
+ />
95
+ </div>
96
+ <section>
97
+ {drawerProductAssortment.length ? (
98
+ <div>
99
+ <AddProductAssortmentDrawerTable
100
+ data={drawerProductAssortment}
101
+ onChange={setSelectedAssortment}
102
+ />
103
+ {drawerProductAssortment.length > 0 && (
104
+ <div data-fs-bp-drawer-product-assortment-paginator>
105
+ {drawerProductAssortment.length > 0 ? (
106
+ <Paginator.NextPageButton>
107
+ {isLoading ? "Loading" : "Load More"}
108
+ </Paginator.NextPageButton>
109
+ ) : (
110
+ <span />
111
+ )}
112
+
113
+ <Paginator.Counter
114
+ total={drawerProductAssortment.length}
115
+ itemsLength={drawerProductAssortment.length}
116
+ />
117
+ </div>
118
+ )}
119
+ </div>
120
+ ) : (
121
+ <div data-fs-bp-add-product-assortment-drawer-empty-state>
122
+ <Icon name="Shapes" />
123
+ <p>No product assortment found</p>
124
+ </div>
125
+ )}
126
+ </section>
127
+ </BasicDrawer.Body>
128
+ <BasicDrawer.Footer data-fs-bp-add-product-assortment-drawer-footer>
129
+ <BasicDrawer.Button variant="ghost" onClick={close}>
130
+ Cancel
131
+ </BasicDrawer.Button>
132
+ <BasicDrawer.Button
133
+ variant="confirm"
134
+ disabled={selectedAssortment.length === 0}
135
+ onClick={handleSuccess}
136
+ isLoading={addProductAssortmentMutation.isLoading}
137
+ >
138
+ Add
139
+ {selectedAssortment.length > 0 && `(${selectedAssortment.length})`}
140
+ </BasicDrawer.Button>
141
+ </BasicDrawer.Footer>
142
+ </BasicDrawer>
143
+ );
144
+ };