@vtex/faststore-plugin-buyer-portal 1.1.96 → 1.1.97
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/package.json +1 -1
- package/src/features/buying-policies/components/AddBuyingPolicyDrawer/AddBuyingPolicyDrawer.tsx +4 -0
- package/src/features/buying-policies/components/BasicBuyingPolicyDrawer/BasicBuyingPolicyDrawer.tsx +35 -4
- package/src/features/buying-policies/components/BasicBuyingPolicyDrawer/basic-buying-policy-drawer.scss +2 -1
- package/src/features/buying-policies/components/BudgetCriteriaSelector/BudgetCriteriaSelector.tsx +113 -0
- package/src/features/buying-policies/components/BudgetCriteriaSelector/budget-criteria-selector.scss +139 -0
- package/src/features/buying-policies/components/BudgetCriteriaSelector/index.ts +4 -0
- package/src/features/buying-policies/components/BuyingPolicyDropdownMenu/BuyingPolicyDropdownMenu.tsx +5 -0
- package/src/features/buying-policies/components/UpdateBuyingPolicyDrawer/UpdateBuyingPolicyDrawer.tsx +5 -0
- package/src/features/buying-policies/layouts/BuyingPoliciesLayout/BuyingPoliciesLayout.tsx +6 -1
- package/src/features/buying-policies/utils/index.ts +4 -1
- package/src/features/buying-policies/utils/orderFieldsCriteriaOptions.ts +12 -9
- package/src/pages/budgets.tsx +1 -1
- package/src/pages/buying-policies.tsx +14 -1
package/package.json
CHANGED
package/src/features/buying-policies/components/AddBuyingPolicyDrawer/AddBuyingPolicyDrawer.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
|
7
7
|
import { useAddBuyingPolicy } from "../../hooks";
|
|
8
8
|
import { buyingPolicyDefault } from "../../utils";
|
|
9
9
|
|
|
10
|
+
import type { BudgetListResponse } from "../../../budgets/types";
|
|
10
11
|
import type { BuyingPolicy } from "../../types";
|
|
11
12
|
|
|
12
13
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -19,6 +20,7 @@ export type AddBuyingPolicyDrawerProps = Omit<
|
|
|
19
20
|
onCreate?: () => void;
|
|
20
21
|
orgUnitId: string;
|
|
21
22
|
contractId: string;
|
|
23
|
+
budgetData?: BudgetListResponse;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
export const AddBuyingPolicyDrawer = ({
|
|
@@ -26,6 +28,7 @@ export const AddBuyingPolicyDrawer = ({
|
|
|
26
28
|
onCreate,
|
|
27
29
|
orgUnitId,
|
|
28
30
|
contractId,
|
|
31
|
+
budgetData = { data: [], total: 0 },
|
|
29
32
|
...props
|
|
30
33
|
}: AddBuyingPolicyDrawerProps) => {
|
|
31
34
|
const { pushToast } = useUI();
|
|
@@ -85,6 +88,7 @@ export const AddBuyingPolicyDrawer = ({
|
|
|
85
88
|
orgUnitId={orgUnitId}
|
|
86
89
|
contractId={contractId}
|
|
87
90
|
initialValues={defaultBuyingPolicyValues}
|
|
91
|
+
budgetData={budgetData}
|
|
88
92
|
{...props}
|
|
89
93
|
/>
|
|
90
94
|
);
|
package/src/features/buying-policies/components/BasicBuyingPolicyDrawer/BasicBuyingPolicyDrawer.tsx
CHANGED
|
@@ -23,11 +23,15 @@ import { type BuyingPolicy } from "../../types";
|
|
|
23
23
|
import {
|
|
24
24
|
BUYING_POLICIES_WORKFLOW_TYPES,
|
|
25
25
|
buyingPolicyDefault,
|
|
26
|
+
BUDGET_CRITERIA,
|
|
26
27
|
orderFieldsCriteriaOptions,
|
|
27
28
|
spendingLimitsCriteriaOptions,
|
|
28
29
|
} from "../../utils";
|
|
29
30
|
import { BUYING_POLICIES_WORKFLOW_LABELS } from "../../utils/buyingPoliciesWorkflowTypes";
|
|
30
31
|
import { getHighlightedText } from "../../utils/criteriaHighlightSyntax";
|
|
32
|
+
import { BudgetCriteriaSelector } from "../BudgetCriteriaSelector";
|
|
33
|
+
|
|
34
|
+
import type { BudgetListResponse } from "../../../budgets/types";
|
|
31
35
|
|
|
32
36
|
const LIMIT_OF_LEVELS = 5;
|
|
33
37
|
|
|
@@ -50,6 +54,7 @@ export type BasicBuyingPolicyDrawerProps = Omit<
|
|
|
50
54
|
drawerTitle?: string;
|
|
51
55
|
saveButtonLabel?: string;
|
|
52
56
|
isDrawerLoading?: boolean;
|
|
57
|
+
budgetData?: BudgetListResponse;
|
|
53
58
|
};
|
|
54
59
|
|
|
55
60
|
export const BasicBuyingPolicyDrawer = ({
|
|
@@ -62,6 +67,7 @@ export const BasicBuyingPolicyDrawer = ({
|
|
|
62
67
|
orgUnitId,
|
|
63
68
|
contractId,
|
|
64
69
|
initialValues = defaultBuyingPolicyValues,
|
|
70
|
+
budgetData = { data: [], total: 0 },
|
|
65
71
|
...props
|
|
66
72
|
}: BasicBuyingPolicyDrawerProps) => {
|
|
67
73
|
const [form, setForm] = useState<BuyingPolicyForm>(initialValues);
|
|
@@ -167,6 +173,27 @@ export const BasicBuyingPolicyDrawer = ({
|
|
|
167
173
|
});
|
|
168
174
|
};
|
|
169
175
|
|
|
176
|
+
const isBudgetCriteria =
|
|
177
|
+
criteria === BUDGET_CRITERIA ||
|
|
178
|
+
criteria.includes('$exists(budgetData.unitAllocations.*[budgetId="');
|
|
179
|
+
|
|
180
|
+
const renderCriteria = (criteria: string) => {
|
|
181
|
+
if (isBudgetCriteria) {
|
|
182
|
+
return (
|
|
183
|
+
<BudgetCriteriaSelector
|
|
184
|
+
budgetData={budgetData}
|
|
185
|
+
currentCriteria={criteria}
|
|
186
|
+
onCriteriaChange={(newCriteria) =>
|
|
187
|
+
updateField("criteria", newCriteria)
|
|
188
|
+
}
|
|
189
|
+
onEditablePartClick={() => setCriteriaFocused(true)}
|
|
190
|
+
/>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return getHighlightedText(criteria);
|
|
195
|
+
};
|
|
196
|
+
|
|
170
197
|
const isConfirmButtonEnabled = isAllFieldsFilled && !isLoading;
|
|
171
198
|
|
|
172
199
|
return (
|
|
@@ -258,10 +285,14 @@ export const BasicBuyingPolicyDrawer = ({
|
|
|
258
285
|
{!criteriaFocused && (
|
|
259
286
|
<div
|
|
260
287
|
data-fs-bp-basic-buying-policy-criteria-input-highlighted
|
|
261
|
-
onClick={() =>
|
|
288
|
+
onClick={() => {
|
|
289
|
+
if (!isBudgetCriteria) {
|
|
290
|
+
setCriteriaFocused(true);
|
|
291
|
+
}
|
|
292
|
+
}}
|
|
262
293
|
>
|
|
263
294
|
{criteria ? (
|
|
264
|
-
|
|
295
|
+
renderCriteria(criteria)
|
|
265
296
|
) : (
|
|
266
297
|
<span data-fs-bp-label>Example: value {">"} 10000</span>
|
|
267
298
|
)}
|
|
@@ -280,7 +311,7 @@ export const BasicBuyingPolicyDrawer = ({
|
|
|
280
311
|
label: option.label,
|
|
281
312
|
value: option.criteria,
|
|
282
313
|
}))}
|
|
283
|
-
onSelect={(value) => updateField("criteria", value)}
|
|
314
|
+
onSelect={(value: string) => updateField("criteria", value)}
|
|
284
315
|
triggerLabel="Set spending limit criteria"
|
|
285
316
|
highlightText={getHighlightedText}
|
|
286
317
|
/>
|
|
@@ -290,7 +321,7 @@ export const BasicBuyingPolicyDrawer = ({
|
|
|
290
321
|
label: option.label,
|
|
291
322
|
value: option.criteria,
|
|
292
323
|
}))}
|
|
293
|
-
onSelect={(value) => updateField("criteria", value)}
|
|
324
|
+
onSelect={(value: string) => updateField("criteria", value)}
|
|
294
325
|
triggerLabel="Set order field criteria"
|
|
295
326
|
highlightText={getHighlightedText}
|
|
296
327
|
/>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
@import "../../../shared/components/BasicDrawer/basic-drawer.scss";
|
|
2
2
|
@import "../../../shared/components/OrgUnitInputSearch/org-unit-input-search.scss";
|
|
3
3
|
@import "../../../shared/components/CustomDropdown/custom-dropdown.scss";
|
|
4
|
+
@import "../BudgetCriteriaSelector/budget-criteria-selector.scss";
|
|
4
5
|
|
|
5
6
|
[data-fs-bp-basic-buying-policy-drawer] {
|
|
6
7
|
@import "../../../shared/components/InputText/input-text.scss";
|
|
@@ -105,7 +106,6 @@
|
|
|
105
106
|
var(--fs-spacing-3);
|
|
106
107
|
border-radius: calc(var(--fs-border-radius) * 2);
|
|
107
108
|
text-wrap: nowrap;
|
|
108
|
-
overflow: hidden;
|
|
109
109
|
text-overflow: ellipsis;
|
|
110
110
|
white-space: nowrap;
|
|
111
111
|
cursor: pointer;
|
|
@@ -124,6 +124,7 @@
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
|
|
127
128
|
[data-fs-bp-input-text-criteria] {
|
|
128
129
|
color: #5c5c5c;
|
|
129
130
|
|
package/src/features/buying-policies/components/BudgetCriteriaSelector/BudgetCriteriaSelector.tsx
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
import { CustomDropdown } from "../../../shared/components/CustomDropdown/CustomDropdown";
|
|
4
|
+
|
|
5
|
+
import type { BudgetListResponse } from "../../../budgets/types";
|
|
6
|
+
|
|
7
|
+
export type BudgetCriteriaSelectorProps = {
|
|
8
|
+
onCriteriaChange: (criteria: string) => void;
|
|
9
|
+
currentCriteria: string;
|
|
10
|
+
onEditablePartClick?: () => void;
|
|
11
|
+
budgetData?: BudgetListResponse;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const BudgetCriteriaSelector = ({
|
|
15
|
+
onCriteriaChange,
|
|
16
|
+
currentCriteria,
|
|
17
|
+
onEditablePartClick,
|
|
18
|
+
budgetData = { data: [], total: 0 },
|
|
19
|
+
}: BudgetCriteriaSelectorProps) => {
|
|
20
|
+
const extractBudgetValueFromCriteria = (criteria: string): string => {
|
|
21
|
+
const match = criteria.match(/budgetId="([^"]+)"/);
|
|
22
|
+
return match ? match[1] : "budget-001";
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const [currentBudgetValue, setCurrentBudgetValue] = useState<string>(
|
|
26
|
+
extractBudgetValueFromCriteria(currentCriteria)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const extractedValue = extractBudgetValueFromCriteria(currentCriteria);
|
|
31
|
+
setCurrentBudgetValue(extractedValue);
|
|
32
|
+
}, [currentCriteria]);
|
|
33
|
+
|
|
34
|
+
const getMatchingBudget = () => {
|
|
35
|
+
return budgetData.data.find(
|
|
36
|
+
(budget) =>
|
|
37
|
+
budget.id === currentBudgetValue || budget.id === currentBudgetValue
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const generateCriteriaString = (value: string) => {
|
|
42
|
+
return `$exists(budgetData.unitAllocations.*[budgetId="${value}"])`;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const handleBudgetSelect = (budgetId: string) => {
|
|
46
|
+
const selectedBudget = budgetData.data.find(
|
|
47
|
+
(budget) => budget.id === budgetId
|
|
48
|
+
);
|
|
49
|
+
if (selectedBudget) {
|
|
50
|
+
const newValue = selectedBudget.id;
|
|
51
|
+
setCurrentBudgetValue(newValue);
|
|
52
|
+
const newCriteria = generateCriteriaString(newValue);
|
|
53
|
+
onCriteriaChange(newCriteria);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const handleBudgetSelectorClick = (e: React.MouseEvent) => {
|
|
58
|
+
e.stopPropagation();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const handleEditablePartClick = (e: React.MouseEvent) => {
|
|
62
|
+
e.stopPropagation();
|
|
63
|
+
onEditablePartClick?.();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const matchingBudget = getMatchingBudget();
|
|
67
|
+
const displayValue = matchingBudget ? matchingBudget.id : currentBudgetValue;
|
|
68
|
+
const hasBudgetData = budgetData.data.length > 0;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
data-fs-bp-budget-criteria-selector
|
|
73
|
+
onClick={handleBudgetSelectorClick}
|
|
74
|
+
>
|
|
75
|
+
<div data-fs-bp-budget-criteria-string>
|
|
76
|
+
<span onClick={handleEditablePartClick} data-fs-bp-editable-part>
|
|
77
|
+
$exists(budgetData.unitAllocations.*[budgetId="
|
|
78
|
+
</span>
|
|
79
|
+
{hasBudgetData ? (
|
|
80
|
+
<div onClick={handleBudgetSelectorClick}>
|
|
81
|
+
<CustomDropdown
|
|
82
|
+
options={budgetData.data.map((budget) => ({
|
|
83
|
+
label: budget.name,
|
|
84
|
+
value: budget.id,
|
|
85
|
+
}))}
|
|
86
|
+
onSelect={handleBudgetSelect}
|
|
87
|
+
triggerLabel={displayValue}
|
|
88
|
+
highlightText={(text) => (
|
|
89
|
+
<div data-fs-bp-dropdown-option>
|
|
90
|
+
<div data-fs-bp-dropdown-option-label>{text}</div>
|
|
91
|
+
<div data-fs-bp-dropdown-option-uuid>
|
|
92
|
+
{budgetData.data.find((b) => b.name === text)?.id}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
) : (
|
|
99
|
+
<span
|
|
100
|
+
onClick={handleEditablePartClick}
|
|
101
|
+
data-fs-bp-editable-part
|
|
102
|
+
data-fs-bp-budget-value
|
|
103
|
+
>
|
|
104
|
+
{displayValue}
|
|
105
|
+
</span>
|
|
106
|
+
)}
|
|
107
|
+
<span onClick={handleEditablePartClick} data-fs-bp-editable-part>
|
|
108
|
+
"])
|
|
109
|
+
</span>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
};
|
package/src/features/buying-policies/components/BudgetCriteriaSelector/budget-criteria-selector.scss
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
[data-fs-bp-budget-criteria-selector] {
|
|
2
|
+
position: absolute;
|
|
3
|
+
top: 0;
|
|
4
|
+
left: 0;
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 100%;
|
|
7
|
+
margin: 0;
|
|
8
|
+
background-color: #f5f5f5;
|
|
9
|
+
border: var(--fs-border-width) solid #d6d6d6;
|
|
10
|
+
border-radius: calc(var(--fs-border-radius) * 2);
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
align-items: flex-start;
|
|
14
|
+
|
|
15
|
+
[data-fs-bp-budget-criteria-string] {
|
|
16
|
+
font-family: "Roboto", monospace;
|
|
17
|
+
font-size: var(--fs-text-size-1);
|
|
18
|
+
color: #5c5c5c;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
padding: var(--fs-spacing-3) var(--fs-spacing-3);
|
|
22
|
+
gap: 0;
|
|
23
|
+
flex-wrap: wrap;
|
|
24
|
+
width: 100%;
|
|
25
|
+
|
|
26
|
+
> span {
|
|
27
|
+
white-space: nowrap;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
[data-fs-bp-editable-part] {
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
transition: background-color 0.2s ease;
|
|
33
|
+
padding: 0.125rem 0 0.25rem;
|
|
34
|
+
border-radius: calc(var(--fs-border-radius) * 0.5);
|
|
35
|
+
|
|
36
|
+
&:hover {
|
|
37
|
+
background-color: #f5f5f5;
|
|
38
|
+
color: #1f1f1f;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
[data-fs-bp-custom-dropdown] {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
|
|
45
|
+
[data-fs-bp-custom-dropdown-trigger] {
|
|
46
|
+
color: #b521b6;
|
|
47
|
+
border: none;
|
|
48
|
+
min-width: 0;
|
|
49
|
+
border-radius: calc(var(--fs-border-radius) * 1.5);
|
|
50
|
+
font-family: "Roboto", monospace;
|
|
51
|
+
font-size: calc(var(--fs-text-size-1) * 0.9);
|
|
52
|
+
padding: 0;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
transition: background-color 0.2s ease;
|
|
55
|
+
background-color: transparent;
|
|
56
|
+
|
|
57
|
+
&:hover {
|
|
58
|
+
background-color: #f5f5f5;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
[data-fs-bp-custom-dropdown-menu] {
|
|
63
|
+
position: absolute;
|
|
64
|
+
top: 100%;
|
|
65
|
+
left: 0;
|
|
66
|
+
margin-top: var(--fs-spacing-1);
|
|
67
|
+
padding: var(--fs-spacing-1) 0;
|
|
68
|
+
background-color: #fff;
|
|
69
|
+
box-shadow: 0rem 0.5rem 0.625rem 0rem rgba(0, 0, 0, 0.0784313725);
|
|
70
|
+
max-height: 15rem;
|
|
71
|
+
min-width: 20rem;
|
|
72
|
+
overflow-y: auto;
|
|
73
|
+
z-index: 1;
|
|
74
|
+
border-radius: calc(var(--fs-border-radius) * 2);
|
|
75
|
+
|
|
76
|
+
&::-webkit-scrollbar {
|
|
77
|
+
width: 0.75rem;
|
|
78
|
+
height: 0.75rem;
|
|
79
|
+
background-color: #fff;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
&::-webkit-scrollbar-thumb {
|
|
83
|
+
background-color: #adadad;
|
|
84
|
+
border: 0.25rem solid transparent;
|
|
85
|
+
border-radius: 0.375rem;
|
|
86
|
+
background-clip: content-box;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
[data-fs-bp-custom-dropdown-item] {
|
|
90
|
+
padding: var(--fs-spacing-2) var(--fs-spacing-3);
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
border-bottom: var(--fs-border-width) solid #f0f0f0;
|
|
93
|
+
|
|
94
|
+
&:last-child {
|
|
95
|
+
border-bottom: none;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
&:hover,
|
|
99
|
+
&[data-focused="true"] {
|
|
100
|
+
background-color: #ebebeb;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-fs-bp-dropdown-option] {
|
|
104
|
+
font-family: Inter;
|
|
105
|
+
|
|
106
|
+
[data-fs-bp-dropdown-option-label] {
|
|
107
|
+
font-weight: var(--fs-text-weight-semibold);
|
|
108
|
+
color: #1f1f1f;
|
|
109
|
+
font-size: var(--fs-text-size-1);
|
|
110
|
+
margin-bottom: var(--fs-spacing-0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
[data-fs-bp-dropdown-option-uuid] {
|
|
114
|
+
font-size: var(--fs-text-size-0);
|
|
115
|
+
color: #5c5c5c;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
[data-fs-bp-custom-value-indicator] {
|
|
124
|
+
font-size: calc(var(--fs-text-size-0) * 0.85);
|
|
125
|
+
color: #666666;
|
|
126
|
+
font-style: italic;
|
|
127
|
+
padding: 0 var(--fs-spacing-3) calc(var(--fs-spacing-1) * 0.5);
|
|
128
|
+
border-top: 0.0625rem solid #e0e0e0;
|
|
129
|
+
margin-top: auto;
|
|
130
|
+
width: 100%;
|
|
131
|
+
background-color: #fafafa;
|
|
132
|
+
border-radius: 0 0 calc(var(--fs-border-radius) * 2)
|
|
133
|
+
calc(var(--fs-border-radius) * 2);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
[data-fs-bp-budget-value] {
|
|
137
|
+
color: #b521b6;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -7,11 +7,14 @@ import { BasicDropdownMenu, Icon } from "../../../shared/components";
|
|
|
7
7
|
import { useBuyerPortal, useDrawerProps } from "../../../shared/hooks";
|
|
8
8
|
import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
9
9
|
|
|
10
|
+
import type { BudgetListResponse } from "../../../budgets/types";
|
|
11
|
+
|
|
10
12
|
export type BuyingPolicyDropdownMenuProps = {
|
|
11
13
|
id: string;
|
|
12
14
|
name: string;
|
|
13
15
|
onDelete?: () => void;
|
|
14
16
|
onUpdate?: () => void;
|
|
17
|
+
budgetData?: BudgetListResponse;
|
|
15
18
|
};
|
|
16
19
|
|
|
17
20
|
export const BuyingPolicyDropdownMenu = ({
|
|
@@ -19,6 +22,7 @@ export const BuyingPolicyDropdownMenu = ({
|
|
|
19
22
|
name,
|
|
20
23
|
onDelete,
|
|
21
24
|
onUpdate,
|
|
25
|
+
budgetData = { data: [], total: 0 },
|
|
22
26
|
}: BuyingPolicyDropdownMenuProps) => {
|
|
23
27
|
const { currentContract, currentOrgUnit } = useBuyerPortal();
|
|
24
28
|
|
|
@@ -80,6 +84,7 @@ export const BuyingPolicyDropdownMenu = ({
|
|
|
80
84
|
contractId={currentContract?.id ?? ""}
|
|
81
85
|
buyingPolicyId={id}
|
|
82
86
|
onUpdate={onUpdate}
|
|
87
|
+
budgetData={budgetData}
|
|
83
88
|
{...updateBuyingPolicyDrawerProps}
|
|
84
89
|
isOpen={isUpdateBuyingPolicyDrawerOpen}
|
|
85
90
|
/>
|
|
@@ -7,6 +7,8 @@ import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
|
7
7
|
import { useGetBuyingPolicy, useUpdateBuyingPolicy } from "../../hooks";
|
|
8
8
|
import { buyingPolicyDefault } from "../../utils";
|
|
9
9
|
|
|
10
|
+
import type { BudgetListResponse } from "../../../budgets/types";
|
|
11
|
+
|
|
10
12
|
export type UpdateBuyingPolicyDrawerProps = Omit<
|
|
11
13
|
BasicBuyingPolicyDrawerProps,
|
|
12
14
|
"children" | "initialValues"
|
|
@@ -15,6 +17,7 @@ export type UpdateBuyingPolicyDrawerProps = Omit<
|
|
|
15
17
|
orgUnitId: string;
|
|
16
18
|
contractId: string;
|
|
17
19
|
buyingPolicyId: string;
|
|
20
|
+
budgetData?: BudgetListResponse;
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
export const UpdateBuyingPolicyDrawer = ({
|
|
@@ -23,6 +26,7 @@ export const UpdateBuyingPolicyDrawer = ({
|
|
|
23
26
|
orgUnitId,
|
|
24
27
|
contractId,
|
|
25
28
|
buyingPolicyId,
|
|
29
|
+
budgetData = { data: [], total: 0 },
|
|
26
30
|
...props
|
|
27
31
|
}: UpdateBuyingPolicyDrawerProps) => {
|
|
28
32
|
const { buyingPolicy, isBuyingPolicyLoading } = useGetBuyingPolicy(
|
|
@@ -93,6 +97,7 @@ export const UpdateBuyingPolicyDrawer = ({
|
|
|
93
97
|
initialValues={{ ...buyingPolicyDefault, ...(buyingPolicy ?? {}) }}
|
|
94
98
|
orgUnitId={orgUnitId}
|
|
95
99
|
contractId={contractId}
|
|
100
|
+
budgetData={budgetData}
|
|
96
101
|
{...props}
|
|
97
102
|
/>
|
|
98
103
|
);
|
|
@@ -21,6 +21,7 @@ import { BuyingPolicyDropdownMenu } from "../../components";
|
|
|
21
21
|
import { AddBuyingPolicyDrawer } from "../../components/AddBuyingPolicyDrawer/AddBuyingPolicyDrawer";
|
|
22
22
|
import { useGetBuyingPolicies } from "../../hooks/useGetBuyingPolicies";
|
|
23
23
|
|
|
24
|
+
import type { BudgetListResponse } from "../../../budgets/types";
|
|
24
25
|
import type { BuyingPolicy } from "../../types";
|
|
25
26
|
|
|
26
27
|
export type BuyingPoliciesLayoutProps = {
|
|
@@ -28,12 +29,14 @@ export type BuyingPoliciesLayoutProps = {
|
|
|
28
29
|
total: number;
|
|
29
30
|
search: string;
|
|
30
31
|
page: number;
|
|
32
|
+
budgetData?: BudgetListResponse;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
export const BuyingPoliciesLayout = ({
|
|
34
36
|
buyingPolicies: initialBuyingPolicies,
|
|
35
37
|
search,
|
|
36
38
|
page,
|
|
39
|
+
budgetData = { data: [], total: 0 },
|
|
37
40
|
}: BuyingPoliciesLayoutProps) => {
|
|
38
41
|
const { currentContract, currentOrgUnit } = useBuyerPortal();
|
|
39
42
|
|
|
@@ -108,7 +111,7 @@ export const BuyingPoliciesLayout = ({
|
|
|
108
111
|
return renderLoading();
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
if (buyingPolicies.length
|
|
114
|
+
if (buyingPolicies.length === 0) {
|
|
112
115
|
return renderEmptyState();
|
|
113
116
|
}
|
|
114
117
|
|
|
@@ -133,6 +136,7 @@ export const BuyingPoliciesLayout = ({
|
|
|
133
136
|
name={buyingPolicy.name}
|
|
134
137
|
onUpdate={handleRefetchBuyingPolicies}
|
|
135
138
|
onDelete={handleRefetchBuyingPolicies}
|
|
139
|
+
budgetData={budgetData}
|
|
136
140
|
/>
|
|
137
141
|
}
|
|
138
142
|
/>
|
|
@@ -187,6 +191,7 @@ export const BuyingPoliciesLayout = ({
|
|
|
187
191
|
contractId={currentContract?.id ?? ""}
|
|
188
192
|
isOpen={isAddBuyingPolicyDrawerOpen}
|
|
189
193
|
onCreate={handleRefetchBuyingPolicies}
|
|
194
|
+
budgetData={budgetData}
|
|
190
195
|
{...addBuyingPolicyDrawerProps}
|
|
191
196
|
/>
|
|
192
197
|
)}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { buyingPolicyDefault } from "./buyingPolicyDefault";
|
|
2
|
-
export {
|
|
2
|
+
export {
|
|
3
|
+
BUDGET_CRITERIA,
|
|
4
|
+
orderFieldsCriteriaOptions,
|
|
5
|
+
} from "./orderFieldsCriteriaOptions";
|
|
3
6
|
export { spendingLimitsCriteriaOptions } from "./spendingLimitsCriteriaOptions";
|
|
4
7
|
export {
|
|
5
8
|
BUYING_POLICIES_WORKFLOW_TYPES,
|
|
@@ -1,39 +1,42 @@
|
|
|
1
|
+
export const BUDGET_CRITERIA =
|
|
2
|
+
'$exists(budgetData.unitAllocations.*[budgetId="${budget-id}"])';
|
|
3
|
+
|
|
1
4
|
export const orderFieldsCriteriaOptions = [
|
|
2
5
|
{
|
|
3
6
|
label: "If the order contains a budget",
|
|
4
|
-
criteria: "$
|
|
7
|
+
criteria: "$exists(budgetData.unitAllocations.*[])",
|
|
5
8
|
},
|
|
6
9
|
{
|
|
7
10
|
label: "If the order contains the budgets X, Y, Z",
|
|
8
|
-
criteria:
|
|
9
|
-
'budgetData.unitAllocations.budgetId = "Tech" or budgetData.unitAllocations.budgetId = "Growth"',
|
|
11
|
+
criteria: BUDGET_CRITERIA,
|
|
10
12
|
},
|
|
11
13
|
{
|
|
12
14
|
label: "If the order contains an exceeded budget",
|
|
13
|
-
criteria:
|
|
15
|
+
criteria:
|
|
16
|
+
"$exists(budgetData.unitAllocations.**.budgetBalance[remaining < 0])",
|
|
14
17
|
},
|
|
15
18
|
{
|
|
16
19
|
label: "If the order contains an expired budget Pending confirmation",
|
|
17
|
-
criteria: "budgetData.unitAllocations
|
|
20
|
+
criteria: "$exists(budgetData.unitAllocations.*[budgetEndDate < $now()])",
|
|
18
21
|
},
|
|
19
22
|
{
|
|
20
23
|
label: "If the order contains a PO number",
|
|
21
|
-
criteria: '$exists(customData.customFields.fields[name="
|
|
24
|
+
criteria: '$exists(customData.customFields.fields[name="PO Number"].value)',
|
|
22
25
|
},
|
|
23
26
|
{
|
|
24
27
|
label: "If the order contains the PO numbers X, Y, Z",
|
|
25
28
|
criteria:
|
|
26
|
-
'customData.customFields.fields[name="
|
|
29
|
+
'customData.customFields.fields[name="PO Number"].value = "1112223334444" or customData.customFields.fields[name="PO Number"].value = "1234567890"',
|
|
27
30
|
},
|
|
28
31
|
{
|
|
29
32
|
label: "If the order contains a cost center",
|
|
30
33
|
criteria:
|
|
31
|
-
'$exists(customData.customFields.fields[name="
|
|
34
|
+
'$exists(customData.customFields.fields[name="Cost Center"].value)',
|
|
32
35
|
},
|
|
33
36
|
{
|
|
34
37
|
label: "If the order contains the cost centers X, Y, Z",
|
|
35
38
|
criteria:
|
|
36
|
-
'customData.customFields.fields[name="
|
|
39
|
+
'customData.customFields.fields[name="Cost Center"].value = "CC1" or customData.customFields.fields[name="Cost Center"].value = "CC2"',
|
|
37
40
|
},
|
|
38
41
|
{
|
|
39
42
|
label: "If all order items are negotiated items",
|
package/src/pages/budgets.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { BudgetsLayout } from "../features/budgets/layouts";
|
|
2
2
|
import { listBudgetsService } from "../features/budgets/services";
|
|
3
|
-
import { BudgetListResponse } from "../features/budgets/types";
|
|
4
3
|
import { getContractDetailsService } from "../features/contracts/services";
|
|
5
4
|
import { getOrgUnitBasicDataService } from "../features/org-units/services";
|
|
6
5
|
import {
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
} from "../features/shared/utils";
|
|
17
16
|
import { getUserByIdService } from "../features/users/services";
|
|
18
17
|
|
|
18
|
+
import type { BudgetListResponse } from "../features/budgets/types";
|
|
19
19
|
import type { ContractData } from "../features/contracts/types";
|
|
20
20
|
import type { OrgUnitBasicData } from "../features/org-units/types";
|
|
21
21
|
import type { LoaderData } from "../features/shared/types";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { listBudgetsService } from "../features/budgets/services";
|
|
1
2
|
import { BuyingPoliciesLayout } from "../features/buying-policies/layouts";
|
|
2
3
|
import { getBuyingPoliciesService } from "../features/buying-policies/services";
|
|
3
4
|
import { getContractDetailsService } from "../features/contracts/services";
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
import { getUserByIdService } from "../features/users/services";
|
|
18
19
|
|
|
19
20
|
import type { GetAddressesServiceProps } from "../features/addresses/services";
|
|
21
|
+
import type { BudgetListResponse } from "../features/budgets/types";
|
|
20
22
|
import type { BuyingPolicy } from "../features/buying-policies/types";
|
|
21
23
|
import type { ContractData } from "../features/contracts/types";
|
|
22
24
|
import type { OrgUnitBasicData } from "../features/org-units/types";
|
|
@@ -34,6 +36,7 @@ export type BuyingPoliciesPageData = {
|
|
|
34
36
|
currentOrgUnit: OrgUnitBasicData | null;
|
|
35
37
|
currentUser: UserData | null;
|
|
36
38
|
};
|
|
39
|
+
budgetData?: BudgetListResponse;
|
|
37
40
|
hasError?: boolean;
|
|
38
41
|
error?: ErrorBoundaryProps;
|
|
39
42
|
};
|
|
@@ -54,7 +57,7 @@ const loaderFunction = async (
|
|
|
54
57
|
|
|
55
58
|
const { cookie, userId, ...clientContext } = await getClientContext(data);
|
|
56
59
|
|
|
57
|
-
const [currentOrgUnit, user, contract, buyingPoliciesResponse] =
|
|
60
|
+
const [currentOrgUnit, user, contract, budgetData, buyingPoliciesResponse] =
|
|
58
61
|
await Promise.all([
|
|
59
62
|
getOrgUnitBasicDataService({
|
|
60
63
|
id: orgUnitId,
|
|
@@ -66,6 +69,13 @@ const loaderFunction = async (
|
|
|
66
69
|
cookie,
|
|
67
70
|
unitId: orgUnitId,
|
|
68
71
|
}),
|
|
72
|
+
|
|
73
|
+
listBudgetsService({
|
|
74
|
+
customerId: clientContext.customerId,
|
|
75
|
+
cookie,
|
|
76
|
+
unitId: orgUnitId,
|
|
77
|
+
}),
|
|
78
|
+
|
|
69
79
|
getBuyingPoliciesService({
|
|
70
80
|
orgUnitId,
|
|
71
81
|
contractId,
|
|
@@ -94,6 +104,7 @@ const loaderFunction = async (
|
|
|
94
104
|
currentUser: user,
|
|
95
105
|
currentContract: contract,
|
|
96
106
|
},
|
|
107
|
+
budgetData: budgetData ?? { data: [], total: 0 },
|
|
97
108
|
};
|
|
98
109
|
};
|
|
99
110
|
|
|
@@ -110,6 +121,7 @@ const BuyingPoliciesPage = ({
|
|
|
110
121
|
total,
|
|
111
122
|
hasError,
|
|
112
123
|
error,
|
|
124
|
+
budgetData = { data: [], total: 0 },
|
|
113
125
|
}: BuyingPoliciesPageData) => (
|
|
114
126
|
<BuyerPortalProvider {...context}>
|
|
115
127
|
{hasError ? (
|
|
@@ -120,6 +132,7 @@ const BuyingPoliciesPage = ({
|
|
|
120
132
|
search={search}
|
|
121
133
|
page={page}
|
|
122
134
|
total={total}
|
|
135
|
+
budgetData={budgetData}
|
|
123
136
|
/>
|
|
124
137
|
)}
|
|
125
138
|
</BuyerPortalProvider>
|