mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-16 21:37:51 +00:00
Compare commits
6 Commits
d35565dee4
...
13536790fe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13536790fe | ||
|
|
9f7ea648fe | ||
|
|
e81dc5bedf | ||
|
|
4790d9788b | ||
|
|
d949587c44 | ||
|
|
bc2f120700 |
248
docs/arquitetura.md
Normal file
248
docs/arquitetura.md
Normal file
@ -0,0 +1,248 @@
|
||||
# Arquitetura do Sistema
|
||||
|
||||
## Visão Geral
|
||||
|
||||
A plataforma é construída usando uma arquitetura moderna e escalável, focada em proporcionar uma experiência educacional interativa e segura para crianças.
|
||||
|
||||
## Stack Tecnológica
|
||||
|
||||
### Frontend
|
||||
- Next.js 14 (App Router)
|
||||
- TypeScript
|
||||
- Tailwind CSS
|
||||
- Framer Motion
|
||||
- React Query
|
||||
|
||||
### Backend
|
||||
- Node.js
|
||||
- Supabase
|
||||
- Redis Upstash (cache)
|
||||
- Supabase Storage (mídia)
|
||||
|
||||
## Estrutura de Diretórios
|
||||
|
||||
```
|
||||
src/
|
||||
├── app/ # Rotas e páginas
|
||||
├── components/ # Componentes React
|
||||
│ ├── ui/ # Componentes base
|
||||
│ ├── forms/ # Formulários
|
||||
│ └── exercises/ # Componentes de exercícios
|
||||
├── features/ # Features do sistema
|
||||
├── layouts/ # Layouts do sistema
|
||||
├── constants/ # Constantes do sistema
|
||||
├── lib/ # Utilitários e configurações
|
||||
├── hooks/ # Hooks personalizados
|
||||
├── services/ # Serviços externos
|
||||
└── types/ # Definições de tipos
|
||||
```
|
||||
|
||||
## Componentes Principais
|
||||
|
||||
### 1. Sistema de Autenticação
|
||||
```typescript
|
||||
interface AuthConfig {
|
||||
providers: {
|
||||
google: boolean;
|
||||
email: boolean;
|
||||
};
|
||||
session: {
|
||||
maxAge: number;
|
||||
updateAge: number;
|
||||
};
|
||||
security: {
|
||||
jwtSecret: string;
|
||||
cookiePrefix: string;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Gerenciamento de Estado
|
||||
```typescript
|
||||
interface AppState {
|
||||
user: UserState;
|
||||
exercises: ExerciseState;
|
||||
progress: ProgressState;
|
||||
settings: SettingsState;
|
||||
}
|
||||
```
|
||||
|
||||
## Segurança
|
||||
|
||||
### 1. Autenticação
|
||||
- JWT tokens
|
||||
- Refresh tokens
|
||||
- Sessões seguras
|
||||
- Rate limiting
|
||||
|
||||
### 2. Dados
|
||||
- Criptografia em trânsito
|
||||
- Backup automático
|
||||
- Sanitização de inputs
|
||||
- Logs de auditoria
|
||||
|
||||
## Performance
|
||||
|
||||
### 1. Otimizações
|
||||
- Lazy loading
|
||||
- Code splitting
|
||||
- Caching estratégico
|
||||
- Compressão de assets
|
||||
|
||||
### 2. Monitoramento
|
||||
- Métricas de tempo real
|
||||
- Alertas automáticos
|
||||
- Análise de performance
|
||||
- Debug em produção
|
||||
|
||||
## Integração Contínua
|
||||
|
||||
### 1. Pipeline
|
||||
```yaml
|
||||
steps:
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
|
||||
- name: Type Check
|
||||
run: yarn tsc
|
||||
|
||||
- name: Test
|
||||
run: yarn test
|
||||
|
||||
- name: Build
|
||||
run: yarn build
|
||||
|
||||
- name: Deploy
|
||||
if: branch = main
|
||||
run: yarn deploy
|
||||
```
|
||||
|
||||
### 2. Qualidade de Código
|
||||
- ESLint
|
||||
- Prettier
|
||||
- Jest
|
||||
- Cypress
|
||||
|
||||
## Escalabilidade
|
||||
|
||||
### 1. Infraestrutura
|
||||
- Containers Docker
|
||||
- Load balancing
|
||||
- Auto-scaling
|
||||
- CDN global
|
||||
|
||||
### 2. Database
|
||||
- Sharding
|
||||
- Replicação
|
||||
- Índices otimizados
|
||||
- Migrations automáticas
|
||||
|
||||
## APIs e Integrações
|
||||
|
||||
### 1. REST APIs
|
||||
```typescript
|
||||
interface APIEndpoints {
|
||||
auth: {
|
||||
login: string;
|
||||
register: string;
|
||||
refresh: string;
|
||||
};
|
||||
exercises: {
|
||||
list: string;
|
||||
submit: string;
|
||||
progress: string;
|
||||
};
|
||||
content: {
|
||||
stories: string;
|
||||
media: string;
|
||||
exercises: string;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 2. WebSockets
|
||||
- Chat em tempo real
|
||||
- Notificações
|
||||
- Multiplayer
|
||||
- Status online
|
||||
|
||||
## Acessibilidade e SEO
|
||||
|
||||
### 1. Acessibilidade
|
||||
- WCAG 2.1
|
||||
- ARIA labels
|
||||
- Keyboard navigation
|
||||
- Screen readers
|
||||
|
||||
### 2. SEO
|
||||
- Meta tags
|
||||
- Sitemap
|
||||
- Robots.txt
|
||||
- Schema.org
|
||||
|
||||
## Ambiente de Desenvolvimento
|
||||
|
||||
### 1. Setup
|
||||
```bash
|
||||
# Instalação
|
||||
yarn install
|
||||
|
||||
# Desenvolvimento
|
||||
yarn dev
|
||||
|
||||
# Testes
|
||||
yarn test
|
||||
|
||||
# Build
|
||||
yarn build
|
||||
```
|
||||
|
||||
### 2. Ferramentas
|
||||
- VSCode
|
||||
- Docker
|
||||
- Postman
|
||||
- Git
|
||||
|
||||
## Monitoramento e Logs
|
||||
|
||||
### 1. Métricas
|
||||
```typescript
|
||||
interface SystemMetrics {
|
||||
performance: {
|
||||
responseTime: number;
|
||||
errorRate: number;
|
||||
userCount: number;
|
||||
};
|
||||
resources: {
|
||||
cpuUsage: number;
|
||||
memoryUsage: number;
|
||||
diskSpace: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Logs
|
||||
- Error tracking
|
||||
- User actions
|
||||
- Performance
|
||||
- Security events
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
1. **Microserviços**
|
||||
- Auth service
|
||||
- Content service
|
||||
- Analytics service
|
||||
- Notification service
|
||||
|
||||
2. **Machine Learning**
|
||||
- Recomendações
|
||||
- Análise de progresso
|
||||
- Detecção de padrões
|
||||
- Personalização
|
||||
|
||||
3. **Mobile**
|
||||
- PWA
|
||||
- App nativo
|
||||
- Offline mode
|
||||
- Push notifications
|
||||
246
docs/desenvolvimento.md
Normal file
246
docs/desenvolvimento.md
Normal file
@ -0,0 +1,246 @@
|
||||
# Fluxo de Desenvolvimento
|
||||
|
||||
## Padrões de Código
|
||||
|
||||
### 1. Nomenclatura
|
||||
- Diretórios em kebab-case: `components/form-wizard`
|
||||
- Componentes em PascalCase: `StoryCard.tsx`
|
||||
- Utilitários em camelCase: `formatText.ts`
|
||||
- Variáveis em camelCase: `userScore`
|
||||
|
||||
### 2. TypeScript
|
||||
- Preferir interfaces sobre types
|
||||
- Usar const com asserção `as const`
|
||||
- Retornos explícitos em funções
|
||||
- Imports relativos
|
||||
|
||||
## Estrutura de Commits
|
||||
|
||||
### 1. Prefixos
|
||||
- `fix:` Correções de bugs
|
||||
- `feat:` Novos recursos
|
||||
- `perf:` Melhorias de performance
|
||||
- `docs:` Documentação
|
||||
- `style:` Formatação
|
||||
- `refactor:` Refatoração
|
||||
- `test:` Testes
|
||||
- `chore:` Manutenção
|
||||
|
||||
### 2. Formato
|
||||
```
|
||||
<tipo>: <descrição>
|
||||
|
||||
[corpo]
|
||||
|
||||
[rodapé]
|
||||
```
|
||||
|
||||
## Fluxo de Branches
|
||||
|
||||
### 1. Principais
|
||||
- `main`: Produção
|
||||
- `develop`: Desenvolvimento
|
||||
- `staging`: Homologação
|
||||
|
||||
### 2. Features
|
||||
```bash
|
||||
# Nova feature
|
||||
git checkout -b feature/nome-da-feature
|
||||
|
||||
# Commit das mudanças
|
||||
git commit -m "feat: adiciona novo componente"
|
||||
|
||||
# Push para remote
|
||||
git push origin feature/nome-da-feature
|
||||
```
|
||||
|
||||
## Code Review
|
||||
|
||||
### 1. Checklist
|
||||
- Código limpo e legível
|
||||
- Testes adequados
|
||||
- Documentação atualizada
|
||||
- Performance otimizada
|
||||
- Segurança verificada
|
||||
|
||||
### 2. Pull Request
|
||||
```markdown
|
||||
## Descrição
|
||||
Breve descrição das mudanças
|
||||
|
||||
## Mudanças
|
||||
- [ ] Item 1
|
||||
- [ ] Item 2
|
||||
|
||||
## Screenshots
|
||||
[Se aplicável]
|
||||
|
||||
## Testes
|
||||
- [ ] Unitários
|
||||
- [ ] Integração
|
||||
- [ ] E2E
|
||||
```
|
||||
|
||||
## Testes
|
||||
|
||||
### 1. Unitários
|
||||
```typescript
|
||||
describe('StoryComponent', () => {
|
||||
it('deve renderizar corretamente', () => {
|
||||
const { getByText } = render(<Story />);
|
||||
expect(getByText('Título')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Integração
|
||||
```typescript
|
||||
describe('ExerciseFlow', () => {
|
||||
it('deve completar exercício', async () => {
|
||||
const result = await completeExercise({
|
||||
type: 'word-formation',
|
||||
answer: 'casa'
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Documentação
|
||||
|
||||
### 1. Código
|
||||
```typescript
|
||||
/**
|
||||
* Componente de exercício de formação de palavras
|
||||
* @param {WordFormationProps} props - Propriedades do componente
|
||||
* @returns {JSX.Element} Componente renderizado
|
||||
*/
|
||||
export const WordFormation: React.FC<WordFormationProps> = ({
|
||||
word,
|
||||
syllables,
|
||||
onComplete
|
||||
}) => {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
### 2. README
|
||||
- Setup do projeto
|
||||
- Comandos disponíveis
|
||||
- Estrutura de arquivos
|
||||
- Contribuição
|
||||
- Licença
|
||||
|
||||
## Segurança
|
||||
|
||||
### 1. Checklist
|
||||
- Validação de inputs
|
||||
- Sanitização de dados
|
||||
- Proteção contra XSS
|
||||
- Autenticação segura
|
||||
|
||||
### 2. Revisão
|
||||
```typescript
|
||||
// ❌ Inseguro
|
||||
const query = `SELECT * FROM users WHERE id = ${id}`;
|
||||
|
||||
// ✅ Seguro
|
||||
const query = 'SELECT * FROM users WHERE id = $1';
|
||||
const values = [id];
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### 1. Frontend
|
||||
- Lazy loading
|
||||
- Memoização
|
||||
- Bundle splitting
|
||||
- Image optimization
|
||||
|
||||
### 2. Backend
|
||||
- Caching
|
||||
- Query optimization
|
||||
- Connection pooling
|
||||
- Rate limiting
|
||||
|
||||
## Deploy
|
||||
|
||||
### 1. Staging
|
||||
```bash
|
||||
# Build
|
||||
yarn build
|
||||
|
||||
# Testes
|
||||
yarn test
|
||||
|
||||
# Deploy staging
|
||||
yarn deploy:staging
|
||||
```
|
||||
|
||||
### 2. Produção
|
||||
```bash
|
||||
# Merge para main
|
||||
git checkout main
|
||||
git merge develop
|
||||
|
||||
# Deploy produção
|
||||
yarn deploy:prod
|
||||
```
|
||||
|
||||
## Monitoramento
|
||||
|
||||
### 1. Métricas
|
||||
- Tempo de resposta
|
||||
- Taxa de erro
|
||||
- Uso de recursos
|
||||
- Satisfação do usuário
|
||||
|
||||
### 2. Logs
|
||||
```typescript
|
||||
logger.info('Exercício completado', {
|
||||
userId: user.id,
|
||||
exerciseId: exercise.id,
|
||||
score: result.score,
|
||||
timeSpent: result.time
|
||||
});
|
||||
```
|
||||
|
||||
## Manutenção
|
||||
|
||||
### 1. Dependências
|
||||
```bash
|
||||
# Atualizar deps
|
||||
yarn upgrade-interactive --latest
|
||||
|
||||
# Auditar segurança
|
||||
yarn audit
|
||||
|
||||
# Limpar cache
|
||||
yarn cache clean
|
||||
```
|
||||
|
||||
### 2. Backup
|
||||
- Database dumps
|
||||
- Logs históricos
|
||||
- Configurações
|
||||
- Assets
|
||||
|
||||
## Contribuição
|
||||
|
||||
### 1. Setup
|
||||
```bash
|
||||
# Clone
|
||||
git clone https://github.com/org/repo.git
|
||||
|
||||
# Install
|
||||
yarn install
|
||||
|
||||
# Dev
|
||||
yarn dev
|
||||
```
|
||||
|
||||
### 2. Guidelines
|
||||
- Código limpo
|
||||
- Testes completos
|
||||
- Documentação clara
|
||||
- Pull requests concisos
|
||||
177
docs/exercicios.md
Normal file
177
docs/exercicios.md
Normal file
@ -0,0 +1,177 @@
|
||||
# Sistema de Exercícios
|
||||
|
||||
## Visão Geral
|
||||
O sistema de exercícios oferece diferentes tipos de atividades para reforçar o aprendizado da leitura e compreensão textual.
|
||||
|
||||
## Tipos de Exercícios
|
||||
|
||||
### 1. Formação de Palavras
|
||||
```typescript
|
||||
interface WordFormationExercise {
|
||||
word: string;
|
||||
syllables: string[];
|
||||
hints?: string[];
|
||||
difficulty: 'easy' | 'medium' | 'hard';
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Completar Sentenças
|
||||
```typescript
|
||||
interface SentenceCompletionExercise {
|
||||
sentence: string;
|
||||
options: string[];
|
||||
correctAnswer: string;
|
||||
context: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Prática de Pronúncia
|
||||
```typescript
|
||||
interface PronunciationExercise {
|
||||
word: string;
|
||||
phonemes: string[];
|
||||
audioUrl?: string;
|
||||
examples: string[];
|
||||
}
|
||||
```
|
||||
|
||||
## Fluxo de Exercícios
|
||||
|
||||
### 1. Seleção
|
||||
- Baseada no nível do aluno
|
||||
- Progressão gradual
|
||||
- Adaptação por desempenho
|
||||
- Variedade de tipos
|
||||
|
||||
### 2. Execução
|
||||
- Instruções claras
|
||||
- Feedback imediato
|
||||
- Dicas contextuais
|
||||
- Suporte visual
|
||||
|
||||
### 3. Avaliação
|
||||
- Pontuação automática
|
||||
- Feedback detalhado
|
||||
- Sugestões de melhoria
|
||||
- Registro de progresso
|
||||
|
||||
## Componentes Principais
|
||||
|
||||
### 1. ExercisePlayer
|
||||
- Controle de fluxo
|
||||
- Timer integrado
|
||||
- Sistema de pontuação
|
||||
- Feedback visual
|
||||
|
||||
### 2. ExerciseFactory
|
||||
- Criação dinâmica
|
||||
- Validação de respostas
|
||||
- Adaptação de dificuldade
|
||||
- Geração de feedback
|
||||
|
||||
## Integração com Banco de Dados
|
||||
|
||||
### 1. Tabelas Relacionadas
|
||||
```sql
|
||||
-- Exercícios
|
||||
create table exercises (
|
||||
id uuid primary key,
|
||||
type text,
|
||||
difficulty text,
|
||||
content jsonb,
|
||||
created_at timestamptz
|
||||
);
|
||||
|
||||
-- Progresso
|
||||
create table exercise_progress (
|
||||
student_id uuid,
|
||||
exercise_id uuid,
|
||||
score numeric,
|
||||
completed_at timestamptz
|
||||
);
|
||||
```
|
||||
|
||||
### 2. Métricas Armazenadas
|
||||
- Tempo de conclusão
|
||||
- Taxa de acerto
|
||||
- Tentativas realizadas
|
||||
- Padrões de erro
|
||||
|
||||
## Acessibilidade
|
||||
|
||||
### 1. Visual
|
||||
- Alto contraste
|
||||
- Fontes ajustáveis
|
||||
- Ícones intuitivos
|
||||
- Animações suaves
|
||||
|
||||
### 2. Auditiva
|
||||
- Instruções em áudio
|
||||
- Feedback sonoro
|
||||
- Controle de volume
|
||||
- Legendas
|
||||
|
||||
### 3. Motora
|
||||
- Controles simplificados
|
||||
- Atalhos de teclado
|
||||
- Tempo ajustável
|
||||
- Pausas automáticas
|
||||
|
||||
## Gamificação
|
||||
|
||||
### 1. Sistema de Pontos
|
||||
- Pontuação base
|
||||
- Bônus por velocidade
|
||||
- Combos de acertos
|
||||
- Conquistas especiais
|
||||
|
||||
### 2. Progressão
|
||||
- Níveis de dificuldade
|
||||
- Desbloqueio gradual
|
||||
- Medalhas e troféus
|
||||
- Rankings opcionais
|
||||
|
||||
### 3. Recompensas
|
||||
- Novos conteúdos
|
||||
- Personalização
|
||||
- Badges especiais
|
||||
- Poder de escolha
|
||||
|
||||
## Monitoramento
|
||||
|
||||
### 1. Métricas Coletadas
|
||||
```typescript
|
||||
interface ExerciseMetrics {
|
||||
timeSpent: number;
|
||||
correctAnswers: number;
|
||||
totalAttempts: number;
|
||||
hintsUsed: number;
|
||||
score: number;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Análise de Desempenho
|
||||
- Padrões de erro
|
||||
- Tempo de resposta
|
||||
- Uso de dicas
|
||||
- Evolução temporal
|
||||
|
||||
## Próximas Melhorias
|
||||
|
||||
1. **Novos Tipos**
|
||||
- Exercícios de ritmo
|
||||
- Compreensão auditiva
|
||||
- Produção textual
|
||||
- Jogos educativos
|
||||
|
||||
2. **Personalização**
|
||||
- Temas customizados
|
||||
- Níveis adaptativos
|
||||
- Conteúdo dinâmico
|
||||
- Preferências salvas
|
||||
|
||||
3. **Interatividade**
|
||||
- Multiplayer
|
||||
- Desafios em grupo
|
||||
- Compartilhamento
|
||||
- Competições
|
||||
@ -85,9 +85,9 @@ export function ExerciseSuggestions({ storyId, storyText, readingMetrics }: Exer
|
||||
|
||||
return (
|
||||
<div className="mt-8">
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-4">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4 ">
|
||||
Exercícios Sugeridos
|
||||
</h3>
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{generateExercises().map((exercise) => (
|
||||
<button
|
||||
|
||||
@ -394,7 +394,7 @@ export function StoryPage() {
|
||||
const { session } = useSession();
|
||||
const { isUpperCase, toggleUppercase, isLoading } = useUppercasePreference(session?.user?.id);
|
||||
const { isHighlighted: isSyllablesEnabled, toggleHighlight: toggleSyllables } = useSyllables();
|
||||
const [fontSize, setFontSize] = useState(18);
|
||||
const [fontSize, setFontSize] = useState(24);
|
||||
const [readingSpeed, setReadingSpeed] = useState(60); // 60 palavras por minuto
|
||||
const [letterSpacing, setLetterSpacing] = useState(0);
|
||||
const [wordSpacing, setWordSpacing] = useState(0);
|
||||
@ -854,64 +854,74 @@ export function StoryPage() {
|
||||
<h1 className="text-2xl font-bold text-gray-900">{story?.title}</h1>
|
||||
</div>
|
||||
|
||||
{/* 3. Texto da História */}
|
||||
<div className="border-b border-gray-200 pb-6">
|
||||
<div
|
||||
className="prose max-w-none overflow-hidden"
|
||||
style={{
|
||||
fontSize: `${fontSize}px`,
|
||||
letterSpacing: `${letterSpacing}px`,
|
||||
wordSpacing: `${wordSpacing}px`,
|
||||
lineHeight: lineHeight
|
||||
}}
|
||||
>
|
||||
{renderHighlightedText(story?.content?.pages?.[currentPage]?.text || '')}
|
||||
{/* Container responsivo */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-10 gap-6 border-b border-gray-200 pb-6">
|
||||
{/* 3. Texto da História - Coluna que expande quando não há imagem */}
|
||||
<div className={cn(
|
||||
"col-span-1",
|
||||
focusMode.isActive ? "md:col-span-10" : "md:col-span-4"
|
||||
)}>
|
||||
<div
|
||||
className="prose max-w-none overflow-hidden"
|
||||
style={{
|
||||
fontSize: `${fontSize}px`,
|
||||
letterSpacing: `${letterSpacing}px`,
|
||||
wordSpacing: `${wordSpacing}px`,
|
||||
lineHeight: lineHeight
|
||||
}}
|
||||
>
|
||||
{renderHighlightedText(story?.content?.pages?.[currentPage]?.text || '')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 5. Imagem da História - Coluna Direita (60% em desktop) */}
|
||||
{!focusMode.isActive && (
|
||||
<div className="col-span-1 md:col-span-6">
|
||||
{story?.content?.pages?.[currentPage]?.image ? (
|
||||
<ImageWithLoading
|
||||
src={story.content.pages[currentPage].image}
|
||||
alt={`Ilustração da página ${currentPage + 1}`}
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
) : (
|
||||
<div className="aspect-video bg-gray-100 rounded-lg flex items-center justify-center">
|
||||
<p className="text-gray-500">Sem imagem para esta página</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 4. Controles de Navegação */}
|
||||
<div className="border-b border-gray-200 pb-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex flex-col sm:flex-row justify-between items-center gap-4 sm:gap-2">
|
||||
<button
|
||||
onClick={() => setCurrentPage(prev => prev - 1)}
|
||||
disabled={currentPage === 0}
|
||||
className="flex items-center gap-2 px-4 py-2 text-gray-600 hover:text-gray-900 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="w-full sm:w-auto flex items-center justify-center gap-2 px-4 py-2 text-purple-600 border border-purple-600 rounded-md hover:bg-purple-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
Página Anterior
|
||||
<span className="hidden sm:inline">Página Anterior</span>
|
||||
<span className="sm:hidden">Anterior</span>
|
||||
</button>
|
||||
|
||||
<span className="text-sm text-gray-500">
|
||||
<span className="text-sm text-gray-500 order-first sm:order-none">
|
||||
Página {currentPage + 1} de {story?.content?.pages?.length}
|
||||
</span>
|
||||
|
||||
<button
|
||||
onClick={() => setCurrentPage(prev => prev + 1)}
|
||||
disabled={currentPage === (story?.content?.pages?.length ?? 0) - 1}
|
||||
className="flex items-center gap-2 px-4 py-2 text-gray-600 hover:text-gray-900 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="w-full sm:w-auto flex items-center justify-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Próxima Página
|
||||
<span className="hidden sm:inline">Próxima Página</span>
|
||||
<span className="sm:hidden">Próxima</span>
|
||||
<ArrowRight className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 5. Imagem da História - Agora com controle de visibilidade */}
|
||||
{!focusMode.isActive && (
|
||||
<div className="border-b border-gray-200 pb-6">
|
||||
{story?.content?.pages?.[currentPage]?.image ? (
|
||||
<ImageWithLoading
|
||||
src={story.content.pages[currentPage].image}
|
||||
alt={`Ilustração da página ${currentPage + 1}`}
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
) : (
|
||||
<div className="aspect-video bg-gray-100 rounded-lg flex items-center justify-center">
|
||||
<p className="text-gray-500">Sem imagem para esta página</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* 6. Controle de Gravação */}
|
||||
{/*
|
||||
@ -923,7 +933,24 @@ export function StoryPage() {
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
</div>
|
||||
{/* Depois do StoryMetrics */}
|
||||
{recordings.length > 0 && (
|
||||
<ExerciseSuggestions
|
||||
storyId={story.id}
|
||||
storyText={story.content.pages[currentPage].text}
|
||||
readingMetrics={{
|
||||
difficultWords: getLatestRecording().improvements
|
||||
.filter(imp => imp.includes('palavra'))
|
||||
.map(imp => imp.match(/palavra "([^"]+)"/)?.[1])
|
||||
.filter(Boolean) as string[],
|
||||
errorCount: getLatestRecording().error_count,
|
||||
pauseCount: getLatestRecording().pause_count,
|
||||
fluencyScore: getLatestRecording().fluency_score
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Dashboard de métricas */}
|
||||
{loadingRecordings ? (
|
||||
@ -931,7 +958,7 @@ export function StoryPage() {
|
||||
<div className="h-48 bg-gray-100 rounded-lg mb-6" />
|
||||
</div>
|
||||
) : recordings.length > 0 ? (
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-8 pt-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Dashboard de Leitura</h2>
|
||||
<StoryMetrics
|
||||
data={formatMetricsData(getLatestRecording())}
|
||||
@ -1014,22 +1041,7 @@ export function StoryPage() {
|
||||
</Dialog.Root>
|
||||
</div>
|
||||
|
||||
{/* Depois do StoryMetrics */}
|
||||
{recordings.length > 0 && (
|
||||
<ExerciseSuggestions
|
||||
storyId={story.id}
|
||||
storyText={story.content.pages[currentPage].text}
|
||||
readingMetrics={{
|
||||
difficultWords: getLatestRecording().improvements
|
||||
.filter(imp => imp.includes('palavra'))
|
||||
.map(imp => imp.match(/palavra "([^"]+)"/)?.[1])
|
||||
.filter(Boolean) as string[],
|
||||
errorCount: getLatestRecording().error_count,
|
||||
pauseCount: getLatestRecording().pause_count,
|
||||
fluencyScore: getLatestRecording().fluency_score
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user