Implementação do GTM

This commit is contained in:
Lucas Santana 2025-01-11 14:23:39 -03:00
parent 75d9d4635b
commit 1542572be4
4 changed files with 108 additions and 0 deletions

View File

@ -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;
}

View File

@ -3,6 +3,7 @@ import { LogIn, Eye, EyeOff, School, GraduationCap, User } from 'lucide-react';
import { useAuth } from '../../hooks/useAuth'; import { useAuth } from '../../hooks/useAuth';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { supabase } from '../../lib/supabase'; import { supabase } from '../../lib/supabase';
import { useDataLayer } from '../../hooks/useDataLayer';
interface LoginFormProps { interface LoginFormProps {
userType: 'school' | 'teacher' | 'student'; userType: 'school' | 'teacher' | 'student';
@ -30,6 +31,7 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { signIn } = useAuth(); const { signIn } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
const { trackEvent } = useDataLayer();
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@ -75,6 +77,7 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps
throw new Error('Tipo de usuário inválido'); throw new Error('Tipo de usuário inválido');
} }
trackEvent('auth', 'login_success', 'form');
} catch (err) { } catch (err) {
console.error('Erro no login:', err); console.error('Erro no login:', err);
if (err instanceof Error) { if (err instanceof Error) {
@ -82,6 +85,7 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps
} else { } else {
setError('Email ou senha incorretos'); setError('Email ou senha incorretos');
} }
trackEvent('auth', 'login_error', err.message);
} finally { } finally {
setLoading(false); setLoading(false);
} }

60
src/hooks/useDataLayer.ts Normal file
View File

@ -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<string, any>) => {
push({
event: 'set_user_properties',
user_properties: properties,
});
};
return {
push,
pageView,
trackEvent,
trackUserProperties,
};
}

View File

@ -5,8 +5,12 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import { router } from './routes'; import { router } from './routes';
import { Toaster } from './components/ui/toaster'; import { Toaster } from './components/ui/toaster';
import { GoogleTagManager } from './components/analytics/GoogleTagManager';
import './index.css'; 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 // Inicialização do Sentry
Sentry.init({ Sentry.init({
dsn: "https://6c15876055bf4a860c1b63a8e4e7ca65@o544400.ingest.us.sentry.io/4508626073092096", dsn: "https://6c15876055bf4a860c1b63a8e4e7ca65@o544400.ingest.us.sentry.io/4508626073092096",
@ -49,6 +53,7 @@ const queryClient = new QueryClient({
function Root() { function Root() {
return ( return (
<StrictMode> <StrictMode>
<GoogleTagManager gtmId={GTM_ID} />
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<RouterProvider router={router} /> <RouterProvider router={router} />
<Toaster /> <Toaster />