diff --git a/src/App.tsx b/src/App.tsx index 9c72211..ecca7f5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,8 @@ import { AuthProvider } from './contexts/AuthContext' import { useNavigate } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { Toaster } from './components/ui/toaster'; +import { router } from './routes'; +import { RouterProvider } from 'react-router-dom'; type AppStep = | 'welcome' @@ -88,6 +90,7 @@ export function App() { return ( +
{step === 'welcome' && ( + + +); +``` + +## 🔧 Troubleshooting + +### Erros Comuns + +1. **Falha na Inicialização** +```typescript +Failed to initialize analytics: Event {...} +``` +Possíveis causas: +- Variáveis de ambiente não configuradas +- Script do Rudderstack bloqueado +- Erro na carga do script + +Soluções: +- Verifique as variáveis de ambiente +- Verifique se o domínio do Rudderstack está liberado +- Adicione tratamento de erro na inicialização: +```typescript +try { + await analytics.init(); +} catch (error) { + console.error('Falha ao inicializar analytics:', error); + // Continue renderizando o app mesmo com falha no analytics +} +``` + +2. **Eventos Não Rastreados** +Se os eventos não estão sendo rastreados, verifique: +- Se o analytics foi inicializado corretamente +- Se há erros no console +- Se o writeKey e dataPlaneUrl estão corretos +- Se há bloqueadores de rastreamento no navegador + +3. **Erros de Tipo** +Se encontrar erros de tipo ao usar os hooks: +- Verifique se está usando as interfaces corretas +- Importe os tipos necessários +- Use as constantes de EVENT_CATEGORIES + ## 📦 Componentes ### PageTracker diff --git a/src/components/layouts/BaseLayout.tsx b/src/components/layouts/BaseLayout.tsx new file mode 100644 index 0000000..2634d04 --- /dev/null +++ b/src/components/layouts/BaseLayout.tsx @@ -0,0 +1,11 @@ +import { Outlet } from 'react-router-dom'; +import { PageTracker } from '../analytics/PageTracker'; + +export function BaseLayout() { + return ( + <> + + + + ); +} \ No newline at end of file diff --git a/src/constants/analytics.ts b/src/constants/analytics.ts index 0ea5b48..c67eed6 100644 --- a/src/constants/analytics.ts +++ b/src/constants/analytics.ts @@ -134,7 +134,7 @@ export const EVENT_PROPERTIES = { STARTED_AT: 'started_at', COMPLETED_AT: 'completed_at', DURATION: 'duration', - TIMESTAMP: 'timestamp', + EVENT_TIMESTAMP: 'event_timestamp', }, // Propriedades de Formulário diff --git a/src/hooks/useButtonTracking.ts b/src/hooks/useButtonTracking.ts index 01a3974..a47a29c 100644 --- a/src/hooks/useButtonTracking.ts +++ b/src/hooks/useButtonTracking.ts @@ -36,7 +36,6 @@ export function useButtonTracking(options: ButtonTrackingOptions = {}) { button_id: buttonId, category, location, - timestamp: new Date().toISOString(), ...properties, // Informações da página page_title: document.title, diff --git a/src/hooks/useErrorTracking.ts b/src/hooks/useErrorTracking.ts index 136038a..e2efdd8 100644 --- a/src/hooks/useErrorTracking.ts +++ b/src/hooks/useErrorTracking.ts @@ -44,7 +44,6 @@ export function useErrorTracking(options: ErrorTrackingOptions = {}) { // Informações do ambiente url: window.location.href, user_agent: navigator.userAgent, - timestamp: new Date().toISOString(), }); }; @@ -71,7 +70,6 @@ export function useErrorTracking(options: ErrorTrackingOptions = {}) { component_stack: componentStack, url: window.location.href, user_agent: navigator.userAgent, - timestamp: new Date().toISOString(), }); }; @@ -101,7 +99,6 @@ export function useErrorTracking(options: ErrorTrackingOptions = {}) { status_code: error.status || error.statusCode, request_data: requestData, url: window.location.href, - timestamp: new Date().toISOString(), }); }; diff --git a/src/hooks/useStudentTracking.ts b/src/hooks/useStudentTracking.ts index 19ba876..a40915f 100644 --- a/src/hooks/useStudentTracking.ts +++ b/src/hooks/useStudentTracking.ts @@ -50,35 +50,30 @@ export function useStudentTracking() { const trackStoryGenerated = (properties: StoryGeneratedProps) => { analytics.track('story_generated', { ...properties, - timestamp: new Date().toISOString() }); }; const trackAudioRecorded = (properties: AudioRecordedProps) => { analytics.track('audio_recorded', { ...properties, - timestamp: new Date().toISOString() }); }; const trackExerciseCompleted = (properties: ExerciseCompletedProps) => { analytics.track('exercise_completed', { ...properties, - timestamp: new Date().toISOString() }); }; const trackInterestAdded = (properties: InterestActionProps) => { analytics.track('interest_added', { ...properties, - timestamp: new Date().toISOString() }); }; const trackInterestRemoved = (properties: InterestActionProps) => { analytics.track('interest_removed', { ...properties, - timestamp: new Date().toISOString() }); }; diff --git a/src/lib/analytics/index.ts b/src/lib/analytics/index.ts index 0ef34dd..d3ef0de 100644 --- a/src/lib/analytics/index.ts +++ b/src/lib/analytics/index.ts @@ -47,58 +47,68 @@ export class Analytics { } // Inicializa o Rudderstack - init(options?: { writeKey?: string; dataPlaneUrl?: string }): Promise { + async init(): Promise { if (this.initPromise) return this.initPromise; - this.initPromise = new Promise((resolve, reject) => { - const writeKey = options?.writeKey || this.writeKey || import.meta.env.VITE_RUDDERSTACK_WRITE_KEY; - const dataPlaneUrl = options?.dataPlaneUrl || this.dataPlaneUrl || import.meta.env.VITE_RUDDERSTACK_DATA_PLANE_URL; - - if (!writeKey || !dataPlaneUrl) { - const error = new Error('Missing Rudderstack configuration'); - this.handleError('init', error); - reject(error); - return; - } - - try { - const script = document.createElement('script'); - script.src = 'https://cdn.rudderlabs.com/v1.1/rudder-analytics.min.js'; - script.async = true; - - script.onload = () => { - if (window.rudderanalytics) { - window.rudderanalytics.load(writeKey, dataPlaneUrl, { - configUrl: 'https://api.rudderlabs.com', - logLevel: this.debug ? 'DEBUG' : 'ERROR', + this.initPromise = new Promise((resolve, reject) => { + const initializeAnalytics = async () => { + try { + const writeKey = this.writeKey; + const dataPlaneUrl = this.dataPlaneUrl; + + if (this.debug) { + console.log('Inicializando analytics com:', { + writeKey, + dataPlaneUrl, + debug: this.debug }); - - // Espera um pequeno intervalo para garantir que o Rudderstack está pronto - setTimeout(() => { - this.initialized = true; - this.processEventQueue(); - if (this.debug) { - console.log('Rudderstack initialized successfully'); - } - resolve(); - }, 100); - } else { - const error = new Error('Failed to load Rudderstack'); - this.handleError('init', error); - reject(error); } - }; + + if (!writeKey || !dataPlaneUrl) { + throw new Error('Analytics configuration missing'); + } - script.onerror = (error) => { + await new Promise((scriptResolve, scriptReject) => { + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = 'https://cdn.rudderlabs.com/v1.1/rudder-analytics.min.js'; + script.crossOrigin = 'anonymous'; + script.async = true; + + script.onload = () => { + if (!window.rudderanalytics) { + scriptReject(new Error('Rudderstack failed to initialize')); + return; + } + + window.rudderanalytics.load(writeKey, dataPlaneUrl, { + configUrl: 'https://api.rudderlabs.com', + logLevel: this.debug ? 'DEBUG' : 'ERROR', + secureCookie: true, + crossDomainLinker: true + }); + scriptResolve(); + }; + + script.onerror = (error) => { + scriptReject(new Error('Failed to load Rudderstack script: ' + error)); + }; + + document.head.appendChild(script); + }); + + await new Promise((waitResolve) => setTimeout(waitResolve, 1000)); + this.initialized = true; + this.processEventQueue(); + resolve(); + } catch (error) { + console.error('Analytics initialization failed:', error); this.handleError('init', error); reject(error); - }; + } + }; - document.head.appendChild(script); - } catch (error) { - this.handleError('init', error); - reject(error); - } + initializeAnalytics(); }); return this.initPromise; @@ -210,7 +220,7 @@ export class Analytics { // Enriquece as propriedades com dados padrão private enrichEventProperties(properties?: Record): BaseEventProperties { const baseProperties: BaseEventProperties = { - [EVENT_PROPERTIES.TIMING.TIMESTAMP]: new Date().toISOString(), + [EVENT_PROPERTIES.TIMING.EVENT_TIMESTAMP]: new Date().toISOString(), [EVENT_PROPERTIES.PAGE.URL]: window.location.href, [EVENT_PROPERTIES.PAGE.TITLE]: document.title, [EVENT_PROPERTIES.PAGE.PATH]: window.location.pathname, @@ -248,5 +258,7 @@ export class Analytics { // Instância global export const analytics = new Analytics({ - debug: import.meta.env.DEV + debug: import.meta.env.DEV, + writeKey: import.meta.env.VITE_RUDDERSTACK_WRITE_KEY, + dataPlaneUrl: import.meta.env.VITE_RUDDERSTACK_DATA_PLANE_URL }); \ No newline at end of file diff --git a/src/pages/demo/DemoPage.tsx b/src/pages/demo/DemoPage.tsx index 2ce2ae1..b229328 100644 --- a/src/pages/demo/DemoPage.tsx +++ b/src/pages/demo/DemoPage.tsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { AudioRecorderDemo } from '../../components/demo/AudioRecorderDemo'; import { ArrowRight, Sparkles } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; +import { analytics } from '../../lib/analytics'; export function DemoPage() { const navigate = useNavigate(); @@ -14,6 +15,15 @@ export function DemoPage() { const handleDemoComplete = (result: typeof demoResult) => { setDemoResult(result); + // Rastreia quando o resultado é exibido + if (result) { + analytics.track('demo_completed', { + fluency: result.fluency, + accuracy: result.accuracy, + confidence: result.confidence, + has_feedback: !!result.feedback + }); + } }; return ( diff --git a/src/pages/landing/EducationalForParents.tsx b/src/pages/landing/EducationalForParents.tsx index f32fc60..f5fa7df 100644 --- a/src/pages/landing/EducationalForParents.tsx +++ b/src/pages/landing/EducationalForParents.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useNavigate } from 'react-router-dom'; -import { +import { ArrowRight, Brain, BookOpen, @@ -41,28 +41,28 @@ export function EducationalForParents() {
Tempo de leitura: 5 minutos -
- +
+

Transforme o
Aprendizado em
Uma Aventura Mágica -

- + +

Histórias educativas personalizadas que encantam e ensinam, criadas especialmente para o desenvolvimento único do seu filho.

- -
+ +
@@ -101,7 +101,7 @@ export function EducationalForParents() {
{challenges.map((challenge, index) => (

Um processo simples e eficaz para transformar a leitura em uma aventura inesquecível. -

-
+

+
{magicSteps.map((step, index) => ( - ))} + ))} +
- {/* Comparison Section */}
@@ -149,18 +149,18 @@ export function EducationalForParents() {

Descubra todas as vantagens que nossa plataforma oferece para o desenvolvimento do seu filho.

-
+
{detailedBenefits.map((benefit, index) => ( - ))} -
- + /> + ))} + + {/* Results Section */}
@@ -171,7 +171,7 @@ export function EducationalForParents() {

Números que demonstram o impacto do Leiturama no aprendizado.

-
+
))}
- - + + {/* Pricing Section */}
-
+ {/* FAQ Section */}
@@ -236,7 +236,7 @@ export function EducationalForParents() { description="Encontre respostas para as perguntas mais comuns sobre nossa plataforma." items={faqItems} /> -
+ {/* Final CTA */}
@@ -371,7 +371,7 @@ const comparisonData = [ 'Pais confiantes no desenvolvimento mágico' ] } -]; +]; const detailedBenefits = [ { diff --git a/src/routes.tsx b/src/routes.tsx index cc93cc9..0a89eea 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,4 +1,5 @@ import { createBrowserRouter } from 'react-router-dom'; +import { BaseLayout } from './components/layouts/BaseLayout'; import { HomePage } from './components/home/HomePage'; import { LoginForm } from './components/auth/LoginForm'; import { SchoolRegistrationForm } from './components/auth/SchoolRegistrationForm'; @@ -46,181 +47,187 @@ function RootLayout({ children }: { children: React.ReactNode }) { export const router = createBrowserRouter([ { path: '/', - element: , - }, - { - path: '/teste', - element: , - }, - { - path: '/para-pais', - element: , - }, - { - path: '/evidencias', - element: , - }, - { - path: '/evidencias/tsl', - element: , - }, - { - path: '/login', + element: , children: [ { - path: 'school', - element: , + path: '/', + element: , }, { - path: 'teacher', - element: , + path: '/teste', + element: , }, { - path: 'student', - element: , - } - ] - }, - { - path: '/register', - children: [ - { - path: 'school', - element: , + path: '/para-pais', + element: , }, { - path: 'teacher', - element: { - console.log('Registro completo:', userData); - }} - />, - } - ] - }, - { - path: '/dashboard', - element: ( - - - - ), - children: [ - { - index: true, - element: , + path: '/evidencias', + element: , }, { - path: 'turmas', + path: '/evidencias/tsl', + element: , + }, + { + path: '/login', children: [ { - index: true, - element: , + path: 'school', + element: , }, { - path: 'nova', - element: , + path: 'teacher', + element: , + }, + { + path: 'student', + element: , } ] }, { - path: 'professores', + path: '/register', children: [ { - index: true, - element: , + path: 'school', + element: , }, { - path: 'convidar', - element: , + path: 'teacher', + element: { + console.log('Registro completo:', userData); + }} + />, } ] }, { - path: 'alunos', + path: '/dashboard', + element: ( + + + + ), children: [ { index: true, - element: , + element: , }, { - path: 'novo', - element: , + path: 'turmas', + children: [ + { + index: true, + element: , + }, + { + path: 'nova', + element: , + } + ] + }, + { + path: 'professores', + children: [ + { + index: true, + element: , + }, + { + path: 'convidar', + element: , + } + ] + }, + { + path: 'alunos', + children: [ + { + index: true, + element: , + }, + { + path: 'novo', + element: , + } + ] + }, + { + path: 'configuracoes', + element: } ] }, { - path: 'configuracoes', - element: + path: '/demo', + element: + }, + { + path: '/auth/callback', + element: + }, + { + path: '/aluno', + element: ( + + + + ), + children: [ + { + index: true, + element: , + }, + { + path: 'historias', + children: [ + { + index: true, + element: , + }, + { + path: 'nova', + element: , + }, + { + path: ':id', + element: , + } + ] + }, + { + path: 'configuracoes', + element: , + }, + { + path: 'conquistas', + element: , + }, + { + path: 'turmas/:classId', + element: , + } + ] + }, + { + path: '/admin/users', + element: ( + + + + ), + }, + { + path: '/para-educadores', + element: , + }, + { + path: '/aluno/historias/:id/exercicios/:type', + element: } ] - }, - { - path: '/demo', - element: - }, - { - path: '/auth/callback', - element: - }, - { - path: '/aluno', - element: ( - - - - ), - children: [ - { - index: true, - element: , - }, - { - path: 'historias', - children: [ - { - index: true, - element: , - }, - { - path: 'nova', - element: , - }, - { - path: ':id', - element: , - } - ] - }, - { - path: 'configuracoes', - element: , - }, - { - path: 'conquistas', - element: , - }, - { - path: 'turmas/:classId', - element: , - } - ] - }, - { - path: '/admin/users', - element: ( - - - - ), - }, - { - path: '/para-educadores', - element: , - }, - { - path: '/aluno/historias/:id/exercicios/:type', - element: } ]); \ No newline at end of file