@vtex/faststore-plugin-buyer-portal 1.3.27 → 1.3.29

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 (38) hide show
  1. package/CHANGELOG.md +16 -2
  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/DeleteBuyingPolicyDrawer/DeleteBuyingPolicyDrawer.tsx +28 -2
  17. package/src/features/buying-policies/components/UpdateBuyingPolicyDrawer/UpdateBuyingPolicyDrawer.tsx +49 -5
  18. package/src/features/custom-fields/components/CreateCustomFieldValueDrawer/CreateCustomFieldValueDrawer.tsx +60 -5
  19. package/src/features/custom-fields/components/DeleteCustomFieldValueDrawer/DeleteCustomFieldValueDrawer.tsx +61 -5
  20. package/src/features/custom-fields/components/UpdateCustomFieldValueDrawer/UpdateCustomFieldValueDrawer.tsx +32 -3
  21. package/src/features/org-units/components/CreateOrgUnitDrawer/CreateOrgUnitDrawer.tsx +18 -0
  22. package/src/features/org-units/components/DeleteOrgUnitDrawer/DeleteOrgUnitDrawer.tsx +28 -0
  23. package/src/features/org-units/components/UpdateOrgUnitDrawer/UpdateOrgUnitDrawer.tsx +28 -0
  24. package/src/features/shared/hooks/analytics/types.ts +14 -0
  25. package/src/features/shared/hooks/analytics/useAnalytics.ts +249 -0
  26. package/src/features/shared/hooks/index.ts +1 -0
  27. package/src/features/shared/services/logger/analytics/analytics.ts +101 -0
  28. package/src/features/shared/services/logger/analytics/constants.ts +83 -0
  29. package/src/features/shared/services/logger/analytics/types.ts +108 -0
  30. package/src/features/shared/services/logger/index.ts +1 -0
  31. package/src/features/shared/utils/constants.ts +1 -1
  32. package/src/features/users/components/CreateUserDrawer/CreateUserDrawer.tsx +24 -0
  33. package/src/features/users/components/DeleteUserDrawer/DeleteUserDrawer.tsx +23 -2
  34. package/src/features/users/components/UpdateUserDrawer/UpdateUserDrawer.tsx +45 -4
  35. package/src/themes/colors.scss +10 -0
  36. package/src/themes/layouts.scss +4 -0
  37. package/src/themes/tokens.scss +169 -0
  38. package/src/themes/typography.scss +76 -0
package/CHANGELOG.md CHANGED
@@ -7,8 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [1.3.27] - 2025-11-25
10
+ ## [1.3.29] - 2025-11-25
11
+
12
+ ### Added
13
+
14
+ - Create tokens file with variables according with design system definition
15
+ - Create typography mixins
16
+
17
+ ## [1.3.28] - 2025-11-25
11
18
 
19
+ ### Added
20
+
21
+ - Setup business metrics and analytics events
22
+
23
+ ## [1.3.27] - 2025-11-25
12
24
 
13
25
  ### Changed
14
26
 
@@ -269,7 +281,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
269
281
  - Add CHANGELOG file
270
282
  - Add README file
271
283
 
272
- [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.27...HEAD
284
+ [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.29...HEAD
273
285
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.2.2...1.2.3
274
286
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.3
275
287
  [1.2.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.4
@@ -284,6 +296,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
284
296
 
285
297
  # <<<<<<< HEAD
286
298
 
299
+ [1.3.29]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.28...v1.3.29
300
+ [1.3.28]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.27...v1.3.28
287
301
  [1.3.27]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.26...v1.3.27
288
302
  [1.3.26]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.25...v1.3.26
289
303
  [1.3.25]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.24...v1.3.25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.3.27",
3
+ "version": "1.3.29",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -6,7 +6,8 @@ import { useUI } from "@faststore/ui";
6
6
 
7
7
  import { RecipientsForm } from "..";
8
8
  import { type BasicDrawerProps, BasicDrawer } from "../../../shared/components";
9
- import { useBuyerPortal } from "../../../shared/hooks";
9
+ import { useAnalytics, useBuyerPortal } from "../../../shared/hooks";
10
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
10
11
  import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
11
12
  import { useAddRecipientsToAddress } from "../../hooks/useAddRecipientsToAddress";
12
13
 
@@ -30,12 +31,24 @@ export const AddRecipientsDrawer = ({
30
31
  const router = useRouter();
31
32
  const { pushToast } = useUI();
32
33
  const { currentOrgUnit } = useBuyerPortal();
34
+ const { trackEvent, trackEntityCreateError } = useAnalytics({
35
+ entityType: "recipient",
36
+ defaultTimerName: "recipient_creation",
37
+ shouldTrackDefaultTimer: true,
38
+ });
33
39
 
34
40
  const { addRecipientsToAddress, isAddRecipientToAddressLoading } =
35
41
  useAddRecipientsToAddress({
36
42
  onSuccess: () => {
37
43
  const hasTabParam = router.query?.tab === "recipient-tab";
38
44
 
45
+ trackEvent(ANALYTICS_EVENTS.RECIPIENT_CREATED, {
46
+ recipients_count: recipients.length,
47
+ recipient_names: recipients.map((r) => r.recipientName),
48
+ address_id: addressId ?? router.query.addressId,
49
+ org_unit_id: currentOrgUnit?.id,
50
+ });
51
+
39
52
  pushToast({
40
53
  message: "Recipients added successfully ",
41
54
  status: "INFO",
@@ -63,7 +76,12 @@ export const AddRecipientsDrawer = ({
63
76
  refetchRecipients?.();
64
77
  }
65
78
  },
66
- onError: () => {
79
+ onError: (error) => {
80
+ trackEntityCreateError(ANALYTICS_EVENTS.RECIPIENT_CREATE_ERROR, error, {
81
+ address_id: addressId ?? router.query.addressId,
82
+ org_unit_id: currentOrgUnit?.id,
83
+ });
84
+
67
85
  pushToast({
68
86
  message: "An error occurred while adding recipients",
69
87
  status: "ERROR",
@@ -12,7 +12,12 @@ import {
12
12
  import { TabBar } from "../../../shared/components/Tab/TabBar";
13
13
  import { TabContent } from "../../../shared/components/Tab/TabContent";
14
14
  import { TabOption } from "../../../shared/components/Tab/TabOption";
15
- import { useAddToScope, useBuyerPortal } from "../../../shared/hooks";
15
+ import {
16
+ useAddToScope,
17
+ useAnalytics,
18
+ useBuyerPortal,
19
+ } from "../../../shared/hooks";
20
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
16
21
  import { useCreateNewAddress } from "../../hooks";
17
22
  import { LocationInput } from "../../types/AddressData";
18
23
 
@@ -32,6 +37,13 @@ export const CreateAddressDrawer = ({
32
37
  const [isTouched, setIsTouched] = useState(false);
33
38
  const [useExistingAddress, setUseExistingAddress] = useState(false);
34
39
  const { currentOrgUnit } = useBuyerPortal();
40
+ const { trackEvent, trackEntityCreated, trackEntityCreateError } =
41
+ useAnalytics({
42
+ entityType: "address",
43
+ defaultTimerName: "address_creation",
44
+ shouldTrackDefaultTimer: true,
45
+ });
46
+
35
47
  const [completedAddress, setCompletedAddress] = useState<AddressData>(
36
48
  {} as AddressData
37
49
  );
@@ -52,8 +64,33 @@ export const CreateAddressDrawer = ({
52
64
  const [locations, setLocations] = useState<LocationInput[]>([{ name: "" }]);
53
65
  const [recipients, setRecipients] = useState<RecipientInput[]>([]);
54
66
  const [invalidAddress, setInvalidAddress] = useState(false);
67
+ const [currentTab, setCurrentTab] = useState<string>("address-tab");
68
+
69
+ const handleTabChange = (tabId: string) => {
70
+ if (currentTab !== tabId) {
71
+ trackEvent(ANALYTICS_EVENTS.ADDRESS_STEP_TIMING, {
72
+ flow_name: "address_creation",
73
+ from_step: currentTab,
74
+ to_step: tabId,
75
+ step_name: tabId,
76
+ org_unit_id: currentOrgUnit?.id,
77
+ });
78
+ setCurrentTab(tabId);
79
+ }
80
+ };
55
81
 
56
82
  const handleCreateNewAddressSuccess = () => {
83
+ trackEntityCreated(ANALYTICS_EVENTS.ADDRESS_CREATED, "address", {
84
+ address_name: address.name,
85
+ address_country: address.country,
86
+ address_city: address.city,
87
+ address_types: address.types,
88
+ locations_count: locations.filter((l) => l.name).length,
89
+ recipients_count: recipients.length,
90
+ use_existing_address: useExistingAddress,
91
+ org_unit_id: currentOrgUnit?.id,
92
+ });
93
+
57
94
  pushToast({
58
95
  message: "Address added successfully",
59
96
  status: "INFO",
@@ -64,7 +101,11 @@ export const CreateAddressDrawer = ({
64
101
 
65
102
  const { createNewAddress, isCreateNewAddressLoading } = useCreateNewAddress({
66
103
  onSuccess: handleCreateNewAddressSuccess,
67
- onError: () => {
104
+ onError: (error) => {
105
+ trackEntityCreateError(ANALYTICS_EVENTS.ADDRESS_CREATE_ERROR, error, {
106
+ org_unit_id: currentOrgUnit?.id,
107
+ });
108
+
68
109
  pushToast({
69
110
  message: "An error occurred while creating the address",
70
111
  status: "ERROR",
@@ -74,7 +115,12 @@ export const CreateAddressDrawer = ({
74
115
 
75
116
  const { addToScope } = useAddToScope({
76
117
  onSuccess: handleCreateNewAddressSuccess,
77
- onError: () => {
118
+ onError: (error) => {
119
+ trackEntityCreateError(ANALYTICS_EVENTS.ADDRESS_CREATE_ERROR, error, {
120
+ org_unit_id: currentOrgUnit?.id,
121
+ use_existing_address: true,
122
+ });
123
+
78
124
  pushToast({
79
125
  message: "An error occurred while creating the address",
80
126
  status: "ERROR",
@@ -124,7 +170,7 @@ export const CreateAddressDrawer = ({
124
170
  return (
125
171
  <BasicDrawer data-fs-bp-create-address-drawer close={close} {...props}>
126
172
  <BasicDrawer.Heading title="Add address" onClose={close} />
127
- <Tab defaultTabId="address-tab">
173
+ <Tab defaultTabId="address-tab" onChange={handleTabChange}>
128
174
  <TabBar>
129
175
  <TabOption id="address-tab">Details</TabOption>
130
176
  <TabOption id="location-tab">Locations</TabOption>
@@ -7,7 +7,8 @@ import {
7
7
  BasicDrawer,
8
8
  InputText,
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 { useDeleteAddress } from "../../hooks";
12
13
 
13
14
  export type DeleteAddressDrawerProps = Omit<BasicDrawerProps, "children"> & {
@@ -26,9 +27,22 @@ export const DeleteAddressDrawer = ({
26
27
  }: DeleteAddressDrawerProps) => {
27
28
  const { pushToast } = useUI();
28
29
  const { currentOrgUnit } = useBuyerPortal();
30
+ const { trackEvent } = useAnalytics({
31
+ entityType: "address",
32
+ entityId: addressId,
33
+ defaultTimerName: "address_delete",
34
+ shouldTrackDefaultTimer: true,
35
+ });
29
36
  const [addressNameConfirmation, setAddressNameConfirmation] = useState("");
30
37
 
31
38
  const handleDeleteSuccess = () => {
39
+ trackEvent(ANALYTICS_EVENTS.ADDRESS_DELETED, {
40
+ address_id: addressId,
41
+ address_name: addressName,
42
+ org_unit_id: currentOrgUnit?.id,
43
+ entity_type: "address",
44
+ });
45
+
32
46
  pushToast({
33
47
  message: "Address deleted successfully",
34
48
  status: "INFO",
@@ -39,7 +53,17 @@ export const DeleteAddressDrawer = ({
39
53
 
40
54
  const { deleteAddress, isDeleteAddressLoading } = useDeleteAddress({
41
55
  onSuccess: handleDeleteSuccess,
42
- onError: () => {
56
+ onError: (error) => {
57
+ const errorMessage = typeof error === "string" ? error : error.message;
58
+
59
+ trackEvent(ANALYTICS_EVENTS.ADDRESS_DELETE_ERROR, {
60
+ entity_type: "address",
61
+ entity_id: addressId,
62
+ address_name: addressName,
63
+ org_unit_id: currentOrgUnit?.id,
64
+ error_message: errorMessage,
65
+ });
66
+
43
67
  pushToast({
44
68
  message: "An error occurred while removing the address from the unit",
45
69
  status: "ERROR",
@@ -3,7 +3,8 @@ import { useState } from "react";
3
3
  import { useUI } from "@faststore/ui";
4
4
 
5
5
  import { BasicDrawer, BasicDrawerProps } from "../../../shared/components";
6
- import { useBuyerPortal } from "../../../shared/hooks";
6
+ import { useAnalytics, useBuyerPortal } from "../../../shared/hooks";
7
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
7
8
  import { useAddressLocationDelete } from "../../hooks";
8
9
  import { LocationField } from "../LocationForm/LocationField/LocationField";
9
10
 
@@ -28,10 +29,24 @@ export const DeleteAddressLocationDrawer = ({
28
29
  const [confirmationValue, setConfirmationValue] = useState({ name: "" });
29
30
  const deleteIsDisable = confirmationValue.name !== location.value;
30
31
  const { currentOrgUnit } = useBuyerPortal();
32
+ const { trackEvent } = useAnalytics({
33
+ entityType: "location",
34
+ entityId: location.id,
35
+ defaultTimerName: "location_delete",
36
+ shouldTrackDefaultTimer: true,
37
+ });
31
38
 
32
39
  const { deleteLocationMutate, deleteLocationLoading } =
33
40
  useAddressLocationDelete({
34
41
  onSuccess: () => {
42
+ trackEvent(ANALYTICS_EVENTS.LOCATION_DELETED, {
43
+ location_id: location.id,
44
+ location_name: location.value,
45
+ address_id: location.auxId,
46
+ org_unit_id: currentOrgUnit?.id,
47
+ entity_type: "location",
48
+ });
49
+
35
50
  pushToast({
36
51
  message: "Location removed successfully",
37
52
  status: "INFO",
@@ -40,11 +55,22 @@ export const DeleteAddressLocationDrawer = ({
40
55
  onDeleteSuccess?.();
41
56
  close();
42
57
  },
43
- onError: () =>
58
+ onError: (error) => {
59
+ const errorMessage = typeof error === "string" ? error : error.message;
60
+
61
+ trackEvent(ANALYTICS_EVENTS.LOCATION_DELETE_ERROR, {
62
+ entity_type: "location",
63
+ entity_id: location.id,
64
+ location_name: location.value,
65
+ org_unit_id: currentOrgUnit?.id,
66
+ error_message: errorMessage,
67
+ });
68
+
44
69
  pushToast({
45
70
  message: "An error occurred while removing location",
46
71
  status: "ERROR",
47
- }),
72
+ });
73
+ },
48
74
  });
49
75
 
50
76
  const handleDeleteClick = () => {
@@ -9,7 +9,8 @@ import {
9
9
  BasicDrawer,
10
10
  InputText,
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 { useDeleteRecipientAddress } from "../../hooks/useDeleteRecipientAddress";
14
15
 
15
16
  export type DeleteRecipientAddressDrawerProps = Omit<
@@ -32,10 +33,24 @@ export const DeleteRecipientAddressDrawer = ({
32
33
  const { pushToast } = useUI();
33
34
  const router = useRouter();
34
35
  const { currentOrgUnit } = useBuyerPortal();
36
+ const { trackEvent } = useAnalytics({
37
+ entityType: "recipient",
38
+ entityId: recipientAddressId,
39
+ defaultTimerName: "recipient_delete",
40
+ shouldTrackDefaultTimer: true,
41
+ });
35
42
  const [recipientNameConfirmation, setRecipientNameConfirmation] =
36
43
  useState("");
37
44
 
38
45
  const handleDeleteSuccess = () => {
46
+ trackEvent(ANALYTICS_EVENTS.RECIPIENT_DELETED, {
47
+ recipient_id: recipientAddressId,
48
+ recipient_name: recipientAddressName,
49
+ address_id: router.query.addressId as string,
50
+ org_unit_id: currentOrgUnit?.id,
51
+ entity_type: "recipient",
52
+ });
53
+
39
54
  pushToast({
40
55
  message: "Recipient deleted successfully",
41
56
  status: "INFO",
@@ -47,7 +62,14 @@ export const DeleteRecipientAddressDrawer = ({
47
62
  const { deleteRecipientAddress, isDeleteRecipientAddressLoading } =
48
63
  useDeleteRecipientAddress({
49
64
  onSuccess: handleDeleteSuccess,
50
- onError: () => {
65
+ onError: (error) => {
66
+ trackEvent(ANALYTICS_EVENTS.RECIPIENT_DELETE_ERROR, {
67
+ entity_id: recipientAddressId,
68
+ recipient_name: recipientAddressName,
69
+ error_message: error instanceof Error ? error.message : String(error),
70
+ org_unit_id: currentOrgUnit?.id,
71
+ });
72
+
51
73
  pushToast({
52
74
  message:
53
75
  "An error occurred while removing the recipient from address",
@@ -4,7 +4,8 @@ import { useUI } from "@faststore/ui";
4
4
 
5
5
  import { AddressForm } from "..";
6
6
  import { type BasicDrawerProps, BasicDrawer } from "../../../shared/components";
7
- import { useBuyerPortal } from "../../../shared/hooks";
7
+ import { useAnalytics, useBuyerPortal } from "../../../shared/hooks";
8
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
8
9
  import { useEditAddress } from "../../hooks";
9
10
 
10
11
  import type { AddressInput, AddressData } from "../../types";
@@ -24,6 +25,12 @@ export const EditAddressDrawer = ({
24
25
  const { pushToast } = useUI();
25
26
  const [isTouched, setIsTouched] = useState(false);
26
27
  const { currentOrgUnit } = useBuyerPortal();
28
+ const { trackEntityEdited, trackEntityEditError } = useAnalytics({
29
+ entityType: "address",
30
+ entityId: currentAddress.id,
31
+ defaultTimerName: "address_edit",
32
+ shouldTrackDefaultTimer: true,
33
+ });
27
34
 
28
35
  const [address, setAddress] = useState<AddressInput>({
29
36
  ...currentAddress,
@@ -33,6 +40,15 @@ export const EditAddressDrawer = ({
33
40
  const [, setInvalidAddress] = useState(false);
34
41
 
35
42
  const handleUpdatedAddressSuccess = () => {
43
+ trackEntityEdited(ANALYTICS_EVENTS.ADDRESS_EDITED, {
44
+ address_id: currentAddress.id,
45
+ address_name: address.name,
46
+ address_country: address.country,
47
+ address_city: address.city,
48
+ address_types: address.types,
49
+ org_unit_id: currentOrgUnit?.id,
50
+ });
51
+
36
52
  pushToast({
37
53
  message: "Address updated successfully",
38
54
  status: "INFO",
@@ -43,7 +59,17 @@ export const EditAddressDrawer = ({
43
59
 
44
60
  const { editAddress, isEditAddressLoading } = useEditAddress({
45
61
  onSuccess: handleUpdatedAddressSuccess,
46
- onError: () => {
62
+ onError: (error) => {
63
+ trackEntityEditError(
64
+ ANALYTICS_EVENTS.ADDRESS_EDIT_ERROR,
65
+ "address",
66
+ currentAddress.id ?? "",
67
+ error,
68
+ {
69
+ org_unit_id: currentOrgUnit?.id,
70
+ }
71
+ );
72
+
47
73
  pushToast({
48
74
  message: "An error occurred while updating the address",
49
75
  status: "ERROR",
@@ -1,9 +1,10 @@
1
- import { useEffect, useState } from "react";
1
+ import { useState, useEffect } from "react";
2
2
 
3
3
  import { CheckboxField, useUI } from "@faststore/ui";
4
4
 
5
5
  import { BasicDrawer, BasicDrawerProps } from "../../../shared/components";
6
- import { useBuyerPortal } from "../../../shared/hooks";
6
+ import { useAnalytics, useBuyerPortal } from "../../../shared/hooks";
7
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
7
8
  import { LOCAL_STORAGE_LOCATION_EDIT_KEY } from "../../../shared/utils/constants";
8
9
  import { useAddressLocationEdit } from "../../hooks";
9
10
  import { LocationField } from "../LocationForm/LocationField/LocationField";
@@ -38,9 +39,23 @@ export const EditAddressLocationDrawer = ({
38
39
  const saveIsDisable = location.value === field.name;
39
40
  const drawerTitle = confirmView ? "Confirm location update" : "Edit location";
40
41
  const { currentOrgUnit } = useBuyerPortal();
42
+ const { trackEntityEdited, trackEntityEditError } = useAnalytics({
43
+ entityType: "location",
44
+ entityId: location.id,
45
+ defaultTimerName: "location_edit",
46
+ shouldTrackDefaultTimer: true,
47
+ });
41
48
 
42
49
  const { editLocationMutate, editLocationLoading } = useAddressLocationEdit({
43
50
  onSuccess: () => {
51
+ trackEntityEdited(ANALYTICS_EVENTS.LOCATION_EDITED, {
52
+ location_id: location.id,
53
+ location_old_name: location.value,
54
+ location_new_name: field.name,
55
+ address_id: location.auxId,
56
+ org_unit_id: currentOrgUnit?.id,
57
+ });
58
+
44
59
  pushToast({
45
60
  message: "Location edited successfully",
46
61
  status: "INFO",
@@ -49,11 +64,22 @@ export const EditAddressLocationDrawer = ({
49
64
  onEditSuccess();
50
65
  close();
51
66
  },
52
- onError: () =>
67
+ onError: (error) => {
68
+ trackEntityEditError(
69
+ ANALYTICS_EVENTS.LOCATION_EDIT_ERROR,
70
+ "location",
71
+ location.id,
72
+ error,
73
+ {
74
+ org_unit_id: currentOrgUnit?.id,
75
+ }
76
+ );
77
+
53
78
  pushToast({
54
79
  message: "An error occurred while editing the location",
55
80
  status: "ERROR",
56
- }),
81
+ });
82
+ },
57
83
  });
58
84
 
59
85
  const handleSaveClick = () => {
@@ -5,7 +5,8 @@ import { useRouter } from "next/router";
5
5
  import { CheckboxField, useUI } from "@faststore/ui";
6
6
 
7
7
  import { type BasicDrawerProps, BasicDrawer } 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 { LOCAL_STORAGE_RECIPIENT_EDIT_KEY } from "../../../shared/utils/constants";
10
11
  import { useEditRecipientsToAddress } from "../../hooks/useEditRecipientsToAddress";
11
12
  import { type RecipientData, RecipientInput } from "../../types";
@@ -29,6 +30,13 @@ export const EditRecipientAddressDrawer = ({
29
30
  const { pushToast } = useUI();
30
31
  const router = useRouter();
31
32
  const { currentOrgUnit } = useBuyerPortal();
33
+ const { trackEntityEdited, trackEntityEditError } = useAnalytics({
34
+ entityType: "recipient",
35
+ entityId: recipient.id,
36
+ defaultTimerName: "recipient_edit",
37
+ shouldTrackDefaultTimer: true,
38
+ });
39
+
32
40
  const initialRecipientName = recipient.firstName + " " + recipient.lastName;
33
41
  const [newRecipient, setNewRecipient] = useState({
34
42
  name: initialRecipientName,
@@ -45,6 +53,16 @@ export const EditRecipientAddressDrawer = ({
45
53
  }, []);
46
54
 
47
55
  const handleEditSuccess = () => {
56
+ trackEntityEdited(ANALYTICS_EVENTS.RECIPIENT_EDITED, {
57
+ recipient_id: recipient.id,
58
+ recipient_old_name: initialRecipientName,
59
+ recipient_new_name: newRecipient.name,
60
+ recipient_old_phone: recipient.phone,
61
+ recipient_new_phone: newRecipient.phone,
62
+ address_id: router.query.addressId as string,
63
+ org_unit_id: currentOrgUnit?.id,
64
+ });
65
+
48
66
  pushToast({
49
67
  message: "Recipient edited successfully",
50
68
  status: "INFO",
@@ -56,7 +74,17 @@ export const EditRecipientAddressDrawer = ({
56
74
  const { editRecipientsToAddress, isEditRecipientToAddressLoading } =
57
75
  useEditRecipientsToAddress({
58
76
  onSuccess: handleEditSuccess,
59
- onError: () => {
77
+ onError: (error) => {
78
+ trackEntityEditError(
79
+ ANALYTICS_EVENTS.RECIPIENT_EDIT_ERROR,
80
+ "recipient",
81
+ recipient.id,
82
+ error,
83
+ {
84
+ org_unit_id: currentOrgUnit?.id,
85
+ }
86
+ );
87
+
60
88
  pushToast({
61
89
  message: "An error occurred while editing the recipient",
62
90
  status: "ERROR",
@@ -6,7 +6,8 @@ import { useUI } from "@faststore/ui";
6
6
 
7
7
  import { LocationForm } from "../";
8
8
  import { type BasicDrawerProps, BasicDrawer } from "../../../shared/components";
9
- import { useBuyerPortal } from "../../../shared/hooks";
9
+ import { useAnalytics, useBuyerPortal } from "../../../shared/hooks";
10
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
10
11
  import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
11
12
  import { useAddLocationsToAddress } from "../../hooks";
12
13
 
@@ -28,13 +29,34 @@ export const LocationDrawer = ({
28
29
  const [locations, setLocations] = useState<LocationInput[]>([{ name: "" }]);
29
30
  const { pushToast } = useUI();
30
31
  const router = useRouter();
32
+ const { trackEvent, trackEntityCreated, trackEntityCreateError } =
33
+ useAnalytics({
34
+ entityType: "location",
35
+ defaultTimerName: "location_creation",
36
+ shouldTrackDefaultTimer: true,
37
+ });
31
38
 
32
39
  const saveIsDisabled = locations.filter(({ name }) => !!name).length === 0;
33
40
 
34
41
  const { addLocationsMutate, addLocationsLoading } = useAddLocationsToAddress({
35
42
  onSuccess: () => {
43
+ const validLocations = locations.filter(({ name }) => !!name);
36
44
  const hasTabParam = router.query?.tab === "location-tab";
37
45
 
46
+ trackEvent(ANALYTICS_EVENTS.LOCATION_CREATED, {
47
+ locations_count: validLocations.length,
48
+ location_names: validLocations.map((l) => l.name),
49
+ address_id: addressId ?? router.query.addressId,
50
+ org_unit_id: currentOrgUnit?.id,
51
+ });
52
+
53
+ trackEntityCreated(ANALYTICS_EVENTS.LOCATION_CREATED, "location", {
54
+ locations_count: validLocations.length,
55
+ location_names: validLocations.map((l) => l.name),
56
+ address_id: addressId ?? router.query.addressId,
57
+ org_unit_id: currentOrgUnit?.id,
58
+ });
59
+
38
60
  pushToast({
39
61
  message: "Locations added successfully ",
40
62
  status: "INFO",
@@ -62,7 +84,12 @@ export const LocationDrawer = ({
62
84
  onAddLocationSuccess?.();
63
85
  }
64
86
  },
65
- onError: () => {
87
+ onError: (error) => {
88
+ trackEntityCreateError(ANALYTICS_EVENTS.LOCATION_CREATE_ERROR, error, {
89
+ address_id: addressId ?? router.query.addressId,
90
+ org_unit_id: currentOrgUnit?.id,
91
+ });
92
+
66
93
  pushToast({
67
94
  message: "An error occurred while adding locations",
68
95
  status: "ERROR",
@@ -10,7 +10,8 @@ import {
10
10
  ErrorMessage,
11
11
  InputText,
12
12
  } from "../../../shared/components";
13
- import { useDebounce } from "../../../shared/hooks";
13
+ import { useAnalytics, useDebounce } from "../../../shared/hooks";
14
+ import { ANALYTICS_EVENTS } from "../../../shared/services/logger/analytics/constants";
14
15
  import { useDeleteBudget } from "../../hooks/useDeleteBudget";
15
16
 
16
17
  export interface BudgetDeleteDrawerProps
@@ -33,6 +34,12 @@ export function BudgetDeleteDrawer({
33
34
  }: BudgetDeleteDrawerProps) {
34
35
  const { pushToast } = useUI();
35
36
  const router = useRouter();
37
+ const { trackEvent } = useAnalytics({
38
+ entityType: "budget",
39
+ entityId: budgetId,
40
+ defaultTimerName: "budget_delete",
41
+ shouldTrackDefaultTimer: true,
42
+ });
36
43
 
37
44
  const [inputValue, setInputValue] = useState<string>("");
38
45
  const [isTouched, setIsTouched] = useState<boolean>(false);
@@ -47,21 +54,42 @@ export function BudgetDeleteDrawer({
47
54
  return debouncedValue && debouncedValue !== budgetName
48
55
  ? "Budget name is invalid"
49
56
  : null;
50
- }, [debouncedValue, isTouched]);
57
+ }, [debouncedValue, isTouched, inputValue, budgetName]);
51
58
 
52
59
  const { mutate: deleteBudget, isLoading: isLoadingDeleteBudget } =
53
60
  useDeleteBudget({
54
61
  options: {
55
62
  onSuccess: handleDeleteSuccess,
56
- onError: () =>
63
+ onError: (error: Error) => {
64
+ const errorMessage =
65
+ typeof error === "string" ? error : error.message;
66
+
67
+ trackEvent(ANALYTICS_EVENTS.BUDGET_DELETE_ERROR, {
68
+ entity_type: "budget",
69
+ entity_id: budgetId,
70
+ budget_name: budgetName,
71
+ org_unit_id: orgUnitId,
72
+ contract_id: contractId,
73
+ error_message: errorMessage,
74
+ });
75
+
57
76
  pushToast({
58
77
  status: "ERROR",
59
78
  message: "An error occurred while removing the budget",
60
- }),
79
+ });
80
+ },
61
81
  },
62
82
  });
63
83
 
64
84
  function handleDeleteSuccess() {
85
+ trackEvent(ANALYTICS_EVENTS.BUDGET_DELETED, {
86
+ budget_id: budgetId,
87
+ budget_name: budgetName,
88
+ org_unit_id: orgUnitId,
89
+ contract_id: contractId,
90
+ entity_type: "budget",
91
+ });
92
+
65
93
  pushToast({
66
94
  status: "INFO",
67
95
  message: "Budget deleted successfully",