@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.
- package/CHANGELOG.md +15 -1
- package/package.json +1 -1
- package/src/features/addresses/components/AddRecipientsDrawer/AddRecipientsDrawer.tsx +20 -2
- package/src/features/addresses/components/CreateAddressDrawer/CreateAddressDrawer.tsx +50 -4
- package/src/features/addresses/components/DeleteAddressDrawer/DeleteAddressDrawer.tsx +26 -2
- package/src/features/addresses/components/DeleteAddressLocationDrawer/DeleteAddressLocationDrawer.tsx +29 -3
- package/src/features/addresses/components/DeleteRecipientAddressDrawer/DeleteRecipientAddressDrawer.tsx +24 -2
- package/src/features/addresses/components/EditAddressDrawer/EditAddressDrawer.tsx +28 -2
- package/src/features/addresses/components/EditAddressLocationDrawer/EditAddressLocationDrawer.tsx +30 -4
- package/src/features/addresses/components/EditRecipientAddressDrawer/EditRecipientAddressDrawer.tsx +30 -2
- package/src/features/addresses/components/LocationsDrawer/LocationsDrawer.tsx +29 -2
- package/src/features/budgets/components/BudgetDeleteDrawer/BudgetDeleteDrawer.tsx +32 -4
- package/src/features/budgets/components/CreateBudgetDrawer/CreateBudgetDrawer.tsx +59 -4
- package/src/features/budgets/components/EditBudgetDrawer/EditBudgetDrawer.tsx +28 -1
- package/src/features/buying-policies/components/AddBuyingPolicyDrawer/AddBuyingPolicyDrawer.tsx +48 -5
- package/src/features/buying-policies/components/BasicBuyingPolicyDrawer/basic-buying-policy-drawer.scss +25 -18
- package/src/features/buying-policies/components/DeleteBuyingPolicyDrawer/DeleteBuyingPolicyDrawer.tsx +28 -2
- package/src/features/buying-policies/components/UpdateBuyingPolicyDrawer/UpdateBuyingPolicyDrawer.tsx +49 -5
- package/src/features/custom-fields/components/CreateCustomFieldValueDrawer/CreateCustomFieldValueDrawer.tsx +60 -5
- package/src/features/custom-fields/components/DeleteCustomFieldValueDrawer/DeleteCustomFieldValueDrawer.tsx +61 -5
- package/src/features/custom-fields/components/UpdateCustomFieldValueDrawer/UpdateCustomFieldValueDrawer.tsx +32 -3
- package/src/features/org-units/components/CreateOrgUnitDrawer/CreateOrgUnitDrawer.tsx +18 -0
- package/src/features/org-units/components/DeleteOrgUnitDrawer/DeleteOrgUnitDrawer.tsx +28 -0
- package/src/features/org-units/components/UpdateOrgUnitDrawer/UpdateOrgUnitDrawer.tsx +28 -0
- package/src/features/shared/hooks/analytics/types.ts +14 -0
- package/src/features/shared/hooks/analytics/useAnalytics.ts +249 -0
- package/src/features/shared/hooks/index.ts +1 -0
- package/src/features/shared/services/logger/analytics/analytics.ts +101 -0
- package/src/features/shared/services/logger/analytics/constants.ts +83 -0
- package/src/features/shared/services/logger/analytics/types.ts +108 -0
- package/src/features/shared/services/logger/index.ts +1 -0
- package/src/features/shared/utils/constants.ts +1 -1
- package/src/features/users/components/CreateUserDrawer/CreateUserDrawer.tsx +24 -0
- package/src/features/users/components/DeleteUserDrawer/DeleteUserDrawer.tsx +23 -2
- package/src/features/users/components/UpdateUserDrawer/UpdateUserDrawer.tsx +45 -4
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import storeConfig from "discovery.config";
|
|
2
|
+
|
|
3
|
+
import { isDevelopment } from "../../../utils/environment";
|
|
4
|
+
|
|
5
|
+
import type { DataIngestionApiFields, DataIngestionApiParams } from "./types";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* VTEX Data Ingestion API for Analytics
|
|
9
|
+
* https://internal-docs.vtex.com/Analytics/VTEX-Data-Platform/data-ingestion-API/
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const APP_NAME = "b2b-organization-account";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Send event to VTEX Data Ingestion API
|
|
16
|
+
*/
|
|
17
|
+
export async function dataIngestionApi(
|
|
18
|
+
fields: DataIngestionApiParams
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
const isDevEnvironment = fields.workspace !== "master";
|
|
21
|
+
|
|
22
|
+
delete fields.workspace;
|
|
23
|
+
|
|
24
|
+
const isInsideVtexNetwork = false;
|
|
25
|
+
const endpoint = isInsideVtexNetwork
|
|
26
|
+
? "https://analytics.vtex.com/api/analytics/schemaless-events"
|
|
27
|
+
: "https://rc.vtex.com/api/analytics/schemaless-events";
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await fetch(endpoint, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
},
|
|
35
|
+
body: JSON.stringify({
|
|
36
|
+
name: APP_NAME,
|
|
37
|
+
...fields,
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (isDevEnvironment) {
|
|
42
|
+
console.warn(
|
|
43
|
+
`Error when sending analytics data for "${fields.event_name}".`,
|
|
44
|
+
error
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get common analytics context
|
|
52
|
+
*/
|
|
53
|
+
function getCommonAnalyticsContext() {
|
|
54
|
+
const account = storeConfig?.api?.storeId || "unknown";
|
|
55
|
+
const locale =
|
|
56
|
+
typeof window !== "undefined"
|
|
57
|
+
? window.navigator.language || "undefined"
|
|
58
|
+
: "undefined";
|
|
59
|
+
const production = !isDevelopment();
|
|
60
|
+
|
|
61
|
+
// Detect device type
|
|
62
|
+
let device = "desktop";
|
|
63
|
+
if (typeof window !== "undefined") {
|
|
64
|
+
const userAgent = window.navigator.userAgent.toLowerCase();
|
|
65
|
+
device =
|
|
66
|
+
/mobile|android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(
|
|
67
|
+
userAgent
|
|
68
|
+
)
|
|
69
|
+
? "mobile"
|
|
70
|
+
: "desktop";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
account,
|
|
75
|
+
locale,
|
|
76
|
+
production,
|
|
77
|
+
device,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Hook to send analytics events
|
|
83
|
+
* This is a simple version that will be extended by useAnalytics hook
|
|
84
|
+
*/
|
|
85
|
+
export function useDataIngestionApi() {
|
|
86
|
+
const commonFields = getCommonAnalyticsContext();
|
|
87
|
+
|
|
88
|
+
const sendEvent = (fields: DataIngestionApiFields) => {
|
|
89
|
+
// Get workspace from environment
|
|
90
|
+
const workspace = isDevelopment() ? "dev" : "master";
|
|
91
|
+
|
|
92
|
+
dataIngestionApi({
|
|
93
|
+
...commonFields,
|
|
94
|
+
event_category: fields.event_category || "click",
|
|
95
|
+
...fields,
|
|
96
|
+
workspace,
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return { sendEvent };
|
|
101
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event name constants for consistent tracking
|
|
3
|
+
*/
|
|
4
|
+
export const ANALYTICS_EVENTS = {
|
|
5
|
+
// Budget events
|
|
6
|
+
BUDGET_CREATED: "budget_created",
|
|
7
|
+
BUDGET_EDITED: "budget_edited",
|
|
8
|
+
BUDGET_DELETED: "budget_deleted",
|
|
9
|
+
BUDGET_CREATE_ERROR: "budget_create_error",
|
|
10
|
+
BUDGET_EDIT_ERROR: "budget_edit_error",
|
|
11
|
+
BUDGET_DELETE_ERROR: "budget_delete_error",
|
|
12
|
+
BUDGET_STEP_TIMING: "budget_step_timing",
|
|
13
|
+
BUDGET_FLOW_COMPLETED: "budget_flow_completed",
|
|
14
|
+
|
|
15
|
+
// Buying Policy events
|
|
16
|
+
BUYING_POLICY_CREATED: "buying_policy_created",
|
|
17
|
+
BUYING_POLICY_EDITED: "buying_policy_edited",
|
|
18
|
+
BUYING_POLICY_DELETED: "buying_policy_deleted",
|
|
19
|
+
BUYING_POLICY_CREATE_ERROR: "buying_policy_create_error",
|
|
20
|
+
BUYING_POLICY_EDIT_ERROR: "buying_policy_edit_error",
|
|
21
|
+
BUYING_POLICY_DELETE_ERROR: "buying_policy_delete_error",
|
|
22
|
+
BUYING_POLICY_STEP_TIMING: "buying_policy_step_timing",
|
|
23
|
+
BUYING_POLICY_FLOW_COMPLETED: "buying_policy_flow_completed",
|
|
24
|
+
|
|
25
|
+
// Custom Field events
|
|
26
|
+
CUSTOM_FIELD_CREATED: "custom_field_created",
|
|
27
|
+
CUSTOM_FIELD_EDITED: "custom_field_edited",
|
|
28
|
+
CUSTOM_FIELD_DELETED: "custom_field_deleted",
|
|
29
|
+
CUSTOM_FIELD_CREATE_ERROR: "custom_field_create_error",
|
|
30
|
+
CUSTOM_FIELD_EDIT_ERROR: "custom_field_edit_error",
|
|
31
|
+
CUSTOM_FIELD_DELETE_ERROR: "custom_field_delete_error",
|
|
32
|
+
|
|
33
|
+
// Address events
|
|
34
|
+
ADDRESS_CREATED: "address_created",
|
|
35
|
+
ADDRESS_EDITED: "address_edited",
|
|
36
|
+
ADDRESS_DELETED: "address_deleted",
|
|
37
|
+
ADDRESS_CREATE_ERROR: "address_create_error",
|
|
38
|
+
ADDRESS_EDIT_ERROR: "address_edit_error",
|
|
39
|
+
ADDRESS_DELETE_ERROR: "address_delete_error",
|
|
40
|
+
ADDRESS_STEP_TIMING: "address_step_timing",
|
|
41
|
+
|
|
42
|
+
// Location events
|
|
43
|
+
LOCATION_CREATED: "location_created",
|
|
44
|
+
LOCATION_EDITED: "location_edited",
|
|
45
|
+
LOCATION_DELETED: "location_deleted",
|
|
46
|
+
LOCATION_CREATE_ERROR: "location_create_error",
|
|
47
|
+
LOCATION_EDIT_ERROR: "location_edit_error",
|
|
48
|
+
LOCATION_DELETE_ERROR: "location_delete_error",
|
|
49
|
+
|
|
50
|
+
// Recipient events
|
|
51
|
+
RECIPIENT_CREATED: "recipient_created",
|
|
52
|
+
RECIPIENT_EDITED: "recipient_edited",
|
|
53
|
+
RECIPIENT_DELETED: "recipient_deleted",
|
|
54
|
+
RECIPIENT_CREATE_ERROR: "recipient_create_error",
|
|
55
|
+
RECIPIENT_EDIT_ERROR: "recipient_edit_error",
|
|
56
|
+
RECIPIENT_DELETE_ERROR: "recipient_delete_error",
|
|
57
|
+
|
|
58
|
+
// User events
|
|
59
|
+
USER_CREATED: "user_created",
|
|
60
|
+
USER_EDITED: "user_edited",
|
|
61
|
+
USER_DELETED: "user_deleted",
|
|
62
|
+
USER_CREATE_ERROR: "user_create_error",
|
|
63
|
+
USER_EDIT_ERROR: "user_edit_error",
|
|
64
|
+
USER_DELETE_ERROR: "user_delete_error",
|
|
65
|
+
|
|
66
|
+
// Organization Unit events
|
|
67
|
+
ORG_UNIT_CREATED: "org_unit_created",
|
|
68
|
+
ORG_UNIT_EDITED: "org_unit_edited",
|
|
69
|
+
ORG_UNIT_DELETED: "org_unit_deleted",
|
|
70
|
+
ORG_UNIT_CREATE_ERROR: "org_unit_create_error",
|
|
71
|
+
ORG_UNIT_EDIT_ERROR: "org_unit_edit_error",
|
|
72
|
+
ORG_UNIT_DELETE_ERROR: "org_unit_delete_error",
|
|
73
|
+
|
|
74
|
+
// Interaction events
|
|
75
|
+
TOOLTIP_INTERACTION: "tooltip_interaction",
|
|
76
|
+
STEP_CHANGE: "step_change",
|
|
77
|
+
|
|
78
|
+
// Generic events
|
|
79
|
+
ENTITY_CREATED: "entity_created",
|
|
80
|
+
ENTITY_EDITED: "entity_edited",
|
|
81
|
+
ENTITY_ERROR: "entity_error",
|
|
82
|
+
TIMER_COMPLETED: "timer_completed",
|
|
83
|
+
} as const;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics event categories
|
|
3
|
+
*/
|
|
4
|
+
export type EventCategory =
|
|
5
|
+
| "click"
|
|
6
|
+
| "view"
|
|
7
|
+
| "apiAnswer"
|
|
8
|
+
| "webVitals"
|
|
9
|
+
| "creation"
|
|
10
|
+
| "edition"
|
|
11
|
+
| "error"
|
|
12
|
+
| "timing"
|
|
13
|
+
| "interaction";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Analytics event fields
|
|
17
|
+
*/
|
|
18
|
+
export interface DataIngestionApiFields {
|
|
19
|
+
/** The type of the event */
|
|
20
|
+
event_category?: EventCategory;
|
|
21
|
+
/** The name of the event. Has to be unique through the app. */
|
|
22
|
+
event_name: string;
|
|
23
|
+
/** Additional informations */
|
|
24
|
+
/** Time spent on page */
|
|
25
|
+
time_on_page?: number;
|
|
26
|
+
/** Additional metadata about the event */
|
|
27
|
+
metadata?: Record<string, unknown>;
|
|
28
|
+
/** Entity type (e.g., 'budget', 'buying_policy') */
|
|
29
|
+
entity_type?: string;
|
|
30
|
+
/** Entity ID */
|
|
31
|
+
entity_id?: string;
|
|
32
|
+
/** Is this a new entity? */
|
|
33
|
+
is_new?: boolean;
|
|
34
|
+
/** Duration in milliseconds */
|
|
35
|
+
duration_ms?: number;
|
|
36
|
+
/** Step name for multi-step flows */
|
|
37
|
+
step_name?: string;
|
|
38
|
+
/** Flow name for multi-step flows */
|
|
39
|
+
flow_name?: string;
|
|
40
|
+
/** Component type for interactions */
|
|
41
|
+
component_type?: string;
|
|
42
|
+
/** Action performed */
|
|
43
|
+
action?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Internal params for data ingestion API
|
|
48
|
+
*/
|
|
49
|
+
export interface DataIngestionApiParams {
|
|
50
|
+
[key: string]: string | number | boolean | object | undefined;
|
|
51
|
+
workspace?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Step timing properties
|
|
56
|
+
*/
|
|
57
|
+
export interface StepTimingProperties {
|
|
58
|
+
flow_name: string;
|
|
59
|
+
step_name: string;
|
|
60
|
+
from_step?: string;
|
|
61
|
+
to_step?: string;
|
|
62
|
+
duration_ms: number;
|
|
63
|
+
step_index?: number;
|
|
64
|
+
total_steps?: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Error tracking properties
|
|
69
|
+
*/
|
|
70
|
+
export interface ErrorEventProperties {
|
|
71
|
+
error_message: string;
|
|
72
|
+
error_type?: string;
|
|
73
|
+
error_stack?: string;
|
|
74
|
+
entity_type?: string;
|
|
75
|
+
entity_id?: string;
|
|
76
|
+
is_new_entity: boolean;
|
|
77
|
+
operation?: string; // create, update, delete
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Interaction properties
|
|
82
|
+
*/
|
|
83
|
+
export interface InteractionEventProperties {
|
|
84
|
+
component_type: string;
|
|
85
|
+
component_id?: string;
|
|
86
|
+
action: string;
|
|
87
|
+
selected_option?: string;
|
|
88
|
+
metadata?: Record<string, unknown>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Generic entity creation properties
|
|
93
|
+
*/
|
|
94
|
+
export interface EntityCreatedProperties {
|
|
95
|
+
entity_type: string;
|
|
96
|
+
entity_id?: string;
|
|
97
|
+
properties: Record<string, unknown>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generic entity edition properties
|
|
102
|
+
*/
|
|
103
|
+
export interface EntityEditedProperties {
|
|
104
|
+
entity_type: string;
|
|
105
|
+
entity_id: string;
|
|
106
|
+
changed_fields?: string[];
|
|
107
|
+
properties: Record<string, unknown>;
|
|
108
|
+
}
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
Icon,
|
|
13
13
|
InputText,
|
|
14
14
|
} from "../../../shared/components";
|
|
15
|
+
import { useAnalytics } from "../../../shared/hooks";
|
|
16
|
+
import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
|
|
15
17
|
import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
16
18
|
import { maskPhoneNumber } from "../../../shared/utils/phoneNumber";
|
|
17
19
|
import { useAddUserToOrgUnit } from "../../hooks";
|
|
@@ -31,6 +33,11 @@ export const CreateUserDrawer = ({
|
|
|
31
33
|
}: CreateUserDrawerProps) => {
|
|
32
34
|
const { pushToast } = useUI();
|
|
33
35
|
const router = useRouter();
|
|
36
|
+
const { trackEntityCreated, trackEntityCreateError } = useAnalytics({
|
|
37
|
+
entityType: "user",
|
|
38
|
+
defaultTimerName: "user_creation",
|
|
39
|
+
shouldTrackDefaultTimer: true,
|
|
40
|
+
});
|
|
34
41
|
const [drawerState, setDrawerState] = useState<
|
|
35
42
|
"CREATE_USER" | "USER_ALREADY_EXISTS"
|
|
36
43
|
>("CREATE_USER");
|
|
@@ -74,6 +81,15 @@ export const CreateUserDrawer = ({
|
|
|
74
81
|
};
|
|
75
82
|
|
|
76
83
|
const handleAddUserSuccess = ({ user }: { user: { id: string } }) => {
|
|
84
|
+
trackEntityCreated(ANALYTICS_EVENTS.USER_CREATED, "user", {
|
|
85
|
+
user_name: name,
|
|
86
|
+
user_email: email,
|
|
87
|
+
user_phone: phone,
|
|
88
|
+
roles_count: roles.length,
|
|
89
|
+
role_ids: roles,
|
|
90
|
+
org_unit_id: orgUnitId,
|
|
91
|
+
});
|
|
92
|
+
|
|
77
93
|
pushToast({
|
|
78
94
|
message: "User successfully added",
|
|
79
95
|
status: "INFO",
|
|
@@ -112,6 +128,14 @@ export const CreateUserDrawer = ({
|
|
|
112
128
|
};
|
|
113
129
|
};
|
|
114
130
|
|
|
131
|
+
trackEntityCreateError(ANALYTICS_EVENTS.USER_CREATE_ERROR, err, {
|
|
132
|
+
org_unit_id: orgUnitId,
|
|
133
|
+
error_type:
|
|
134
|
+
error.code === "USER_ALREADY_EXISTS"
|
|
135
|
+
? "user_already_exists"
|
|
136
|
+
: "unknown",
|
|
137
|
+
});
|
|
138
|
+
|
|
115
139
|
setUserAlreadyInUse({
|
|
116
140
|
...error.user,
|
|
117
141
|
orgUnit: error.orgUnit,
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
InputText,
|
|
9
9
|
type BasicDrawerProps,
|
|
10
10
|
} from "../../../shared/components";
|
|
11
|
+
import { useAnalytics } from "../../../shared/hooks";
|
|
12
|
+
import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
|
|
11
13
|
import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
|
|
12
14
|
import { useRemoveUserFromOrgUnit } from "../../hooks";
|
|
13
15
|
|
|
@@ -26,10 +28,22 @@ export const DeleteUserDrawer = ({
|
|
|
26
28
|
const [userNameConfirmation, setUserNameConfirmation] = useState("");
|
|
27
29
|
|
|
28
30
|
const { pushToast } = useUI();
|
|
29
|
-
|
|
30
31
|
const { orgUnit } = useOrgUnitByUser(user.id);
|
|
32
|
+
const { trackEvent } = useAnalytics({
|
|
33
|
+
entityType: "user",
|
|
34
|
+
entityId: user.id,
|
|
35
|
+
defaultTimerName: "user_delete",
|
|
36
|
+
shouldTrackDefaultTimer: true,
|
|
37
|
+
});
|
|
31
38
|
|
|
32
39
|
const handleRemoveSuccess = () => {
|
|
40
|
+
trackEvent(ANALYTICS_EVENTS.USER_DELETED, {
|
|
41
|
+
user_id: user.id,
|
|
42
|
+
user_name: user.name,
|
|
43
|
+
org_unit_id: orgUnit?.id,
|
|
44
|
+
entity_type: "user",
|
|
45
|
+
});
|
|
46
|
+
|
|
33
47
|
pushToast({
|
|
34
48
|
message: "User successfully deleted",
|
|
35
49
|
status: "INFO",
|
|
@@ -52,7 +66,14 @@ export const DeleteUserDrawer = ({
|
|
|
52
66
|
const { removeUserFromOrgUnit, isRemoveUserFromOrgUnitLoading } =
|
|
53
67
|
useRemoveUserFromOrgUnit({
|
|
54
68
|
onSuccess: handleRemoveSuccess,
|
|
55
|
-
onError: () => {
|
|
69
|
+
onError: (error) => {
|
|
70
|
+
trackEvent(ANALYTICS_EVENTS.USER_DELETE_ERROR, {
|
|
71
|
+
entity_id: user.id,
|
|
72
|
+
user_name: user.name,
|
|
73
|
+
error_message: error instanceof Error ? error.message : String(error),
|
|
74
|
+
org_unit_id: orgUnit?.id,
|
|
75
|
+
});
|
|
76
|
+
|
|
56
77
|
pushToast({
|
|
57
78
|
message: "An error occurred while deleting the user",
|
|
58
79
|
status: "ERROR",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
1
|
+
import { useRef, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { Skeleton, useUI } from "@faststore/ui";
|
|
4
4
|
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
ErrorMessage,
|
|
11
11
|
InputText,
|
|
12
12
|
} from "../../../shared/components";
|
|
13
|
+
import { useAnalytics } from "../../../shared/hooks";
|
|
14
|
+
import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
|
|
13
15
|
import { maskPhoneNumber } from "../../../shared/utils/phoneNumber";
|
|
14
16
|
import { useGetUserById, useUpdateUser } from "../../hooks";
|
|
15
17
|
|
|
@@ -29,16 +31,25 @@ export const UpdateUserDrawer = ({
|
|
|
29
31
|
...props
|
|
30
32
|
}: UpdateUserDrawerProps) => {
|
|
31
33
|
const { pushToast } = useUI();
|
|
34
|
+
const { trackEntityEdited, trackEntityEditError } = useAnalytics({
|
|
35
|
+
entityType: "user",
|
|
36
|
+
entityId: userId,
|
|
37
|
+
defaultTimerName: "user_edit",
|
|
38
|
+
shouldTrackDefaultTimer: true,
|
|
39
|
+
});
|
|
40
|
+
const initialRolesRef = useRef<string[]>([]);
|
|
32
41
|
|
|
33
42
|
const { isUserLoading } = useGetUserById(
|
|
34
43
|
{ orgUnitId, userId },
|
|
35
44
|
{
|
|
36
45
|
onSuccess: (data) => {
|
|
46
|
+
const userRoles = data?.roles ? data.roles : [];
|
|
47
|
+
initialRolesRef.current = userRoles;
|
|
37
48
|
setForm({
|
|
38
49
|
name: data?.name ?? "",
|
|
39
50
|
email: data?.email ?? "",
|
|
40
51
|
phone: data?.phone ?? "",
|
|
41
|
-
roles:
|
|
52
|
+
roles: userRoles,
|
|
42
53
|
});
|
|
43
54
|
},
|
|
44
55
|
onError: () => {
|
|
@@ -74,6 +85,16 @@ export const UpdateUserDrawer = ({
|
|
|
74
85
|
};
|
|
75
86
|
|
|
76
87
|
const handleUpdateUserSuccess = () => {
|
|
88
|
+
trackEntityEdited(ANALYTICS_EVENTS.USER_EDITED, {
|
|
89
|
+
user_id: userId,
|
|
90
|
+
user_name: name,
|
|
91
|
+
user_phone: phone,
|
|
92
|
+
old_roles: initialRolesRef.current,
|
|
93
|
+
new_roles: roles,
|
|
94
|
+
roles_count: roles.length,
|
|
95
|
+
org_unit_id: orgUnitId,
|
|
96
|
+
});
|
|
97
|
+
|
|
77
98
|
pushToast({
|
|
78
99
|
message: "User successfully updated",
|
|
79
100
|
status: "INFO",
|
|
@@ -84,7 +105,17 @@ export const UpdateUserDrawer = ({
|
|
|
84
105
|
|
|
85
106
|
const { updateUser, isUpdateUserLoading } = useUpdateUser({
|
|
86
107
|
onSuccess: handleUpdateUserSuccess,
|
|
87
|
-
onError: () => {
|
|
108
|
+
onError: (error) => {
|
|
109
|
+
trackEntityEditError(
|
|
110
|
+
ANALYTICS_EVENTS.USER_EDIT_ERROR,
|
|
111
|
+
"user",
|
|
112
|
+
userId,
|
|
113
|
+
error,
|
|
114
|
+
{
|
|
115
|
+
org_unit_id: orgUnitId,
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
88
119
|
pushToast({
|
|
89
120
|
message: "Failed to update user",
|
|
90
121
|
status: "ERROR",
|
|
@@ -94,7 +125,17 @@ export const UpdateUserDrawer = ({
|
|
|
94
125
|
|
|
95
126
|
const { updateRoles, isUpdateRolesLoading } = useUpdateRoles({
|
|
96
127
|
onSuccess: handleUpdateUserSuccess,
|
|
97
|
-
onError: () => {
|
|
128
|
+
onError: (error) => {
|
|
129
|
+
trackEntityEditError(
|
|
130
|
+
ANALYTICS_EVENTS.USER_EDIT_ERROR,
|
|
131
|
+
"user",
|
|
132
|
+
userId,
|
|
133
|
+
error,
|
|
134
|
+
{
|
|
135
|
+
org_unit_id: orgUnitId,
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
|
|
98
139
|
pushToast({
|
|
99
140
|
message: "Failed to update user",
|
|
100
141
|
status: "ERROR",
|