mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-17 05:47:52 +00:00
fix: PageTracker geral
This commit is contained in:
parent
98411b2aa1
commit
bcbdd07a41
@ -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 (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>
|
||||
<RouterProvider router={router} />
|
||||
<div className="min-h-screen bg-gradient-to-b from-purple-100 to-blue-100">
|
||||
{step === 'welcome' && (
|
||||
<WelcomePage
|
||||
|
||||
@ -70,7 +70,6 @@ export function PageTracker() {
|
||||
...userTraits,
|
||||
|
||||
// Metadados adicionais
|
||||
timestamp: new Date().toISOString(),
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
});
|
||||
|
||||
|
||||
@ -2,6 +2,71 @@
|
||||
|
||||
Este diretório contém a implementação do sistema de analytics do Leiturama, utilizando Rudderstack como principal ferramenta de tracking.
|
||||
|
||||
## 🚀 Inicialização
|
||||
|
||||
Para inicializar o sistema de analytics corretamente, certifique-se de:
|
||||
|
||||
1. Configurar as variáveis de ambiente:
|
||||
```env
|
||||
VITE_RUDDERSTACK_WRITE_KEY=seu_write_key
|
||||
VITE_RUDDERSTACK_DATA_PLANE_URL=sua_url
|
||||
```
|
||||
|
||||
2. Inicializar o analytics antes de usar:
|
||||
```typescript
|
||||
// main.tsx
|
||||
import { analytics } from './lib/analytics';
|
||||
|
||||
// Inicializa o analytics antes de renderizar o app
|
||||
await analytics.init();
|
||||
|
||||
// Renderiza o app
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
```
|
||||
|
||||
## 🔧 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
|
||||
|
||||
11
src/components/layouts/BaseLayout.tsx
Normal file
11
src/components/layouts/BaseLayout.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { PageTracker } from '../analytics/PageTracker';
|
||||
|
||||
export function BaseLayout() {
|
||||
return (
|
||||
<>
|
||||
<PageTracker />
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -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()
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -47,58 +47,68 @@ export class Analytics {
|
||||
}
|
||||
|
||||
// Inicializa o Rudderstack
|
||||
init(options?: { writeKey?: string; dataPlaneUrl?: string }): Promise<void> {
|
||||
async init(): Promise<void> {
|
||||
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<void>((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<void>((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<string, unknown>): 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
|
||||
});
|
||||
@ -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 (
|
||||
|
||||
@ -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() {
|
||||
<div className="absolute right-4 top-4 text-gray-600 flex items-center gap-2">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span className="text-sm">Tempo de leitura: 5 minutos</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h1 className="mt-10 text-4xl font-bold tracking-tight text-gray-900 sm:text-6xl">
|
||||
Transforme o<br />
|
||||
Aprendizado em<br />
|
||||
Uma <span className="text-purple-600">Aventura Mágica</span>
|
||||
</h1>
|
||||
|
||||
</h1>
|
||||
|
||||
<p className="mt-6 text-lg leading-8 text-gray-600 max-w-xl">
|
||||
Histórias educativas personalizadas que encantam e ensinam,
|
||||
criadas especialmente para o desenvolvimento único do seu filho.
|
||||
</p>
|
||||
|
||||
<div className="mt-10">
|
||||
<button
|
||||
<button
|
||||
onClick={() => window.location.href = '/cadastro'}
|
||||
className="rounded-xl bg-purple-600 px-8 py-4 text-base font-semibold text-white shadow-sm hover:bg-purple-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-purple-600 flex items-center gap-2"
|
||||
>
|
||||
Comece Sua Aventura Mágica Grátis
|
||||
>
|
||||
Comece Sua Aventura Mágica Grátis
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 flex items-center gap-x-8 text-sm text-gray-600">
|
||||
<div className="flex items-center gap-x-2">
|
||||
@ -101,7 +101,7 @@ export function EducationalForParents() {
|
||||
<div className="mt-16 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{challenges.map((challenge, index) => (
|
||||
<InfoCard
|
||||
key={index}
|
||||
key={index}
|
||||
icon={challenge.icon}
|
||||
title={challenge.title}
|
||||
description={challenge.description}
|
||||
@ -118,8 +118,8 @@ export function EducationalForParents() {
|
||||
</h2>
|
||||
<p className="mt-4 text-lg leading-8 text-gray-600">
|
||||
Um processo simples e eficaz para transformar a leitura em uma aventura inesquecível.
|
||||
</p>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-16 space-y-12">
|
||||
{magicSteps.map((step, index) => (
|
||||
<ProcessStep
|
||||
@ -128,9 +128,9 @@ export function EducationalForParents() {
|
||||
title={step.title}
|
||||
description={step.description}
|
||||
/>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Comparison Section */}
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-24">
|
||||
@ -149,18 +149,18 @@ export function EducationalForParents() {
|
||||
<p className="mt-4 text-lg leading-8 text-gray-600">
|
||||
Descubra todas as vantagens que nossa plataforma oferece para o desenvolvimento do seu filho.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-16 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{detailedBenefits.map((benefit, index) => (
|
||||
<FeatureCard
|
||||
key={index}
|
||||
key={index}
|
||||
icon={benefit.icon}
|
||||
title={benefit.title}
|
||||
description={benefit.description}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Results Section */}
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-24">
|
||||
@ -171,7 +171,7 @@ export function EducationalForParents() {
|
||||
<p className="mt-4 text-lg leading-8 text-gray-600">
|
||||
Números que demonstram o impacto do Leiturama no aprendizado.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-16 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<StatCard
|
||||
icon={LineChart}
|
||||
@ -222,12 +222,12 @@ export function EducationalForParents() {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Pricing Section */}
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-24">
|
||||
<PlanForParents />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* FAQ Section */}
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-24 bg-gradient-to-b from-purple-50">
|
||||
@ -236,7 +236,7 @@ export function EducationalForParents() {
|
||||
description="Encontre respostas para as perguntas mais comuns sobre nossa plataforma."
|
||||
items={faqItems}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Final CTA */}
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-24">
|
||||
@ -371,7 +371,7 @@ const comparisonData = [
|
||||
'Pais confiantes no desenvolvimento mágico'
|
||||
]
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
const detailedBenefits = [
|
||||
{
|
||||
|
||||
285
src/routes.tsx
285
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: <RootLayout><HomePage /></RootLayout>,
|
||||
},
|
||||
{
|
||||
path: '/teste',
|
||||
element: <TestWordHighlighter />,
|
||||
},
|
||||
{
|
||||
path: '/para-pais',
|
||||
element: <ParentsLandingPage />,
|
||||
},
|
||||
{
|
||||
path: '/evidencias',
|
||||
element: <EvidenceBased />,
|
||||
},
|
||||
{
|
||||
path: '/evidencias/tsl',
|
||||
element: <TextSalesLetter />,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
element: <BaseLayout />,
|
||||
children: [
|
||||
{
|
||||
path: 'school',
|
||||
element: <LoginForm userType="school" />,
|
||||
path: '/',
|
||||
element: <RootLayout><HomePage /></RootLayout>,
|
||||
},
|
||||
{
|
||||
path: 'teacher',
|
||||
element: <LoginForm userType="teacher" />,
|
||||
path: '/teste',
|
||||
element: <TestWordHighlighter />,
|
||||
},
|
||||
{
|
||||
path: 'student',
|
||||
element: <LoginForm userType="student" />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/register',
|
||||
children: [
|
||||
{
|
||||
path: 'school',
|
||||
element: <SchoolRegistrationForm />,
|
||||
path: '/para-pais',
|
||||
element: <ParentsLandingPage />,
|
||||
},
|
||||
{
|
||||
path: 'teacher',
|
||||
element: <RegistrationForm
|
||||
userType="teacher"
|
||||
onComplete={(userData) => {
|
||||
console.log('Registro completo:', userData);
|
||||
}}
|
||||
/>,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/dashboard',
|
||||
element: (
|
||||
<ProtectedRoute allowedRoles={['school']}>
|
||||
<DashboardLayout />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <DashboardHome />,
|
||||
path: '/evidencias',
|
||||
element: <EvidenceBased />,
|
||||
},
|
||||
{
|
||||
path: 'turmas',
|
||||
path: '/evidencias/tsl',
|
||||
element: <TextSalesLetter />,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <ClassesPage />,
|
||||
path: 'school',
|
||||
element: <LoginForm userType="school" />,
|
||||
},
|
||||
{
|
||||
path: 'nova',
|
||||
element: <CreateClassPage />,
|
||||
path: 'teacher',
|
||||
element: <LoginForm userType="teacher" />,
|
||||
},
|
||||
{
|
||||
path: 'student',
|
||||
element: <LoginForm userType="student" />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'professores',
|
||||
path: '/register',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <TeachersPage />,
|
||||
path: 'school',
|
||||
element: <SchoolRegistrationForm />,
|
||||
},
|
||||
{
|
||||
path: 'convidar',
|
||||
element: <InviteTeacherPage />,
|
||||
path: 'teacher',
|
||||
element: <RegistrationForm
|
||||
userType="teacher"
|
||||
onComplete={(userData) => {
|
||||
console.log('Registro completo:', userData);
|
||||
}}
|
||||
/>,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'alunos',
|
||||
path: '/dashboard',
|
||||
element: (
|
||||
<ProtectedRoute allowedRoles={['school']}>
|
||||
<DashboardLayout />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <StudentsPage />,
|
||||
element: <DashboardHome />,
|
||||
},
|
||||
{
|
||||
path: 'novo',
|
||||
element: <AddStudentPage />,
|
||||
path: 'turmas',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <ClassesPage />,
|
||||
},
|
||||
{
|
||||
path: 'nova',
|
||||
element: <CreateClassPage />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'professores',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <TeachersPage />,
|
||||
},
|
||||
{
|
||||
path: 'convidar',
|
||||
element: <InviteTeacherPage />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'alunos',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <StudentsPage />,
|
||||
},
|
||||
{
|
||||
path: 'novo',
|
||||
element: <AddStudentPage />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'configuracoes',
|
||||
element: <SettingsPage />
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'configuracoes',
|
||||
element: <SettingsPage />
|
||||
path: '/demo',
|
||||
element: <StoryPageDemo />
|
||||
},
|
||||
{
|
||||
path: '/auth/callback',
|
||||
element: <AuthCallback />
|
||||
},
|
||||
{
|
||||
path: '/aluno',
|
||||
element: (
|
||||
<ProtectedRoute allowedRoles={['student']}>
|
||||
<StudentDashboardLayout />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <StudentDashboardPage />,
|
||||
},
|
||||
{
|
||||
path: 'historias',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <StudentStoriesPage />,
|
||||
},
|
||||
{
|
||||
path: 'nova',
|
||||
element: <CreateStoryPage />,
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
element: <StoryPage />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'configuracoes',
|
||||
element: <StudentSettingsPage />,
|
||||
},
|
||||
{
|
||||
path: 'conquistas',
|
||||
element: <AchievementsPage />,
|
||||
},
|
||||
{
|
||||
path: 'turmas/:classId',
|
||||
element: <StudentClassPage />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/admin/users',
|
||||
element: (
|
||||
<ProtectedRoute allowedRoles={['admin']}>
|
||||
<UserManagementPage />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '/para-educadores',
|
||||
element: <EducationalForParents />,
|
||||
},
|
||||
{
|
||||
path: '/aluno/historias/:id/exercicios/:type',
|
||||
element: <ExercisePage />
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo',
|
||||
element: <StoryPageDemo />
|
||||
},
|
||||
{
|
||||
path: '/auth/callback',
|
||||
element: <AuthCallback />
|
||||
},
|
||||
{
|
||||
path: '/aluno',
|
||||
element: (
|
||||
<ProtectedRoute allowedRoles={['student']}>
|
||||
<StudentDashboardLayout />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <StudentDashboardPage />,
|
||||
},
|
||||
{
|
||||
path: 'historias',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <StudentStoriesPage />,
|
||||
},
|
||||
{
|
||||
path: 'nova',
|
||||
element: <CreateStoryPage />,
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
element: <StoryPage />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'configuracoes',
|
||||
element: <StudentSettingsPage />,
|
||||
},
|
||||
{
|
||||
path: 'conquistas',
|
||||
element: <AchievementsPage />,
|
||||
},
|
||||
{
|
||||
path: 'turmas/:classId',
|
||||
element: <StudentClassPage />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/admin/users',
|
||||
element: (
|
||||
<ProtectedRoute allowedRoles={['admin']}>
|
||||
<UserManagementPage />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '/para-educadores',
|
||||
element: <EducationalForParents />,
|
||||
},
|
||||
{
|
||||
path: '/aluno/historias/:id/exercicios/:type',
|
||||
element: <ExercisePage />
|
||||
}
|
||||
]);
|
||||
Loading…
Reference in New Issue
Block a user