@vtex/faststore-plugin-buyer-portal 2.0.7 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yaml +4 -4
- package/AGENTS.md +6 -0
- package/CHANGELOG.md +30 -1
- package/docs/superpowers/plans/2026-06-26-contracts-listing-selection-search-pagination.md +1070 -0
- package/docs/superpowers/specs/2026-06-26-contracts-listing-selection-search-pagination-design.md +157 -0
- package/package.json +1 -1
- package/plugin.config.js +8 -0
- package/public/buyer-portal-icons.svg +37 -1
- package/specs/001-suma-phase-2/checklists/requirements.md +36 -0
- package/specs/001-suma-phase-2/contracts/contracts-api.md +187 -0
- package/specs/001-suma-phase-2/contracts/feature-flag.md +91 -0
- package/specs/001-suma-phase-2/data-model.md +119 -0
- package/specs/001-suma-phase-2/plan.md +123 -0
- package/specs/001-suma-phase-2/quickstart.md +156 -0
- package/specs/001-suma-phase-2/research.md +150 -0
- package/specs/001-suma-phase-2/spec.md +186 -0
- package/specs/001-suma-phase-2/tasks.md +253 -0
- package/specs/suma-add-contracts.md +224 -0
- package/specs/suma-contract-listing.md +295 -0
- package/specs/suma-contract-switching.md +201 -0
- package/specs/suma-remove-contracts.md +233 -0
- package/specs/suma-set-default-contract.md +208 -0
- package/src/features/budgets/layouts/BudgetsLayout/BudgetsLayout.tsx +113 -105
- package/src/features/buying-policies/components/BasicBuyingPolicyDrawer/BasicBuyingPolicyDrawer.tsx +2 -2
- package/src/features/buying-policies/layouts/BuyingPoliciesLayout/BuyingPoliciesLayout.tsx +60 -46
- package/src/features/contracts/clients/ContractsClient.ts +45 -0
- package/src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx +323 -0
- package/src/features/contracts/components/AddContractsDrawer/add-contracts-drawer.scss +282 -0
- package/src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx +58 -0
- package/src/features/contracts/components/ConfirmRemoveDrawer/confirm-remove-drawer.scss +18 -0
- package/src/features/contracts/components/ContractListItem/ContractListItem.tsx +120 -0
- package/src/features/contracts/components/ContractSelectAllRow/ContractSelectAllRow.tsx +37 -0
- package/src/features/contracts/components/ContractSelectAllRow/index.ts +2 -0
- package/src/features/contracts/components/ContractSelectionBar/ContractSelectionBar.tsx +47 -0
- package/src/features/contracts/components/ContractSelectionBar/index.ts +2 -0
- package/src/features/contracts/components/ContractSelectionList/ContractSelectionList.tsx +55 -0
- package/src/features/contracts/components/ContractSwitchOverlay/ContractSwitchOverlay.tsx +36 -0
- package/src/features/contracts/components/ContractSwitchOverlay/contract-switch-overlay.scss +7 -0
- package/src/features/contracts/components/ContractsListHeader/ContractsListHeader.tsx +58 -0
- package/src/features/contracts/components/ContractsListHeader/index.ts +2 -0
- package/src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx +69 -0
- package/src/features/contracts/components/UnableToRemoveContractsDrawer/UnableToRemoveContractsDrawer.tsx +52 -0
- package/src/features/contracts/components/UnableToRemoveContractsDrawer/unable-to-remove-contracts-drawer.scss +29 -0
- package/src/features/contracts/components/index.ts +14 -0
- package/src/features/contracts/hooks/__tests__/contractListingHelpers.test.ts +71 -0
- package/src/features/contracts/hooks/__tests__/contractSwitchChannelHelpers.test.ts +32 -0
- package/src/features/contracts/hooks/__tests__/useAddContracts.test.ts +63 -0
- package/src/features/contracts/hooks/__tests__/useContractSelection.test.ts +72 -0
- package/src/features/contracts/hooks/__tests__/useRemoveContracts.test.ts +130 -0
- package/src/features/contracts/hooks/__tests__/useSetDefaultContract.test.ts +82 -0
- package/src/features/contracts/hooks/contractListingHelpers.ts +45 -0
- package/src/features/contracts/hooks/contractSelectionHelpers.ts +28 -0
- package/src/features/contracts/hooks/contractSwitchChannelHelpers.ts +9 -0
- package/src/features/contracts/hooks/index.ts +19 -0
- package/src/features/contracts/hooks/useAddContracts.ts +27 -0
- package/src/features/contracts/hooks/useContractListing.ts +71 -0
- package/src/features/contracts/hooks/useContractSelection.ts +53 -0
- package/src/features/contracts/hooks/useContractSwitchChannel.ts +49 -0
- package/src/features/contracts/hooks/useListAvailableContracts.ts +27 -0
- package/src/features/contracts/hooks/useRemoveContracts.ts +21 -0
- package/src/features/contracts/hooks/useSetDefaultContract.ts +21 -0
- package/src/features/contracts/hooks/useSwitchContract.ts +76 -0
- package/src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx +316 -0
- package/src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx +415 -0
- package/src/features/contracts/layouts/ContractListingLayout/ContractSettingsNav.tsx +83 -0
- package/src/features/contracts/layouts/ContractListingLayout/contract-listing-layout.scss +468 -0
- package/src/features/contracts/layouts/ContractListingLayout/utils.ts +4 -0
- package/src/features/contracts/layouts/index.ts +8 -0
- package/src/features/contracts/services/__tests__/contracts-client.test.ts +148 -0
- package/src/features/contracts/services/__tests__/list-attached-contracts.service.test.ts +95 -0
- package/src/features/contracts/services/add-contracts.service.ts +27 -0
- package/src/features/contracts/services/index.ts +24 -0
- package/src/features/contracts/services/list-attached-contracts.service.ts +40 -0
- package/src/features/contracts/services/list-available-contracts.service.ts +32 -0
- package/src/features/contracts/services/remove-contract.service.ts +27 -0
- package/src/features/contracts/services/remove-contracts.service.ts +36 -0
- package/src/features/contracts/services/set-default-contract.service.ts +27 -0
- package/src/features/contracts/types/ContractData.ts +1 -0
- package/src/features/contracts/utils/__tests__/changeContractToken.test.ts +141 -0
- package/src/features/contracts/utils/changeContractToken.ts +122 -0
- package/src/features/contracts/utils/clearPersistedSessionState.ts +74 -0
- package/src/features/org-units/components/OrgUnitDetailsNavbar/OrgUnitDetailsNavbar.tsx +45 -3
- package/src/features/org-units/components/OrgUnitDetailsNavbar/org-unit-details-navbar.scss +35 -0
- package/src/features/org-units/layouts/OrgUnitsLayout/OrgUnitsLayout.tsx +54 -48
- package/src/features/roles/layout/RolesLayout/RolesLayout.tsx +32 -23
- package/src/features/shared/components/BuyerPortalProvider/BuyerPortalProvider.tsx +1 -0
- package/src/features/shared/components/HeaderInside/HeaderInside.tsx +8 -1
- package/src/features/shared/components/HeaderInside/header-inside.scss +9 -0
- package/src/features/shared/layouts/BaseTabsLayout/SidebarMenu.tsx +6 -97
- package/src/features/shared/layouts/ContractTabsLayout/ContractTabsLayout.tsx +42 -5
- package/src/features/shared/layouts/LoadingTabsLayout/LoadingTabsLayout.tsx +1 -1
- package/src/features/shared/layouts/SumaPageLayout/FullSidebarNav.tsx +124 -0
- package/src/features/shared/layouts/SumaPageLayout/SumaPageLayout.tsx +56 -0
- package/src/features/shared/layouts/SumaPageLayout/SumaSidebar.tsx +80 -0
- package/src/features/shared/layouts/SumaPageLayout/SumaSidebarDrawer.tsx +48 -0
- package/src/features/shared/layouts/SumaPageLayout/index.ts +6 -0
- package/src/features/shared/layouts/SumaPageLayout/suma-sidebar-drawer.scss +37 -0
- package/src/features/shared/layouts/index.ts +6 -0
- package/src/features/shared/services/feature-flags/__tests__/get-feature-flags.service.test.ts +71 -0
- package/src/features/shared/services/feature-flags/get-feature-flags.service.ts +1 -0
- package/src/features/shared/utils/buyerPortalRoutes.ts +9 -0
- package/src/features/shared/utils/constants.ts +1 -1
- package/src/features/shared/utils/getSumaListingSidebarLinks.ts +18 -0
- package/src/features/shared/utils/index.ts +1 -0
- package/src/features/users/layouts/UsersLayout/UsersLayout.tsx +97 -103
- package/src/pages/contract-information.tsx +128 -0
- package/src/pages/contracts.tsx +104 -0
- package/src/pages/home.tsx +10 -6
- package/src/pages/org-unit-details.tsx +17 -13
- package/src/themes/layouts.scss +2 -0
- package/.agents/skills/create-page/SKILL.md +0 -154
- package/.agents/skills/create-page/metadata.json +0 -14
- package/.specify/memory/constitution.md +0 -83
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { useUI } from "@faststore/ui";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
BasicDrawer,
|
|
7
|
+
type BasicDrawerProps,
|
|
8
|
+
Icon,
|
|
9
|
+
LetterHighlight,
|
|
10
|
+
} from "../../../shared/components";
|
|
11
|
+
import { useAddContracts, useListAvailableContracts } from "../../hooks";
|
|
12
|
+
import {
|
|
13
|
+
buildRangeSet,
|
|
14
|
+
buildToggleSet,
|
|
15
|
+
} from "../../hooks/contractSelectionHelpers";
|
|
16
|
+
|
|
17
|
+
const PAGE_SIZE = 25;
|
|
18
|
+
|
|
19
|
+
export type AddContractsDrawerProps = Omit<BasicDrawerProps, "children"> & {
|
|
20
|
+
orgUnitId: string;
|
|
21
|
+
orgUnitName: string;
|
|
22
|
+
existingContractIds: string[];
|
|
23
|
+
onSuccess: () => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const AddContractsDrawer = ({
|
|
27
|
+
orgUnitId,
|
|
28
|
+
orgUnitName,
|
|
29
|
+
existingContractIds,
|
|
30
|
+
onSuccess,
|
|
31
|
+
...drawerProps
|
|
32
|
+
}: AddContractsDrawerProps) => {
|
|
33
|
+
const { pushToast } = useUI();
|
|
34
|
+
|
|
35
|
+
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
|
36
|
+
const [lastSelectedId, setLastSelectedId] = useState<string | null>(null);
|
|
37
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
38
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
39
|
+
const {
|
|
40
|
+
availableContracts,
|
|
41
|
+
availableContractsLoading,
|
|
42
|
+
fetchAvailableContracts,
|
|
43
|
+
} = useListAvailableContracts(orgUnitId);
|
|
44
|
+
|
|
45
|
+
const { addContracts, isAddContractsLoading } = useAddContracts({
|
|
46
|
+
onSuccess: () => {
|
|
47
|
+
onSuccess();
|
|
48
|
+
drawerProps.onDismiss?.();
|
|
49
|
+
},
|
|
50
|
+
onError: () => {
|
|
51
|
+
drawerProps.onDismiss?.();
|
|
52
|
+
pushToast({
|
|
53
|
+
message: "Failed to add contracts. Please try again.",
|
|
54
|
+
status: "ERROR",
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (drawerProps.isOpen) {
|
|
61
|
+
setSelectedIds(new Set());
|
|
62
|
+
setLastSelectedId(null);
|
|
63
|
+
setSearchQuery("");
|
|
64
|
+
setCurrentPage(1);
|
|
65
|
+
fetchAvailableContracts();
|
|
66
|
+
}
|
|
67
|
+
}, [drawerProps.isOpen]);
|
|
68
|
+
|
|
69
|
+
const existingSet = useMemo(
|
|
70
|
+
() => new Set(existingContractIds),
|
|
71
|
+
[existingContractIds]
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const filteredContracts = useMemo(() => {
|
|
75
|
+
const pool = availableContracts.filter((c) => !existingSet.has(c.id));
|
|
76
|
+
const q = searchQuery.toLowerCase().trim();
|
|
77
|
+
if (!q) return pool;
|
|
78
|
+
return pool.filter((c) => c.name.toLowerCase().includes(q));
|
|
79
|
+
}, [availableContracts, existingSet, searchQuery]);
|
|
80
|
+
|
|
81
|
+
const totalPages = Math.max(
|
|
82
|
+
1,
|
|
83
|
+
Math.ceil(filteredContracts.length / PAGE_SIZE)
|
|
84
|
+
);
|
|
85
|
+
const safePage = Math.min(currentPage, totalPages);
|
|
86
|
+
|
|
87
|
+
const displayedContracts = useMemo(() => {
|
|
88
|
+
const start = (safePage - 1) * PAGE_SIZE;
|
|
89
|
+
return filteredContracts.slice(start, start + PAGE_SIZE);
|
|
90
|
+
}, [filteredContracts, safePage]);
|
|
91
|
+
|
|
92
|
+
const allFilteredIds = filteredContracts.map((c) => c.id);
|
|
93
|
+
const isAllSelected =
|
|
94
|
+
filteredContracts.length > 0 &&
|
|
95
|
+
selectedIds.size === filteredContracts.length;
|
|
96
|
+
const isSomeSelected = selectedIds.size > 0 && !isAllSelected;
|
|
97
|
+
|
|
98
|
+
const handleToggleSelectAll = () => {
|
|
99
|
+
if (isAllSelected) {
|
|
100
|
+
setSelectedIds(new Set());
|
|
101
|
+
} else {
|
|
102
|
+
setSelectedIds(new Set(allFilteredIds));
|
|
103
|
+
}
|
|
104
|
+
setLastSelectedId(null);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const handleRowClick = (id: string, event: React.MouseEvent) => {
|
|
108
|
+
if (event.shiftKey) {
|
|
109
|
+
setSelectedIds((prev) =>
|
|
110
|
+
buildRangeSet(prev, allFilteredIds, lastSelectedId, id)
|
|
111
|
+
);
|
|
112
|
+
} else {
|
|
113
|
+
setSelectedIds((prev) => buildToggleSet(prev, id));
|
|
114
|
+
}
|
|
115
|
+
setLastSelectedId(id);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const handleKeyDown = (event: React.KeyboardEvent) => {
|
|
119
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "a") {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
handleToggleSelectAll();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
126
|
+
setSearchQuery(e.target.value);
|
|
127
|
+
setCurrentPage(1);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const handleConfirm = () => {
|
|
131
|
+
addContracts({
|
|
132
|
+
orgUnitId,
|
|
133
|
+
ids: Array.from(selectedIds),
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const rangeStart =
|
|
138
|
+
filteredContracts.length === 0 ? 0 : (safePage - 1) * PAGE_SIZE + 1;
|
|
139
|
+
const rangeEnd = Math.min(safePage * PAGE_SIZE, filteredContracts.length);
|
|
140
|
+
const isDisabled = isAddContractsLoading;
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<BasicDrawer size="large" {...drawerProps}>
|
|
144
|
+
<BasicDrawer.Heading
|
|
145
|
+
title="Add contracts"
|
|
146
|
+
onClose={isAddContractsLoading ? undefined : drawerProps.onDismiss}
|
|
147
|
+
/>
|
|
148
|
+
|
|
149
|
+
<BasicDrawer.Body data-fs-bp-add-contracts-drawer-body>
|
|
150
|
+
<p data-fs-bp-add-contracts-drawer-description>
|
|
151
|
+
Add available contracts to{" "}
|
|
152
|
+
<strong data-fs-bp-add-contracts-drawer-unit-name>
|
|
153
|
+
{orgUnitName}
|
|
154
|
+
</strong>
|
|
155
|
+
</p>
|
|
156
|
+
|
|
157
|
+
<div data-fs-bp-add-contracts-drawer-toolbar>
|
|
158
|
+
<div data-fs-bp-add-contracts-drawer-search>
|
|
159
|
+
<input
|
|
160
|
+
type="text"
|
|
161
|
+
placeholder="Search"
|
|
162
|
+
value={searchQuery}
|
|
163
|
+
onChange={handleSearchChange}
|
|
164
|
+
disabled={isDisabled}
|
|
165
|
+
aria-label="Search contracts"
|
|
166
|
+
data-fs-bp-add-contracts-drawer-search-input
|
|
167
|
+
/>
|
|
168
|
+
{searchQuery && (
|
|
169
|
+
<button
|
|
170
|
+
type="button"
|
|
171
|
+
onClick={() => {
|
|
172
|
+
setSearchQuery("");
|
|
173
|
+
setCurrentPage(1);
|
|
174
|
+
}}
|
|
175
|
+
disabled={isDisabled}
|
|
176
|
+
aria-label="Clear search"
|
|
177
|
+
data-fs-bp-add-contracts-drawer-search-clear
|
|
178
|
+
>
|
|
179
|
+
<Icon name="X" width={14} height={14} />
|
|
180
|
+
</button>
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
{filteredContracts.length > PAGE_SIZE && (
|
|
185
|
+
<div
|
|
186
|
+
data-fs-bp-add-contracts-drawer-pagination
|
|
187
|
+
aria-label="Pagination"
|
|
188
|
+
>
|
|
189
|
+
<span data-fs-bp-add-contracts-drawer-pagination-range>
|
|
190
|
+
{rangeStart}–{rangeEnd} of {filteredContracts.length}
|
|
191
|
+
</span>
|
|
192
|
+
<button
|
|
193
|
+
type="button"
|
|
194
|
+
onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
|
|
195
|
+
disabled={safePage === 1 || isDisabled}
|
|
196
|
+
aria-label="Previous page"
|
|
197
|
+
data-fs-bp-add-contracts-drawer-pagination-prev
|
|
198
|
+
>
|
|
199
|
+
<Icon name="ArrowLeft" width={16} height={16} />
|
|
200
|
+
</button>
|
|
201
|
+
<button
|
|
202
|
+
type="button"
|
|
203
|
+
onClick={() =>
|
|
204
|
+
setCurrentPage((p) => Math.min(totalPages, p + 1))
|
|
205
|
+
}
|
|
206
|
+
disabled={safePage >= totalPages || isDisabled}
|
|
207
|
+
aria-label="Next page"
|
|
208
|
+
data-fs-bp-add-contracts-drawer-pagination-next
|
|
209
|
+
>
|
|
210
|
+
<Icon name="ArrowRight" width={16} height={16} />
|
|
211
|
+
</button>
|
|
212
|
+
</div>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
{availableContractsLoading ? (
|
|
217
|
+
<div data-fs-bp-add-contracts-drawer-loading aria-busy="true">
|
|
218
|
+
Loading contracts…
|
|
219
|
+
</div>
|
|
220
|
+
) : filteredContracts.length === 0 ? (
|
|
221
|
+
<div data-fs-bp-add-contracts-drawer-empty>
|
|
222
|
+
<p>
|
|
223
|
+
{searchQuery
|
|
224
|
+
? "No contracts match your search."
|
|
225
|
+
: "No contracts available to add."}
|
|
226
|
+
</p>
|
|
227
|
+
</div>
|
|
228
|
+
) : (
|
|
229
|
+
// biome-ignore lint/a11y/useKeyWithClickEvents: keyboard handled via onKeyDown on wrapper
|
|
230
|
+
<div
|
|
231
|
+
data-fs-bp-add-contracts-drawer-list
|
|
232
|
+
role="grid"
|
|
233
|
+
aria-label="Available contracts"
|
|
234
|
+
tabIndex={0}
|
|
235
|
+
onKeyDown={handleKeyDown}
|
|
236
|
+
>
|
|
237
|
+
<div
|
|
238
|
+
data-fs-bp-add-contracts-drawer-select-all
|
|
239
|
+
role="row"
|
|
240
|
+
onClick={isDisabled ? undefined : handleToggleSelectAll}
|
|
241
|
+
aria-checked={
|
|
242
|
+
isAllSelected ? true : isSomeSelected ? "mixed" : false
|
|
243
|
+
}
|
|
244
|
+
aria-disabled={isDisabled || undefined}
|
|
245
|
+
>
|
|
246
|
+
<div data-fs-bp-add-contracts-drawer-checkbox aria-hidden="true">
|
|
247
|
+
{(isAllSelected || isSomeSelected) && (
|
|
248
|
+
<Icon name="Check" width={12} height={12} />
|
|
249
|
+
)}
|
|
250
|
+
</div>
|
|
251
|
+
<span>Select all ({filteredContracts.length})</span>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
{displayedContracts.map((contract) => {
|
|
255
|
+
const isSelected = selectedIds.has(contract.id);
|
|
256
|
+
return (
|
|
257
|
+
<div
|
|
258
|
+
key={contract.id}
|
|
259
|
+
data-fs-bp-add-contracts-drawer-row
|
|
260
|
+
data-fs-bp-add-contracts-drawer-row-selected={
|
|
261
|
+
isSelected || undefined
|
|
262
|
+
}
|
|
263
|
+
role="row"
|
|
264
|
+
aria-selected={isSelected}
|
|
265
|
+
onClick={(e) => !isDisabled && handleRowClick(contract.id, e)}
|
|
266
|
+
>
|
|
267
|
+
<div
|
|
268
|
+
data-fs-bp-add-contracts-drawer-checkbox
|
|
269
|
+
aria-hidden="true"
|
|
270
|
+
>
|
|
271
|
+
{isSelected && <Icon name="Check" width={12} height={12} />}
|
|
272
|
+
</div>
|
|
273
|
+
<LetterHighlight size="small" letter={contract.name} />
|
|
274
|
+
<span data-fs-bp-add-contracts-drawer-row-name>
|
|
275
|
+
{contract.name}
|
|
276
|
+
</span>
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
})}
|
|
280
|
+
</div>
|
|
281
|
+
)}
|
|
282
|
+
</BasicDrawer.Body>
|
|
283
|
+
|
|
284
|
+
<BasicDrawer.Footer>
|
|
285
|
+
<div data-fs-bp-add-contracts-drawer-selection-count>
|
|
286
|
+
{selectedIds.size > 0 && !isDisabled && (
|
|
287
|
+
<>
|
|
288
|
+
<button
|
|
289
|
+
type="button"
|
|
290
|
+
onClick={() => {
|
|
291
|
+
setSelectedIds(new Set());
|
|
292
|
+
setLastSelectedId(null);
|
|
293
|
+
}}
|
|
294
|
+
aria-label="Clear selection"
|
|
295
|
+
data-fs-bp-add-contracts-drawer-clear-selection
|
|
296
|
+
>
|
|
297
|
+
<Icon name="X" width={14} height={14} />
|
|
298
|
+
</button>
|
|
299
|
+
<span>{selectedIds.size} selected</span>
|
|
300
|
+
</>
|
|
301
|
+
)}
|
|
302
|
+
</div>
|
|
303
|
+
<BasicDrawer.Button
|
|
304
|
+
variant="ghost"
|
|
305
|
+
onClick={drawerProps.onDismiss}
|
|
306
|
+
type="button"
|
|
307
|
+
disabled={isDisabled}
|
|
308
|
+
>
|
|
309
|
+
Cancel
|
|
310
|
+
</BasicDrawer.Button>
|
|
311
|
+
<BasicDrawer.Button
|
|
312
|
+
variant="confirm"
|
|
313
|
+
onClick={handleConfirm}
|
|
314
|
+
type="button"
|
|
315
|
+
disabled={selectedIds.size === 0 || isDisabled}
|
|
316
|
+
isLoading={isAddContractsLoading}
|
|
317
|
+
>
|
|
318
|
+
Add
|
|
319
|
+
</BasicDrawer.Button>
|
|
320
|
+
</BasicDrawer.Footer>
|
|
321
|
+
</BasicDrawer>
|
|
322
|
+
);
|
|
323
|
+
};
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
@use "~@faststore/ui/src/styles/base/utilities" as u;
|
|
2
|
+
|
|
3
|
+
[data-fs-bp-add-contracts-drawer-body] {
|
|
4
|
+
@import "../../../shared/components/LetterHighlight/letter-highlight.scss";
|
|
5
|
+
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: 0;
|
|
9
|
+
padding: var(--fs-bp-padding-5) 0 0;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
height: 100%;
|
|
12
|
+
|
|
13
|
+
[data-fs-bp-add-contracts-drawer-description] {
|
|
14
|
+
font-size: var(--fs-bp-text-body);
|
|
15
|
+
color: var(--fs-bp-color-neutral-7);
|
|
16
|
+
padding: 0 var(--fs-bp-padding-5) var(--fs-bp-padding-3);
|
|
17
|
+
margin: 0;
|
|
18
|
+
|
|
19
|
+
[data-fs-bp-add-contracts-drawer-unit-name] {
|
|
20
|
+
color: var(--fs-bp-color-black);
|
|
21
|
+
font-weight: var(--fs-bp-weight-semibold);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
[data-fs-bp-add-contracts-drawer-toolbar] {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: space-between;
|
|
29
|
+
gap: var(--fs-bp-gap-4);
|
|
30
|
+
padding: 0 var(--fs-bp-padding-5) var(--fs-bp-padding-3);
|
|
31
|
+
|
|
32
|
+
[data-fs-bp-add-contracts-drawer-search] {
|
|
33
|
+
position: relative;
|
|
34
|
+
flex: 1;
|
|
35
|
+
max-width: 14rem;
|
|
36
|
+
|
|
37
|
+
&::before {
|
|
38
|
+
content: "";
|
|
39
|
+
position: absolute;
|
|
40
|
+
left: var(--fs-bp-padding-3);
|
|
41
|
+
top: 50%;
|
|
42
|
+
transform: translateY(-50%);
|
|
43
|
+
width: 1rem;
|
|
44
|
+
height: 1rem;
|
|
45
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%239e9e9e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.35-4.35'/%3E%3C/svg%3E");
|
|
46
|
+
background-repeat: no-repeat;
|
|
47
|
+
background-size: contain;
|
|
48
|
+
pointer-events: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
[data-fs-bp-add-contracts-drawer-search-input] {
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: 2rem;
|
|
54
|
+
padding: 0 var(--fs-bp-padding-3) 0 2.25rem;
|
|
55
|
+
border: 1px solid var(--fs-border-color-secondary, #e0e0e0);
|
|
56
|
+
border-radius: var(--fs-bp-border-radius-full);
|
|
57
|
+
font-size: var(--fs-bp-text-body);
|
|
58
|
+
color: var(--fs-bp-color-black);
|
|
59
|
+
background-color: var(--fs-bp-color-white);
|
|
60
|
+
outline: none;
|
|
61
|
+
|
|
62
|
+
&::placeholder {
|
|
63
|
+
color: var(--fs-bp-color-neutral-5);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&:focus {
|
|
67
|
+
border-color: var(--fs-bp-color-brand);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&:disabled {
|
|
71
|
+
opacity: 0.5;
|
|
72
|
+
cursor: not-allowed;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
[data-fs-bp-add-contracts-drawer-search-clear] {
|
|
77
|
+
position: absolute;
|
|
78
|
+
right: var(--fs-bp-padding-2);
|
|
79
|
+
top: 50%;
|
|
80
|
+
transform: translateY(-50%);
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
justify-content: center;
|
|
84
|
+
background: none;
|
|
85
|
+
border: none;
|
|
86
|
+
padding: 2px;
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
color: var(--fs-bp-color-neutral-5);
|
|
89
|
+
border-radius: 50%;
|
|
90
|
+
|
|
91
|
+
&:hover {
|
|
92
|
+
color: var(--fs-bp-color-black);
|
|
93
|
+
background-color: var(--fs-bp-color-neutral-1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[data-fs-bp-add-contracts-drawer-pagination] {
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
gap: var(--fs-bp-gap-2);
|
|
102
|
+
flex-shrink: 0;
|
|
103
|
+
|
|
104
|
+
[data-fs-bp-add-contracts-drawer-pagination-range] {
|
|
105
|
+
font-size: var(--fs-bp-text-body);
|
|
106
|
+
color: var(--fs-bp-color-neutral-7);
|
|
107
|
+
white-space: nowrap;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[data-fs-bp-add-contracts-drawer-pagination-prev],
|
|
111
|
+
[data-fs-bp-add-contracts-drawer-pagination-next] {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
justify-content: center;
|
|
115
|
+
width: 1.5rem;
|
|
116
|
+
height: 1.5rem;
|
|
117
|
+
background: none;
|
|
118
|
+
border: none;
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
color: var(--fs-bp-color-neutral-7);
|
|
121
|
+
border-radius: 50%;
|
|
122
|
+
padding: 0;
|
|
123
|
+
|
|
124
|
+
&:hover:not(:disabled) {
|
|
125
|
+
background-color: var(--fs-bp-color-neutral-1);
|
|
126
|
+
color: var(--fs-bp-color-black);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&:disabled {
|
|
130
|
+
opacity: 0.4;
|
|
131
|
+
cursor: default;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
[data-fs-bp-add-contracts-drawer-loading] {
|
|
138
|
+
flex: 1;
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
justify-content: center;
|
|
142
|
+
font-size: var(--fs-bp-text-body);
|
|
143
|
+
color: var(--fs-bp-color-neutral-5);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
[data-fs-bp-add-contracts-drawer-empty] {
|
|
147
|
+
flex: 1;
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: flex-start;
|
|
150
|
+
padding: var(--fs-bp-padding-3) var(--fs-bp-padding-5);
|
|
151
|
+
|
|
152
|
+
p {
|
|
153
|
+
font-size: var(--fs-bp-text-body);
|
|
154
|
+
color: var(--fs-bp-color-neutral-7);
|
|
155
|
+
margin: 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
[data-fs-bp-add-contracts-drawer-list] {
|
|
160
|
+
flex: 1;
|
|
161
|
+
overflow-y: auto;
|
|
162
|
+
outline: none;
|
|
163
|
+
border-top: 1px solid #e0e0e0;
|
|
164
|
+
|
|
165
|
+
[data-fs-bp-add-contracts-drawer-select-all] {
|
|
166
|
+
display: flex;
|
|
167
|
+
align-items: center;
|
|
168
|
+
gap: var(--fs-bp-gap-3);
|
|
169
|
+
padding: var(--fs-bp-padding-3) var(--fs-bp-padding-5);
|
|
170
|
+
border-bottom: 1px solid #e0e0e0;
|
|
171
|
+
cursor: pointer;
|
|
172
|
+
user-select: none;
|
|
173
|
+
font-size: var(--fs-bp-text-body);
|
|
174
|
+
font-weight: var(--fs-bp-weight-regular);
|
|
175
|
+
color: var(--fs-bp-color-black);
|
|
176
|
+
|
|
177
|
+
&:hover {
|
|
178
|
+
background-color: #f5f5f5;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
[data-fs-bp-add-contracts-drawer-checkbox] {
|
|
182
|
+
flex-shrink: 0;
|
|
183
|
+
width: 1.125rem;
|
|
184
|
+
height: 1.125rem;
|
|
185
|
+
border: 2px solid #9e9e9e;
|
|
186
|
+
border-radius: 2px;
|
|
187
|
+
display: flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
justify-content: center;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
&[aria-checked="true"],
|
|
193
|
+
&[aria-checked="mixed"] {
|
|
194
|
+
[data-fs-bp-add-contracts-drawer-checkbox] {
|
|
195
|
+
background-color: var(--fs-bp-color-brand);
|
|
196
|
+
border-color: var(--fs-bp-color-brand);
|
|
197
|
+
color: var(--fs-bp-color-white);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
[data-fs-bp-add-contracts-drawer-row] {
|
|
203
|
+
display: flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
gap: var(--fs-bp-gap-3);
|
|
206
|
+
padding: var(--fs-bp-padding-3) var(--fs-bp-padding-5);
|
|
207
|
+
border-bottom: 1px solid #e0e0e0;
|
|
208
|
+
cursor: pointer;
|
|
209
|
+
user-select: none;
|
|
210
|
+
|
|
211
|
+
&:hover {
|
|
212
|
+
background-color: #f5f5f5;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
&[data-fs-bp-add-contracts-drawer-row-selected] {
|
|
216
|
+
background-color: #f0f7ff;
|
|
217
|
+
|
|
218
|
+
[data-fs-bp-add-contracts-drawer-checkbox] {
|
|
219
|
+
background-color: var(--fs-bp-color-brand);
|
|
220
|
+
border-color: var(--fs-bp-color-brand);
|
|
221
|
+
color: var(--fs-bp-color-white);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
[data-fs-bp-add-contracts-drawer-checkbox] {
|
|
226
|
+
flex-shrink: 0;
|
|
227
|
+
width: 1.125rem;
|
|
228
|
+
height: 1.125rem;
|
|
229
|
+
border: 2px solid #9e9e9e;
|
|
230
|
+
border-radius: 2px;
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
justify-content: center;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
[data-fs-bp-letter-highlight] {
|
|
237
|
+
flex-shrink: 0;
|
|
238
|
+
background-color: #1f1f1f;
|
|
239
|
+
color: var(--fs-bp-color-white);
|
|
240
|
+
width: var(--fs-spacing-5);
|
|
241
|
+
height: var(--fs-spacing-5);
|
|
242
|
+
font-size: var(--fs-bp-text-size-0);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
[data-fs-bp-add-contracts-drawer-row-name] {
|
|
246
|
+
flex: 1;
|
|
247
|
+
font-size: var(--fs-bp-text-body);
|
|
248
|
+
font-weight: var(--fs-bp-weight-regular);
|
|
249
|
+
color: var(--fs-bp-color-black);
|
|
250
|
+
white-space: nowrap;
|
|
251
|
+
overflow: hidden;
|
|
252
|
+
text-overflow: ellipsis;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
[data-fs-bp-add-contracts-drawer-selection-count] {
|
|
259
|
+
display: flex;
|
|
260
|
+
align-items: center;
|
|
261
|
+
gap: var(--fs-bp-gap-2);
|
|
262
|
+
flex: 1;
|
|
263
|
+
font-size: var(--fs-bp-text-body);
|
|
264
|
+
color: var(--fs-bp-color-neutral-7);
|
|
265
|
+
|
|
266
|
+
[data-fs-bp-add-contracts-drawer-clear-selection] {
|
|
267
|
+
display: flex;
|
|
268
|
+
align-items: center;
|
|
269
|
+
justify-content: center;
|
|
270
|
+
background: none;
|
|
271
|
+
border: none;
|
|
272
|
+
padding: 2px;
|
|
273
|
+
cursor: pointer;
|
|
274
|
+
color: var(--fs-bp-color-neutral-5);
|
|
275
|
+
border-radius: 50%;
|
|
276
|
+
|
|
277
|
+
&:hover {
|
|
278
|
+
color: var(--fs-bp-color-black);
|
|
279
|
+
background-color: var(--fs-bp-color-neutral-1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { BasicDrawer, type BasicDrawerProps } from "../../../shared/components";
|
|
2
|
+
|
|
3
|
+
export type ConfirmRemoveDrawerProps = Omit<BasicDrawerProps, "children"> & {
|
|
4
|
+
orgUnitName: string;
|
|
5
|
+
count: number;
|
|
6
|
+
onConfirm: () => void;
|
|
7
|
+
isLoading?: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const ConfirmRemoveDrawer = ({
|
|
11
|
+
orgUnitName,
|
|
12
|
+
count,
|
|
13
|
+
onConfirm,
|
|
14
|
+
isLoading = false,
|
|
15
|
+
...drawerProps
|
|
16
|
+
}: ConfirmRemoveDrawerProps) => {
|
|
17
|
+
const label = count === 1 ? "contract" : "contracts";
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<BasicDrawer {...drawerProps}>
|
|
21
|
+
<BasicDrawer.Heading
|
|
22
|
+
title={`Remove ${label}`}
|
|
23
|
+
onClose={drawerProps.onDismiss}
|
|
24
|
+
/>
|
|
25
|
+
<BasicDrawer.Body data-fs-bp-confirm-remove-drawer-body>
|
|
26
|
+
<p>
|
|
27
|
+
Are you sure you want to remove <strong>{count}</strong> {label} from{" "}
|
|
28
|
+
<strong>{orgUnitName}</strong>?
|
|
29
|
+
</p>
|
|
30
|
+
<p>
|
|
31
|
+
Removed contracts can be added again later, but any specific
|
|
32
|
+
configurations for these contracts within this organizational unit
|
|
33
|
+
will be lost. This will prevent users in this organization unit from
|
|
34
|
+
accessing these contracts during store navigation and checkout.
|
|
35
|
+
</p>
|
|
36
|
+
</BasicDrawer.Body>
|
|
37
|
+
<BasicDrawer.Footer>
|
|
38
|
+
<BasicDrawer.Button
|
|
39
|
+
variant="ghost"
|
|
40
|
+
onClick={drawerProps.onDismiss}
|
|
41
|
+
type="button"
|
|
42
|
+
disabled={isLoading}
|
|
43
|
+
>
|
|
44
|
+
Cancel
|
|
45
|
+
</BasicDrawer.Button>
|
|
46
|
+
<BasicDrawer.Button
|
|
47
|
+
variant="danger"
|
|
48
|
+
onClick={onConfirm}
|
|
49
|
+
type="button"
|
|
50
|
+
isLoading={isLoading}
|
|
51
|
+
disabled={isLoading}
|
|
52
|
+
>
|
|
53
|
+
Remove
|
|
54
|
+
</BasicDrawer.Button>
|
|
55
|
+
</BasicDrawer.Footer>
|
|
56
|
+
</BasicDrawer>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@use "~@faststore/ui/src/styles/base/utilities" as u;
|
|
2
|
+
|
|
3
|
+
[data-fs-bp-confirm-remove-drawer-body] {
|
|
4
|
+
[data-fs-bp-basic-drawer-body-wrapper] {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
gap: var(--fs-bp-gap-4);
|
|
8
|
+
|
|
9
|
+
p {
|
|
10
|
+
margin: 0;
|
|
11
|
+
font-size: var(--fs-bp-text-body);
|
|
12
|
+
color: var(--fs-bp-color-neutral-7);
|
|
13
|
+
line-height: 1.5;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
}
|