diff --git a/src/components/analytics/GoogleTagManager.tsx b/src/components/analytics/GoogleTagManager.tsx new file mode 100644 index 0000000..d92eaf9 --- /dev/null +++ b/src/components/analytics/GoogleTagManager.tsx @@ -0,0 +1,39 @@ +import React from 'react'; + +interface GoogleTagManagerProps { + gtmId: string; +} + +export function GoogleTagManager({ gtmId }: GoogleTagManagerProps) { + React.useEffect(() => { + // Carrega o script do GTM + const script = document.createElement('script'); + script.innerHTML = ` + (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': + new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], + j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= + 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); + })(window,document,'script','dataLayer','${gtmId}'); + `; + document.head.appendChild(script); + + // Adiciona o noscript iframe + const noscript = document.createElement('noscript'); + const iframe = document.createElement('iframe'); + iframe.src = `https://www.googletagmanager.com/ns.html?id=${gtmId}`; + iframe.height = '0'; + iframe.width = '0'; + iframe.style.display = 'none'; + iframe.style.visibility = 'hidden'; + noscript.appendChild(iframe); + document.body.insertBefore(noscript, document.body.firstChild); + + return () => { + // Cleanup + document.head.removeChild(script); + document.body.removeChild(noscript); + }; + }, [gtmId]); + + return null; +} \ No newline at end of file diff --git a/src/components/auth/LoginForm.tsx b/src/components/auth/LoginForm.tsx index 2fe9ef6..397fe75 100644 --- a/src/components/auth/LoginForm.tsx +++ b/src/components/auth/LoginForm.tsx @@ -3,6 +3,7 @@ import { LogIn, Eye, EyeOff, School, GraduationCap, User } from 'lucide-react'; import { useAuth } from '../../hooks/useAuth'; import { useNavigate } from 'react-router-dom'; import { supabase } from '../../lib/supabase'; +import { useDataLayer } from '../../hooks/useDataLayer'; interface LoginFormProps { userType: 'school' | 'teacher' | 'student'; @@ -30,6 +31,7 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps const [loading, setLoading] = useState(false); const { signIn } = useAuth(); const navigate = useNavigate(); + const { trackEvent } = useDataLayer(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -75,6 +77,7 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps throw new Error('Tipo de usuário inválido'); } + trackEvent('auth', 'login_success', 'form'); } catch (err) { console.error('Erro no login:', err); if (err instanceof Error) { @@ -82,6 +85,7 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps } else { setError('Email ou senha incorretos'); } + trackEvent('auth', 'login_error', err.message); } finally { setLoading(false); } diff --git a/src/hooks/useDataLayer.ts b/src/hooks/useDataLayer.ts new file mode 100644 index 0000000..ea1847b --- /dev/null +++ b/src/hooks/useDataLayer.ts @@ -0,0 +1,60 @@ +interface DataLayerEvent { + event: string; + [key: string]: any; +} + +declare global { + interface Window { + dataLayer: any[]; + } +} + +export function useDataLayer() { + // Inicializa o dataLayer se não existir + if (!window.dataLayer) { + window.dataLayer = []; + } + + const push = (data: DataLayerEvent) => { + window.dataLayer.push(data); + }; + + const pageView = (path: string) => { + push({ + event: 'pageview', + page: { + path, + title: document.title, + }, + }); + }; + + const trackEvent = ( + category: string, + action: string, + label?: string, + value?: number + ) => { + push({ + event: 'track_event', + category, + action, + label, + value, + }); + }; + + const trackUserProperties = (properties: Record) => { + push({ + event: 'set_user_properties', + user_properties: properties, + }); + }; + + return { + push, + pageView, + trackEvent, + trackUserProperties, + }; +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 2ed1204..24c5a02 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,8 +5,12 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import * as Sentry from "@sentry/react"; import { router } from './routes'; import { Toaster } from './components/ui/toaster'; +import { GoogleTagManager } from './components/analytics/GoogleTagManager'; import './index.css'; +// GTM ID - Substitua pelo seu ID real do GTM +const GTM_ID = import.meta.env.VITE_GTM_ID; + // Inicialização do Sentry Sentry.init({ dsn: "https://6c15876055bf4a860c1b63a8e4e7ca65@o544400.ingest.us.sentry.io/4508626073092096", @@ -49,6 +53,7 @@ const queryClient = new QueryClient({ function Root() { return ( +