docs: improved analytics and tracking (#13671)

* docs: improved analytics and tracking

* remove detailed feedback component

* remove ignore build script for api reference

* improvements

* fix pathname
This commit is contained in:
Shahed Nasser
2025-10-03 13:33:48 +03:00
committed by GitHub
parent 50c1417378
commit 6ea97443de
32 changed files with 214 additions and 381 deletions
@@ -105,28 +105,31 @@ export const Feedback = ({
if (showForm) {
setLoading(true)
}
track(
event,
{
url: pathName,
label: document.title,
feedback:
(feedback !== null && feedback) ||
(feedback === null && positiveFeedback)
? "yes"
: "no",
message: message?.length ? message : null,
os: window.navigator.userAgent,
feedbackOption,
...extraData,
track({
event: {
event,
options: {
url: pathName,
label: document.title,
feedback:
(feedback !== null && feedback) ||
(feedback === null && positiveFeedback)
? "yes"
: "no",
message: message?.length ? message : null,
os: window.navigator.userAgent,
feedbackOption,
...extraData,
},
callback: function () {
if (showForm) {
setLoading(false)
resetForm()
}
},
tracker: ["segment", "posthog"],
},
function () {
if (showForm) {
setLoading(false)
resetForm()
}
}
)
})
}
function resetForm() {
@@ -31,14 +31,16 @@ export const Rating: React.FC<RatingProps> = ({
const submitTracking = useCallback(
(selectedRating?: number, feedback?: string) => {
track(
event,
{
rating: selectedRating || rating,
additionalFeedback: feedback || additionalFeedback,
track({
event: {
event,
options: {
rating: selectedRating || rating,
additionalFeedback: feedback || additionalFeedback,
},
callback: () => onRating?.(selectedRating || rating),
},
() => onRating?.(selectedRating || rating)
)
})
},
[rating, additionalFeedback]
)
@@ -1,98 +0,0 @@
"use client"
import clsx from "clsx"
import React, { useRef, useState } from "react"
import {
BorderedIcon,
getOsShortcut,
useAnalytics,
useClickOutside,
useSidebar,
} from "../../../.."
import {
EllipsisHorizontal,
SidebarLeft,
TimelineVertical,
} from "@medusajs/icons"
import { HouseIcon } from "../../../Icons/House"
import { Menu } from "../../../Menu"
export const SidebarTopMedusaMenu = () => {
const [openMenu, setOpenMenu] = useState(false)
const { setDesktopSidebarOpen } = useSidebar()
const { track } = useAnalytics()
const ref = useRef<HTMLDivElement>(null)
const toggleOpen = () => {
setOpenMenu((prev) => !prev)
if (!openMenu) {
track("nav_sidebar_open", {
url: window.location.href,
})
}
}
useClickOutside({
elmRef: ref,
onClickOutside: () => {
setOpenMenu(false)
},
})
return (
<div className={clsx("p-docs_0.75", "relative")} ref={ref}>
<div
className={clsx(
"flex justify-between items-center gap-docs_0.5",
"rounded-docs_sm hover:bg-medusa-bg-subtle-hover cursor-pointer",
"py-docs_0.125 pl-docs_0.125 pr-docs_0.5"
)}
tabIndex={-1}
onClick={toggleOpen}
>
<BorderedIcon icon="/images/logo.png" />
<span className="text-compact-small-plus text-medusa-fg-base flex-1">
Medusa Docs
</span>
<EllipsisHorizontal className="text-medusa-fg-muted" />
</div>
<div
className={clsx(
"absolute w-[calc(100%-16px)] bottom-[calc(-100%-40px)]",
"left-docs_0.5 z-40",
!openMenu && "hidden"
)}
>
<Menu
items={[
{
type: "link",
icon: <HouseIcon />,
title: "Homepage",
link: "https://medusajs.com",
},
{
type: "link",
icon: <TimelineVertical />,
title: "Changelog",
link: "https://medusajs.com/changelog",
},
{
type: "divider",
},
{
type: "action",
title: "Hide Sidebar",
icon: <SidebarLeft />,
shortcut: `${getOsShortcut()}.`,
action: () => {
setDesktopSidebarOpen(false)
setOpenMenu(false)
},
},
]}
/>
</div>
</div>
)
}
@@ -8,7 +8,6 @@ import React, {
useState,
} from "react"
import { Analytics, AnalyticsBrowser } from "@segment/analytics-next"
import { PostHogProvider as PHProvider } from "posthog-js/react"
import posthog from "posthog-js"
// @ts-expect-error Doesn't have a types package
@@ -22,16 +21,22 @@ export type ExtraData = {
export type AnalyticsContextType = {
loaded: boolean
analytics: Analytics | null
track: (
event: string,
options?: Record<string, any>,
callback?: () => void
) => void
track: ({
event,
instant,
}: {
event: TrackedEvent
instant?: boolean
}) => void
}
type Trackers = "segment" | "posthog"
export type TrackedEvent = {
event: string
options?: Record<string, any>
callback?: () => void
tracker?: Trackers | Trackers[]
}
const AnalyticsContext = createContext<AnalyticsContextType | null>(null)
@@ -39,8 +44,6 @@ const AnalyticsContext = createContext<AnalyticsContextType | null>(null)
export type AnalyticsProviderProps = {
segmentWriteKey?: string
reoDevKey?: string
postHogKey?: string
postHogApiHost?: string
children?: React.ReactNode
}
@@ -50,8 +53,6 @@ export const AnalyticsProvider = ({
segmentWriteKey = "temp",
reoDevKey,
children,
postHogKey,
postHogApiHost = "https://eu.i.posthog.com",
}: AnalyticsProviderProps) => {
// loaded is used to ensure that a connection has been made to segment
// even if it failed. This is to ensure that the connection isn't
@@ -85,21 +86,13 @@ export const AnalyticsProvider = ({
}
}, [loaded, segmentWriteKey])
const track = useCallback(
async (
event: string,
options?: Record<string, any>,
callback?: () => void
) => {
const trackWithSegment = useCallback(
async ({ event, options }: TrackedEvent) => {
if (analytics) {
void analytics.track(
event,
{
...options,
uuid: analytics.user().anonymousId(),
},
callback
)
void analytics.track(event, {
...options,
uuid: analytics.user().anonymousId(),
})
} else {
// push the event into the queue
setQueue((prevQueue) => [
@@ -107,45 +100,70 @@ export const AnalyticsProvider = ({
{
event,
options,
tracker: "segment",
},
])
if (callback) {
console.warn(
"Segment is either not installed or not configured. Simulating success..."
)
callback()
}
console.warn(
"Segment is either not installed or not configured. Simulating success..."
)
}
},
[analytics, loaded]
)
const initPostHog = useCallback(() => {
if (!postHogKey) {
return
}
const trackWithPostHog = async ({ event, options }: TrackedEvent) => {
posthog.capture(event, options)
}
posthog.init(postHogKey, {
api_host: postHogApiHost,
person_profiles: "always",
defaults: "2025-05-24",
})
}, [])
const processEvent = useCallback(
async (event: TrackedEvent) => {
const trackers = Array.isArray(event.tracker)
? event.tracker
: [event.tracker]
await Promise.all(
trackers.map(async (tracker) => {
switch (tracker) {
case "posthog":
return trackWithPostHog(event)
case "segment":
default:
return trackWithSegment(event)
}
})
)
},
[trackWithSegment, trackWithPostHog]
)
const track = ({ event }: { event: TrackedEvent }) => {
// Always queue events - this makes tracking non-blocking
setQueue((prevQueue) => [...prevQueue, event])
// Process event callback immediately
// This ensures that the callback is called even if the event is queued
event.callback?.()
}
useEffect(() => {
initSegment()
initPostHog()
}, [initSegment])
useEffect(() => {
if (analytics && queue.length) {
// track stuff in queue
queue.forEach(async (trackEvent) =>
track(trackEvent.event, trackEvent.options)
)
// Process queue in background without blocking
const currentQueue = [...queue]
setQueue([])
// Process events asynchronously in batches to avoid overwhelming the system
const batchSize = 5
for (let i = 0; i < currentQueue.length; i += batchSize) {
const batch = currentQueue.slice(i, i + batchSize)
setTimeout(() => {
batch.forEach(processEvent)
}, i * 10) // Small delay between batches
}
}
}, [analytics, queue])
}, [analytics, queue, trackWithSegment, trackWithPostHog, processEvent])
useEffect(() => {
if (!reoDevKey) {
@@ -155,12 +173,12 @@ export const AnalyticsProvider = ({
loadReoScript({
clientID: reoDevKey,
})
.then((Reo: any) => {
Reo.init({
.then((Reo: unknown) => {
;(Reo as { init: (config: { clientID: string }) => void }).init({
clientID: reoDevKey,
})
})
.catch((e: any) => {
.catch((e: Error) => {
console.error(`Could not connect to Reodotdev. Error: ${e}`)
})
}, [reoDevKey])
@@ -173,11 +191,7 @@ export const AnalyticsProvider = ({
loaded,
}}
>
{postHogKey ? (
<PHProvider client={posthog}>{children}</PHProvider>
) : (
children
)}
{children}
</AnalyticsContext.Provider>
)
}
@@ -71,9 +71,14 @@ export const LearningPathProvider: React.FC<LearningPathProviderProps> = ({
)
}
track(`learning_path_${path.name}`, {
url: pathname,
state: `start`,
track({
event: {
event: `learning_path_${path.name}`,
options: {
url: pathname,
state: `start`,
},
},
})
}
@@ -86,14 +91,19 @@ export const LearningPathProvider: React.FC<LearningPathProviderProps> = ({
const endPath = () => {
const didFinish = currentStep === (path?.steps.length || 0) - 1
const reachedIndex = currentStep === -1 ? 0 : currentStep
track(`learning_path_${path?.name}`, {
url: pathname,
state: !didFinish ? `closed` : `end`,
reachedStep:
path?.steps[reachedIndex]?.title ||
path?.steps[reachedIndex]?.description ||
path?.steps[reachedIndex]?.descriptionJSX ||
reachedIndex,
track({
event: {
event: `learning_path_${path?.name}`,
options: {
url: pathname,
state: !didFinish ? `closed` : `end`,
reachedStep:
path?.steps[reachedIndex]?.title ||
path?.steps[reachedIndex]?.description ||
path?.steps[reachedIndex]?.descriptionJSX ||
reachedIndex,
},
},
})
setPath(null)
setCurrentStep(-1)