From d9ecd49c01e7b91f97e50e0bf2a6fafc105370f4 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Fri, 28 Jul 2023 13:51:40 -0300 Subject: [PATCH 01/22] Add Impersonate metrics --- react/components/OrganizationUsersTable.tsx | 40 ++++++- react/components/UserWidget.tsx | 33 +++++- react/package.json | 3 +- react/tsconfig.json | 1 + react/utils/constants.ts | 2 + react/utils/metrics/impersonate.ts | 113 ++++++++++++++++++++ react/utils/metrics/metrics.ts | 26 +++++ react/yarn.lock | 30 +++++- 8 files changed, 242 insertions(+), 6 deletions(-) create mode 100644 react/utils/metrics/impersonate.ts create mode 100644 react/utils/metrics/metrics.ts diff --git a/react/components/OrganizationUsersTable.tsx b/react/components/OrganizationUsersTable.tsx index 03ecd0ab..dbbccb51 100644 --- a/react/components/OrganizationUsersTable.tsx +++ b/react/components/OrganizationUsersTable.tsx @@ -16,7 +16,12 @@ import UPDATE_USER from '../graphql/updateUser.graphql' import REMOVE_USER from '../graphql/removeUser.graphql' import GET_COST_CENTER from '../graphql/getCostCenterStorefront.graphql' import IMPERSONATE_USER from '../graphql/impersonateB2BUser.graphql' -import { B2B_CHECKOUT_SESSION_KEY } from '../utils/constants' +import { + B2B_CHECKOUT_SESSION_KEY, + B2B_IMPERSONATE_USER, +} from '../utils/constants' +import { useSessionResponse } from '../modules/session' +import { sendImpersonateMetric } from '../utils/metrics/impersonate' interface Props { organizationId: string @@ -32,7 +37,7 @@ interface CellRendererProps { updateCellMeasurements: () => void } -interface B2BUserSimple extends UserDetails { +export interface B2BUserSimple extends UserDetails { costCenterName: string role: RoleSimple organizationName: string @@ -85,6 +90,7 @@ const OrganizationUsersTable: FunctionComponent = ({ isAdmin = false, isSalesAdmin = false, }) => { + const sessionResponse: any = useSessionResponse() const { formatMessage } = useIntl() const { showToast } = useContext(ToastContext) const toast = isAdmin ? useToast() : null @@ -377,6 +383,34 @@ const OrganizationUsersTable: FunctionComponent = ({ showToast(formatMessage(storeMessages.toastImpersonateFailure)) } } else { + const { + id: costCenterId, + name: costCenterName, + organization, + } = costCenterData?.getCostCenterByIdStorefront + + const user = { + costCenterId, + costCenterName, + organizationId: organization, + email: sessionResponse?.namespaces.profile?.email?.value, + } + + const metricParams = { + account: sessionResponse?.namespaces?.account?.accountName?.value, + target: { + costCenterId: rowData.costId, + costCenterName: rowData.costCenterName, + organizationId: rowData.orgId, + organizationName: rowData.organizationName, + email: rowData.email, + }, + user, + } + + sessionStorage.setItem(B2B_IMPERSONATE_USER, JSON.stringify(user)) + + sendImpersonateMetric(metricParams) window.location.reload() } }) @@ -451,7 +485,7 @@ const OrganizationUsersTable: FunctionComponent = ({ } const handleNextClick = () => { - const newPage = variableState.page + 1 + const newPage = 0 // variableState.page + 1 setVariables({ ...variableState, diff --git a/react/components/UserWidget.tsx b/react/components/UserWidget.tsx index a07d606c..c706b25e 100644 --- a/react/components/UserWidget.tsx +++ b/react/components/UserWidget.tsx @@ -22,8 +22,12 @@ import { useSessionResponse } from '../modules/session' import USER_WIDGET_QUERY from '../graphql/userWidgetQuery.graphql' import SET_CURRENT_ORGANIZATION from '../graphql/setCurrentOrganization.graphql' import STOP_IMPERSONATION from '../graphql/impersonateUser.graphql' -import { B2B_CHECKOUT_SESSION_KEY } from '../utils/constants' +import { + B2B_CHECKOUT_SESSION_KEY, + B2B_IMPERSONATE_USER, +} from '../utils/constants' import '../css/user-widget.css' +import { sendStopImpersonateMetric } from '../utils/metrics/impersonate' const CSS_HANDLES = [ 'userWidgetContainer', @@ -216,6 +220,33 @@ const UserWidget: VtexFunctionComponent = ({ sessionStorage.removeItem(B2B_CHECKOUT_SESSION_KEY) } + const { + currentCostCenter: costCenterId, + costCenterInput: costCenterName, + currentOrganization: organizationId, + organizationInput: organizationName, + } = organizationsState + + const target = { + costCenterId, + costCenterName, + organizationId, + organizationName, + email: userWidgetData?.checkImpersonation?.email as string, + } + + const userStorage = sessionStorage.getItem(B2B_IMPERSONATE_USER) + const user = userStorage ? JSON.parse(userStorage) : undefined + + const metricParams = { + account: sessionResponse?.namespaces?.account?.accountName?.value, + target, + user, + } + + sendStopImpersonateMetric(metricParams) + sessionStorage.removeItem(B2B_IMPERSONATE_USER) + window.location.reload() }) .catch(error => { diff --git a/react/package.json b/react/package.json index b909b20c..b5c96d4a 100644 --- a/react/package.json +++ b/react/package.json @@ -39,6 +39,7 @@ "react-apollo": "^3.1.5", "react-intl": "^5.13.4", "react-router": "^5.1.2", - "react-router-dom": "^5.1.2" + "react-router-dom": "^5.1.2", + "axios": "1.4.0" } } diff --git a/react/tsconfig.json b/react/tsconfig.json index 9d838926..b19356c2 100644 --- a/react/tsconfig.json +++ b/react/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "@vtex/tsconfig", "compilerOptions": { + "jsx": "react", "noEmitOnError": false, "lib": ["dom"], "module": "esnext", diff --git a/react/utils/constants.ts b/react/utils/constants.ts index 4c7a4190..1e032095 100644 --- a/react/utils/constants.ts +++ b/react/utils/constants.ts @@ -1 +1,3 @@ export const B2B_CHECKOUT_SESSION_KEY = 'b2b-checkout-settings' + +export const B2B_IMPERSONATE_USER = 'b2b-impersonate-user' diff --git a/react/utils/metrics/impersonate.ts b/react/utils/metrics/impersonate.ts new file mode 100644 index 00000000..2268cded --- /dev/null +++ b/react/utils/metrics/impersonate.ts @@ -0,0 +1,113 @@ +import type { Metric } from './metrics' +import { sendMetric } from './metrics' + +type ImpersonatePerson = { + email: string + buy_org_id: string + cost_center_id: string + cost_center_name: string +} + +type ImpersonateUser = ImpersonatePerson +type ImpersonateTarget = ImpersonatePerson & { buy_org_name: string } + +type ImpersonateFieldsMetric = { + user: ImpersonateUser + target: ImpersonateTarget + date: string +} + +type ImpersonateMetric = Metric & { fields: ImpersonateFieldsMetric } + +type ImpersonatePersonParams = { + costCenterId: string + costCenterName: string + organizationId: string + email: string +} + +type ImpersonateUserParams = ImpersonatePersonParams +type ImpersonateTargetParams = ImpersonatePersonParams & { + organizationName: string +} + +export type ImpersonateMetricParams = { + account: string + target: ImpersonateTargetParams + user: ImpersonateUserParams +} + +const buildMetric = (metricParams: ImpersonateMetricParams) => { + const { account, user, target } = metricParams + + const metric = { + name: 'b2b-suite-buyerorg-data' as const, + account, + fields: { + user: { + email: user.email, + buy_org_id: user.organizationId, + cost_center_id: user.costCenterId, + cost_center_name: user.costCenterName, + }, + target: { + email: target.email, + buy_org_id: target.organizationId, + buy_org_name: target.organizationName, + cost_center_id: target.costCenterId, + cost_center_name: target.costCenterName, + }, + date: new Date().toISOString(), + }, + } + + return metric +} + +const buildImpersonateMetric = ( + metricParams: ImpersonateMetricParams +): ImpersonateMetric => { + const metric: ImpersonateMetric = { + kind: 'impersonate-ui-event', + description: 'Impersonate User Action - UI', + ...buildMetric(metricParams), + } + + return metric +} + +const buildStopImpersonateMetric = ( + metricParams: ImpersonateMetricParams +): ImpersonateMetric => { + const metric: ImpersonateMetric = { + kind: 'stop-impersonate-ui-event', + description: 'Stop Impersonate User Action - UI', + ...buildMetric(metricParams), + } + + return metric +} + +export const sendImpersonateMetric = async ( + metricParams: ImpersonateMetricParams +) => { + try { + const metric = buildImpersonateMetric(metricParams) + + await sendMetric(metric) + } catch (error) { + console.warn('Unable to log metrics', error) + } +} + +export const sendStopImpersonateMetric = async ( + metricParams: ImpersonateMetricParams +) => { + try { + const metric = buildStopImpersonateMetric(metricParams) + + await sendMetric(metric) + } catch (error) { + console.warn('Unable to log metrics', error) + } +} diff --git a/react/utils/metrics/metrics.ts b/react/utils/metrics/metrics.ts new file mode 100644 index 00000000..26ee22ae --- /dev/null +++ b/react/utils/metrics/metrics.ts @@ -0,0 +1,26 @@ +import axios from 'axios' + +const ANALYTICS_URL = 'https://rc.vtex.com/api/analytics/schemaless-events' + +type ImpersonateMetric = { + kind: 'impersonate-ui-event' + description: 'Impersonate User Action - UI' +} + +type StopImpersonateMetric = { + kind: 'stop-impersonate-ui-event' + description: 'Stop Impersonate User Action - UI' +} + +export type Metric = { + name: 'b2b-suite-buyerorg-data' + account: string +} & (ImpersonateMetric | StopImpersonateMetric) + +export const sendMetric = async (metric: Metric) => { + try { + await axios.post(ANALYTICS_URL, metric) + } catch (error) { + console.warn('Unable to log metrics', error) + } +} diff --git a/react/yarn.lock b/react/yarn.lock index ee0d4848..a33682cf 100644 --- a/react/yarn.lock +++ b/react/yarn.lock @@ -2495,6 +2495,15 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-jest@^25.5.1: version "25.5.1" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.5.1.tgz#bc2e6101f849d6f6aec09720ffc7bc5332e62853" @@ -2851,7 +2860,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -3335,6 +3344,11 @@ focus-visible@^5.2.0: resolved "https://registry.yarnpkg.com/focus-visible/-/focus-visible-5.2.0.tgz#3a9e41fccf587bd25dcc2ef045508284f0a4d6b3" integrity sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -3345,6 +3359,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -4970,6 +4993,11 @@ prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" From eb14727fc00f93995e704f98746f1b8678ee071a Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Fri, 28 Jul 2023 13:52:10 -0300 Subject: [PATCH 02/22] Add Impersonate metrics --- react/components/OrganizationUsersTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/components/OrganizationUsersTable.tsx b/react/components/OrganizationUsersTable.tsx index dbbccb51..f2fe8059 100644 --- a/react/components/OrganizationUsersTable.tsx +++ b/react/components/OrganizationUsersTable.tsx @@ -485,7 +485,7 @@ const OrganizationUsersTable: FunctionComponent = ({ } const handleNextClick = () => { - const newPage = 0 // variableState.page + 1 + const newPage = variableState.page + 1 setVariables({ ...variableState, From c837501d9f82949f51c138f875222d585333022b Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Fri, 28 Jul 2023 15:56:38 -0300 Subject: [PATCH 03/22] Add changelog info --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16c7a109..937ddfa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +- Added metrics to Impersonate/Stop Impersonating events + ## [1.25.0] - 2023-07-24 ### Added From 3bef490130a44b679627f6e9adc1212ebd1506e6 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Mon, 31 Jul 2023 10:11:47 -0300 Subject: [PATCH 04/22] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 937ddfa4..0ad56fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + - Added metrics to Impersonate/Stop Impersonating events ## [1.25.0] - 2023-07-24 From 3dcbf6ed39472f0a3245a8405f3877b22d980b26 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Mon, 31 Jul 2023 10:14:43 -0300 Subject: [PATCH 05/22] Changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ad56fd3..590286d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added -- Added metrics to Impersonate/Stop Impersonating events +- Added metrics to Impersonate events +- Added metrics to Stop Impersonating events ## [1.25.0] - 2023-07-24 From 428759f9ef14e770a24cb7aaf3e94aa4545c3a12 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Mon, 31 Jul 2023 11:03:38 -0300 Subject: [PATCH 06/22] Handle possible undefined --- react/components/OrganizationUsersTable.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/react/components/OrganizationUsersTable.tsx b/react/components/OrganizationUsersTable.tsx index f2fe8059..321aa115 100644 --- a/react/components/OrganizationUsersTable.tsx +++ b/react/components/OrganizationUsersTable.tsx @@ -383,16 +383,11 @@ const OrganizationUsersTable: FunctionComponent = ({ showToast(formatMessage(storeMessages.toastImpersonateFailure)) } } else { - const { - id: costCenterId, - name: costCenterName, - organization, - } = costCenterData?.getCostCenterByIdStorefront - const user = { - costCenterId, - costCenterName, - organizationId: organization, + costCenterId: costCenterData?.getCostCenterByIdStorefront.id, + costCenterName: costCenterData?.getCostCenterByIdStorefront.name, + organizationId: + costCenterData?.getCostCenterByIdStorefront.organization, email: sessionResponse?.namespaces.profile?.email?.value, } @@ -485,7 +480,7 @@ const OrganizationUsersTable: FunctionComponent = ({ } const handleNextClick = () => { - const newPage = variableState.page + 1 + const newPage = 1 // variableState.page + 1 setVariables({ ...variableState, From 4d3f00ff546aa74ab66c228a90d336cc4b0fd41a Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Mon, 31 Jul 2023 11:03:57 -0300 Subject: [PATCH 07/22] Handle possible undefined --- react/components/OrganizationUsersTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/components/OrganizationUsersTable.tsx b/react/components/OrganizationUsersTable.tsx index 321aa115..600f4ae7 100644 --- a/react/components/OrganizationUsersTable.tsx +++ b/react/components/OrganizationUsersTable.tsx @@ -480,7 +480,7 @@ const OrganizationUsersTable: FunctionComponent = ({ } const handleNextClick = () => { - const newPage = 1 // variableState.page + 1 + const newPage = variableState.page + 1 setVariables({ ...variableState, From 6a3a12d1c090baf7f5d33b896caf0516f156e799 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Tue, 1 Aug 2023 17:01:30 -0300 Subject: [PATCH 08/22] Add change team metric --- react/components/UserWidget.tsx | 20 +++++++++++++ react/utils/metrics/changeTeam.ts | 49 +++++++++++++++++++++++++++++++ react/utils/metrics/metrics.ts | 7 ++++- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 react/utils/metrics/changeTeam.ts diff --git a/react/components/UserWidget.tsx b/react/components/UserWidget.tsx index c706b25e..608bec32 100644 --- a/react/components/UserWidget.tsx +++ b/react/components/UserWidget.tsx @@ -28,6 +28,8 @@ import { } from '../utils/constants' import '../css/user-widget.css' import { sendStopImpersonateMetric } from '../utils/metrics/impersonate' +import type { ChangeTeamParams } from '../utils/metrics/changeTeam' +import { sendChangeTeamMetric } from '../utils/metrics/changeTeam' const CSS_HANDLES = [ 'userWidgetContainer', @@ -291,6 +293,24 @@ const UserWidget: VtexFunctionComponent = ({ setLoadingState(false) } + const { + currentCostCenter: costCenterId, + currentOrganization: orgId, + currentRoleName: userRole, + } = organizationsState + + const profile = sessionResponse?.namespaces?.profile + + const metricParams: ChangeTeamParams = { + account: sessionResponse?.namespaces?.account?.accountName?.value, + userId: profile?.id.value, + userRole, + userEmail: profile?.email.value, + orgId, + costCenterId, + } + + sendChangeTeamMetric(metricParams) window.location.reload() } diff --git a/react/utils/metrics/changeTeam.ts b/react/utils/metrics/changeTeam.ts new file mode 100644 index 00000000..0e9b0b28 --- /dev/null +++ b/react/utils/metrics/changeTeam.ts @@ -0,0 +1,49 @@ +import type { Metric } from './metrics' +import { sendMetric } from './metrics' + +type ChangeTeamFieldsMetric = { + date: string + user_id: string + user_role: string + user_email: string + org_id: string + cost_center_id: string +} + +type ChangeTeamMetric = Metric & { fields: ChangeTeamFieldsMetric } + +export type ChangeTeamParams = { + account: string + userId: string + userRole: string + userEmail: string + orgId: string + costCenterId: string +} + +const buildMetric = (metricParams: ChangeTeamParams): ChangeTeamMetric => { + return { + name: 'b2b-suite-buyerorg-data' as const, + account: metricParams.account, + kind: 'change-team-ui-event', + description: 'User change team/organization - UI', + fields: { + date: new Date().toISOString(), + user_id: metricParams.userId, + user_role: metricParams.userRole, + user_email: metricParams.userEmail, + org_id: metricParams.orgId, + cost_center_id: metricParams.costCenterId, + }, + } +} + +export const sendChangeTeamMetric = async (metricParams: ChangeTeamParams) => { + try { + const metric: ChangeTeamMetric = buildMetric(metricParams) + + await sendMetric(metric) + } catch (error) { + console.warn('Unable to log metrics', error) + } +} diff --git a/react/utils/metrics/metrics.ts b/react/utils/metrics/metrics.ts index 26ee22ae..324d0372 100644 --- a/react/utils/metrics/metrics.ts +++ b/react/utils/metrics/metrics.ts @@ -12,10 +12,15 @@ type StopImpersonateMetric = { description: 'Stop Impersonate User Action - UI' } +export type ChangeTeamMetric = { + kind: 'change-team-ui-event' + description: 'User change team/organization - UI' +} + export type Metric = { name: 'b2b-suite-buyerorg-data' account: string -} & (ImpersonateMetric | StopImpersonateMetric) +} & (ImpersonateMetric | StopImpersonateMetric | ChangeTeamMetric) export const sendMetric = async (metric: Metric) => { try { From e51d6db7a1a10997d7e232ae01bfd112d52ba379 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Tue, 1 Aug 2023 17:02:32 -0300 Subject: [PATCH 09/22] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 590286d7..52afe240 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added metrics to Impersonate events - Added metrics to Stop Impersonating events +- Added metrics to change team events ## [1.25.0] - 2023-07-24 From 9de8c4f89f7d55acbcc02074aefd50e6aeb79914 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Wed, 2 Aug 2023 16:28:40 -0300 Subject: [PATCH 10/22] Use get session to get data from impersonate user --- react/components/OrganizationUsersTable.tsx | 30 +---- react/components/UserWidget.tsx | 31 +---- react/utils/constants.ts | 2 - react/utils/metrics/impersonate.ts | 130 ++++++++++++-------- 4 files changed, 89 insertions(+), 104 deletions(-) diff --git a/react/components/OrganizationUsersTable.tsx b/react/components/OrganizationUsersTable.tsx index 600f4ae7..fc4bc820 100644 --- a/react/components/OrganizationUsersTable.tsx +++ b/react/components/OrganizationUsersTable.tsx @@ -16,11 +16,7 @@ import UPDATE_USER from '../graphql/updateUser.graphql' import REMOVE_USER from '../graphql/removeUser.graphql' import GET_COST_CENTER from '../graphql/getCostCenterStorefront.graphql' import IMPERSONATE_USER from '../graphql/impersonateB2BUser.graphql' -import { - B2B_CHECKOUT_SESSION_KEY, - B2B_IMPERSONATE_USER, -} from '../utils/constants' -import { useSessionResponse } from '../modules/session' +import { B2B_CHECKOUT_SESSION_KEY } from '../utils/constants' import { sendImpersonateMetric } from '../utils/metrics/impersonate' interface Props { @@ -90,7 +86,6 @@ const OrganizationUsersTable: FunctionComponent = ({ isAdmin = false, isSalesAdmin = false, }) => { - const sessionResponse: any = useSessionResponse() const { formatMessage } = useIntl() const { showToast } = useContext(ToastContext) const toast = isAdmin ? useToast() : null @@ -383,28 +378,11 @@ const OrganizationUsersTable: FunctionComponent = ({ showToast(formatMessage(storeMessages.toastImpersonateFailure)) } } else { - const user = { - costCenterId: costCenterData?.getCostCenterByIdStorefront.id, - costCenterName: costCenterData?.getCostCenterByIdStorefront.name, - organizationId: - costCenterData?.getCostCenterByIdStorefront.organization, - email: sessionResponse?.namespaces.profile?.email?.value, - } - const metricParams = { - account: sessionResponse?.namespaces?.account?.accountName?.value, - target: { - costCenterId: rowData.costId, - costCenterName: rowData.costCenterName, - organizationId: rowData.orgId, - organizationName: rowData.organizationName, - email: rowData.email, - }, - user, + costCenterData, + target: rowData, } - sessionStorage.setItem(B2B_IMPERSONATE_USER, JSON.stringify(user)) - sendImpersonateMetric(metricParams) window.location.reload() } @@ -480,7 +458,7 @@ const OrganizationUsersTable: FunctionComponent = ({ } const handleNextClick = () => { - const newPage = variableState.page + 1 + const newPage = 1 // variableState.page + 1 setVariables({ ...variableState, diff --git a/react/components/UserWidget.tsx b/react/components/UserWidget.tsx index c706b25e..636b6163 100644 --- a/react/components/UserWidget.tsx +++ b/react/components/UserWidget.tsx @@ -22,10 +22,7 @@ import { useSessionResponse } from '../modules/session' import USER_WIDGET_QUERY from '../graphql/userWidgetQuery.graphql' import SET_CURRENT_ORGANIZATION from '../graphql/setCurrentOrganization.graphql' import STOP_IMPERSONATION from '../graphql/impersonateUser.graphql' -import { - B2B_CHECKOUT_SESSION_KEY, - B2B_IMPERSONATE_USER, -} from '../utils/constants' +import { B2B_CHECKOUT_SESSION_KEY } from '../utils/constants' import '../css/user-widget.css' import { sendStopImpersonateMetric } from '../utils/metrics/impersonate' @@ -220,33 +217,13 @@ const UserWidget: VtexFunctionComponent = ({ sessionStorage.removeItem(B2B_CHECKOUT_SESSION_KEY) } - const { - currentCostCenter: costCenterId, - costCenterInput: costCenterName, - currentOrganization: organizationId, - organizationInput: organizationName, - } = organizationsState - - const target = { - costCenterId, - costCenterName, - organizationId, - organizationName, - email: userWidgetData?.checkImpersonation?.email as string, - } - - const userStorage = sessionStorage.getItem(B2B_IMPERSONATE_USER) - const user = userStorage ? JSON.parse(userStorage) : undefined - const metricParams = { - account: sessionResponse?.namespaces?.account?.accountName?.value, - target, - user, + sessionResponse, + email: userWidgetData?.checkImpersonation?.email as string, + ...organizationsState, } sendStopImpersonateMetric(metricParams) - sessionStorage.removeItem(B2B_IMPERSONATE_USER) - window.location.reload() }) .catch(error => { diff --git a/react/utils/constants.ts b/react/utils/constants.ts index 1e032095..4c7a4190 100644 --- a/react/utils/constants.ts +++ b/react/utils/constants.ts @@ -1,3 +1 @@ export const B2B_CHECKOUT_SESSION_KEY = 'b2b-checkout-settings' - -export const B2B_IMPERSONATE_USER = 'b2b-impersonate-user' diff --git a/react/utils/metrics/impersonate.ts b/react/utils/metrics/impersonate.ts index 2268cded..0f1fbf3b 100644 --- a/react/utils/metrics/impersonate.ts +++ b/react/utils/metrics/impersonate.ts @@ -1,15 +1,17 @@ +import type { B2BUserSimple } from '../../components/OrganizationUsersTable' +import { getSession } from '../../modules/session' import type { Metric } from './metrics' import { sendMetric } from './metrics' type ImpersonatePerson = { email: string - buy_org_id: string + buyer_org_id: string cost_center_id: string cost_center_name: string } type ImpersonateUser = ImpersonatePerson -type ImpersonateTarget = ImpersonatePerson & { buy_org_name: string } +type ImpersonateTarget = ImpersonatePerson & { buyer_org_name: string } type ImpersonateFieldsMetric = { user: ImpersonateUser @@ -19,80 +21,110 @@ type ImpersonateFieldsMetric = { type ImpersonateMetric = Metric & { fields: ImpersonateFieldsMetric } -type ImpersonatePersonParams = { - costCenterId: string - costCenterName: string - organizationId: string - email: string +export type ImpersonateMetricParams = { + costCenterData: { + getCostCenterByIdStorefront: { + id: string + name: string + organization: string + } + } + target: B2BUserSimple } -type ImpersonateUserParams = ImpersonatePersonParams -type ImpersonateTargetParams = ImpersonatePersonParams & { - organizationName: string +export type StopImpersonateMetricParams = { + sessionResponse: SessionResponseParam + currentCostCenter: string + costCenterInput: string + currentOrganization: string + organizationInput: string + email: string } -export type ImpersonateMetricParams = { - account: string - target: ImpersonateTargetParams - user: ImpersonateUserParams +type SessionResponseParam = { + namespaces: { + account: { + accountName: { + value: string + } + } + profile: { + email: { + value: string + } + } + authentication: { + storeUserEmail: { + value: string + } + } + } } -const buildMetric = (metricParams: ImpersonateMetricParams) => { - const { account, user, target } = metricParams +const buildImpersonateMetric = async ( + metricParams: ImpersonateMetricParams +) => { + const { target, costCenterData } = metricParams + + const session = await getSession() + const sessionResponse = (session?.response as unknown) as SessionResponseParam - const metric = { + return { name: 'b2b-suite-buyerorg-data' as const, - account, + kind: 'impersonate-ui-event', + description: 'Impersonate User Action - UI', + account: sessionResponse?.namespaces?.account?.accountName?.value, fields: { user: { - email: user.email, - buy_org_id: user.organizationId, - cost_center_id: user.costCenterId, - cost_center_name: user.costCenterName, + email: sessionResponse?.namespaces?.profile?.email?.value, + buyer_org_id: costCenterData?.getCostCenterByIdStorefront.organization, + cost_center_id: costCenterData?.getCostCenterByIdStorefront.id, + cost_center_name: costCenterData?.getCostCenterByIdStorefront.name, }, target: { email: target.email, - buy_org_id: target.organizationId, - buy_org_name: target.organizationName, - cost_center_id: target.costCenterId, + buyer_org_id: target.orgId, + buyer_org_name: target.organizationName, + cost_center_id: target.costId, cost_center_name: target.costCenterName, }, date: new Date().toISOString(), }, - } - - return metric -} - -const buildImpersonateMetric = ( - metricParams: ImpersonateMetricParams -): ImpersonateMetric => { - const metric: ImpersonateMetric = { - kind: 'impersonate-ui-event', - description: 'Impersonate User Action - UI', - ...buildMetric(metricParams), - } - - return metric + } as ImpersonateMetric } const buildStopImpersonateMetric = ( - metricParams: ImpersonateMetricParams -): ImpersonateMetric => { - const metric: ImpersonateMetric = { + metricParams: StopImpersonateMetricParams +) => { + return { + name: 'b2b-suite-buyerorg-data' as const, kind: 'stop-impersonate-ui-event', description: 'Stop Impersonate User Action - UI', - ...buildMetric(metricParams), - } - - return metric + account: + metricParams.sessionResponse?.namespaces?.account?.accountName?.value, + fields: { + user: { + email: + metricParams.sessionResponse?.namespaces?.authentication + ?.storeUserEmail?.value, + }, + target: { + email: metricParams.email, + buyer_org_id: metricParams.currentOrganization, + buyer_org_name: metricParams.organizationInput, + cost_center_id: metricParams.currentCostCenter, + cost_center_name: metricParams.costCenterInput, + }, + date: new Date().toISOString(), + }, + } as ImpersonateMetric } export const sendImpersonateMetric = async ( metricParams: ImpersonateMetricParams ) => { try { - const metric = buildImpersonateMetric(metricParams) + const metric = await buildImpersonateMetric(metricParams) await sendMetric(metric) } catch (error) { @@ -101,7 +133,7 @@ export const sendImpersonateMetric = async ( } export const sendStopImpersonateMetric = async ( - metricParams: ImpersonateMetricParams + metricParams: StopImpersonateMetricParams ) => { try { const metric = buildStopImpersonateMetric(metricParams) From 614fa9e847501462c5caa4bc059f16beac8e4456 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Wed, 2 Aug 2023 16:28:53 -0300 Subject: [PATCH 11/22] Use get session to get data from impersonate user --- react/components/OrganizationUsersTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/components/OrganizationUsersTable.tsx b/react/components/OrganizationUsersTable.tsx index fc4bc820..8fb93f56 100644 --- a/react/components/OrganizationUsersTable.tsx +++ b/react/components/OrganizationUsersTable.tsx @@ -458,7 +458,7 @@ const OrganizationUsersTable: FunctionComponent = ({ } const handleNextClick = () => { - const newPage = 1 // variableState.page + 1 + const newPage = variableState.page + 1 setVariables({ ...variableState, From df45f1ec77defff0835e6887321ae2973d8b64c2 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Wed, 2 Aug 2023 18:14:12 -0300 Subject: [PATCH 12/22] Removing jsx react line --- react/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/react/tsconfig.json b/react/tsconfig.json index b19356c2..9d838926 100644 --- a/react/tsconfig.json +++ b/react/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "@vtex/tsconfig", "compilerOptions": { - "jsx": "react", "noEmitOnError": false, "lib": ["dom"], "module": "esnext", From 4ad2060ce6da9ac69f4639aea2fc4c95054b460b Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Wed, 2 Aug 2023 21:34:09 -0300 Subject: [PATCH 13/22] Send Change Team metric just on success --- react/components/UserWidget.tsx | 25 ++++++--------------- react/utils/metrics/changeTeam.ts | 35 ++++++++++++++++++------------ react/utils/metrics/impersonate.ts | 22 +------------------ react/utils/metrics/metrics.ts | 20 +++++++++++++++++ 4 files changed, 49 insertions(+), 53 deletions(-) diff --git a/react/components/UserWidget.tsx b/react/components/UserWidget.tsx index 55cdd8de..66af749e 100644 --- a/react/components/UserWidget.tsx +++ b/react/components/UserWidget.tsx @@ -264,30 +264,19 @@ const UserWidget: VtexFunctionComponent = ({ costId: organizationsState.currentCostCenter, }, }) + + const metricParams: ChangeTeamParams = { + sessionResponse, + ...organizationsState, + } + + sendChangeTeamMetric(metricParams) } catch (error) { setErrorOrganization(true) } finally { setLoadingState(false) } - const { - currentCostCenter: costCenterId, - currentOrganization: orgId, - currentRoleName: userRole, - } = organizationsState - - const profile = sessionResponse?.namespaces?.profile - - const metricParams: ChangeTeamParams = { - account: sessionResponse?.namespaces?.account?.accountName?.value, - userId: profile?.id.value, - userRole, - userEmail: profile?.email.value, - orgId, - costCenterId, - } - - sendChangeTeamMetric(metricParams) window.location.reload() } diff --git a/react/utils/metrics/changeTeam.ts b/react/utils/metrics/changeTeam.ts index 0e9b0b28..1ca2a7eb 100644 --- a/react/utils/metrics/changeTeam.ts +++ b/react/utils/metrics/changeTeam.ts @@ -1,9 +1,8 @@ -import type { Metric } from './metrics' +import type { Metric, SessionResponseParam } from './metrics' import { sendMetric } from './metrics' type ChangeTeamFieldsMetric = { date: string - user_id: string user_role: string user_email: string org_id: string @@ -12,28 +11,36 @@ type ChangeTeamFieldsMetric = { type ChangeTeamMetric = Metric & { fields: ChangeTeamFieldsMetric } +export type StopImpersonateMetricParams = { + sessionResponse: SessionResponseParam + currentCostCenter: string + costCenterInput: string + currentOrganization: string + organizationInput: string + email: string +} + export type ChangeTeamParams = { - account: string - userId: string - userRole: string - userEmail: string - orgId: string - costCenterId: string + sessionResponse: SessionResponseParam + currentRoleName: string + currentOrganization: string + currentCostCenter: string } const buildMetric = (metricParams: ChangeTeamParams): ChangeTeamMetric => { return { name: 'b2b-suite-buyerorg-data' as const, - account: metricParams.account, + account: + metricParams.sessionResponse?.namespaces?.account?.accountName?.value, kind: 'change-team-ui-event', description: 'User change team/organization - UI', fields: { date: new Date().toISOString(), - user_id: metricParams.userId, - user_role: metricParams.userRole, - user_email: metricParams.userEmail, - org_id: metricParams.orgId, - cost_center_id: metricParams.costCenterId, + user_role: metricParams.currentRoleName, + user_email: + metricParams.sessionResponse?.namespaces?.profile?.email?.value, + org_id: metricParams.currentOrganization, + cost_center_id: metricParams.currentCostCenter, }, } } diff --git a/react/utils/metrics/impersonate.ts b/react/utils/metrics/impersonate.ts index 0f1fbf3b..443c8e1a 100644 --- a/react/utils/metrics/impersonate.ts +++ b/react/utils/metrics/impersonate.ts @@ -1,6 +1,6 @@ import type { B2BUserSimple } from '../../components/OrganizationUsersTable' import { getSession } from '../../modules/session' -import type { Metric } from './metrics' +import type { Metric, SessionResponseParam } from './metrics' import { sendMetric } from './metrics' type ImpersonatePerson = { @@ -41,26 +41,6 @@ export type StopImpersonateMetricParams = { email: string } -type SessionResponseParam = { - namespaces: { - account: { - accountName: { - value: string - } - } - profile: { - email: { - value: string - } - } - authentication: { - storeUserEmail: { - value: string - } - } - } -} - const buildImpersonateMetric = async ( metricParams: ImpersonateMetricParams ) => { diff --git a/react/utils/metrics/metrics.ts b/react/utils/metrics/metrics.ts index 324d0372..f5f42288 100644 --- a/react/utils/metrics/metrics.ts +++ b/react/utils/metrics/metrics.ts @@ -29,3 +29,23 @@ export const sendMetric = async (metric: Metric) => { console.warn('Unable to log metrics', error) } } + +export type SessionResponseParam = { + namespaces: { + account: { + accountName: { + value: string + } + } + profile: { + email: { + value: string + } + } + authentication: { + storeUserEmail: { + value: string + } + } + } +} From 0b56688f0c28e2e9e3a65f26d323b064e3f5a2d6 Mon Sep 17 00:00:00 2001 From: mtakeda <1576737+mtakeda@users.noreply.github.com> Date: Thu, 3 Aug 2023 00:48:42 +0000 Subject: [PATCH 14/22] Release v1.26.0 --- CHANGELOG.md | 2 ++ manifest.json | 2 +- react/package.json | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52afe240..782d8a47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [1.26.0] - 2023-08-03 + ### Added - Added metrics to Impersonate events diff --git a/manifest.json b/manifest.json index 571db2a2..b915a104 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "b2b-organizations", "vendor": "vtex", - "version": "1.25.0", + "version": "1.26.0", "title": "B2B Organizations", "description": "App to create and manage B2B Organizations and Cost Centers", "mustUpdateAt": "2022-08-28", diff --git a/react/package.json b/react/package.json index b5c96d4a..ff30b9c2 100644 --- a/react/package.json +++ b/react/package.json @@ -1,6 +1,6 @@ { "name": "vtex.b2b-organizations", - "version": "1.25.0", + "version": "1.26.0", "license": "UNLICENSED", "scripts": { "test": "vtex-test-tools test --passWithNoTests" From b0204999a4c0a7a0b1690f9ef39847da730b3aaa Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Wed, 2 Aug 2023 22:48:52 -0300 Subject: [PATCH 15/22] Changelog entry --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 782d8a47..52afe240 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -## [1.26.0] - 2023-08-03 - ### Added - Added metrics to Impersonate events From a8b47f5b807ce772fcbb07b15efdb3d90a5cb940 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Thu, 3 Aug 2023 10:57:07 -0300 Subject: [PATCH 16/22] Use already existent interface Session to derive fields --- react/components/UserWidget.tsx | 2 +- react/modules/session.ts | 4 ++-- react/utils/metrics/metrics.ts | 17 +++++------------ 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/react/components/UserWidget.tsx b/react/components/UserWidget.tsx index 66af749e..354eb482 100644 --- a/react/components/UserWidget.tsx +++ b/react/components/UserWidget.tsx @@ -221,7 +221,7 @@ const UserWidget: VtexFunctionComponent = ({ const metricParams = { sessionResponse, - email: userWidgetData?.checkImpersonation?.email as string, + email: userWidgetData?.checkImpersonation?.email, ...organizationsState, } diff --git a/react/modules/session.ts b/react/modules/session.ts index 79d17c05..499851b6 100644 --- a/react/modules/session.ts +++ b/react/modules/session.ts @@ -1,10 +1,10 @@ import { useState, useEffect } from 'react' -interface KeyValue { +export interface KeyValue { value: string } -interface Session { +export interface Session { id: string namespaces: { store: { diff --git a/react/utils/metrics/metrics.ts b/react/utils/metrics/metrics.ts index f5f42288..d22962a1 100644 --- a/react/utils/metrics/metrics.ts +++ b/react/utils/metrics/metrics.ts @@ -1,5 +1,7 @@ import axios from 'axios' +import type { KeyValue, Session } from '../../modules/session' + const ANALYTICS_URL = 'https://rc.vtex.com/api/analytics/schemaless-events' type ImpersonateMetric = { @@ -30,22 +32,13 @@ export const sendMetric = async (metric: Metric) => { } } -export type SessionResponseParam = { +export type SessionResponseParam = Session & { namespaces: { account: { - accountName: { - value: string - } - } - profile: { - email: { - value: string - } + accountName: KeyValue } authentication: { - storeUserEmail: { - value: string - } + storeUserEmail: KeyValue } } } From 973f8aebc2a486b6244e3d2ad1d0d6e390b27e9b Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Thu, 3 Aug 2023 11:39:39 -0300 Subject: [PATCH 17/22] Handle session type with no casting --- react/modules/session.ts | 9 ++++++++- react/utils/metrics/changeTeam.ts | 7 ++++--- react/utils/metrics/impersonate.ts | 17 ++++++++++++----- react/utils/metrics/metrics.ts | 13 ------------- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/react/modules/session.ts b/react/modules/session.ts index 499851b6..98356c65 100644 --- a/react/modules/session.ts +++ b/react/modules/session.ts @@ -1,10 +1,11 @@ import { useState, useEffect } from 'react' -export interface KeyValue { +interface KeyValue { value: string } export interface Session { + type?: 'Session' id: string namespaces: { store: { @@ -14,6 +15,12 @@ export interface Session { isAuthenticated: KeyValue email?: KeyValue } + account: { + accountName: KeyValue + } + authentication: { + storeUserEmail: KeyValue + } } } diff --git a/react/utils/metrics/changeTeam.ts b/react/utils/metrics/changeTeam.ts index 1ca2a7eb..e7a4ada5 100644 --- a/react/utils/metrics/changeTeam.ts +++ b/react/utils/metrics/changeTeam.ts @@ -1,4 +1,5 @@ -import type { Metric, SessionResponseParam } from './metrics' +import type { Session } from '../../modules/session' +import type { Metric } from './metrics' import { sendMetric } from './metrics' type ChangeTeamFieldsMetric = { @@ -12,7 +13,7 @@ type ChangeTeamFieldsMetric = { type ChangeTeamMetric = Metric & { fields: ChangeTeamFieldsMetric } export type StopImpersonateMetricParams = { - sessionResponse: SessionResponseParam + sessionResponse: Session currentCostCenter: string costCenterInput: string currentOrganization: string @@ -21,7 +22,7 @@ export type StopImpersonateMetricParams = { } export type ChangeTeamParams = { - sessionResponse: SessionResponseParam + sessionResponse: Session currentRoleName: string currentOrganization: string currentCostCenter: string diff --git a/react/utils/metrics/impersonate.ts b/react/utils/metrics/impersonate.ts index 443c8e1a..9f414039 100644 --- a/react/utils/metrics/impersonate.ts +++ b/react/utils/metrics/impersonate.ts @@ -1,6 +1,7 @@ import type { B2BUserSimple } from '../../components/OrganizationUsersTable' +import type { Session } from '../../modules/session' import { getSession } from '../../modules/session' -import type { Metric, SessionResponseParam } from './metrics' +import type { Metric } from './metrics' import { sendMetric } from './metrics' type ImpersonatePerson = { @@ -33,7 +34,7 @@ export type ImpersonateMetricParams = { } export type StopImpersonateMetricParams = { - sessionResponse: SessionResponseParam + sessionResponse: Session currentCostCenter: string costCenterInput: string currentOrganization: string @@ -47,16 +48,22 @@ const buildImpersonateMetric = async ( const { target, costCenterData } = metricParams const session = await getSession() - const sessionResponse = (session?.response as unknown) as SessionResponseParam + const sessionResponse = session?.response + const isSession = + !sessionResponse?.type || sessionResponse?.type === 'Session' return { name: 'b2b-suite-buyerorg-data' as const, kind: 'impersonate-ui-event', description: 'Impersonate User Action - UI', - account: sessionResponse?.namespaces?.account?.accountName?.value, + account: isSession + ? sessionResponse?.namespaces?.account?.accountName?.value + : undefined, fields: { user: { - email: sessionResponse?.namespaces?.profile?.email?.value, + email: isSession + ? sessionResponse?.namespaces?.profile?.email?.value + : undefined, buyer_org_id: costCenterData?.getCostCenterByIdStorefront.organization, cost_center_id: costCenterData?.getCostCenterByIdStorefront.id, cost_center_name: costCenterData?.getCostCenterByIdStorefront.name, diff --git a/react/utils/metrics/metrics.ts b/react/utils/metrics/metrics.ts index d22962a1..324d0372 100644 --- a/react/utils/metrics/metrics.ts +++ b/react/utils/metrics/metrics.ts @@ -1,7 +1,5 @@ import axios from 'axios' -import type { KeyValue, Session } from '../../modules/session' - const ANALYTICS_URL = 'https://rc.vtex.com/api/analytics/schemaless-events' type ImpersonateMetric = { @@ -31,14 +29,3 @@ export const sendMetric = async (metric: Metric) => { console.warn('Unable to log metrics', error) } } - -export type SessionResponseParam = Session & { - namespaces: { - account: { - accountName: KeyValue - } - authentication: { - storeUserEmail: KeyValue - } - } -} From 463226bde2bd7f27f56eb6668efc6e9ddc2b9e16 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Thu, 3 Aug 2023 11:41:09 -0300 Subject: [PATCH 18/22] Revert version in package.json --- react/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/package.json b/react/package.json index ff30b9c2..b5c96d4a 100644 --- a/react/package.json +++ b/react/package.json @@ -1,6 +1,6 @@ { "name": "vtex.b2b-organizations", - "version": "1.26.0", + "version": "1.25.0", "license": "UNLICENSED", "scripts": { "test": "vtex-test-tools test --passWithNoTests" From 90959036a37f292bf91654bcba38a97bbcc68b9f Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Thu, 3 Aug 2023 11:49:47 -0300 Subject: [PATCH 19/22] Casting to make webpack happy --- react/utils/metrics/impersonate.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/react/utils/metrics/impersonate.ts b/react/utils/metrics/impersonate.ts index 9f414039..868cba30 100644 --- a/react/utils/metrics/impersonate.ts +++ b/react/utils/metrics/impersonate.ts @@ -50,19 +50,20 @@ const buildImpersonateMetric = async ( const session = await getSession() const sessionResponse = session?.response const isSession = - !sessionResponse?.type || sessionResponse?.type === 'Session' + sessionResponse && + (!sessionResponse.type || sessionResponse.type === 'Session') return { name: 'b2b-suite-buyerorg-data' as const, kind: 'impersonate-ui-event', description: 'Impersonate User Action - UI', account: isSession - ? sessionResponse?.namespaces?.account?.accountName?.value + ? (sessionResponse as Session).namespaces?.account?.accountName?.value : undefined, fields: { user: { email: isSession - ? sessionResponse?.namespaces?.profile?.email?.value + ? (sessionResponse as Session).namespaces?.profile?.email?.value : undefined, buyer_org_id: costCenterData?.getCostCenterByIdStorefront.organization, cost_center_id: costCenterData?.getCostCenterByIdStorefront.id, From 372de21645dd254620ae3dba806ea10afb5518c6 Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Thu, 3 Aug 2023 11:50:54 -0300 Subject: [PATCH 20/22] Optional value to user email --- react/utils/metrics/changeTeam.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/utils/metrics/changeTeam.ts b/react/utils/metrics/changeTeam.ts index e7a4ada5..7c1bfc53 100644 --- a/react/utils/metrics/changeTeam.ts +++ b/react/utils/metrics/changeTeam.ts @@ -5,7 +5,7 @@ import { sendMetric } from './metrics' type ChangeTeamFieldsMetric = { date: string user_role: string - user_email: string + user_email?: string org_id: string cost_center_id: string } From fd911c8a131abd1370670881022dd06ad951f90d Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Mon, 14 Aug 2023 09:47:50 -0300 Subject: [PATCH 21/22] Lint --- react/components/EditUserModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/react/components/EditUserModal.tsx b/react/components/EditUserModal.tsx index c9bcfcbf..1d6ed328 100644 --- a/react/components/EditUserModal.tsx +++ b/react/components/EditUserModal.tsx @@ -43,7 +43,7 @@ const EditUserModal: FunctionComponent = ({ canEdit, canEditSales, isSalesAdmin, - canManageOrg + canManageOrg, }) => { const { formatMessage } = useIntl() const [userState, setUserState] = useState({} as UserDetails) @@ -116,7 +116,7 @@ const EditUserModal: FunctionComponent = ({ const filteredArray = rolesData.listRoles.filter((role: any) => { if (isAdmin) return true - if(canManageOrg) { + if (canManageOrg) { return true } From a7c3435aa73e7004055b8bc9172718b529c2fccc Mon Sep 17 00:00:00 2001 From: Mauro Takeda Date: Mon, 14 Aug 2023 09:53:31 -0300 Subject: [PATCH 22/22] Lint --- react/components/NewUserModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/react/components/NewUserModal.tsx b/react/components/NewUserModal.tsx index 5331a19a..08768599 100644 --- a/react/components/NewUserModal.tsx +++ b/react/components/NewUserModal.tsx @@ -40,7 +40,7 @@ const NewUserModal: FunctionComponent = ({ canEdit, canEditSales, isSalesAdmin, - canManageOrg + canManageOrg, }) => { const { formatMessage } = useIntl() const [userState, setUserState] = useState({ @@ -119,7 +119,7 @@ const NewUserModal: FunctionComponent = ({ const filteredArray = rolesData.listRoles.filter((role: any) => { if (isAdmin) return true - if(canManageOrg) return true + if (canManageOrg) return true if (role.slug.includes('customer') && canEdit) { return true