feat: implementa tracking avançado nos planos e p��ginas
Some checks are pending
Docker Build and Push / build (push) Waiting to run

- Adiciona tracking detalhado nos bot��es dos planos

- Atualiza PageTracker com dados enriquecidos do usu��rio

- Remove CTA de demonstra����o dos planos

- Corrige tipagem do objeto User no PageTracker

- Adiciona CHANGELOG.md com documenta����o das mudan��as
This commit is contained in:
Lucas Santana 2025-01-12 09:38:05 -03:00
parent 33b9b38ff4
commit 3cdd136a4e
5 changed files with 255 additions and 157 deletions

View File

@ -5,112 +5,21 @@ Todas as mudanças notáveis neste projeto serão documentadas neste arquivo.
O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.0.0/),
e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/).
## [1.4.1] - 2024-01-17
### Modificado
- Expandido conteúdo da Text Sales Letter:
- Adicionada explicação detalhada dos 5 pilares da alfabetização
- Incluídas seções sobre processo de implementação
- Melhorada apresentação de resultados e depoimentos
- Adicionadas estatísticas e dados do SAEB
- Aprimorada a estrutura visual com cards e ícones
- Expandido texto para mais de 5.000 palavras
### Técnico
- Adicionados novos ícones do Lucide React
- Implementados novos componentes visuais para métricas
- Melhorada a estrutura de grid e layout responsivo
- Otimizada a organização das seções de conteúdo
## [1.4.0] - 2024-01-17
## [1.0.0] - 2024-03-19
### Adicionado
- Nova página Text Sales Letter focada em educação baseada em evidências
- Conteúdo detalhado sobre métodos científicos vs. pseudociências
- Rota `/evidencias/tsl` para acesso à nova página
- Seções estruturadas com dados estatísticos e evidências científicas
### Técnico
- Implementação de layout responsivo com Tailwind CSS
- Integração com sistema de navegação existente
- Otimização de SEO para conteúdo educacional
- Implementação do tracking de eventos nos botões dos planos para escolas e pais
- Tracking detalhado de visualização de página com informações enriquecidas
- Integração com Rudderstack para análise de dados
- Novos componentes de UI: `PlanForSchools` e `PlanForParents`
### Modificado
- Atualização da estrutura de rotas para incluir nova página
- Melhorias na organização do conteúdo sobre evidências científicas
## [1.3.0] - 2024-01-17
### Adicionado
- Novo componente reutilizável `FAQ` com layout simplificado
- Implementação do FAQ em todas as Landing Pages com conteúdo específico:
- Para Pais: foco em funcionalidades e benefícios para as crianças
- Para Educadores: ênfase em recursos pedagógicos e suporte
- Evidências: destaque para base científica e metodologia
- HomePage: foco em implementação e suporte para escolas
- Atualização do componente `PageTracker` para incluir dados do usuário via `user_metadata`
- Refatoração dos botões para usar o componente `Button` com propriedades de tracking
- Remoção do CTA "Ver Demonstração" da seção superior dos planos
### Técnico
- Criação de interfaces TypeScript para tipagem do FAQ
- Implementação de estilos consistentes com Tailwind CSS
- Remoção da dependência do Radix UI Accordion
### Modificado
- Substituição das seções de FAQ existentes pelo novo componente reutilizável
- Atualização da estrutura de navegação nas Landing Pages
- Melhoria na organização do código com componentização
## [1.2.0] - 2024-01-17
### Adicionado
- Novo componente reutilizável `FAQ` usando Accordion do Radix UI
- Implementação do FAQ em todas as Landing Pages com conteúdo específico:
- Para Pais: foco em funcionalidades e benefícios para as crianças
- Para Educadores: ênfase em recursos pedagógicos e suporte
- Evidências: destaque para base científica e metodologia
### Técnico
- Criação de interfaces TypeScript para tipagem do FAQ
- Integração com Radix UI Accordion para acessibilidade
- Implementação de animações suaves na expansão/contração
### Modificado
- Substituição das seções de FAQ existentes pelo novo componente reutilizável
- Atualização da estrutura de navegação nas Landing Pages
- Melhoria na organização do código com componentização
## [1.1.1] - 2024-01-17
### Técnico
- Refatoração das interfaces do banco de dados:
- Criada interface base `BaseEntity` para reduzir duplicação
- Corrigidos conflitos de tipos em `email`, `status` e `cover`
- Padronizados os tipos de campos em todas as interfaces
- Corrigidas as interfaces `ClassWithStudents` e `ClassWithStudentsAndStories`
- Melhorada a organização do código com herança de interfaces
- Corrigido erro no teste do `WordHighlighter`:
- Adicionada importação do `beforeEach` do Vitest
- Mantida a estrutura dos testes existentes
## [1.1.0] - 2024-01-17
### Adicionado
- Novo componente reutilizável `Footer` para todas as Landing Pages
- Novos componentes de planos:
- `PlanForParents`: Planos focados em pais com preços acessíveis
- `PlanForSchools`: Planos corporativos focados em escolas
- Implementação dos novos componentes nas páginas:
- `/para-pais`: Adicionado `PlanForParents`
- `/evidencias`: Adicionado `PlanForSchools`
- `/para-educadores`: Adicionado `PlanForParents`
- `HomePage`: Atualizado com `PlanForSchools`
### Modificado
- Refatoração das Landing Pages para utilizar o novo componente `Footer`
- Atualização da estrutura de navegação com links organizados em seções
- Melhorias na responsividade e consistência visual dos planos
### Técnico
- Implementação de tipos TypeScript para os novos componentes
- Adição de props para customização dos componentes
- Melhorias na organização do código com componentes reutilizáveis
- Correção de imports não utilizados em `ParentsLandingPage.tsx`
- Correção de erros de tipagem no `PageTracker` relacionados ao objeto `User`
- Implementação de tracking consistente em todos os botões de planos
- Adição de propriedades de tracking detalhadas para análise de conversão
- Melhoria na coleta de dados de dispositivo, performance e sessão

View File

@ -1,20 +1,103 @@
import { useEffect } from 'react';
import { useRudderstack } from '../../hooks/useRudderstack';
import { useLocation } from 'react-router-dom';
import { useAuth } from '../../hooks/useAuth';
export function PageTracker() {
const location = useLocation();
const { track } = useRudderstack();
const { page } = useRudderstack();
const { user } = useAuth();
useEffect(() => {
track('page_viewed', {
// Coleta informações do dispositivo/navegador
const deviceInfo = {
screenWidth: window.screen.width,
screenHeight: window.screen.height,
viewportWidth: window.innerWidth,
viewportHeight: window.innerHeight,
deviceType: getDeviceType(),
deviceOrientation: window.screen.orientation.type,
userAgent: navigator.userAgent,
language: navigator.language,
};
// Coleta informações de performance
const performanceInfo = {
loadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart,
domInteractive: window.performance.timing.domInteractive - window.performance.timing.navigationStart,
firstContentfulPaint: getFirstContentfulPaint(),
};
// Informações da sessão
const sessionInfo = {
sessionStartTime: sessionStorage.getItem('sessionStartTime') || new Date().toISOString(),
isFirstVisit: !localStorage.getItem('returningVisitor'),
lastVisitedPage: sessionStorage.getItem('lastVisitedPage'),
};
// Traits do usuário (se autenticado)
const userTraits = user ? {
user_id: user.id,
email: user.email,
school_id: user.user_metadata?.school_id,
class_id: user.user_metadata?.class_id,
name: user.user_metadata?.name,
role: user.user_metadata?.role,
last_updated: user.updated_at,
created_at: user.created_at
} : {};
// Envia dados adicionais usando o page() do Rudderstack
page(undefined, {
// Informações da página
path: location.pathname,
url: window.location.href,
search: location.search,
hash: location.hash,
title: document.title,
referrer: document.referrer,
url: window.location.href,
// Informações do dispositivo e navegador
...deviceInfo,
// Informações de performance
...performanceInfo,
// Informações da sessão
...sessionInfo,
// Traits do usuário
...userTraits,
// Metadados adicionais
timestamp: new Date().toISOString(),
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
});
}, [location, track]);
// Atualiza informações da sessão
sessionStorage.setItem('lastVisitedPage', location.pathname);
if (!localStorage.getItem('returningVisitor')) {
localStorage.setItem('returningVisitor', 'true');
}
if (!sessionStorage.getItem('sessionStartTime')) {
sessionStorage.setItem('sessionStartTime', new Date().toISOString());
}
}, [location, page, user]);
return null;
}
// Função auxiliar para determinar o tipo de dispositivo
function getDeviceType() {
const width = window.innerWidth;
if (width < 768) return 'mobile';
if (width < 1024) return 'tablet';
return 'desktop';
}
// Função auxiliar para obter o First Contentful Paint
function getFirstContentfulPaint() {
const perfEntries = performance.getEntriesByType('paint');
const fcpEntry = perfEntries.find(entry => entry.name === 'first-contentful-paint');
return fcpEntry ? fcpEntry.startTime : null;
}

View File

@ -15,6 +15,7 @@ import { FeatureCard } from '@/components/ui/feature-card';
import { ProcessStep } from '@/components/ui/process-step';
import { InfoCard } from '@/components/ui/info-card';
import { ComparisonSection } from '@/components/ui/comparison-section';
import { Button } from '@/components/ui/button';
const navigation = [
{ name: 'Início', href: '/' },
@ -34,9 +35,7 @@ export function HomePage() {
const handleSchoolLogin = () => navigate('/login/school');
const handleTeacherLogin = () => navigate('/login/teacher');
const handleStudentLogin = () => navigate('/login/student');
const handleSchoolRegister = () => {
navigate('/register');
};
const handleSchoolRegister = () => navigate('/register');
const handleDemo = () => navigate('/demo');
return (
@ -51,43 +50,76 @@ export function HomePage() {
</div>
<div className="flex items-center gap-4">
<div className="relative">
<button
<Button
onClick={handleLoginClick}
className="text-gray-600 hover:text-gray-900 px-3 py-2"
variant="ghost"
trackingId="nav_login_button"
trackingProperties={{
category: 'navigation',
action: 'click',
label: 'login_dropdown'
}}
>
Entrar
</button>
</Button>
{showUserOptions && (
<div className="absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5">
<div className="py-1" role="menu">
<button
<Button
onClick={handleSchoolLogin}
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-purple-50"
variant="ghost"
className="w-full text-left"
trackingId="nav_school_login"
trackingProperties={{
category: 'navigation',
action: 'click',
label: 'school_login'
}}
>
Entrar como Escola
</button>
<button
</Button>
<Button
onClick={handleTeacherLogin}
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-purple-50"
variant="ghost"
className="w-full text-left"
trackingId="nav_teacher_login"
trackingProperties={{
category: 'navigation',
action: 'click',
label: 'teacher_login'
}}
>
Entrar como Professor
</button>
<button
</Button>
<Button
onClick={handleStudentLogin}
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-purple-50"
variant="ghost"
className="w-full text-left"
trackingId="nav_student_login"
trackingProperties={{
category: 'navigation',
action: 'click',
label: 'student_login'
}}
>
Entrar como Aluno
</button>
</Button>
</div>
</div>
)}
</div>
<button
<Button
onClick={handleSchoolRegister}
className="bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 transition"
variant="primary"
trackingId="nav_register_button"
trackingProperties={{
category: 'navigation',
action: 'click',
label: 'register_school'
}}
>
Cadastrar Escola
</button>
</Button>
</div>
</div>
</div>
@ -110,20 +142,38 @@ export function HomePage() {
aprendizado personalizadas através de histórias interativas.
</p>
<div className="flex flex-col sm:flex-row gap-4">
<button
<Button
onClick={handleSchoolRegister}
className="bg-purple-600 text-white px-8 py-4 rounded-xl hover:bg-purple-700 transition flex items-center justify-center gap-2 text-lg font-semibold"
variant="primary"
size="lg"
className="gap-2"
trackingId="hero_register_button"
trackingProperties={{
category: 'hero',
action: 'click',
label: 'start_free',
position: 'hero_section'
}}
>
Começar Gratuitamente
<ArrowRight className="w-5 h-5" />
</button>
<button
</Button>
<Button
onClick={handleDemo}
className="border-2 border-purple-600 text-purple-600 px-8 py-4 rounded-xl hover:bg-purple-50 transition text-lg font-semibold flex items-center justify-center gap-2"
variant="outline"
size="lg"
className="gap-2"
trackingId="hero_demo_button"
trackingProperties={{
category: 'hero',
action: 'click',
label: 'watch_demo',
position: 'hero_section'
}}
>
<Play className="w-5 h-5" />
Ver Demo
</button>
</Button>
</div>
<div className="mt-8 flex items-center gap-4">
<div className="flex -space-x-2">
@ -149,14 +199,22 @@ export function HomePage() {
alt="Platform demo"
className="w-full h-full object-cover"
/>
<button
<Button
onClick={handleDemo}
variant="ghost"
className="absolute inset-0 flex items-center justify-center bg-black/30 hover:bg-black/40 transition group"
trackingId="hero_video_play"
trackingProperties={{
category: 'hero',
action: 'click',
label: 'play_demo_video',
position: 'hero_video'
}}
>
<div className="w-16 h-16 rounded-full bg-white/90 flex items-center justify-center">
<Play className="w-8 h-8 text-purple-600 group-hover:scale-110 transition" />
</div>
</button>
</Button>
</div>
</div>
</div>

View File

@ -1,5 +1,6 @@
import React from 'react';
import { CheckCircle } from 'lucide-react';
import { Button } from './button';
interface PlanProps {
title: string;
@ -113,17 +114,27 @@ export function PlanForParents({
))}
</ul>
<button
<Button
onClick={plan.onSubscribe}
className={`
w-full py-4 rounded-xl font-medium transition-all
${plan.highlighted
? 'bg-gradient-to-r from-purple-600 to-blue-500 text-white hover:from-purple-700 hover:to-blue-600'
: 'border-2 border-purple-600 text-purple-600 hover:bg-purple-50'}
`}
variant={plan.highlighted ? 'primary' : 'outline'}
className="w-full"
trackingId={`parent_plan_subscribe_${plan.title.toLowerCase().replace(/\s+/g, '_')}`}
trackingProperties={{
category: 'pricing',
action: 'click',
label: plan.title,
value: parseFloat(plan.price.replace(/[.,]/g, '')),
plan_type: plan.title,
plan_price: plan.price,
plan_period: plan.period,
plan_commitment: plan.commitment || 'mensal',
is_highlighted: plan.highlighted,
features_count: plan.features.length,
position: index.toString()
}}
>
Começar Agora
</button>
</Button>
</div>
))}
</div>

View File

@ -1,5 +1,6 @@
import React from 'react';
import { CheckCircle } from 'lucide-react';
import { Button } from './button';
interface PlanProps {
title: string;
@ -14,6 +15,7 @@ interface PlanForSchoolsProps {
title?: string;
description?: string;
plans?: PlanProps[];
onContactClick?: () => void;
}
const defaultPlans: PlanProps[] = [
@ -60,19 +62,22 @@ const defaultPlans: PlanProps[] = [
export function PlanForSchools({
title = "Invista em Educação Baseada em Evidências",
description = "Escolha o plano ideal para transformar a educação com base científica",
plans = defaultPlans
plans = defaultPlans,
onContactClick
}: PlanForSchoolsProps) {
return (
<section className="px-4 py-24 bg-gradient-to-b from-purple-50 via-white to-purple-50">
<div className="mx-auto max-w-7xl">
<h2 className="text-4xl font-bold text-center text-gray-900 mb-4">
{title}
</h2>
<p className="text-center text-gray-600 mb-16 max-w-2xl mx-auto">
{description}
</p>
<div className="text-center mb-16">
<h2 className="text-4xl font-bold text-gray-900 mb-4">
{title}
</h2>
<p className="text-gray-600 mb-8 max-w-2xl mx-auto">
{description}
</p>
</div>
<div className="grid md:grid-cols-3 gap-8">
<div className="grid md:grid-cols-3 gap-8 mb-16">
{plans.map((plan, index) => (
<div key={index} className={`
p-8 rounded-xl shadow-lg border-2
@ -98,20 +103,52 @@ export function PlanForSchools({
))}
</ul>
<button
<Button
onClick={plan.onSubscribe}
className={`
w-full py-4 rounded-xl font-medium transition-all
${plan.highlighted
? 'bg-gradient-to-r from-purple-600 to-blue-500 text-white hover:from-purple-700 hover:to-blue-600'
: 'border-2 border-purple-600 text-purple-600 hover:bg-purple-50'}
`}
variant={plan.highlighted ? 'primary' : 'outline'}
className="w-full"
trackingId={`plan_subscribe_${plan.title.toLowerCase().replace(/\s+/g, '_')}`}
trackingProperties={{
category: 'pricing',
action: 'click',
label: plan.title,
value: parseFloat(plan.price.replace(/[.,]/g, '')),
plan_type: plan.title,
plan_price: plan.price,
is_highlighted: plan.highlighted,
features_count: plan.features.length,
position: index.toString()
}}
>
Começar Agora
</button>
</Button>
</div>
))}
</div>
<div className="text-center">
<p className="text-xl text-gray-600 mb-8 max-w-2xl mx-auto">
Precisa de um plano personalizado para sua instituição?
</p>
<div className="flex justify-center gap-4">
<Button
onClick={onContactClick}
variant="outline"
size="lg"
className="gap-2"
trackingId="pricing_contact_button"
trackingProperties={{
category: 'pricing',
action: 'click',
label: 'contact_sales',
position: 'footer',
section: 'pricing'
}}
>
Fale com um Consultor
</Button>
</div>
</div>
</div>
</section>
);