docs: track code copy + AI assistant event (#14193)

* docs: track code copy + AI assistant event

* add pathname to callback
This commit is contained in:
Shahed Nasser authored and GitHub committed 2025-12-02 17:24:31 +02:00
1 parent ef162f8b5f
commit 00052b9e50
12 files changed
+73 -62

No files matched your search

@@ -1,23 +1,24 @@
"use client" "use client"
import { Feedback as UiFeedback, FeedbackProps } from "docs-ui" import {
import { usePathname } from "next/navigation" Feedback as UiFeedback,
FeedbackProps,
DocsTrackingEvents,
} from "docs-ui"
import { useArea } from "../../providers/area" import { useArea } from "../../providers/area"
export const Feedback = (props: Partial<FeedbackProps>) => { export const Feedback = (props: Partial<FeedbackProps>) => {
const pathname = usePathname()
const { area } = useArea() const { area } = useArea()
return ( return (
<UiFeedback <UiFeedback
vertical={true} vertical={true}
{...props} {...props}
event="survey_api-ref" event={DocsTrackingEvents.SURVEY_API}
extraData={{ extraData={{
area, area,
...props.extraData, ...props.extraData,
}} }}
pathName={`/api/${pathname}`}
/> />
) )
} }
+2 -9
View File
@@ -1,24 +1,17 @@
"use client" "use client"
import { import {
DocsTrackingEvents,
Feedback as UiFeedback, Feedback as UiFeedback,
FeedbackProps as UiFeedbackProps, FeedbackProps as UiFeedbackProps,
} from "docs-ui" } from "docs-ui"
import { usePathname } from "next/navigation"
import { basePathUrl } from "../../utils/base-path-url"
import { useMemo } from "react"
type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName"> type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName">
const Feedback = (props: FeedbackProps) => { const Feedback = (props: FeedbackProps) => {
const pathname = usePathname()
const feedbackPathname = useMemo(() => basePathUrl(pathname), [pathname])
return ( return (
<UiFeedback <UiFeedback
event="survey" event={DocsTrackingEvents.SURVEY}
pathName={feedbackPathname}
question={props.question || "Was this chapter helpful?"} question={props.question || "Was this chapter helpful?"}
{...props} {...props}
/> />
+2 -9
View File
@@ -1,24 +1,17 @@
"use client" "use client"
import { import {
DocsTrackingEvents,
Feedback as UiFeedback, Feedback as UiFeedback,
FeedbackProps as UiFeedbackProps, FeedbackProps as UiFeedbackProps,
} from "docs-ui" } from "docs-ui"
import { usePathname } from "next/navigation"
import { basePathUrl } from "../../utils/base-path-url"
import { useMemo } from "react"
type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName"> type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName">
const Feedback = (props: FeedbackProps) => { const Feedback = (props: FeedbackProps) => {
const pathname = usePathname()
const feedbackPathname = useMemo(() => basePathUrl(pathname), [pathname])
return ( return (
<UiFeedback <UiFeedback
event="survey" event={DocsTrackingEvents.SURVEY}
pathName={feedbackPathname}
question="Was this guide helpful?" question="Was this guide helpful?"
{...props} {...props}
/> />
@@ -1,24 +1,17 @@
"use client" "use client"
import { import {
DocsTrackingEvents,
Feedback as UiFeedback, Feedback as UiFeedback,
FeedbackProps as UiFeedbackProps, FeedbackProps as UiFeedbackProps,
} from "docs-ui" } from "docs-ui"
import { usePathname } from "next/navigation"
import { useMemo } from "react"
import { basePathUrl } from "../../utils/base-path-url"
type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName"> type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName">
export const Feedback = (props: FeedbackProps) => { export const Feedback = (props: FeedbackProps) => {
const pathname = usePathname()
const feedbackPathname = useMemo(() => basePathUrl(pathname), [pathname])
return ( return (
<UiFeedback <UiFeedback
event="survey" event={DocsTrackingEvents.SURVEY}
pathName={feedbackPathname}
question="Was this page helpful?" question="Was this page helpful?"
{...props} {...props}
/> />
+2 -9
View File
@@ -1,24 +1,17 @@
"use client" "use client"
import { import {
DocsTrackingEvents,
Feedback as UiFeedback, Feedback as UiFeedback,
FeedbackProps as UiFeedbackProps, FeedbackProps as UiFeedbackProps,
} from "docs-ui" } from "docs-ui"
import { usePathname } from "next/navigation"
import { basePathUrl } from "../../utils/base-path-url"
import { useMemo } from "react"
type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName"> type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName">
const Feedback = (props: FeedbackProps) => { const Feedback = (props: FeedbackProps) => {
const pathname = usePathname()
const feedbackPathname = useMemo(() => basePathUrl(pathname), [pathname])
return ( return (
<UiFeedback <UiFeedback
event="survey" event={DocsTrackingEvents.SURVEY}
pathName={feedbackPathname}
question="Was this guide helpful?" question="Was this guide helpful?"
{...props} {...props}
/> />
@@ -1,24 +1,17 @@
"use client" "use client"
import { import {
DocsTrackingEvents,
Feedback as UiFeedback, Feedback as UiFeedback,
FeedbackProps as UiFeedbackProps, FeedbackProps as UiFeedbackProps,
} from "docs-ui" } from "docs-ui"
import { usePathname } from "next/navigation"
import { basePathUrl } from "../../utils/base-path-url"
import { useMemo } from "react"
type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName"> type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName">
const Feedback = (props: FeedbackProps) => { const Feedback = (props: FeedbackProps) => {
const pathname = usePathname()
const feedbackPathname = useMemo(() => basePathUrl(pathname), [pathname])
return ( return (
<UiFeedback <UiFeedback
event="survey" event={DocsTrackingEvents.SURVEY}
pathName={feedbackPathname}
question="Was this guide helpful?" question="Was this guide helpful?"
{...props} {...props}
/> />
@@ -1,10 +1,15 @@
import React, { useEffect, useMemo, useRef } from "react" import React, { useEffect, useMemo, useRef } from "react"
import clsx from "clsx" import clsx from "clsx"
import { ArrowUpCircleSolid, LightBulb, LightBulbSolid } from "@medusajs/icons" import { ArrowUpCircleSolid, LightBulb, LightBulbSolid } from "@medusajs/icons"
import { useAiAssistant, useIsBrowser } from "../../../../providers" import {
useAiAssistant,
useAnalytics,
useIsBrowser,
} from "../../../../providers"
import { useChat, useDeepThinking } from "@kapaai/react-sdk" import { useChat, useDeepThinking } from "@kapaai/react-sdk"
import { useAiAssistantChatNavigation } from "../../../../hooks" import { useAiAssistantChatNavigation } from "../../../../hooks"
import { Tooltip } from "../../../Tooltip" import { Tooltip } from "../../../Tooltip"
import { DocsTrackingEvents } from "../../../../constants"
type AiAssistantChatWindowInputProps = { type AiAssistantChatWindowInputProps = {
chatWindowRef: React.RefObject<HTMLDivElement | null> chatWindowRef: React.RefObject<HTMLDivElement | null>
@@ -16,6 +21,7 @@ export const AiAssistantChatWindowInput = ({
const { chatOpened, inputRef, loading, setChatOpened, isCaptchaLoaded } = const { chatOpened, inputRef, loading, setChatOpened, isCaptchaLoaded } =
useAiAssistant() useAiAssistant()
const { submitQuery, conversation } = useChat() const { submitQuery, conversation } = useChat()
const { track } = useAnalytics()
const { active, toggle } = useDeepThinking() const { active, toggle } = useDeepThinking()
const { isBrowser } = useIsBrowser() const { isBrowser } = useIsBrowser()
const { searchQuery, searchQueryType } = useMemo(() => { const { searchQuery, searchQueryType } = useMemo(() => {
@@ -38,6 +44,13 @@ export const AiAssistantChatWindowInput = ({
) => { ) => {
e?.preventDefault() e?.preventDefault()
submitQuery(overrideQuestion || question) submitQuery(overrideQuestion || question)
if (!conversation.length) {
track({
event: {
event: DocsTrackingEvents.AI_ASSISTANT_START_CHAT,
},
})
}
setQuestion("") setQuestion("")
} }
@@ -1,7 +1,7 @@
"use client" "use client"
import React, { useEffect, useState } from "react" import React, { useEffect, useState } from "react"
import { CopyButton } from "../../../.." import { CopyButton, DocsTrackingEvents, useAnalytics } from "../../../.."
import clsx from "clsx" import clsx from "clsx"
import { CheckMini, SquareTwoStack } from "@medusajs/icons" import { CheckMini, SquareTwoStack } from "@medusajs/icons"
@@ -15,6 +15,7 @@ export const CodeBlockCopyAction = ({
inHeader, inHeader,
}: CodeBlockCopyActionProps) => { }: CodeBlockCopyActionProps) => {
const [copied, setCopied] = useState(false) const [copied, setCopied] = useState(false)
const { track } = useAnalytics()
useEffect(() => { useEffect(() => {
if (copied) { if (copied) {
@@ -22,6 +23,12 @@ export const CodeBlockCopyAction = ({
setCopied(false) setCopied(false)
}, 1000) }, 1000)
} }
track({
event: {
event: DocsTrackingEvents.CODE_BLOCK_COPY,
},
})
}, [copied]) }, [copied])
const iconClassName = [ const iconClassName = [
@@ -4,13 +4,13 @@ import React, { useEffect, useMemo, useRef, useState } from "react"
import clsx from "clsx" import clsx from "clsx"
import { Highlight, HighlightProps, themes, Token } from "prism-react-renderer" import { Highlight, HighlightProps, themes, Token } from "prism-react-renderer"
import { ApiRunner } from "@/components" import { ApiRunner } from "@/components"
import { useColorMode } from "@/providers" import { useAnalytics, useColorMode } from "@/providers"
import { CodeBlockHeader, CodeBlockHeaderMeta } from "./Header" import { CodeBlockHeader, CodeBlockHeaderMeta } from "./Header"
import { CodeBlockLine } from "./Line" import { CodeBlockLine } from "./Line"
import { ApiAuthType, ApiDataOptions, ApiMethod } from "types" import { ApiAuthType, ApiDataOptions, ApiMethod } from "types"
// @ts-expect-error can't install the types package because it doesn't support React v19 // @ts-expect-error can't install the types package because it doesn't support React v19
import { CSSTransition } from "react-transition-group" import { CSSTransition } from "react-transition-group"
import { useCollapsibleCodeLines } from "../.." import { DocsTrackingEvents, useCollapsibleCodeLines } from "../.."
import { HighlightProps as CollapsibleHighlightProps } from "@/hooks" import { HighlightProps as CollapsibleHighlightProps } from "@/hooks"
import { CodeBlockActions, CodeBlockActionsProps } from "./Actions" import { CodeBlockActions, CodeBlockActionsProps } from "./Actions"
import { CodeBlockCollapsibleButton } from "./Collapsible/Button" import { CodeBlockCollapsibleButton } from "./Collapsible/Button"
@@ -103,6 +103,7 @@ export const CodeBlock = ({
} }
const { colorMode } = useColorMode() const { colorMode } = useColorMode()
const { track } = useAnalytics()
const [showTesting, setShowTesting] = useState(false) const [showTesting, setShowTesting] = useState(false)
const codeContainerRef = useRef<HTMLDivElement>(null) const codeContainerRef = useRef<HTMLDivElement>(null)
const codeRef = useRef<HTMLElement>(null) const codeRef = useRef<HTMLElement>(null)
@@ -296,6 +297,14 @@ export const CodeBlock = ({
) )
}, [codeContainerRef.current, codeRef.current]) }, [codeContainerRef.current, codeRef.current])
const trackCopy = () => {
track({
event: {
event: DocsTrackingEvents.CODE_BLOCK_COPY,
},
})
}
const actionsProps: Omit<CodeBlockActionsProps, "inHeader"> = useMemo( const actionsProps: Omit<CodeBlockActionsProps, "inHeader"> = useMemo(
() => ({ () => ({
source, source,
@@ -435,6 +444,7 @@ export const CodeBlock = ({
"pl-docs_1", "pl-docs_1",
preClassName preClassName
)} )}
onCopy={trackCopy}
> >
<code <code
className={clsx( className={clsx(
@@ -20,7 +20,6 @@ import { RadioGroup } from "@medusajs/ui"
export type FeedbackProps = { export type FeedbackProps = {
event: string event: string
pathName: string
reportLink?: string reportLink?: string
question?: string question?: string
positiveBtn?: string positiveBtn?: string
@@ -55,7 +54,6 @@ const feedbackOptions = {
export const Feedback = ({ export const Feedback = ({
event, event,
pathName,
reportLink: initReportLink, reportLink: initReportLink,
question = "Was this page helpful?", question = "Was this page helpful?",
positiveBtn = "It was helpful", positiveBtn = "It was helpful",
@@ -106,15 +104,12 @@ export const Feedback = ({
event: { event: {
event, event,
options: { options: {
url: pathName,
label: document.title,
feedback: feedback:
(feedback !== null && feedback) || (feedback !== null && feedback) ||
(feedback === null && positiveFeedback) (feedback === null && positiveFeedback)
? "yes" ? "yes"
: "no", : "no",
message: message?.length ? message : null, message: message?.length ? message : null,
os: window.navigator.userAgent,
feedbackOption, feedbackOption,
...extraData, ...extraData,
}, },
@@ -124,7 +119,6 @@ export const Feedback = ({
resetForm() resetForm()
} }
}, },
tracker: ["segment", "posthog"],
}, },
}) })
} }
+7
View File
@@ -494,3 +494,10 @@ export const products: Product[] = [
image: "/images/user-icon.png", image: "/images/user-icon.png",
}, },
] ]
export enum DocsTrackingEvents {
SURVEY = "survey",
SURVEY_API = "survey_api-ref",
CODE_BLOCK_COPY = "code_block_copy",
AI_ASSISTANT_START_CHAT = "ai_assistant_start_chat",
}
@@ -10,6 +10,7 @@ import React, {
import { useSegmentAnalytics } from "./providers/segment" import { useSegmentAnalytics } from "./providers/segment"
import { usePostHogAnalytics } from "./providers/posthog" import { usePostHogAnalytics } from "./providers/posthog"
import { useReoDevAnalytics } from "./providers/reo-dev" import { useReoDevAnalytics } from "./providers/reo-dev"
import { usePathname } from "next/navigation"
export type ExtraData = { export type ExtraData = {
section?: string section?: string
@@ -43,6 +44,8 @@ export type AnalyticsProviderProps = {
children?: React.ReactNode children?: React.ReactNode
} }
const DEFAULT_TRACKER: Trackers = "posthog"
export const AnalyticsProvider = ({ export const AnalyticsProvider = ({
segmentWriteKey, segmentWriteKey,
reoDevKey, reoDevKey,
@@ -55,12 +58,23 @@ export const AnalyticsProvider = ({
}) })
const { track: trackWithPostHog } = usePostHogAnalytics() const { track: trackWithPostHog } = usePostHogAnalytics()
useReoDevAnalytics({ reoDevKey }) useReoDevAnalytics({ reoDevKey })
const pathname = usePathname()
const processEvent = useCallback( const processEvent = useCallback(
async (event: TrackedEvent) => { async (event: TrackedEvent) => {
const trackers = Array.isArray(event.tracker) const trackers = !event.tracker
? [DEFAULT_TRACKER]
: Array.isArray(event.tracker)
? event.tracker ? event.tracker
: [event.tracker] : [event.tracker]
event.options = {
url: pathname,
label: document.title,
os: window.navigator.userAgent,
...event.options,
}
await Promise.all( await Promise.all(
trackers.map(async (tracker) => { trackers.map(async (tracker) => {
switch (tracker) { switch (tracker) {
@@ -73,7 +87,7 @@ export const AnalyticsProvider = ({
}) })
) )
}, },
[trackWithSegment, trackWithPostHog] [trackWithSegment, trackWithPostHog, pathname]
) )
const track = ({ event }: { event: TrackedEvent }) => { const track = ({ event }: { event: TrackedEvent }) => {