diff --git a/.gitignore b/.gitignore index d157814..1b4fc46 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .wrangler .DS_Store .env +.dev.vars *.log build node_modules diff --git a/app/hooks/use-analytics.ts b/app/hooks/use-analytics.ts new file mode 100644 index 0000000..f6762a1 --- /dev/null +++ b/app/hooks/use-analytics.ts @@ -0,0 +1,82 @@ +import { useEffect } from 'react'; + +interface UseAnalyticsOptions { + token?: string | null; + enabled?: boolean; +} + +export function useAnalytics({ token, enabled = true }: UseAnalyticsOptions) { + useEffect(() => { + if (!enabled || !token) { + if (!token) { + console.warn('Cloudflare Analytics: No token provided'); + } + return; + } + + // Check if the beacon script is already loaded + const existingScript = document.querySelector('script[src*="beacon.min.js"]'); + if (existingScript) { + console.log('Cloudflare Analytics: Beacon script already loaded'); + return; + } + + // Create and load the analytics script + const script = document.createElement('script'); + script.src = 'https://static.cloudflareinsights.com/beacon.min.js'; + script.setAttribute('data-cf-beacon', JSON.stringify({ token })); + script.defer = true; + + script.onload = () => { + console.log('Cloudflare Analytics: Beacon loaded successfully'); + }; + + script.onerror = (error) => { + console.error('Cloudflare Analytics: Failed to load beacon', error); + }; + + // Add the script to the document head + document.head.appendChild(script); + + // Cleanup function to remove the script if needed + return () => { + if (script.parentNode) { + script.parentNode.removeChild(script); + } + }; + }, [token, enabled]); +} + +// Alternative function for manual initialization +export function initializeCloudflareAnalytics(token: string) { + if (!token) { + console.warn('Cloudflare Analytics: No token provided for manual initialization'); + return; + } + + // Set up global settings + (window as any)._cfSettings = (window as any)._cfSettings || {}; + (window as any)._cfSettings.token = token; + + // Check if Cloudflare beacon is available + if (typeof (window as any).__cfBeacon !== 'undefined') { + console.log('Cloudflare Analytics: Beacon already initialized'); + return; + } + + // Load the beacon script + const script = document.createElement('script'); + script.src = 'https://static.cloudflareinsights.com/beacon.min.js'; + script.setAttribute('data-cf-beacon', JSON.stringify({ token })); + script.async = true; + + script.onload = () => { + console.log('Cloudflare Analytics: Manual initialization successful'); + }; + + script.onerror = () => { + console.error('Cloudflare Analytics: Manual initialization failed'); + }; + + document.head.appendChild(script); +} diff --git a/app/root.tsx b/app/root.tsx index f9327a7..11878b9 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -1,4 +1,4 @@ -import type { MetaFunction } from '@remix-run/cloudflare'; +import type { LoaderFunctionArgs, MetaFunction } from '@remix-run/cloudflare'; import { Links, Meta, @@ -6,6 +6,7 @@ import { Scripts, ScrollRestoration, isRouteErrorResponse, + useLoaderData, useRouteError, } from '@remix-run/react'; @@ -17,9 +18,16 @@ import { ThemeSwitcherSafeHTML, ThemeSwitcherScript, } from '@/components/theme-switcher'; +import { useAnalytics } from '@/hooks/use-analytics'; import './globals.css'; +export const loader = async ({ context }: LoaderFunctionArgs) => { + return { + analyticsToken: context.CLOUDFLARE_ANALYTICS_TOKEN || null, + }; +}; + export const meta: MetaFunction = ({ error, location }) => { const title = 'Hack@UCF - UCF Collegiate Cyber Defense Club'; const defaultDescription = @@ -53,12 +61,46 @@ export const meta: MetaFunction = ({ error, location }) => { }; function App({ children }: { children: React.ReactNode }) { + const { analyticsToken } = useLoaderData(); + + // Use analytics hook as fallback + useAnalytics({ token: analyticsToken, enabled: true }); + return ( + {/* Cloudflare Web Analytics */} + {analyticsToken && ( + <> +