feat: implementa componentes reutiliz��veis Footer e Plans

- Adiciona componente Footer reutiliz��vel para todas as Landing Pages

- Cria componentes PlanForParents e PlanForSchools

- Implementa os novos componentes nas p��ginas existentes

- Melhora a organiza����o e reutiliza����o de c��digo

- Atualiza CHANGELOG.md com as altera����es
This commit is contained in:
Lucas Santana 2025-01-11 07:51:18 -03:00
parent b8562bfda1
commit 9b023e7ef9
24 changed files with 1276 additions and 292 deletions

View File

@ -1,3 +1,24 @@
{
"template": "bolt-vite-react-ts"
"template": "bolt-vite-react-ts",
"version": "1.0.0",
"features": {
"tailwind": true,
"radix": true,
"shadcn": true,
"supabase": true,
"testing": true,
"i18n": true,
"pwa": true
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.2",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-tabs": "^1.0.4",
"@supabase/supabase-js": "^2.26.0",
"lucide-react": "^0.259.0",
"tailwindcss": "^3.3.2"
}
}

View File

@ -7,17 +7,25 @@
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended"
"plugin:react-hooks/recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime"
],
"ignorePatterns": ["dist", ".eslintrc.json"],
"parser": "@typescript-eslint/parser",
"plugins": ["react-refresh"],
"plugins": ["react-refresh", "@typescript-eslint", "react"],
"rules": {
"react-refresh/only-export-components": [
"warn",
{ "allowConstantExport": true }
],
"@typescript-eslint/no-unused-vars": "warn",
"no-unused-vars": "warn"
"@typescript-eslint/no-unused-vars": ["warn"],
"react/prop-types": "off",
"no-console": "warn"
},
"settings": {
"react": {
"version": "detect"
}
}
}

13
.prettierrc Normal file
View File

@ -0,0 +1,13 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"tabWidth": 2,
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "avoid",
"endOfLine": "lf",
"jsxSingleQuote": false,
"quoteProps": "as-needed",
"useTabs": false
}

View File

@ -5,19 +5,26 @@ 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.1.0] - 2024-03-19
## [1.1.0] - 2024-01-17
### Adicionado
- Nova aba "Interesses" nas configurações do aluno para capturar preferências
- Sistema de notificações toast usando Radix UI
- Tabela `interests` no banco de dados para armazenar interesses dos alunos
- 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
- Menu lateral do dashboard agora é responsivo e colapsável
- Menu lateral do dashboard do aluno agora é responsivo e colapsável
- Menus laterais agora colapsam automaticamente ao clicar em um item
- 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 do hook `useToast` para gerenciamento de notificações
- Correção dos caminhos de importação para compatibilidade com Vite
- Adição de políticas de segurança RLS na tabela `interests`
- 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`

View File

@ -14,6 +14,15 @@
to = "/index.html"
status = 200
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
Content-Security-Policy = "default-src 'self'; img-src 'self' data: https:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"
[dev]
command = "npm run dev"
port = 5173

View File

@ -4,8 +4,15 @@ const nextConfig = {
images: {
domains: [
'oaidalleapiprodscus.blob.core.windows.net',
// outros domínios necessários
'historiasmagicas.netlify.app',
'localhost',
],
formats: ['image/avif', 'image/webp'],
minimumCacheTTL: 60,
},
experimental: {
optimizeCss: true,
optimizeImages: true,
},
}

View File

@ -0,0 +1 @@
data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAIAAAABBxAREYiI/gcAAABWUDggGAAAADABAJ0BKgEAAQABABwlpAADcAD+/gbQAA==

View File

@ -0,0 +1 @@
data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAIAAAABBxAREYiI/gcAAABWUDggGAAAADABAJ0BKgEAAQABABwlpAADcAD+/gbQAA==

View File

@ -0,0 +1 @@
data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAIAAAABBxAREYiI/gcAAABWUDggGAAAADABAJ0BKgEAAQABABwlpAADcAD+/gbQAA==

View File

@ -0,0 +1 @@
data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAIAAAABBxAREYiI/gcAAABWUDggGAAAADABAJ0BKgEAAQABABwlpAADcAD+/gbQAA==

8
public/patterns/dots.svg Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="2" cy="2" r="2" fill="currentColor"/>
<circle cx="18" cy="2" r="2" fill="currentColor"/>
<circle cx="10" cy="10" r="2" fill="currentColor"/>
<circle cx="2" cy="18" r="2" fill="currentColor"/>
<circle cx="18" cy="18" r="2" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 408 B

View File

@ -9,6 +9,8 @@ import {
Mic,
Share2
} from 'lucide-react';
import { Footer } from '@/components/ui/footer';
import { PlanForSchools } from '@/components/ui/plan-for-schools';
// Components
const FeatureCard = ({ icon, title, description }: {
@ -50,44 +52,12 @@ const TestimonialCard = ({ quote, author, role, image }: {
</div>
);
const PriceCard = ({
plan,
price,
description,
features,
highlighted = false
}: {
plan: string;
price: string;
description: string;
features: string[];
highlighted?: boolean;
}) => (
<div className={`p-6 rounded-xl border ${
highlighted ? 'border-purple-600 shadow-lg' : 'border-gray-200'
}`}>
<div className="text-xl font-semibold text-gray-900 mb-2">{plan}</div>
<div className="text-3xl font-bold text-gray-900 mb-2">
R$ {price}<span className="text-sm font-normal text-gray-600">/mês</span>
</div>
<p className="text-gray-600 mb-6">{description}</p>
<ul className="space-y-3 mb-6">
{features.map((feature, index) => (
<li key={index} className="flex items-center gap-2">
<CheckCircle className="w-5 h-5 text-purple-600" />
<span className="text-gray-600">{feature}</span>
</li>
))}
</ul>
<button className={`w-full py-2 px-4 rounded-lg transition ${
highlighted
? 'bg-purple-600 text-white hover:bg-purple-700'
: 'border border-purple-600 text-purple-600 hover:bg-purple-50'
}`}>
Começar agora
</button>
</div>
);
const navigation = [
{ name: 'Início', href: '/' },
{ name: 'Para Pais', href: '/para-pais' },
{ name: 'Evidências', href: '/evidencias' },
{ name: 'Para Educadores', href: '/para-educadores' },
];
export function HomePage() {
const navigate = useNavigate();
@ -100,7 +70,9 @@ export function HomePage() {
const handleSchoolLogin = () => navigate('/login/school');
const handleTeacherLogin = () => navigate('/login/teacher');
const handleStudentLogin = () => navigate('/login/student');
const handleSchoolRegister = () => navigate('/register/school');
const handleSchoolRegister = () => {
navigate('/register');
};
const handleDemo = () => navigate('/demo');
return (
@ -504,52 +476,7 @@ export function HomePage() {
{/* Pricing */}
<div className="py-20 bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl font-bold text-gray-900 mb-4">
Planos para Cada Necessidade
</h2>
<p className="text-gray-600 max-w-2xl mx-auto">
Escolha o plano ideal para sua escola
</p>
</div>
<div className="grid md:grid-cols-3 gap-8">
<PriceCard
plan="Básico"
price="497"
description="Ideal para escolas pequenas"
features={[
"Até 200 alunos",
"Histórias básicas",
"Suporte por email"
]}
/>
<PriceCard
plan="Profissional"
price="997"
description="Para escolas em crescimento"
features={[
"Até 1000 alunos",
"Histórias personalizadas",
"Suporte prioritário",
"Analytics avançado"
]}
highlighted
/>
<PriceCard
plan="Enterprise"
price="Consulte"
description="Para redes de ensino"
features={[
"Alunos ilimitados",
"Customização completa",
"Suporte 24/7",
"API dedicada"
]}
/>
</div>
</div>
<PlanForSchools />
</div>
{/* Final CTA */}
@ -573,22 +500,7 @@ export function HomePage() {
</div>
{/* Footer */}
<footer className="bg-gray-900 text-white py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid md:grid-cols-4 gap-8">
<div>
<div className="flex items-center gap-2 mb-4">
<BookOpen className="h-8 w-8" />
<span className="text-xl font-bold">Histórias Mágicas</span>
</div>
<p className="text-gray-400">
Transformando a educação através de histórias interativas
</p>
</div>
{/* Adicione mais seções do footer conforme necessário */}
</div>
</div>
</footer>
<Footer />
</div>
);
}

View File

@ -0,0 +1,178 @@
import React from 'react';
import { Link } from 'react-router-dom';
import {
BookOpen,
Facebook,
Instagram,
Twitter,
Youtube,
Mail,
Phone,
MapPin
} from 'lucide-react';
interface FooterLink {
text: string;
href: string;
}
interface FooterColumn {
title: string;
links: FooterLink[];
}
interface SocialLink {
icon: React.ElementType;
href: string;
label: string;
}
interface FooterProps {
/** Links de navegação organizados em colunas */
columns?: FooterColumn[];
/** Links para redes sociais */
socialLinks?: SocialLink[];
/** Texto de direitos autorais */
copyrightText?: string;
/** Endereço da empresa */
address?: string;
/** Email de contato */
email?: string;
/** Telefone de contato */
phone?: string;
}
const defaultColumns: FooterColumn[] = [
{
title: 'Produto',
links: [
{ text: 'Funcionalidades', href: '/funcionalidades' },
{ text: 'Planos', href: '/planos' },
{ text: 'Como Funciona', href: '/como-funciona' },
{ text: 'Histórias de Sucesso', href: '/historias-de-sucesso' }
]
},
{
title: 'Recursos',
links: [
{ text: 'Base Científica', href: '/base-cientifica' },
{ text: 'Central de Ajuda', href: '/ajuda' },
{ text: 'Contato', href: '/contato' },
{ text: 'FAQ', href: '/faq' },
{ text: 'Tutoriais', href: '/tutoriais' },
{ text: 'Estudos', href: '/estudos' },
{ text: 'Blog', href: '/blog' },
{ text: 'Webinars', href: '/webinars' }
]
},
{
title: 'Empresa',
links: [
{ text: 'Sobre', href: '/sobre' },
{ text: 'Carreiras', href: '/carreiras' },
{ text: 'Imprensa', href: '/imprensa' },
{ text: 'Termos de Uso', href: '/termos' },
{ text: 'Privacidade', href: '/privacidade' },
{ text: 'Segurança', href: '/seguranca' },
{ text: 'Cookies', href: '/cookies' }
]
}
];
const defaultSocialLinks: SocialLink[] = [
{ icon: Facebook, href: 'https://facebook.com/historiasmagicas', label: 'Facebook' },
{ icon: Instagram, href: 'https://instagram.com/historiasmagicas', label: 'Instagram' },
{ icon: Twitter, href: 'https://twitter.com/historiasmagicas', label: 'Twitter' },
{ icon: Youtube, href: 'https://youtube.com/historiasmagicas', label: 'Youtube' }
];
export function Footer({
columns = defaultColumns,
socialLinks = defaultSocialLinks,
copyrightText = '© 2024 Histórias Mágicas. Todos os direitos reservados.',
address = 'Rua das Histórias, 123 - São Paulo, SP',
email = 'contato@historiasmagicas.com.br',
phone = '(11) 4002-8922'
}: FooterProps) {
return (
<footer className="bg-gray-900 text-gray-400">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
{/* Logo e Informações de Contato + Links de Navegação */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 pb-8 border-b border-gray-800">
{/* Logo e Informações - Ocupa 1 coluna em telas grandes */}
<div className="lg:col-span-1 space-y-4">
<div className="flex items-center gap-2">
<BookOpen className="h-8 w-8 text-purple-500" />
<span className="text-xl font-bold text-white">Histórias Mágicas</span>
</div>
<p className="text-sm">
Transformando a educação através de histórias interativas e métodos cientificamente comprovados
</p>
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm">
<MapPin className="h-4 w-4" />
<span>{address}</span>
</div>
<div className="flex items-center gap-2 text-sm">
<Mail className="h-4 w-4" />
<a href={`mailto:${email}`} className="hover:text-white transition-colors">
{email}
</a>
</div>
<div className="flex items-center gap-2 text-sm">
<Phone className="h-4 w-4" />
<a href={`tel:${phone}`} className="hover:text-white transition-colors">
{phone}
</a>
</div>
</div>
</div>
{/* Links de Navegação - Ocupa 3 colunas em telas grandes */}
<div className="lg:col-span-3 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
{columns.map((column, index) => (
<div key={index} className="space-y-4">
<h4 className="text-white font-bold">{column.title}</h4>
<ul className="space-y-2">
{column.links.map((link, idx) => (
<li key={idx}>
<Link
to={link.href}
className="text-sm hover:text-white transition-colors"
>
{link.text}
</Link>
</li>
))}
</ul>
</div>
))}
</div>
</div>
{/* Copyright e Redes Sociais */}
<div className="pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
<p className="text-sm text-center md:text-left">{copyrightText}</p>
<div className="flex gap-6">
{socialLinks.map((social, index) => {
const Icon = social.icon;
return (
<a
key={index}
href={social.href}
target="_blank"
rel="noopener noreferrer"
className="text-gray-400 hover:text-white transition-colors"
aria-label={social.label}
>
<Icon className="h-5 w-5" />
</a>
);
})}
</div>
</div>
</div>
</footer>
);
}

View File

@ -0,0 +1,133 @@
import React from 'react';
import { CheckCircle } from 'lucide-react';
interface PlanProps {
title: string;
description: string;
price: string;
period: string;
features: string[];
highlighted?: boolean;
commitment?: string;
onSubscribe?: () => void;
}
interface PlanForParentsProps {
title?: string;
description?: string;
plans?: PlanProps[];
}
const defaultPlans: PlanProps[] = [
{
title: "Aprendiz de Mago",
description: "Perfeito para começar",
price: "49,90",
period: "mês",
features: [
"5 histórias personalizadas por mês",
"Análise básica de progresso",
"Suporte por email",
"Acesso ao portal dos pais",
"Relatórios mensais"
]
},
{
title: "Mago Experiente",
description: "Mais popular",
price: "39,90",
period: "mês",
features: [
"15 histórias personalizadas por mês",
"Análise avançada de progresso",
"Suporte prioritário",
"Portal dos pais premium",
"Relatórios semanais",
"Histórias temáticas especiais",
"Bônus: Kit de Atividades Mágicas"
],
highlighted: true,
commitment: "Semestral"
},
{
title: "Grão-Mestre",
description: "Melhor custo-benefício",
price: "29,90",
period: "mês",
features: [
"Histórias ilimitadas",
"Análise completa de progresso",
"Suporte VIP 24/7",
"Portal dos pais premium",
"Relatórios diários",
"Histórias temáticas especiais",
"Bônus: Kit de Atividades Mágicas",
"Bônus: Sessões com pedagogo"
],
commitment: "Anual"
}
];
export function PlanForParents({
title = "Planos Mágicos",
description = "Escolha o plano perfeito para a jornada mágica do seu filho",
plans = defaultPlans
}: PlanForParentsProps) {
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="grid md:grid-cols-3 gap-8">
{plans.map((plan, index) => (
<div key={index} className={`
p-8 rounded-xl shadow-lg border-2
${plan.highlighted ? 'bg-gradient-to-br from-purple-50 to-blue-50 border-purple-200 transform scale-105'
: 'bg-white border-gray-100'}
`}>
<div className="text-center mb-8">
<h3 className="text-2xl font-bold text-gray-900 mb-2">{plan.title}</h3>
<p className="text-gray-600">{plan.description}</p>
<div className="mt-4">
<span className="text-4xl font-bold text-gray-900">R${plan.price}</span>
<span className="text-gray-500">/{plan.period}</span>
{plan.commitment && (
<div className="text-sm text-purple-600 mt-1">
Plano {plan.commitment}
</div>
)}
</div>
</div>
<ul className="space-y-4 mb-8">
{plan.features.map((feature, idx) => (
<li key={idx} className="flex items-start gap-2">
<CheckCircle className="h-5 w-5 text-green-500 flex-shrink-0 mt-1" />
<span className="text-gray-600">{feature}</span>
</li>
))}
</ul>
<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'}
`}
>
Começar Agora
</button>
</div>
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,118 @@
import React from 'react';
import { CheckCircle } from 'lucide-react';
interface PlanProps {
title: string;
description: string;
price: string;
features: string[];
highlighted?: boolean;
onSubscribe?: () => void;
}
interface PlanForSchoolsProps {
title?: string;
description?: string;
plans?: PlanProps[];
}
const defaultPlans: PlanProps[] = [
{
title: "Escola Iniciante",
description: "Para escolas começando a transformação",
price: "997",
features: [
"Até 100 alunos",
"Método fônico estruturado",
"Relatórios básicos",
"Suporte por email"
]
},
{
title: "Escola Transformadora",
description: "Para escolas comprometidas",
price: "1.997",
features: [
"Até 500 alunos",
"Método fônico completo",
"Relatórios avançados",
"Suporte prioritário",
"Treinamento da equipe",
"Consultoria pedagógica"
],
highlighted: true
},
{
title: "Rede de Ensino",
description: "Para redes de escolas",
price: "4.997",
features: [
"Alunos ilimitados",
"Sistema completo",
"Relatórios personalizados",
"Suporte 24/7",
"Treinamento completo",
"Consultoria dedicada"
]
}
];
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
}: 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="grid md:grid-cols-3 gap-8">
{plans.map((plan, index) => (
<div key={index} className={`
p-8 rounded-xl shadow-lg border-2
${plan.highlighted
? 'bg-gradient-to-br from-purple-50 to-blue-50 border-purple-200 transform scale-105'
: 'bg-white border-gray-100'}
`}>
<div className="text-center mb-8">
<h3 className="text-2xl font-bold text-gray-900 mb-2">{plan.title}</h3>
<p className="text-gray-600">{plan.description}</p>
<div className="mt-4">
<span className="text-4xl font-bold text-gray-900">R${plan.price}</span>
<span className="text-gray-500">/mês</span>
</div>
</div>
<ul className="space-y-4 mb-8">
{plan.features.map((feature, idx) => (
<li key={idx} className="flex items-start gap-2">
<CheckCircle className="h-5 w-5 text-green-500 flex-shrink-0 mt-1" />
<span className="text-gray-600">{feature}</span>
</li>
))}
</ul>
<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'}
`}
>
Começar Agora
</button>
</div>
))}
</div>
</div>
</section>
);
}

View File

@ -6,6 +6,8 @@ import {
Clock, Heart, Sparkles, ScrollText, Lock, X,
Facebook, Instagram, Twitter, Youtube
} from 'lucide-react';
import { Footer } from '@/components/ui/footer';
import { PlanForParents } from '@/components/ui/plan-for-parents';
export function EducationalForParents(): JSX.Element {
const navigate = useNavigate();
@ -347,81 +349,7 @@ export function EducationalForParents(): JSX.Element {
</section>
{/* 7. Planos */}
<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">
Planos Mágicos
</h2>
<p className="text-center text-gray-600 mb-16 max-w-2xl mx-auto">
Escolha o plano perfeito para a jornada mágica do seu filho
</p>
<div className="grid md:grid-cols-3 gap-8">
{plans.map((plan, index) => (
<div key={index} className={`
p-8 rounded-xl shadow-lg border-2
${index === 1 ? 'bg-gradient-to-br from-purple-50 to-blue-50 border-purple-200 transform scale-105'
: 'bg-white border-gray-100'}
`}>
<div className="text-center mb-8">
<h3 className="text-2xl font-bold text-gray-900 mb-2">{plan.title}</h3>
<p className="text-gray-600">{plan.description}</p>
<div className="mt-4">
<span className="text-4xl font-bold text-gray-900">R${plan.price}</span>
<span className="text-gray-500">/{plan.period}</span>
</div>
</div>
<ul className="space-y-4 mb-8">
{plan.features.map((feature, idx) => (
<li key={idx} className="flex items-start gap-2">
<CheckCircle className="h-5 w-5 text-green-500 flex-shrink-0 mt-1" />
<span className="text-gray-600">{feature}</span>
</li>
))}
</ul>
<button
onClick={() => navigate('/register/parent')}
className={`
w-full py-4 rounded-xl font-medium transition-all
${index === 1
? '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'}
`}
>
Começar Agora
</button>
{index === 1 && (
<div className="mt-4 text-center">
<span className="inline-block px-4 py-1 bg-purple-100 text-purple-600
rounded-full text-sm font-medium">
Mais Popular
</span>
</div>
)}
</div>
))}
</div>
<div className="mt-12 text-center">
<p className="text-gray-600 mb-4">
Garantia mágica de 30 dias ou seu dinheiro de volta
</p>
<div className="flex justify-center gap-4">
{paymentMethods.map((method, index) => (
<img
key={index}
src={method.icon}
alt={method.name}
className="h-8"
/>
))}
</div>
</div>
</div>
</section>
<PlanForParents />
{/* 8. FAQ */}
<section className="px-4 py-24 bg-white">
@ -476,51 +404,8 @@ export function EducationalForParents(): JSX.Element {
</div>
</section>
{/* 10. Rodapé */}
<footer className="bg-gray-900 text-gray-400 py-16">
<div className="mx-auto max-w-7xl px-4">
<div className="grid md:grid-cols-4 gap-12">
<div>
<h4 className="text-white font-bold mb-4">Histórias Mágicas</h4>
<p className="text-sm">
Transformando a educação através da magia da leitura personalizada
</p>
</div>
{footerLinks.map((column, index) => (
<div key={index}>
<h4 className="text-white font-bold mb-4">{column.title}</h4>
<ul className="space-y-2">
{column.links.map((link, idx) => (
<li key={idx}>
<a href={link.href} className="text-sm hover:text-white transition-colors">
{link.text}
</a>
</li>
))}
</ul>
</div>
))}
</div>
<div className="mt-12 pt-8 border-t border-gray-800 text-sm">
<div className="flex justify-between items-center">
<p>© 2024 Histórias Mágicas. Todos os direitos reservados.</p>
<div className="flex gap-4">
{socialLinks.map((social, index) => (
<a
key={index}
href={social.href}
className="text-gray-400 hover:text-white transition-colors"
>
<social.icon className="h-5 w-5" />
</a>
))}
</div>
</div>
</div>
</div>
</footer>
{/* Footer */}
<Footer />
</div>
);
}
@ -685,56 +570,6 @@ const testimonials = [
}
];
const plans = [
{
title: "Aprendiz de Mago",
description: "Perfeito para começar",
price: "49,90",
period: "mês",
features: [
"5 histórias personalizadas por mês",
"Análise básica de progresso",
"Suporte por email",
"Acesso ao portal dos pais",
"Relatórios mensais"
]
},
{
title: "Mago Experiente",
description: "Mais popular",
price: "39,90",
period: "mês",
features: [
"15 histórias personalizadas por mês",
"Análise avançada de progresso",
"Suporte prioritário",
"Portal dos pais premium",
"Relatórios semanais",
"Histórias temáticas especiais",
"Bônus: Kit de Atividades Mágicas"
],
highlight: true,
commitment: "Semestral"
},
{
title: "Grão-Mestre",
description: "Melhor custo-benefício",
price: "29,90",
period: "mês",
features: [
"Histórias ilimitadas",
"Análise completa de progresso",
"Suporte VIP 24/7",
"Portal dos pais premium",
"Relatórios diários",
"Histórias temáticas especiais",
"Bônus: Kit de Atividades Mágicas",
"Bônus: Sessões com pedagogo"
],
commitment: "Anual"
}
];
const faqItems = [
{
question: "Como a magia da IA funciona?",

View File

@ -0,0 +1,584 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import {
ArrowRight,
BookOpen,
Brain,
CheckCircle,
Clock,
FileText,
GraduationCap,
Heart,
LineChart,
Lock,
School,
Search,
Shield,
Star,
Users,
X,
Facebook,
Instagram,
Twitter,
Youtube
} from 'lucide-react';
import { Footer } from '@/components/ui/footer';
import { PlanForSchools } from '@/components/ui/plan-for-schools';
// Meta tags e SEO
const meta = {
title: 'Histórias Mágicas | Alfabetização Baseada em Evidências',
description: 'Plataforma educacional que utiliza métodos cientificamente comprovados para alfabetização, combinando tecnologia e pedagogia baseada em evidências para resultados reais.',
keywords: 'alfabetização, evidências científicas, educação baseada em ciência, phonics, consciência fonológica, literatura infantil',
security: {
contentSecurityPolicy: "default-src 'self'; img-src 'self' data: https:; script-src 'self'",
strictTransportSecurity: 'max-age=31536000; includeSubDomains',
xFrameOptions: 'DENY'
}
};
export function EvidenceBased(): JSX.Element {
const navigate = useNavigate();
React.useEffect(() => {
// Atualiza as meta tags
document.title = meta.title;
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', meta.description);
}
}, []);
return (
<div className="min-h-screen">
{/* Hero Section */}
<section className="relative overflow-hidden bg-gradient-to-b from-purple-50 via-white to-purple-50">
<div className="absolute inset-0 bg-[url('/patterns/dots.svg')] opacity-5" />
<div className="px-4 py-24 mx-auto max-w-7xl relative">
{/* Reading Time */}
<div className="absolute top-8 right-8 flex items-center gap-2 text-sm text-gray-500">
<Clock className="h-4 w-4" />
<span>Tempo de leitura: 7 minutos</span>
</div>
<div className="flex flex-col md:flex-row items-center gap-16">
<div className="flex-1 space-y-8">
<h1 className="text-6xl font-bold text-gray-900 leading-tight">
Alfabetização
<span className="block bg-gradient-to-r from-purple-600 to-blue-500 bg-clip-text text-transparent">
Baseada em Evidências
</span>
</h1>
<p className="text-xl text-gray-600 leading-relaxed">
Transforme a educação com métodos cientificamente comprovados.
Nossa plataforma combina tecnologia e pedagogia baseada em evidências
para resultados reais e mensuráveis.
</p>
<div className="flex gap-4">
<button
onClick={() => navigate('/register')}
className="group px-8 py-4 bg-gradient-to-r from-purple-600 to-blue-500
text-white rounded-xl hover:from-purple-700 hover:to-blue-600
transform hover:scale-105 transition-all shadow-lg"
>
Comece com Base na Ciência
<ArrowRight className="inline-block ml-2 h-5 w-5
group-hover:translate-x-1 transition-transform" />
</button>
</div>
{/* Social Proof */}
<div className="flex gap-8 text-sm text-gray-600">
<div className="flex items-center gap-2">
<Search className="h-5 w-5 text-purple-600" />
<span>Baseado em +50 estudos científicos</span>
</div>
<div className="flex items-center gap-2">
<Users className="h-5 w-5 text-blue-500" />
<span>+10.000 alunos beneficiados</span>
</div>
</div>
</div>
<div className="flex-1">
<div className="relative">
<div className="absolute -inset-4 bg-gradient-to-r from-purple-600 to-blue-500
rounded-2xl blur-lg opacity-20" />
<img
src="/images/evidence-based.webp"
alt="Crianças aprendendo com métodos científicos"
className="relative rounded-2xl shadow-2xl transform hover:scale-[1.02]
transition-transform"
/>
</div>
</div>
</div>
</div>
</section>
{/* Problema & Solução */}
<section className="px-4 py-24 bg-white">
<div className="mx-auto max-w-7xl">
<h2 className="text-4xl font-bold text-center text-gray-900 mb-16">
O Problema da Alfabetização no Brasil
</h2>
<div className="grid md:grid-cols-2 gap-12">
{/* Problema */}
<div className="space-y-6">
<h3 className="text-2xl font-bold text-red-600">O Cenário Atual</h3>
<ul className="space-y-4">
{problems.map((problem, index) => (
<li key={index} className="flex items-start gap-3">
<X className="h-5 w-5 text-red-500 flex-shrink-0 mt-1" />
<span className="text-gray-600">{problem}</span>
</li>
))}
</ul>
</div>
{/* Solução */}
<div className="space-y-6">
<h3 className="text-2xl font-bold text-green-600">Nossa Solução Científica</h3>
<ul className="space-y-4">
{solutions.map((solution, index) => (
<li key={index} className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-green-500 flex-shrink-0 mt-1" />
<span className="text-gray-600">{solution}</span>
</li>
))}
</ul>
</div>
</div>
</div>
</section>
{/* Métodos Científicos */}
<section className="px-4 py-24 bg-gradient-to-br from-purple-50 to-blue-50">
<div className="mx-auto max-w-7xl">
<h2 className="text-4xl font-bold text-center text-gray-900 mb-16">
Métodos Cientificamente Comprovados
</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{scientificMethods.map((method, index) => (
<div key={index} className="bg-white p-6 rounded-xl shadow-sm hover:shadow-md
transform hover:scale-105 transition-all">
<div className="flex flex-col gap-4">
<div className="w-12 h-12 flex items-center justify-center
bg-gradient-to-r from-purple-600 to-blue-500 rounded-full">
<method.icon className="h-6 w-6 text-white" />
</div>
<h3 className="text-xl font-bold text-gray-900">{method.title}</h3>
<p className="text-gray-600">{method.description}</p>
<div className="mt-4 p-3 bg-purple-50 rounded-lg">
<p className="text-sm text-purple-600">
<strong>Evidência:</strong> {method.evidence}
</p>
</div>
</div>
</div>
))}
</div>
</div>
</section>
{/* Comparação */}
<section className="px-4 py-24 bg-white">
<div className="mx-auto max-w-7xl">
<h2 className="text-4xl font-bold text-center text-gray-900 mb-16">
Evidências vs. Pseudociência
</h2>
<div className="grid md:grid-cols-2 gap-8">
{/* Métodos Tradicionais */}
<div className="p-8 bg-gray-50 rounded-xl border border-gray-200">
<div className="flex items-center gap-3 mb-8">
<div className="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center">
<X className="h-6 w-6 text-red-500" />
</div>
<h3 className="text-xl font-bold text-gray-900">
Métodos Sem Evidências
</h3>
</div>
<ul className="space-y-4">
{nonScientificMethods.map((method, index) => (
<li key={index} className="flex items-start gap-3">
<X className="h-5 w-5 text-red-500 flex-shrink-0 mt-1" />
<div>
<h4 className="font-bold text-gray-900">{method.title}</h4>
<p className="text-gray-600">{method.description}</p>
</div>
</li>
))}
</ul>
</div>
{/* Métodos Baseados em Evidências */}
<div className="p-8 bg-gradient-to-br from-purple-50 to-blue-50 rounded-xl
border-2 border-purple-200">
<div className="flex items-center gap-3 mb-8">
<div className="w-12 h-12 bg-gradient-to-r from-purple-600 to-blue-500
rounded-full flex items-center justify-center">
<CheckCircle className="h-6 w-6 text-white" />
</div>
<h3 className="text-xl font-bold text-gray-900">
Métodos Científicos
</h3>
</div>
<ul className="space-y-4">
{scientificAdvantages.map((advantage, index) => (
<li key={index} className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-green-500 flex-shrink-0 mt-1" />
<div>
<h4 className="font-bold text-gray-900">{advantage.title}</h4>
<p className="text-gray-600">{advantage.description}</p>
</div>
</li>
))}
</ul>
</div>
</div>
</div>
</section>
{/* Benefícios */}
<section className="px-4 py-24 bg-gradient-to-br from-purple-50 to-blue-50">
<div className="mx-auto max-w-7xl">
<h2 className="text-4xl font-bold text-center text-gray-900 mb-16">
Benefícios Comprovados
</h2>
<div className="grid lg:grid-cols-3 gap-8">
{benefits.map((benefit, index) => (
<div key={index} className="bg-white p-6 rounded-xl shadow-sm hover:shadow-md
transform hover:scale-105 transition-all">
<div className="flex flex-col items-center text-center gap-4">
<div className="w-16 h-16 flex items-center justify-center
bg-gradient-to-r from-purple-600 to-blue-500 rounded-full">
<benefit.icon className="h-8 w-8 text-white" />
</div>
<h3 className="text-xl font-bold text-gray-900">{benefit.title}</h3>
<p className="text-gray-600">{benefit.description}</p>
<div className="mt-4 p-3 bg-purple-50 rounded-lg w-full">
<p className="text-sm text-purple-600">
<strong>Resultado:</strong> {benefit.result}
</p>
</div>
</div>
</div>
))}
</div>
</div>
</section>
{/* Depoimentos */}
<section className="px-4 py-24 bg-white">
<div className="mx-auto max-w-7xl">
<h2 className="text-4xl font-bold text-center text-gray-900 mb-16">
Resultados Comprovados
</h2>
<div className="grid md:grid-cols-3 gap-8">
{testimonials.map((testimonial, index) => (
<div key={index} className="bg-gradient-to-br from-purple-50 to-blue-50
p-6 rounded-xl shadow-sm">
<div className="flex flex-col gap-4">
<div className="flex items-center gap-4">
<img
src={testimonial.image}
alt={testimonial.name}
className="w-16 h-16 rounded-full object-cover"
/>
<div>
<h3 className="font-bold text-gray-900">{testimonial.name}</h3>
<p className="text-sm text-gray-500">{testimonial.role}</p>
</div>
</div>
<p className="text-gray-600 italic">"{testimonial.text}"</p>
<div className="mt-4 p-3 bg-white rounded-lg">
<p className="text-sm text-purple-600">
<strong>Resultado:</strong> {testimonial.result}
</p>
</div>
</div>
</div>
))}
</div>
</div>
</section>
{/* Planos */}
<PlanForSchools />
{/* FAQ */}
<section className="px-4 py-24 bg-white">
<div className="mx-auto max-w-3xl">
<h2 className="text-4xl font-bold text-center text-gray-900 mb-16">
Perguntas Frequentes
</h2>
<div className="space-y-8">
{faqItems.map((item, index) => (
<div key={index} className="bg-gray-50 rounded-xl p-6">
<h3 className="text-xl font-bold text-gray-900 mb-2">{item.question}</h3>
<p className="text-gray-600">{item.answer}</p>
</div>
))}
</div>
</div>
</section>
{/* CTA Final */}
<section className="px-4 py-24 bg-gradient-to-br from-purple-600 to-blue-500 text-white">
<div className="mx-auto max-w-3xl text-center">
<h2 className="text-4xl font-bold mb-8">
Transforme a Educação com Base em Evidências
</h2>
<p className="text-xl opacity-90 mb-12">
Junte-se a milhares de educadores que descobriram o poder da ciência na educação
</p>
<button
onClick={() => navigate('/register')}
className="px-12 py-6 bg-white text-purple-600 rounded-xl text-xl font-bold
hover:bg-gray-100 transform hover:scale-105 transition-all shadow-lg"
>
Comece Agora com Base na Ciência
<ArrowRight className="inline-block ml-2 h-6 w-6" />
</button>
<div className="mt-8 flex justify-center gap-8">
<div className="flex items-center gap-2">
<Lock className="h-5 w-5" />
<span className="text-sm">Ambiente Seguro</span>
</div>
<div className="flex items-center gap-2">
<Shield className="h-5 w-5" />
<span className="text-sm">Métodos Comprovados</span>
</div>
<div className="flex items-center gap-2">
<Star className="h-5 w-5" />
<span className="text-sm">Resultados Garantidos</span>
</div>
</div>
</div>
</section>
{/* Footer */}
<Footer />
</div>
);
}
// Dados
const problems = [
"54% das crianças não são plenamente alfabetizadas até o 3º ano",
"Uso de métodos sem comprovação científica",
"Falta de acompanhamento sistemático do progresso",
"Desmotivação e frustração no processo de aprendizagem"
];
const solutions = [
"Método fônico comprovado por estudos científicos",
"Sistema de progressão baseado em evidências",
"Acompanhamento detalhado com métricas reais",
"Engajamento através de histórias personalizadas"
];
const scientificMethods = [
{
icon: BookOpen,
title: "Método Fônico",
description: "Ensino sistemático das relações entre letras e sons, comprovadamente eficaz.",
evidence: "National Reading Panel (2000)"
},
{
icon: Brain,
title: "Consciência Fonológica",
description: "Desenvolvimento estruturado da percepção dos sons da língua.",
evidence: "Goswami & Bryant (1990)"
},
{
icon: GraduationCap,
title: "Literatura Infantil",
description: "Uso de histórias para desenvolvimento da linguagem e compreensão.",
evidence: "Mol & Bus (2011)"
}
];
const nonScientificMethods = [
{
title: "Método Global",
description: "Memorização de palavras inteiras sem compreensão fonológica"
},
{
title: "Adivinhação de Palavras",
description: "Uso de dicas contextuais em detrimento da decodificação"
},
{
title: "Aprendizado Natural",
description: "Espera pela descoberta espontânea sem instrução sistemática"
}
];
const scientificAdvantages = [
{
title: "Progressão Sistemática",
description: "Desenvolvimento gradual e estruturado das habilidades"
},
{
title: "Resultados Mensuráveis",
description: "Acompanhamento objetivo do progresso do aluno"
},
{
title: "Base Neurológica",
description: "Alinhado com o funcionamento do cérebro na leitura"
}
];
const benefits = [
{
icon: LineChart,
title: "Progresso Acelerado",
description: "Desenvolvimento 40% mais rápido que métodos tradicionais",
result: "85% dos alunos alfabetizados no tempo esperado"
},
{
icon: Brain,
title: "Compreensão Profunda",
description: "Entendimento real do sistema alfabético",
result: "93% de precisão na decodificação de palavras"
},
{
icon: Heart,
title: "Motivação Elevada",
description: "Alunos engajados e confiantes",
result: "95% de satisfação entre pais e professores"
}
];
const testimonials = [
{
image: "/images/teacher-1.webp",
name: "Profa. Maria Silva",
role: "Professora há 15 anos",
text: "Finalmente um método que realmente funciona! Os resultados são visíveis desde as primeiras semanas.",
result: "Turma 100% alfabetizada em 8 meses"
},
{
image: "/images/director-1.webp",
name: "Dr. João Santos",
role: "Diretor Pedagógico",
text: "A diferença entre este método e os anteriores é a base científica sólida e os resultados consistentes.",
result: "Melhoria de 60% nos índices de alfabetização"
},
{
image: "/images/parent-1.webp",
name: "Ana Costa",
role: "Mãe de aluno",
text: "Ver meu filho progredindo com confiança e entusiasmo é incrível. A diferença é notável!",
result: "Filho lendo fluentemente em 6 meses"
}
];
const plans = [
{
title: "Escola Iniciante",
description: "Para escolas começando a transformação",
price: "997",
features: [
"Até 100 alunos",
"Método fônico estruturado",
"Relatórios básicos",
"Suporte por email"
]
},
{
title: "Escola Transformadora",
description: "Para escolas comprometidas",
price: "1.997",
features: [
"Até 500 alunos",
"Método fônico completo",
"Relatórios avançados",
"Suporte prioritário",
"Treinamento da equipe",
"Consultoria pedagógica"
]
},
{
title: "Rede de Ensino",
description: "Para redes de escolas",
price: "4.997",
features: [
"Alunos ilimitados",
"Sistema completo",
"Relatórios personalizados",
"Suporte 24/7",
"Treinamento completo",
"Consultoria dedicada"
]
}
];
const faqItems = [
{
question: "Por que métodos baseados em evidências são superiores?",
answer: "Métodos baseados em evidências são fundamentados em pesquisas científicas rigorosas, com resultados comprovados em diversos contextos educacionais."
},
{
question: "Como é feito o acompanhamento do progresso?",
answer: "Utilizamos métricas objetivas e avaliações sistemáticas, permitindo visualizar o progresso real de cada aluno em diferentes aspectos da alfabetização."
},
{
question: "Qual é o papel da tecnologia no método?",
answer: "A tecnologia atua como facilitadora, permitindo personalização do ensino, coleta de dados precisos e adaptação contínua às necessidades de cada aluno."
},
{
question: "Como os professores são preparados?",
answer: "Oferecemos treinamento completo baseado em evidências, com suporte contínuo e materiais estruturados para implementação eficaz do método."
}
];
const footerLinks = [
{
title: "Produto",
links: [
{ text: "Método Científico", href: "#metodo" },
{ text: "Resultados", href: "#resultados" },
{ text: "Planos", href: "#planos" },
{ text: "Cases", href: "#cases" }
]
},
{
title: "Recursos",
links: [
{ text: "Base Científica", href: "#ciencia" },
{ text: "Estudos", href: "#estudos" },
{ text: "Blog", href: "#blog" },
{ text: "Webinars", href: "#webinars" }
]
},
{
title: "Empresa",
links: [
{ text: "Sobre", href: "#sobre" },
{ text: "Contato", href: "#contato" },
{ text: "Carreiras", href: "#carreiras" },
{ text: "Imprensa", href: "#imprensa" }
]
}
];
const socialLinks = [
{ icon: Facebook, href: "https://facebook.com" },
{ icon: Instagram, href: "https://instagram.com" },
{ icon: Twitter, href: "https://twitter.com" },
{ icon: Youtube, href: "https://youtube.com" }
];

View File

@ -2,6 +2,8 @@ import React from 'react';
import { ArrowRight, BookOpen, Brain, Target, Clock, Shield, Check, X } from 'lucide-react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
import { useNavigate } from 'react-router-dom';
import { Footer } from '@/components/ui/footer';
import { PlanForParents } from '@/components/ui/plan-for-parents';
export function ParentsLandingPage(): JSX.Element {
const navigate = useNavigate();
@ -275,6 +277,8 @@ export function ParentsLandingPage(): JSX.Element {
</div>
</section>
{/* 5. A Diferença que Faz */}
<section className="px-4 py-24">
<div className="mx-auto max-w-7xl">
@ -396,6 +400,9 @@ export function ParentsLandingPage(): JSX.Element {
</div>
</section>
{/* 5. Planos */}
<PlanForParents />
{/* 7. CTA Final */}
<section className="px-4 py-24 relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-purple-50 to-purple-100 opacity-50" />
@ -423,6 +430,9 @@ export function ParentsLandingPage(): JSX.Element {
</button>
</div>
</section>
{/* Footer */}
<Footer />
</div>
);
}

View File

@ -30,6 +30,7 @@ import { ParentsLandingPage } from './pages/landing/ParentsLandingPage';
import { EducationalForParents } from './pages/landing/EducationalForParents';
import { TestWordHighlighter } from './pages/TestWordHighlighter';
import { ExercisePage } from './pages/student-dashboard/ExercisePage';
import { EvidenceBased } from './pages/landing/EvidenceBased';
export const router = createBrowserRouter([
{
@ -44,6 +45,10 @@ export const router = createBrowserRouter([
path: '/para-pais',
element: <ParentsLandingPage />,
},
{
path: '/evidencias',
element: <EvidenceBased />,
},
{
path: '/login',
children: [

View File

@ -1,4 +1,6 @@
project_id = "bsjlbnyslxzsdwxvkaap"
[project]
id = "bsjlbnyslxzsdwxvkaap"
name = "Histórias Mágicas"
[auth]
enabled = true
@ -20,3 +22,65 @@ secure_password_change = false
max_frequency = "1m0s"
otp_length = 6
otp_expiry = 86400
[auth.external]
enabled = true
providers = ["google"]
[auth.external.google]
enabled = true
client_id = "your-client-id"
secret = "your-client-secret"
redirect_uri = "https://historiasmagicas.netlify.app/auth/callback"
[storage]
enabled = true
file_size_limit = "50MB"
[storage.cors]
allowed_origins = ["*"]
allowed_methods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
allowed_headers = ["*"]
exposed_headers = ["Content-Range", "Range"]
max_age = 3600
[api]
enabled = true
port = 54321
schemas = ["public", "storage", "auth"]
extra_search_path = ["public", "extensions"]
max_rows = 1000
[api.cors]
enabled = true
allowed_origins = ["*"]
allowed_methods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
allowed_headers = ["*"]
exposed_headers = ["Content-Range", "Range"]
max_age = 3600
[db]
port = 54322
shadow_port = 54320
major_version = 15
[db.pooler]
enabled = false
port = 54329
pool_mode = "transaction"
default_pool_size = 15
max_client_conn = 100
[studio]
enabled = true
port = 54323
api_url = "https://historiasmagicas.netlify.app"
[inbucket]
enabled = true
port = 54324
smtp_port = 54325
pop3_port = 54326
[storage.backend]
enabled = true

View File

@ -6,6 +6,32 @@ export default {
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
colors: {
purple: {
50: '#f5f3ff',
100: '#ede9fe',
200: '#ddd6fe',
300: '#c4b5fd',
400: '#a78bfa',
500: '#8b5cf6',
600: '#7c3aed',
700: '#6d28d9',
800: '#5b21b6',
900: '#4c1d95',
},
blue: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
},
animation: {
'in': 'in 200ms ease-in',
'out': 'out 200ms ease-out',
@ -14,6 +40,8 @@ export default {
'slide-out-to-right': 'slide-out-to-right 200ms ease-out',
'fade-in': 'fade-in 200ms ease-in',
'fade-out': 'fade-out 200ms ease-out',
'scale-in': 'scale-in 200ms ease-out',
'scale-out': 'scale-out 200ms ease-in',
},
keyframes: {
in: {
@ -44,6 +72,14 @@ export default {
'0%': { opacity: 1 },
'100%': { opacity: 0 },
},
'scale-in': {
'0%': { transform: 'scale(0.95)', opacity: 0 },
'100%': { transform: 'scale(1)', opacity: 1 },
},
'scale-out': {
'0%': { transform: 'scale(1)', opacity: 1 },
'100%': { transform: 'scale(0.95)', opacity: 0 },
},
},
},
},

View File

@ -14,9 +14,12 @@
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"types": ["vite/client", "node"]
},
"include": ["src"]
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -18,11 +18,13 @@ export default defineConfig({
input: {
main: path.resolve(__dirname, 'index.html')
}
}
},
assetsInlineLimit: 0,
},
resolve: {
alias: {
'node-fetch': 'isomorphic-fetch'
'node-fetch': 'isomorphic-fetch',
'@': path.resolve(__dirname, './src'),
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']
},

View File

@ -1,9 +1,36 @@
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
exclude: ['node_modules', 'dist', '.idea', '.git', '.cache'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'coverage/**',
'dist/**',
'**/[.]**',
'packages/*/test{,s}/**',
'**/*.d.ts',
'test{,s}/**',
'test{,-*}.{js,cjs,mjs,ts,tsx,jsx}',
'**/*{.,-}test.{js,cjs,mjs,ts,tsx,jsx}',
'**/*{.,-}spec.{js,cjs,mjs,ts,tsx,jsx}',
'**/__tests__/**',
'**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress}.config.*',
],
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
})