fix(admin-ui): disabling analytics when opted out (#4939)
* fixes #4423 by either using the `useAnalytics` hook or using an ErrorBoundary specific instance Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
co-authored by
Oli Juhl
parent
6273b4b160
commit
87e3a7d06a
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/admin-ui": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
correctly skipping analytics when user opted out
|
||||||
@@ -2,10 +2,7 @@ import clsx from "clsx"
|
|||||||
import { useForm, useWatch } from "react-hook-form"
|
import { useForm, useWatch } from "react-hook-form"
|
||||||
import useNotification from "../../../hooks/use-notification"
|
import useNotification from "../../../hooks/use-notification"
|
||||||
import { useAnalytics } from "../../../providers/analytics-provider"
|
import { useAnalytics } from "../../../providers/analytics-provider"
|
||||||
import {
|
import { useAdminCreateAnalyticsConfig } from "../../../services/analytics"
|
||||||
analytics,
|
|
||||||
useAdminCreateAnalyticsConfig,
|
|
||||||
} from "../../../services/analytics"
|
|
||||||
import { getErrorMessage } from "../../../utils/error-messages"
|
import { getErrorMessage } from "../../../utils/error-messages"
|
||||||
import { nestedForm } from "../../../utils/nested-form"
|
import { nestedForm } from "../../../utils/nested-form"
|
||||||
import Button from "../../fundamentals/button"
|
import Button from "../../fundamentals/button"
|
||||||
@@ -38,7 +35,7 @@ const AnalyticsPreferencesModal = () => {
|
|||||||
control,
|
control,
|
||||||
} = form
|
} = form
|
||||||
|
|
||||||
const { setSubmittingConfig } = useAnalytics()
|
const { setSubmittingConfig, trackUserEmail } = useAnalytics()
|
||||||
|
|
||||||
const watchOptOut = useWatch({
|
const watchOptOut = useWatch({
|
||||||
control: control,
|
control: control,
|
||||||
@@ -65,7 +62,7 @@ const AnalyticsPreferencesModal = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (shouldTrackEmail) {
|
if (shouldTrackEmail) {
|
||||||
analytics.track("userEmail", { email })
|
trackUserEmail({ email })
|
||||||
}
|
}
|
||||||
|
|
||||||
setSubmittingConfig(false)
|
setSubmittingConfig(false)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { AxiosError } from "axios"
|
import { AxiosError } from "axios"
|
||||||
import React, { ErrorInfo } from "react"
|
import React, { ErrorInfo } from "react"
|
||||||
import { analytics, getAnalyticsConfig } from "../../../services/analytics"
|
import { analyticsOptIn } from "../../../services/analytics"
|
||||||
import Button from "../../fundamentals/button"
|
import Button from "../../fundamentals/button"
|
||||||
|
import { WRITE_KEY } from "../../../constants/analytics"
|
||||||
|
import { AnalyticsBrowser } from "@segment/analytics-next"
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
hasError: boolean
|
hasError: boolean
|
||||||
@@ -13,6 +15,18 @@ type Props = {
|
|||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Analytics instance used for tracking errors
|
||||||
|
let analyticsInstance: ReturnType<typeof AnalyticsBrowser.load> | undefined;
|
||||||
|
|
||||||
|
const analytics = () => {
|
||||||
|
if (!analyticsInstance) {
|
||||||
|
analyticsInstance = AnalyticsBrowser.load({
|
||||||
|
writeKey: WRITE_KEY,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return analyticsInstance
|
||||||
|
}
|
||||||
|
|
||||||
class ErrorBoundary extends React.Component<Props, State> {
|
class ErrorBoundary extends React.Component<Props, State> {
|
||||||
public state: State = {
|
public state: State = {
|
||||||
hasError: false,
|
hasError: false,
|
||||||
@@ -40,7 +54,7 @@ class ErrorBoundary extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const properties = getTrackingInfo(error, errorInfo)
|
const properties = getTrackingInfo(error, errorInfo)
|
||||||
analytics.track("error", properties)
|
analytics().track("error", properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
public dismissError = () => {
|
public dismissError = () => {
|
||||||
@@ -98,19 +112,7 @@ const shouldTrackEvent = async (error: Error) => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await getAnalyticsConfig().catch(() => undefined)
|
return await analyticsOptIn();
|
||||||
|
|
||||||
// Don't track if we have no config to ensure we have permission
|
|
||||||
if (!res) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't track if user has opted out from sharing usage insights
|
|
||||||
if (res.analytics_config.opt_out) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorMessage = (status?: number) => {
|
const errorMessage = (status?: number) => {
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import PublicLayout from "../components/templates/login-layout"
|
|||||||
import useNotification from "../hooks/use-notification"
|
import useNotification from "../hooks/use-notification"
|
||||||
import { getErrorMessage } from "../utils/error-messages"
|
import { getErrorMessage } from "../utils/error-messages"
|
||||||
import FormValidator from "../utils/form-validator"
|
import FormValidator from "../utils/form-validator"
|
||||||
import { analytics, useAdminCreateAnalyticsConfig } from "../services/analytics"
|
import { useAdminCreateAnalyticsConfig } from "../services/analytics"
|
||||||
|
import { useAnalytics } from "../providers/analytics-provider"
|
||||||
import AnalyticsConfigForm, {
|
import AnalyticsConfigForm, {
|
||||||
AnalyticsConfigFormType,
|
AnalyticsConfigFormType,
|
||||||
} from "../components/organisms/analytics-config-form"
|
} from "../components/organisms/analytics-config-form"
|
||||||
@@ -30,6 +31,7 @@ const InvitePage = () => {
|
|||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const parsed = qs.parse(location.search.substring(1))
|
const parsed = qs.parse(location.search.substring(1))
|
||||||
const [signUp, setSignUp] = useState(false)
|
const [signUp, setSignUp] = useState(false)
|
||||||
|
const { trackUserEmail } = useAnalytics()
|
||||||
|
|
||||||
const first_run = !!parsed.first_run
|
const first_run = !!parsed.first_run
|
||||||
|
|
||||||
@@ -117,7 +119,7 @@ const InvitePage = () => {
|
|||||||
await createAnalyticsConfig(data.analytics)
|
await createAnalyticsConfig(data.analytics)
|
||||||
|
|
||||||
if (shouldTrackEmail) {
|
if (shouldTrackEmail) {
|
||||||
await analytics.track("userEmail", {
|
trackUserEmail({
|
||||||
email: token?.user_email,
|
email: token?.user_email,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ type Event =
|
|||||||
| "regions"
|
| "regions"
|
||||||
| "currencies"
|
| "currencies"
|
||||||
| "storeName"
|
| "storeName"
|
||||||
|
| "userEmail"
|
||||||
|
|
||||||
type AnalyticsContextType = {
|
type AnalyticsContextType = {
|
||||||
trackCurrencies: (properties: TrackCurrenciesPayload) => void
|
trackCurrencies: (properties: TrackCurrenciesPayload) => void
|
||||||
@@ -35,6 +36,7 @@ type AnalyticsContextType = {
|
|||||||
trackNumberOfDiscounts: (properties: TrackCountPayload) => void
|
trackNumberOfDiscounts: (properties: TrackCountPayload) => void
|
||||||
trackNumberOfProducts: (properties: TrackCountPayload) => void
|
trackNumberOfProducts: (properties: TrackCountPayload) => void
|
||||||
trackRegions: (properties: TrackRegionsPayload) => void
|
trackRegions: (properties: TrackRegionsPayload) => void
|
||||||
|
trackUserEmail: (properties: TrackUserEmailPayload) => void
|
||||||
setSubmittingConfig: (status: boolean) => void
|
setSubmittingConfig: (status: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +142,10 @@ export const AnalyticsProvider = ({ writeKey, children }: Props) => {
|
|||||||
track("numDiscounts", properties)
|
track("numDiscounts", properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const trackUserEmail = (properties: TrackUserEmailPayload) => {
|
||||||
|
track("userEmail", properties)
|
||||||
|
}
|
||||||
|
|
||||||
// Track number of users
|
// Track number of users
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (users) {
|
if (users) {
|
||||||
@@ -171,6 +177,7 @@ export const AnalyticsProvider = ({ writeKey, children }: Props) => {
|
|||||||
trackNumberOfOrders,
|
trackNumberOfOrders,
|
||||||
trackNumberOfProducts,
|
trackNumberOfProducts,
|
||||||
trackNumberOfDiscounts,
|
trackNumberOfDiscounts,
|
||||||
|
trackUserEmail,
|
||||||
setSubmittingConfig,
|
setSubmittingConfig,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -201,6 +208,10 @@ type TrackRegionsPayload = {
|
|||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TrackUserEmailPayload = {
|
||||||
|
email: string | undefined
|
||||||
|
}
|
||||||
|
|
||||||
export const useAnalytics = () => {
|
export const useAnalytics = () => {
|
||||||
const context = useContext(AnalyticsContext)
|
const context = useContext(AnalyticsContext)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { AdminAnalyticsConfigRes } from "@medusajs/medusa"
|
import { AdminAnalyticsConfigRes } from "@medusajs/medusa"
|
||||||
import { AnalyticsBrowser } from "@segment/analytics-next"
|
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import { WRITE_KEY } from "../constants/analytics"
|
|
||||||
import { MEDUSA_BACKEND_URL } from "../constants/medusa-backend-url"
|
import { MEDUSA_BACKEND_URL } from "../constants/medusa-backend-url"
|
||||||
import { useFeatureFlag } from "../providers/feature-flag-provider"
|
import { useFeatureFlag } from "../providers/feature-flag-provider"
|
||||||
|
|
||||||
@@ -15,10 +13,19 @@ const client = axios.create({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Analytics instance used for tracking one-off events, such as errors and the initial permission request
|
/**
|
||||||
export const analytics = AnalyticsBrowser.load({
|
* Returns true if analytics are enabled for the current user.
|
||||||
writeKey: WRITE_KEY,
|
*/
|
||||||
})
|
export const analyticsOptIn = async () => {
|
||||||
|
const res = await getAnalyticsConfig().catch(() => undefined)
|
||||||
|
|
||||||
|
// Don't track if we have no config to ensure we have permission
|
||||||
|
if (!res) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return !res.analytics_config.opt_out
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the analytics config for the current user.
|
* Fetches the analytics config for the current user.
|
||||||
|
|||||||
Reference in New Issue
Block a user