Implementando Rudderstack

This commit is contained in:
Lucas Santana 2025-01-11 14:52:27 -03:00
parent 6e9d847c77
commit 21f7aa7c40
3 changed files with 149 additions and 10 deletions

View File

@ -57,6 +57,12 @@ export function AudioUploader({
as="span"
disabled={isProcessing}
className="cursor-pointer"
trackingId="audio-upload-button"
trackingProperties={{
category: 'audio',
action: 'upload_click',
label: 'audio_uploader'
}}
>
{isProcessing ? 'Processando...' : 'Enviar Áudio'}
</Button>

View File

@ -6,6 +6,7 @@ import { supabase } from '../../lib/supabase';
import { useDataLayer } from '../../hooks/useDataLayer';
import { useFormTracking } from '../../hooks/useFormTracking';
import { Button } from '../ui/button';
import { useErrorTracking } from '../../hooks/useErrorTracking';
interface LoginFormProps {
userType: 'school' | 'teacher' | 'student';
@ -39,6 +40,10 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps
formName: `${userType}-login`,
category: 'auth'
});
const errorTracking = useErrorTracking({
category: 'auth',
userEmail: email
});
useEffect(() => {
formTracking.trackFormStarted();
@ -60,13 +65,20 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps
console.log('Resposta do Supabase:', { data, error });
if (error) {
errorTracking.trackApiError(error, '/auth/sign-in', 'POST', { email, userType });
formTracking.trackFormError('auth_error', error.message);
throw error;
}
if (!data.user) {
const err = new Error('Usuário não encontrado');
errorTracking.trackError(err, {
componentName: 'LoginForm',
action: 'login_attempt',
metadata: { userType }
});
formTracking.trackFormError('user_not_found', 'Usuário não encontrado');
throw new Error('Usuário não encontrado');
throw err;
}
const userRole = data.user.user_metadata.role;
@ -75,8 +87,17 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps
console.log('Role atual:', userRole);
if (userRole !== userType) {
formTracking.trackFormError('invalid_role', `Este não é um login de ${userTypeLabels[userType]}`);
throw new Error(`Este não é um login de ${userTypeLabels[userType]}`);
const err = new Error(`Este não é um login de ${userTypeLabels[userType]}`);
errorTracking.trackError(err, {
componentName: 'LoginForm',
action: 'role_validation',
metadata: {
expectedRole: userType,
actualRole: userRole
}
});
formTracking.trackFormError('invalid_role', err.message);
throw err;
}
formTracking.trackFormSubmitted(true, {
@ -101,16 +122,14 @@ export function LoginForm({ userType, onLogin, onRegisterClick }: LoginFormProps
trackEvent('auth', 'login_success', 'form');
} catch (err) {
console.error('Erro no login:', err);
if (err instanceof Error) {
setError(err.message);
} else {
setError('Email ou senha incorretos');
}
const errorMessage = err instanceof Error ? err.message : 'Email ou senha incorretos';
setError(errorMessage);
formTracking.trackFormSubmitted(false, {
error_type: err instanceof Error ? 'validation_error' : 'unknown_error',
error_message: err instanceof Error ? err.message : 'Email ou senha incorretos'
error_message: errorMessage
});
trackEvent('auth', 'login_error', err.message);
trackEvent('auth', 'login_error', errorMessage);
} finally {
setLoading(false);
}

View File

@ -0,0 +1,114 @@
import * as Sentry from '@sentry/react';
import { useRudderstack } from './useRudderstack';
interface ErrorTrackingOptions {
category?: string;
userId?: string;
userEmail?: string;
}
export function useErrorTracking(options: ErrorTrackingOptions = {}) {
const { track } = useRudderstack();
const { category = 'error', userId, userEmail } = options;
const trackError = (
error: Error,
context?: {
componentName?: string;
action?: string;
metadata?: Record<string, any>;
}
) => {
// 1. Rastreia no Sentry primeiro (para debug técnico)
Sentry.withScope((scope) => {
// Adiciona contexto do usuário
if (userId) scope.setUser({ id: userId, email: userEmail });
// Adiciona contexto do erro
if (context?.componentName) scope.setTag('component', context.componentName);
if (context?.action) scope.setTag('action', context.action);
if (context?.metadata) scope.setContext('metadata', context.metadata);
// Captura o erro
Sentry.captureException(error);
});
// 2. Rastreia no Rudderstack (para analytics)
track('error_occurred', {
category,
error_name: error.name,
error_message: error.message,
error_stack: error.stack,
component: context?.componentName,
action: context?.action,
...context?.metadata,
// Informações do ambiente
url: window.location.href,
user_agent: navigator.userAgent,
timestamp: new Date().toISOString(),
});
};
const trackErrorBoundary = (
error: Error,
componentStack: string,
componentName: string
) => {
// 1. Rastreia no Sentry
Sentry.withScope((scope) => {
if (userId) scope.setUser({ id: userId, email: userEmail });
scope.setTag('component', componentName);
scope.setTag('error_type', 'react_error_boundary');
scope.setExtra('componentStack', componentStack);
Sentry.captureException(error);
});
// 2. Rastreia no Rudderstack
track('error_boundary_triggered', {
category,
error_name: error.name,
error_message: error.message,
component_name: componentName,
component_stack: componentStack,
url: window.location.href,
user_agent: navigator.userAgent,
timestamp: new Date().toISOString(),
});
};
const trackApiError = (
error: any,
endpoint: string,
method: string,
requestData?: any
) => {
// 1. Rastreia no Sentry
Sentry.withScope((scope) => {
if (userId) scope.setUser({ id: userId, email: userEmail });
scope.setTag('error_type', 'api_error');
scope.setTag('endpoint', endpoint);
scope.setTag('method', method);
scope.setContext('request', { data: requestData });
Sentry.captureException(error);
});
// 2. Rastreia no Rudderstack
track('api_error_occurred', {
category,
endpoint,
method,
error_name: error.name,
error_message: error.message,
status_code: error.status || error.statusCode,
request_data: requestData,
url: window.location.href,
timestamp: new Date().toISOString(),
});
};
return {
trackError,
trackErrorBoundary,
trackApiError,
};
}