diff --git a/docusaurus.config.js b/docusaurus.config.js index 723858e41d..1926343f95 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -132,7 +132,7 @@ const editUrl = ({ docPath }) => { }; } return acc; - }, null); + }, /** @type {Repository & SourceDestination | null} */ (null)); if (!sourceRepository) { return `https://github.com/onflow/docs/tree/main/docs/${docPath}`; } @@ -294,6 +294,7 @@ const config = { { type: 'custom-connectButton', position: 'right', + label: 'Connect', }, { href: 'https://github.com/onflow', @@ -505,17 +506,18 @@ const config = { name: 'docusaurus-svgo', configureWebpack(config) { // allow svgr to use svgo config file - for (const rule of config.module.rules) { + for (const rule of config.module?.rules || []) { if ( + rule && typeof rule === 'object' && - rule.test.toString() === '/\\.svg$/i' + rule.test?.toString() === '/\\.svg$/i' ) { - for (const nestedRule of rule.oneOf) { - if (nestedRule.use instanceof Array) { + for (const nestedRule of rule.oneOf || []) { + if (nestedRule && typeof nestedRule === 'object' && nestedRule.use instanceof Array) { for (const loader of nestedRule.use) { if ( typeof loader === 'object' && - loader.loader === require.resolve('@svgr/webpack') + loader?.loader === require.resolve('@svgr/webpack') ) { if (typeof loader.options === 'object') { loader.options.svgoConfig = null; @@ -531,7 +533,7 @@ const config = { 'module.rules': 'replace', }, module: { - rules: config.module.rules, + rules: config.module?.rules || [], }, }; }, @@ -549,8 +551,7 @@ const config = { }; }, /** this function needs doesn't pick up hot reload event, it needs a restart */ - // @ts-expect-error - function (context, options) { + function (context) { const { siteConfig } = context; return { name: 'docusaurus-flow-networks-plugin', diff --git a/src/components/ConnectButton.tsx b/src/components/ConnectButton.tsx index 2f11e61476..a65abd7d51 100644 --- a/src/components/ConnectButton.tsx +++ b/src/components/ConnectButton.tsx @@ -10,6 +10,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useProfile } from '../hooks/use-profile'; import { useGithubAvatar } from '../hooks/use-github-avatar'; import { SocialType } from '../types/gold-star'; +import { event } from '@site/src/utils/gtags.client'; +import { GA_EVENTS, GA_CATEGORIES } from '@site/src/constants/ga-events'; const shortenAddress = (address: string) => { if (!address) return ''; @@ -40,9 +42,25 @@ const ConnectButton: React.FC = () => { setIsProfileModalOpen(false); }; + const handleSignInClick = () => { + // Check if we're on the homepage + const isHomepage = typeof window !== 'undefined' && window.location.pathname === '/'; + + // Track the Sign In click + event({ + action: isHomepage ? GA_EVENTS.ACTION_CARD_CLICK : GA_EVENTS.NAV_BAR_CLICK, + category: isHomepage ? GA_CATEGORIES.ACTION_CARD : GA_CATEGORIES.NAV_BAR, + label: 'Nav-Sign In', + location: true, + }); + + // Call the original logIn function + logIn(); + }; + if (!user.loggedIn) { return ( - ); diff --git a/src/constants/ga-events.ts b/src/constants/ga-events.ts index 3558990471..385823e464 100644 --- a/src/constants/ga-events.ts +++ b/src/constants/ga-events.ts @@ -2,6 +2,8 @@ export const GA_EVENTS = { // Existing events currently used in the codebase ACTION_CARD_CLICK: 'action_card_click', + NAV_BAR_CLICK: 'nav_bar_click', + SEARCH_CLICK: 'search_click', FEEDBACK_CLICK: 'feedback_click', AUTH_LOGIN: 'login', } as const; @@ -9,6 +11,8 @@ export const GA_EVENTS = { // Event categories for consistent tracking - Using existing categories export const GA_CATEGORIES = { ACTION_CARD: 'action_card', + NAV_BAR: 'nav_bar', + SEARCH: 'search', FEEDBACK: 'feedback', AUTH: 'auth', } as const; diff --git a/src/theme/NavbarItem/ComponentTypes.js b/src/theme/NavbarItem/ComponentTypes.js index f3e72147b7..3adfc55cb0 100644 --- a/src/theme/NavbarItem/ComponentTypes.js +++ b/src/theme/NavbarItem/ComponentTypes.js @@ -1,7 +1,94 @@ +import React from 'react'; import ComponentTypes from '@theme-original/NavbarItem/ComponentTypes'; import ConnectButton from '@site/src/components/ConnectButton'; +import { event } from '@site/src/utils/gtags.client'; +import { GA_EVENTS, GA_CATEGORIES, GA_ACTIONS } from '@site/src/constants/ga-events'; + +// Wrapper component that adds tracking to navbar items +const withTracking = (Component) => { + return (props) => { + const handleClick = (e) => { + // Extract meaningful label from props + const label = props.label || 'unknown-navbar-item'; + + // Check if we're on the homepage + const isHomepage = typeof window !== 'undefined' && window.location.pathname === '/'; + + // Track the navbar item click with appropriate event based on page + event({ + action: isHomepage ? GA_EVENTS.ACTION_CARD_CLICK : GA_EVENTS.NAV_BAR_CLICK, + category: isHomepage ? GA_CATEGORIES.ACTION_CARD : GA_CATEGORIES.NAV_BAR, + label: label, + location: true, + }); + + // Call original onClick if it exists + if (props.onClick) { + props.onClick(e); + } + }; + + return ; + }; +}; + +// Create tracked versions of all component types +const trackedComponentTypes = {}; +Object.keys(ComponentTypes).forEach(key => { + trackedComponentTypes[key] = withTracking(ComponentTypes[key]); +}); + +// Add specific wrapper for html type navbar items +const withHtmlTracking = (Component) => { + return (props) => { + const handleClick = (e) => { + // Debug: log all available props + + // Try to get the label from various sources + let label = props.label || props.item?.label; + + // If no label in props, try to extract from HTML span + if (!label && props.html) { + const spanMatch = props.html.match(/]*>([^<]+)<\/span>/); + if (spanMatch) { + label = spanMatch[1].trim(); + } + } + + // Final fallback + if (!label) { + label = 'unknown-html-item'; + } + + // Add Nav- prefix to match existing pattern + const prefixedLabel = `Nav-${label}`; + + // Check if we're on the homepage + const isHomepage = typeof window !== 'undefined' && window.location.pathname === '/'; + + // Track the navbar item click with appropriate event based on page + event({ + action: isHomepage ? GA_EVENTS.ACTION_CARD_CLICK : GA_EVENTS.NAV_BAR_CLICK, + category: isHomepage ? GA_CATEGORIES.ACTION_CARD : GA_CATEGORIES.NAV_BAR, + label: prefixedLabel, + location: true, + }); + + // Call original onClick if it exists + if (props.onClick) { + props.onClick(e); + } + }; + + return ; + }; +}; export default { - ...ComponentTypes, + ...trackedComponentTypes, 'custom-connectButton': ConnectButton, + // Add html type if it exists + ...(ComponentTypes.html ? { html: withHtmlTracking(ComponentTypes.html) } : {}), + // Add default type if it exists (for href items) + ...(ComponentTypes.default ? { default: withHtmlTracking(ComponentTypes.default) } : {}), }; \ No newline at end of file diff --git a/src/theme/SearchBar.js b/src/theme/SearchBar.js index cd0a7ee0b3..b820e2654d 100644 --- a/src/theme/SearchBar.js +++ b/src/theme/SearchBar.js @@ -2,13 +2,30 @@ import React from 'react'; import SearchBar from '@theme-original/SearchBar'; import AskCookbook from '@cookbookdev/docsbot/react'; import BrowserOnly from '@docusaurus/BrowserOnly'; +import { event } from '@site/src/utils/gtags.client'; +import { GA_EVENTS, GA_CATEGORIES } from '@site/src/constants/ga-events'; /** It's a public API key, so it's safe to expose it here */ const COOKBOOK_PUBLIC_API_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NzEyYWRkYjk5YjBmNWViM2ZkODQxOGMiLCJpYXQiOjE3MjkyNzc0MDMsImV4cCI6MjA0NDg1MzQwM30._bhlmAnFpvxvkTV0PvU-6FwabhFOdSOx-qed2UIogpY'; export default function SearchBarWrapper(props) { + const handleSearchClick = () => { + // Check if we're on the homepage + const isHomepage = typeof window !== 'undefined' && window.location.pathname === '/'; + + // Track the search bar click + event({ + action: isHomepage ? GA_EVENTS.ACTION_CARD_CLICK : GA_EVENTS.SEARCH_CLICK, + category: isHomepage ? GA_CATEGORIES.ACTION_CARD : GA_CATEGORIES.SEARCH, + label: 'Nav-Search', + location: true, + }); + }; + return ( <> - +
+ +
{() => } diff --git a/src/ui/design-system/src/lib/Pages/HomePage/BrowseByCategory.tsx b/src/ui/design-system/src/lib/Pages/HomePage/BrowseByCategory.tsx index aa1fbc5e07..e437dc1e38 100644 --- a/src/ui/design-system/src/lib/Pages/HomePage/BrowseByCategory.tsx +++ b/src/ui/design-system/src/lib/Pages/HomePage/BrowseByCategory.tsx @@ -1,4 +1,6 @@ import React from 'react'; +import { event } from '@site/src/utils/gtags.client'; +import { GA_EVENTS, GA_CATEGORIES } from '@site/src/constants/ga-events'; const CATEGORIES = [ { @@ -92,6 +94,16 @@ const CATEGORIES = [ ]; const BrowseByCategory: React.FC = () => { + const handleLinkClick = (label: string, href: string, category: string) => { + event({ + action: GA_EVENTS.ACTION_CARD_CLICK, + category: GA_CATEGORIES.ACTION_CARD, + label: `${category} - ${label}`, + location: true, + href: href, + }); + }; + return (

Browse by Category

@@ -106,6 +118,7 @@ const BrowseByCategory: React.FC = () => { href={link.href} target={link.href.startsWith('http') ? '_blank' : undefined} rel={link.href.startsWith('http') ? 'noopener noreferrer' : undefined} + onClick={() => handleLinkClick(link.label, link.href, cat.title)} className="text-base text-gray-800 dark:text-gray-100 hover:text-primary-green-500 dark:hover:text-primary-green-300 transition-colors" > {link.label}