@vtex/faststore-plugin-buyer-portal 1.3.26 → 1.3.28

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 (35) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/package.json +1 -1
  3. package/src/features/addresses/components/AddRecipientsDrawer/AddRecipientsDrawer.tsx +20 -2
  4. package/src/features/addresses/components/CreateAddressDrawer/CreateAddressDrawer.tsx +50 -4
  5. package/src/features/addresses/components/DeleteAddressDrawer/DeleteAddressDrawer.tsx +26 -2
  6. package/src/features/addresses/components/DeleteAddressLocationDrawer/DeleteAddressLocationDrawer.tsx +29 -3
  7. package/src/features/addresses/components/DeleteRecipientAddressDrawer/DeleteRecipientAddressDrawer.tsx +24 -2
  8. package/src/features/addresses/components/EditAddressDrawer/EditAddressDrawer.tsx +28 -2
  9. package/src/features/addresses/components/EditAddressLocationDrawer/EditAddressLocationDrawer.tsx +30 -4
  10. package/src/features/addresses/components/EditRecipientAddressDrawer/EditRecipientAddressDrawer.tsx +30 -2
  11. package/src/features/addresses/components/LocationsDrawer/LocationsDrawer.tsx +29 -2
  12. package/src/features/budgets/components/BudgetDeleteDrawer/BudgetDeleteDrawer.tsx +32 -4
  13. package/src/features/budgets/components/CreateBudgetDrawer/CreateBudgetDrawer.tsx +59 -4
  14. package/src/features/budgets/components/EditBudgetDrawer/EditBudgetDrawer.tsx +28 -1
  15. package/src/features/buying-policies/components/AddBuyingPolicyDrawer/AddBuyingPolicyDrawer.tsx +48 -5
  16. package/src/features/buying-policies/components/BasicBuyingPolicyDrawer/basic-buying-policy-drawer.scss +25 -18
  17. package/src/features/buying-policies/components/DeleteBuyingPolicyDrawer/DeleteBuyingPolicyDrawer.tsx +28 -2
  18. package/src/features/buying-policies/components/UpdateBuyingPolicyDrawer/UpdateBuyingPolicyDrawer.tsx +49 -5
  19. package/src/features/custom-fields/components/CreateCustomFieldValueDrawer/CreateCustomFieldValueDrawer.tsx +60 -5
  20. package/src/features/custom-fields/components/DeleteCustomFieldValueDrawer/DeleteCustomFieldValueDrawer.tsx +61 -5
  21. package/src/features/custom-fields/components/UpdateCustomFieldValueDrawer/UpdateCustomFieldValueDrawer.tsx +32 -3
  22. package/src/features/org-units/components/CreateOrgUnitDrawer/CreateOrgUnitDrawer.tsx +18 -0
  23. package/src/features/org-units/components/DeleteOrgUnitDrawer/DeleteOrgUnitDrawer.tsx +28 -0
  24. package/src/features/org-units/components/UpdateOrgUnitDrawer/UpdateOrgUnitDrawer.tsx +28 -0
  25. package/src/features/shared/hooks/analytics/types.ts +14 -0
  26. package/src/features/shared/hooks/analytics/useAnalytics.ts +249 -0
  27. package/src/features/shared/hooks/index.ts +1 -0
  28. package/src/features/shared/services/logger/analytics/analytics.ts +101 -0
  29. package/src/features/shared/services/logger/analytics/constants.ts +83 -0
  30. package/src/features/shared/services/logger/analytics/types.ts +108 -0
  31. package/src/features/shared/services/logger/index.ts +1 -0
  32. package/src/features/shared/utils/constants.ts +1 -1
  33. package/src/features/users/components/CreateUserDrawer/CreateUserDrawer.tsx +24 -0
  34. package/src/features/users/components/DeleteUserDrawer/DeleteUserDrawer.tsx +23 -2
  35. package/src/features/users/components/UpdateUserDrawer/UpdateUserDrawer.tsx +45 -4
@@ -1,4 +1,4 @@
1
- import { useCallback, useState } from "react";
1
+ import { useCallback, useEffect, useState } from "react";
2
2
 
3
3
  import { useRouter } from "next/router";
4
4
 
@@ -9,7 +9,8 @@ import {
9
9
  Icon,
10
10
  type BasicDrawerProps,
11
11
  } from "../../../shared/components";
12
- import { useBuyerPortal } from "../../../shared/hooks";
12
+ import { useAnalytics, useBuyerPortal } from "../../../shared/hooks";
13
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
13
14
  import { sortingOptionsAllocations } from "../../../shared/utils";
14
15
  import { parseAmount } from "../../../shared/utils/budgetAmountParse";
15
16
  import { getKeyByValue } from "../../../shared/utils/getKeyByValue";
@@ -65,6 +66,12 @@ export const CreateBudgetDrawer = ({
65
66
  } = useBuyerPortal();
66
67
  const router = useRouter();
67
68
  const { pushToast } = useUI();
69
+ const { trackEvent, trackEntityCreated, trackEntityCreateError } =
70
+ useAnalytics({
71
+ entityType: "budget",
72
+ defaultTimerName: "budget_creation",
73
+ shouldTrackDefaultTimer: true,
74
+ });
68
75
 
69
76
  const {
70
77
  filter,
@@ -86,6 +93,21 @@ export const CreateBudgetDrawer = ({
86
93
 
87
94
  type DrawerStep = "form" | "allocations" | "confirmation";
88
95
  const [step, setStep] = useState<DrawerStep>("form");
96
+ const [previousStep, setPreviousStep] = useState<DrawerStep | null>(null);
97
+
98
+ useEffect(() => {
99
+ if (previousStep !== null && previousStep !== step) {
100
+ trackEvent(ANALYTICS_EVENTS.BUDGET_STEP_TIMING, {
101
+ flow_name: "budget_creation",
102
+ from_step: previousStep,
103
+ to_step: step,
104
+ step_name: step,
105
+ org_unit_id: unitId,
106
+ contract_id: contractId,
107
+ });
108
+ }
109
+ setPreviousStep(step);
110
+ }, [step, previousStep, trackEvent, unitId, contractId]);
89
111
 
90
112
  const [budget, setBudget] = useState<BudgetInput>({
91
113
  name: "",
@@ -132,6 +154,16 @@ export const CreateBudgetDrawer = ({
132
154
  return;
133
155
  }
134
156
 
157
+ // Track successful budget creation
158
+ trackEntityCreated(ANALYTICS_EVENTS.BUDGET_CREATED, "budget", {
159
+ budget_name: budget.name,
160
+ amount: budget.amount,
161
+ has_notifications: Boolean(budget.notifications?.hasNotification),
162
+ allocations_count: selectedAllocations.length,
163
+ org_unit_id: unitId,
164
+ contract_id: contractId,
165
+ });
166
+
135
167
  pushToast({
136
168
  message: "Budget added successfully.",
137
169
  status: "INFO",
@@ -139,8 +171,14 @@ export const CreateBudgetDrawer = ({
139
171
  close();
140
172
  router.reload();
141
173
  },
142
- onError: (error: Error) =>
143
- pushToast({ message: error.message, status: "ERROR" }),
174
+ onError: (error: Error) => {
175
+ trackEntityCreateError(ANALYTICS_EVENTS.BUDGET_CREATE_ERROR, error, {
176
+ org_unit_id: unitId,
177
+ contract_id: contractId,
178
+ });
179
+
180
+ pushToast({ message: error.message, status: "ERROR" });
181
+ },
144
182
  },
145
183
  }
146
184
  );
@@ -149,6 +187,18 @@ export const CreateBudgetDrawer = ({
149
187
  useUpdateBudget({
150
188
  options: {
151
189
  onSuccess: () => {
190
+ // Track successful budget creation (with policy)
191
+ if (firstBudget) {
192
+ trackEntityCreated(ANALYTICS_EVENTS.BUDGET_CREATED, "budget", {
193
+ budget_name: budget.name,
194
+ amount: budget.amount,
195
+ has_notifications: Boolean(budget.notifications?.hasNotification),
196
+ has_policy: true,
197
+ org_unit_id: unitId,
198
+ contract_id: contractId,
199
+ });
200
+ }
201
+
152
202
  pushToast({
153
203
  message: "Budget added successfully.",
154
204
  status: "INFO",
@@ -167,6 +217,11 @@ export const CreateBudgetDrawer = ({
167
217
  router.reload();
168
218
  },
169
219
  onError: (error: Error) => {
220
+ trackEntityCreateError(ANALYTICS_EVENTS.BUDGET_CREATE_ERROR, error, {
221
+ org_unit_id: unitId,
222
+ contract_id: contractId,
223
+ });
224
+
170
225
  pushToast({
171
226
  message: error?.message || "An error occurred. Please try again.",
172
227
  status: "ERROR",
@@ -5,7 +5,8 @@ import { useRouter } from "next/router";
5
5
  import { useUI } from "@faststore/ui";
6
6
 
7
7
  import { BasicDrawer, type BasicDrawerProps } from "../../../shared/components";
8
- import { useBuyerPortal } from "../../../shared/hooks";
8
+ import { useAnalytics, useBuyerPortal } from "../../../shared/hooks";
9
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
9
10
  import { parseAmount } from "../../../shared/utils/budgetAmountParse";
10
11
  import { useUpdateBudget } from "../../hooks";
11
12
  import { BudgetAddForm } from "../BudgetAddForm/BudgetAddForm";
@@ -75,6 +76,12 @@ export const EditBudgetDrawer = ({
75
76
  const { clientContext } = useBuyerPortal();
76
77
  const { pushToast } = useUI();
77
78
  const router = useRouter();
79
+ const { trackEntityEdited, trackEntityEditError } = useAnalytics({
80
+ entityType: "budget",
81
+ entityId: budgetId,
82
+ defaultTimerName: "budget_edit",
83
+ shouldTrackDefaultTimer: true,
84
+ });
78
85
 
79
86
  const [budget, setBudget] = useState<BudgetInput>(() =>
80
87
  getInitialBudget(initialBudget)
@@ -86,12 +93,32 @@ export const EditBudgetDrawer = ({
86
93
  const { mutate: updateBudget, isLoading } = useUpdateBudget({
87
94
  options: {
88
95
  onSuccess: () => {
96
+ trackEntityEdited(ANALYTICS_EVENTS.BUDGET_EDITED, {
97
+ budget_id: budgetId,
98
+ budget_name: budget.name,
99
+ amount: budget.amount,
100
+ has_notifications: Boolean(budget.notifications?.hasNotification),
101
+ org_unit_id: orgUnitId,
102
+ contract_id: contractId,
103
+ });
104
+
89
105
  pushToast({ message: "Budget updated successfully.", status: "INFO" });
90
106
  onSubmit?.(budget);
91
107
  close();
92
108
  router.reload();
93
109
  },
94
110
  onError: (error: Error) => {
111
+ trackEntityEditError(
112
+ ANALYTICS_EVENTS.BUDGET_EDIT_ERROR,
113
+ "budget",
114
+ budgetId,
115
+ error,
116
+ {
117
+ org_unit_id: orgUnitId,
118
+ contract_id: contractId,
119
+ }
120
+ );
121
+
95
122
  pushToast({
96
123
  message: error?.message || "An error occurred. Please try again.",
97
124
  status: "ERROR",
@@ -1,13 +1,21 @@
1
+ import { useRef } from "react";
2
+
1
3
  import { useRouter } from "next/router";
2
4
 
3
5
  import { useUI } from "@faststore/ui";
4
6
 
5
7
  import { BasicBuyingPolicyDrawer, type BasicBuyingPolicyDrawerProps } from "..";
8
+ import { useAnalytics } from "../../../shared/hooks";
9
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
6
10
  import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
7
11
  import { useAddBuyingPolicy } from "../../hooks";
8
- import { buyingPolicyDefault } from "../../utils";
12
+ import {
13
+ buyingPolicyDefault,
14
+ BUYING_POLICIES_WORKFLOW_TYPES,
15
+ } from "../../utils";
9
16
 
10
17
  import type { BuyingPolicy } from "../../types";
18
+ import type { BuyingPolicyForm } from "../BasicBuyingPolicyDrawer/BasicBuyingPolicyDrawer";
11
19
 
12
20
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
13
21
  const { id, ...defaultBuyingPolicyValues } = buyingPolicyDefault;
@@ -30,8 +38,33 @@ export const AddBuyingPolicyDrawer = ({
30
38
  }: AddBuyingPolicyDrawerProps) => {
31
39
  const { pushToast } = useUI();
32
40
  const router = useRouter();
41
+ const { trackEntityCreated, trackEntityCreateError } = useAnalytics({
42
+ entityType: "buying_policy",
43
+ defaultTimerName: "buying_policy_creation",
44
+ shouldTrackDefaultTimer: true,
45
+ });
46
+ const formDataRef = useRef<BuyingPolicyForm | null>(null);
33
47
 
34
48
  const handleAddBuyingPolicySuccess = (data: BuyingPolicy) => {
49
+ const formData = formDataRef.current;
50
+
51
+ trackEntityCreated(
52
+ ANALYTICS_EVENTS.BUYING_POLICY_CREATED,
53
+ "buying_policy",
54
+ {
55
+ policy_name: formData?.name,
56
+ policy_description: formData?.description,
57
+ policy_criteria: formData?.criteria,
58
+ policy_type: formData?.action.type,
59
+ ...(formData?.action.type ===
60
+ BUYING_POLICIES_WORKFLOW_TYPES.SEQUENTIAL_WORKFLOW && {
61
+ approval_levels_count: formData?.action.levels?.length,
62
+ }),
63
+ org_unit_id: orgUnitId,
64
+ contract_id: contractId,
65
+ }
66
+ );
67
+
35
68
  pushToast({
36
69
  message: "Buying policy added successfully",
37
70
  status: "INFO",
@@ -60,7 +93,16 @@ export const AddBuyingPolicyDrawer = ({
60
93
 
61
94
  const { addBuyingPolicy, isAddBuyingPolicyLoading } = useAddBuyingPolicy({
62
95
  onSuccess: handleAddBuyingPolicySuccess,
63
- onError: () => {
96
+ onError: (error) => {
97
+ trackEntityCreateError(
98
+ ANALYTICS_EVENTS.BUYING_POLICY_CREATE_ERROR,
99
+ error,
100
+ {
101
+ org_unit_id: orgUnitId,
102
+ contract_id: contractId,
103
+ }
104
+ );
105
+
64
106
  pushToast({
65
107
  message: "An error occurred. Please try again.",
66
108
  title: "Error",
@@ -73,15 +115,16 @@ export const AddBuyingPolicyDrawer = ({
73
115
  <BasicBuyingPolicyDrawer
74
116
  close={close}
75
117
  isLoading={isAddBuyingPolicyLoading}
76
- onConfirm={(form) =>
118
+ onConfirm={(form) => {
119
+ formDataRef.current = form;
77
120
  addBuyingPolicy({
78
121
  buyingPolicy: {
79
122
  ...form,
80
123
  },
81
124
  orgUnitId,
82
125
  contractId,
83
- })
84
- }
126
+ });
127
+ }}
85
128
  orgUnitId={orgUnitId}
86
129
  contractId={contractId}
87
130
  initialValues={defaultBuyingPolicyValues}
@@ -1,14 +1,14 @@
1
- @import "../../../shared/components/BasicDrawer/basic-drawer.scss";
2
- @import "../../../shared/components/OrgUnitInputSearch/org-unit-input-search.scss";
3
- @import "../../../shared/components/CustomDropdown/custom-dropdown.scss";
4
- @import "../BudgetCriteriaSelector/budget-criteria-selector.scss";
1
+ @import '../../../shared/components/BasicDrawer/basic-drawer.scss';
2
+ @import '../../../shared/components/OrgUnitInputSearch/org-unit-input-search.scss';
3
+ @import '../../../shared/components/CustomDropdown/custom-dropdown.scss';
4
+ @import '../BudgetCriteriaSelector/budget-criteria-selector.scss';
5
5
 
6
6
  [data-fs-bp-basic-buying-policy-drawer] {
7
- @import "../../../shared/components/InputText/input-text.scss";
8
- @import "../../../shared/components/ErrorMessage/error-message.scss";
9
- @import "../../../shared/components/LevelDivider/level-divider.scss";
10
- @import "@faststore/ui/src/components/molecules/Tooltip/styles.scss";
11
- @import "@faststore/ui/src/components/molecules/Dropdown/styles.scss";
7
+ @import '../../../shared/components/InputText/input-text.scss';
8
+ @import '../../../shared/components/ErrorMessage/error-message.scss';
9
+ @import '../../../shared/components/LevelDivider/level-divider.scss';
10
+ @import '@faststore/ui/src/components/molecules/Tooltip/styles.scss';
11
+ @import '@faststore/ui/src/components/molecules/Dropdown/styles.scss';
12
12
 
13
13
  [data-fs-bp-autocomplete-dropdown] {
14
14
  [data-fs-bp-input-text-input] {
@@ -65,7 +65,7 @@
65
65
  gap: var(--fs-spacing-2);
66
66
  margin-top: calc(var(--fs-spacing-0) + var(--fs-spacing-3));
67
67
 
68
- @include media("<=tablet") {
68
+ @include media('<=tablet') {
69
69
  justify-content: center;
70
70
  }
71
71
  }
@@ -94,15 +94,16 @@
94
94
  height: 100%;
95
95
  margin: 0;
96
96
  background-color: #f5f5f5;
97
- font-family: "Roboto", monospace;
97
+ font-family: 'Roboto', monospace;
98
98
  font-size: var(--fs-text-size-1);
99
99
  border: var(--fs-border-width) solid #d6d6d6;
100
- padding: calc(var(--fs-spacing-3) + var(--fs-spacing-0))
101
- var(--fs-spacing-3);
100
+ padding: calc(var(--fs-spacing-3) + var(--fs-spacing-0)) var(--fs-spacing-3);
102
101
  border-radius: calc(var(--fs-border-radius) * 2);
103
102
  text-wrap: nowrap;
104
103
  text-overflow: ellipsis;
105
104
  white-space: nowrap;
105
+ overflow: hidden;
106
+ max-width: 840px;
106
107
  cursor: pointer;
107
108
 
108
109
  [data-fs-bp-label] {
@@ -119,18 +120,17 @@
119
120
  }
120
121
  }
121
122
 
122
-
123
123
  [data-fs-bp-input-text-criteria] {
124
124
  color: #5c5c5c;
125
125
 
126
126
  label {
127
- font-family: "Roboto", monospace;
127
+ font-family: 'Roboto', monospace;
128
128
  font-size: var(--fs-text-size-1);
129
129
  }
130
130
 
131
131
  &[data-fs-bp-input-text-code] {
132
132
  background-color: #f5f5f5;
133
- font-family: "Roboto", monospace;
133
+ font-family: 'Roboto', monospace;
134
134
  font-size: var(--fs-text-size-1);
135
135
  }
136
136
 
@@ -142,8 +142,7 @@
142
142
  }
143
143
 
144
144
  [data-fs-bp-buying-policy-add-level-button] {
145
- padding: var(--fs-spacing-2)
146
- calc(var(--fs-spacing-0) + var(--fs-spacing-3));
145
+ padding: var(--fs-spacing-2) calc(var(--fs-spacing-0) + var(--fs-spacing-3));
147
146
  margin: calc(var(--fs-spacing-0) + var(--fs-spacing-3)) 0;
148
147
  border-radius: var(--fs-border-radius-pill);
149
148
  border: var(--fs-border-width) solid #d6d6d6;
@@ -160,4 +159,12 @@
160
159
  [data-fs-bp-basic-buying-policy-drawer-skeleton] {
161
160
  margin-bottom: var(--fs-spacing-3);
162
161
  }
162
+
163
+ @include media('<=tablet') {
164
+ [data-fs-bp-basic-drawer-body],
165
+ [data-fs-bp-basic-drawer-heading],
166
+ [data-fs-bp-basic-drawer-footer] {
167
+ max-width: 430px;
168
+ }
169
+ }
163
170
  }
@@ -7,7 +7,8 @@ import {
7
7
  InputText,
8
8
  type BasicDrawerProps,
9
9
  } from "../../../shared/components";
10
- import { useBuyerPortal } from "../../../shared/hooks";
10
+ import { useAnalytics, useBuyerPortal } from "../../../shared/hooks";
11
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
11
12
  import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
12
13
  import { useRemoveBuyingPolicy } from "../../hooks";
13
14
 
@@ -32,8 +33,22 @@ export const DeleteBuyingPolicyDrawer = ({
32
33
  const { currentContract, currentOrgUnit } = useBuyerPortal();
33
34
 
34
35
  const { pushToast } = useUI();
36
+ const { trackEvent } = useAnalytics({
37
+ entityType: "buying_policy",
38
+ entityId: buyingPolicy.id,
39
+ defaultTimerName: "buying_policy_delete",
40
+ shouldTrackDefaultTimer: true,
41
+ });
35
42
 
36
43
  const handleRemoveSuccess = () => {
44
+ trackEvent(ANALYTICS_EVENTS.BUYING_POLICY_DELETED, {
45
+ policy_id: buyingPolicy.id,
46
+ policy_name: buyingPolicy.name,
47
+ org_unit_id: currentOrgUnit?.id,
48
+ contract_id: currentContract?.id,
49
+ entity_type: "buying_policy",
50
+ });
51
+
37
52
  pushToast({
38
53
  message: "Buying policy deleted successfully",
39
54
  status: "INFO",
@@ -57,7 +72,18 @@ export const DeleteBuyingPolicyDrawer = ({
57
72
  const { removeBuyingPolicy, isRemoveBuyingPolicyLoading } =
58
73
  useRemoveBuyingPolicy({
59
74
  onSuccess: handleRemoveSuccess,
60
- onError: () => {
75
+ onError: (error) => {
76
+ const errorMessage = typeof error === "string" ? error : error.message;
77
+
78
+ trackEvent(ANALYTICS_EVENTS.BUYING_POLICY_DELETE_ERROR, {
79
+ entity_type: "buying_policy",
80
+ entity_id: buyingPolicy.id,
81
+ policy_name: buyingPolicy.name,
82
+ org_unit_id: currentOrgUnit?.id,
83
+ contract_id: currentContract?.id,
84
+ error_message: errorMessage,
85
+ });
86
+
61
87
  pushToast({
62
88
  message: "An error occurred. Please try again.",
63
89
  title: "Error",
@@ -1,11 +1,20 @@
1
+ import { useRef } from "react";
2
+
1
3
  import { useRouter } from "next/router";
2
4
 
3
5
  import { useUI } from "@faststore/ui";
4
6
 
5
7
  import { BasicBuyingPolicyDrawer, type BasicBuyingPolicyDrawerProps } from "..";
8
+ import { useAnalytics } from "../../../shared/hooks";
9
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
6
10
  import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
7
11
  import { useGetBuyingPolicy, useUpdateBuyingPolicy } from "../../hooks";
8
- import { buyingPolicyDefault } from "../../utils";
12
+ import {
13
+ buyingPolicyDefault,
14
+ BUYING_POLICIES_WORKFLOW_TYPES,
15
+ } from "../../utils";
16
+
17
+ import type { BuyingPolicyForm } from "../BasicBuyingPolicyDrawer/BasicBuyingPolicyDrawer";
9
18
 
10
19
  export type UpdateBuyingPolicyDrawerProps = Omit<
11
20
  BasicBuyingPolicyDrawerProps,
@@ -33,8 +42,31 @@ export const UpdateBuyingPolicyDrawer = ({
33
42
 
34
43
  const { pushToast } = useUI();
35
44
  const router = useRouter();
45
+ const { trackEntityEdited, trackEntityEditError } = useAnalytics({
46
+ entityType: "buying_policy",
47
+ entityId: buyingPolicyId,
48
+ defaultTimerName: "buying_policy_edit",
49
+ shouldTrackDefaultTimer: true,
50
+ });
51
+ const formDataRef = useRef<BuyingPolicyForm | null>(null);
36
52
 
37
53
  const handleUpdateBuyingPolicySuccess = () => {
54
+ const formData = formDataRef.current;
55
+
56
+ trackEntityEdited(ANALYTICS_EVENTS.BUYING_POLICY_EDITED, {
57
+ policy_id: buyingPolicyId,
58
+ policy_name: formData?.name,
59
+ policy_type: formData?.action.type,
60
+ policy_description: formData?.description,
61
+ policy_criteria: formData?.criteria,
62
+ ...(formData?.action.type ===
63
+ BUYING_POLICIES_WORKFLOW_TYPES.SEQUENTIAL_WORKFLOW && {
64
+ approval_levels_count: formData?.action.levels?.length,
65
+ }),
66
+ org_unit_id: orgUnitId,
67
+ contract_id: contractId,
68
+ });
69
+
38
70
  pushToast({
39
71
  message: "Buying policy updated successfully",
40
72
  status: "INFO",
@@ -64,7 +96,18 @@ export const UpdateBuyingPolicyDrawer = ({
64
96
  const { updateBuyingPolicy, isUpdateBuyingPolicyLoading } =
65
97
  useUpdateBuyingPolicy({
66
98
  onSuccess: handleUpdateBuyingPolicySuccess,
67
- onError: () => {
99
+ onError: (error) => {
100
+ trackEntityEditError(
101
+ ANALYTICS_EVENTS.BUYING_POLICY_EDIT_ERROR,
102
+ "buying_policy",
103
+ buyingPolicyId,
104
+ error,
105
+ {
106
+ org_unit_id: orgUnitId,
107
+ contract_id: contractId,
108
+ }
109
+ );
110
+
68
111
  pushToast({
69
112
  message: "An error occurred. Please try again.",
70
113
  title: "Error",
@@ -80,7 +123,8 @@ export const UpdateBuyingPolicyDrawer = ({
80
123
  close={close}
81
124
  isLoading={isUpdateBuyingPolicyLoading}
82
125
  isDrawerLoading={isBuyingPolicyLoading}
83
- onConfirm={(form) =>
126
+ onConfirm={(form) => {
127
+ formDataRef.current = form;
84
128
  updateBuyingPolicy({
85
129
  buyingPolicyId,
86
130
  buyingPolicy: {
@@ -88,8 +132,8 @@ export const UpdateBuyingPolicyDrawer = ({
88
132
  },
89
133
  orgUnitId,
90
134
  contractId,
91
- })
92
- }
135
+ });
136
+ }}
93
137
  initialValues={{ ...buyingPolicyDefault, ...(buyingPolicy ?? {}) }}
94
138
  orgUnitId={orgUnitId}
95
139
  contractId={contractId}
@@ -4,12 +4,13 @@ import { useUI } from "@faststore/ui";
4
4
 
5
5
  import { BasicDrawerProps } from "../../../shared/components";
6
6
  import { CreateEntityDrawer } from "../../../shared/components/CustomField/create-custom-field/CreateCustomFieldDrawer";
7
- import { useBuyerPortal } from "../../../shared/hooks";
7
+ import { useAnalytics, useBuyerPortal } from "../../../shared/hooks";
8
8
  import {
9
9
  useCreateCustomFieldValue,
10
10
  useCustomFieldValues,
11
11
  useAddCustomFieldValueToUnitScope,
12
12
  } from "../../../shared/hooks/custom-field";
13
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
13
14
 
14
15
  export type CustomFieldType = "Cost Center" | "PO Number" | "Release";
15
16
 
@@ -35,8 +36,14 @@ export function CreateCustomFieldValueDrawer({
35
36
  const {
36
37
  clientContext: { cookie },
37
38
  } = useBuyerPortal();
39
+ const { trackEntityCreated, trackEntityCreateError } = useAnalytics({
40
+ entityType: "custom_field",
41
+ defaultTimerName: "custom_field_creation",
42
+ shouldTrackDefaultTimer: isOpen,
43
+ });
38
44
 
39
45
  const [search, setSearch] = useState<string | null>(null);
46
+ const [createdFieldName, setCreatedFieldName] = useState<string>("");
40
47
 
41
48
  const {
42
49
  mutate: createCustomFieldValueToUnitScope,
@@ -44,6 +51,18 @@ export function CreateCustomFieldValueDrawer({
44
51
  } = useAddCustomFieldValueToUnitScope({
45
52
  options: {
46
53
  onSuccess: () => {
54
+ trackEntityCreated(
55
+ ANALYTICS_EVENTS.CUSTOM_FIELD_CREATED,
56
+ "custom_field",
57
+ {
58
+ custom_field_type: customField,
59
+ custom_field_name: createdFieldName,
60
+ is_shared: true,
61
+ org_unit_id: unitId,
62
+ contract_id: contractId,
63
+ }
64
+ );
65
+
47
66
  pushToast({
48
67
  message: `${customField} added successfully`,
49
68
  status: "INFO",
@@ -52,11 +71,22 @@ export function CreateCustomFieldValueDrawer({
52
71
  refetch();
53
72
  close();
54
73
  },
55
- onError: (error) =>
74
+ onError: (error) => {
75
+ trackEntityCreateError(
76
+ ANALYTICS_EVENTS.CUSTOM_FIELD_CREATE_ERROR,
77
+ error,
78
+ {
79
+ custom_field_type: customField,
80
+ org_unit_id: unitId,
81
+ contract_id: contractId,
82
+ }
83
+ );
84
+
56
85
  pushToast({
57
86
  message: error.message,
58
87
  status: "ERROR",
59
- }),
88
+ });
89
+ },
60
90
  },
61
91
  });
62
92
 
@@ -66,6 +96,18 @@ export function CreateCustomFieldValueDrawer({
66
96
  } = useCreateCustomFieldValue({
67
97
  options: {
68
98
  onSuccess: () => {
99
+ trackEntityCreated(
100
+ ANALYTICS_EVENTS.CUSTOM_FIELD_CREATED,
101
+ "custom_field",
102
+ {
103
+ custom_field_type: customField,
104
+ custom_field_name: createdFieldName,
105
+ is_shared: false,
106
+ org_unit_id: unitId,
107
+ contract_id: contractId,
108
+ }
109
+ );
110
+
69
111
  pushToast({
70
112
  message: `${customField} added successfully`,
71
113
  status: "INFO",
@@ -74,11 +116,22 @@ export function CreateCustomFieldValueDrawer({
74
116
  refetch();
75
117
  close();
76
118
  },
77
- onError: (error) =>
119
+ onError: (error) => {
120
+ trackEntityCreateError(
121
+ ANALYTICS_EVENTS.CUSTOM_FIELD_CREATE_ERROR,
122
+ error,
123
+ {
124
+ custom_field_type: customField,
125
+ org_unit_id: unitId,
126
+ contract_id: contractId,
127
+ }
128
+ );
129
+
78
130
  pushToast({
79
131
  message: error.message,
80
132
  status: "ERROR",
81
- }),
133
+ });
134
+ },
82
135
  },
83
136
  });
84
137
 
@@ -104,6 +157,8 @@ export function CreateCustomFieldValueDrawer({
104
157
  });
105
158
 
106
159
  function create(name: string, customFieldValueId?: string) {
160
+ setCreatedFieldName(name);
161
+
107
162
  if (customFieldValueId) {
108
163
  return createCustomFieldValueToUnitScope({
109
164
  data: {