chore: Remove Jitsi and theme documentation files and refine backend gitignore rules.
This commit is contained in:
@@ -1,296 +0,0 @@
|
|||||||
# Correções Implementadas para Integração Jitsi
|
|
||||||
|
|
||||||
## Resumo das Alterações
|
|
||||||
|
|
||||||
Este documento descreve todas as correções implementadas para integrar o servidor Jitsi ao projeto SGSE e fazer as chamadas de áudio e vídeo funcionarem corretamente.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Configuração do JitsiConnection
|
|
||||||
|
|
||||||
### Problema Identificado
|
|
||||||
- A configuração do `serviceUrl` e `muc` estava incorreta para Docker Jitsi local
|
|
||||||
- O domínio incluía a porta, causando problemas na conexão
|
|
||||||
|
|
||||||
### Correção Implementada
|
|
||||||
```typescript
|
|
||||||
// Separar host e porta corretamente
|
|
||||||
const { host, porta } = obterHostEPorta(config.domain);
|
|
||||||
const protocol = config.useHttps ? 'https' : 'http';
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
hosts: {
|
|
||||||
domain: host, // Apenas o host (sem porta)
|
|
||||||
muc: `conference.${host}` // MUC no mesmo domínio
|
|
||||||
},
|
|
||||||
serviceUrl: `${protocol}://${host}:${porta}/http-bind`, // BOSH com porta
|
|
||||||
bosh: `${protocol}://${host}:${porta}/http-bind`, // BOSH alternativo
|
|
||||||
clientNode: config.appId
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Arquivo modificado:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
**Arquivo criado/atualizado:**
|
|
||||||
- `apps/web/src/lib/utils/jitsi.ts` - Adicionada função `obterHostEPorta()`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Criação de Tracks Locais
|
|
||||||
|
|
||||||
### Problema Identificado
|
|
||||||
- Os tracks locais não estavam sendo criados após entrar na conferência
|
|
||||||
- Faltava o evento `CONFERENCE_JOINED` para criar tracks locais
|
|
||||||
|
|
||||||
### Correção Implementada
|
|
||||||
```typescript
|
|
||||||
conference.on(JitsiMeetJS.constants.events.conference.CONFERENCE_JOINED, async () => {
|
|
||||||
// Criar tracks locais com constraints apropriadas
|
|
||||||
const constraints = {
|
|
||||||
audio: estadoAtual.audioHabilitado ? {
|
|
||||||
echoCancellation: true,
|
|
||||||
noiseSuppression: true,
|
|
||||||
autoGainControl: true
|
|
||||||
} : false,
|
|
||||||
video: estadoAtual.videoHabilitado ? {
|
|
||||||
facingMode: 'user',
|
|
||||||
width: { ideal: 1280 },
|
|
||||||
height: { ideal: 720 }
|
|
||||||
} : false
|
|
||||||
};
|
|
||||||
|
|
||||||
const tracks = await JitsiMeetJS.createLocalTracks(constraints, {
|
|
||||||
devices: [],
|
|
||||||
cameraDeviceId: estadoChamada.dispositivos.cameraId || undefined,
|
|
||||||
micDeviceId: estadoChamada.dispositivos.microphoneId || undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
// Adicionar tracks à conferência e anexar ao vídeo local
|
|
||||||
for (const track of tracks) {
|
|
||||||
await conference.addTrack(track);
|
|
||||||
if (track.getType() === 'video' && localVideo) {
|
|
||||||
track.attach(localVideo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**Arquivo modificado:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Gerenciamento de Tracks
|
|
||||||
|
|
||||||
### Problema Identificado
|
|
||||||
- Tracks locais não eram armazenados corretamente
|
|
||||||
- Falta de limpeza adequada ao finalizar chamada
|
|
||||||
|
|
||||||
### Correção Implementada
|
|
||||||
- Adicionada variável de estado `localTracks: JitsiTrack[]` para rastrear todos os tracks locais
|
|
||||||
- Implementada limpeza adequada no método `finalizar()`:
|
|
||||||
- Desconectar tracks antes de liberar
|
|
||||||
- Dispor de todos os tracks locais
|
|
||||||
- Limpar referências
|
|
||||||
|
|
||||||
**Arquivo modificado:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Attach/Detach de Tracks Remotos
|
|
||||||
|
|
||||||
### Problema Identificado
|
|
||||||
- Tracks remotos não eram anexados corretamente aos elementos de vídeo/áudio
|
|
||||||
- Não havia tratamento específico para áudio vs vídeo
|
|
||||||
|
|
||||||
### Correção Implementada
|
|
||||||
```typescript
|
|
||||||
function adicionarTrackRemoto(track: JitsiTrack): void {
|
|
||||||
const participantId = track.getParticipantId();
|
|
||||||
const trackType = track.getType();
|
|
||||||
|
|
||||||
if (trackType === 'audio') {
|
|
||||||
// Criar elemento de áudio invisível
|
|
||||||
const audioElement = document.createElement('audio');
|
|
||||||
audioElement.id = `remote-audio-${participantId}`;
|
|
||||||
audioElement.autoplay = true;
|
|
||||||
track.attach(audioElement);
|
|
||||||
videoContainer.appendChild(audioElement);
|
|
||||||
} else if (trackType === 'video') {
|
|
||||||
// Criar elemento de vídeo
|
|
||||||
const videoElement = document.createElement('video');
|
|
||||||
videoElement.id = `remote-video-${participantId}`;
|
|
||||||
videoElement.autoplay = true;
|
|
||||||
track.attach(videoElement);
|
|
||||||
videoContainer.appendChild(videoElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Arquivo modificado:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Controles de Áudio e Vídeo
|
|
||||||
|
|
||||||
### Problema Identificado
|
|
||||||
- Os métodos `handleToggleAudio` e `handleToggleVideo` não criavam novos tracks quando necessário
|
|
||||||
- Não atualizavam corretamente o estado dos tracks locais
|
|
||||||
|
|
||||||
### Correção Implementada
|
|
||||||
- Implementada lógica para criar tracks se não existirem
|
|
||||||
- Atualização correta do estado dos tracks (mute/unmute)
|
|
||||||
- Sincronização com o backend quando anfitrião
|
|
||||||
- Anexar/desanexar tracks ao vídeo local corretamente
|
|
||||||
|
|
||||||
**Arquivo modificado:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Tratamento de Erros
|
|
||||||
|
|
||||||
### Problema Identificado
|
|
||||||
- Uso de `alert()` para erros (não amigável)
|
|
||||||
- Falta de mensagens de erro claras
|
|
||||||
|
|
||||||
### Correção Implementada
|
|
||||||
- Implementado sistema de tratamento de erros com `ErrorModal`
|
|
||||||
- Integrado com `traduzirErro()` para mensagens amigáveis
|
|
||||||
- Adicionado estado de erro no componente:
|
|
||||||
```typescript
|
|
||||||
let showErrorModal = $state(false);
|
|
||||||
let errorTitle = $state('Erro na Chamada');
|
|
||||||
let errorMessage = $state('');
|
|
||||||
let errorDetails = $state<string | undefined>(undefined);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Arquivos modificados:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
- Integração com `apps/web/src/lib/utils/erroHelpers.ts`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Inicialização do Jitsi Meet JS
|
|
||||||
|
|
||||||
### Problema Identificado
|
|
||||||
- Configuração básica do Jitsi pode estar incompleta
|
|
||||||
- Nível de log muito restritivo
|
|
||||||
|
|
||||||
### Correção Implementada
|
|
||||||
```typescript
|
|
||||||
JitsiMeetJS.init({
|
|
||||||
disableAudioLevels: false, // Habilitado para melhor qualidade
|
|
||||||
disableSimulcast: false,
|
|
||||||
enableWindowOnErrorHandler: true,
|
|
||||||
enableRemb: true, // REMB para controle de bitrate
|
|
||||||
enableTcc: true, // TCC para controle de congestionamento
|
|
||||||
disableThirdPartyRequests: false
|
|
||||||
});
|
|
||||||
|
|
||||||
JitsiMeetJS.setLogLevel(JitsiMeetJS.constants.logLevels.INFO); // Mais verboso para debug
|
|
||||||
```
|
|
||||||
|
|
||||||
**Arquivo modificado:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. UI/UX Melhorias
|
|
||||||
|
|
||||||
### Implementado
|
|
||||||
- Indicador de conexão durante estabelecimento da chamada
|
|
||||||
- Mensagem de "Conectando..." enquanto não há conexão estabelecida
|
|
||||||
- Tratamento visual adequado de estados de conexão
|
|
||||||
|
|
||||||
**Arquivo modificado:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Eventos da Conferência
|
|
||||||
|
|
||||||
### Adicionado
|
|
||||||
- `CONFERENCE_JOINED`: Criar tracks locais após entrar
|
|
||||||
- `CONFERENCE_LEFT`: Limpar tracks ao sair
|
|
||||||
- Melhor tratamento de `TRACK_ADDED` e `TRACK_REMOVED`
|
|
||||||
|
|
||||||
**Arquivo modificado:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Correção de Interfaces TypeScript
|
|
||||||
|
|
||||||
### Adicionado
|
|
||||||
- Método `addTrack()` na interface `JitsiConference`
|
|
||||||
- Melhor tipagem de `JitsiTrack` com propriedade `track: MediaStreamTrack`
|
|
||||||
|
|
||||||
**Arquivo modificado:**
|
|
||||||
- `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Configuração Necessária
|
|
||||||
|
|
||||||
### Variáveis de Ambiente (.env)
|
|
||||||
```env
|
|
||||||
# Jitsi Meet Configuration (Docker Local)
|
|
||||||
VITE_JITSI_DOMAIN=localhost:8443
|
|
||||||
VITE_JITSI_APP_ID=sgse-app
|
|
||||||
VITE_JITSI_ROOM_PREFIX=sgse
|
|
||||||
VITE_JITSI_USE_HTTPS=true
|
|
||||||
```
|
|
||||||
|
|
||||||
**Nota:** Para Docker Jitsi local, geralmente usa-se HTTPS na porta 8443.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Verificações Necessárias
|
|
||||||
|
|
||||||
### 1. Docker Jitsi Rodando
|
|
||||||
```bash
|
|
||||||
docker ps | grep jitsi
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Porta 8443 Acessível
|
|
||||||
```bash
|
|
||||||
curl -k https://localhost:8443
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Permissões do Navegador
|
|
||||||
- Microfone deve estar permitido
|
|
||||||
- Câmera deve estar permitida (para chamadas de vídeo)
|
|
||||||
|
|
||||||
### 4. Logs do Navegador
|
|
||||||
- Abrir DevTools (F12)
|
|
||||||
- Verificar Console para erros de conexão
|
|
||||||
- Verificar Network para erros de rede
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Próximos Passos (Se Necessário)
|
|
||||||
|
|
||||||
1. **Testar conectividade** - Verificar se o servidor Jitsi responde corretamente
|
|
||||||
2. **Ajustar configuração de rede** - Se houver problemas de firewall ou CORS
|
|
||||||
3. **Configurar STUN/TURN** - Para conexões através de NAT (se necessário)
|
|
||||||
4. **Otimizar qualidade** - Ajustar bitrates e resoluções conforme necessário
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
✅ **Todas as correções foram implementadas**
|
|
||||||
✅ **Código sem erros de lint**
|
|
||||||
✅ **Tratamento de erros adequado**
|
|
||||||
✅ **Interfaces TypeScript corretas**
|
|
||||||
✅ **Gerenciamento de recursos adequado**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Data:** $(date)
|
|
||||||
**Versão:** 1.0.0
|
|
||||||
|
|
||||||
@@ -1,701 +0,0 @@
|
|||||||
# Plano de Implementação - Chamadas de Áudio e Vídeo com Jitsi Meet
|
|
||||||
|
|
||||||
## Opção Escolhida: Docker Local (Desenvolvimento)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 Etapas Fora do Código - Configuração Docker
|
|
||||||
|
|
||||||
### Etapa 1: Preparar Ambiente Docker
|
|
||||||
|
|
||||||
**Requisitos:**
|
|
||||||
|
|
||||||
- Docker Desktop instalado e rodando
|
|
||||||
- Mínimo 4GB RAM disponível
|
|
||||||
- Portas livres: 8000, 8443, 10000-20000/udp
|
|
||||||
|
|
||||||
**Passos:**
|
|
||||||
|
|
||||||
1. **Criar diretório para configuração Docker Jitsi:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p ~/jitsi-docker
|
|
||||||
cd ~/jitsi-docker
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Clonar repositório oficial:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/jitsi/docker-jitsi-meet.git
|
|
||||||
cd docker-jitsi-meet
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Configurar variáveis de ambiente:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp env.example .env
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Editar arquivo `.env` com as seguintes configurações:**
|
|
||||||
|
|
||||||
```env
|
|
||||||
# Configuração básica para desenvolvimento local
|
|
||||||
CONFIG=~/.jitsi-meet-cfg
|
|
||||||
TZ=America/Recife
|
|
||||||
|
|
||||||
# Desabilitar Let's Encrypt (não necessário para localhost)
|
|
||||||
ENABLE_LETSENCRYPT=0
|
|
||||||
|
|
||||||
# Portas HTTP/HTTPS
|
|
||||||
HTTP_PORT=8000
|
|
||||||
HTTPS_PORT=8443
|
|
||||||
|
|
||||||
# Domínio local
|
|
||||||
PUBLIC_URL=http://localhost:8000
|
|
||||||
DOMAIN=localhost
|
|
||||||
|
|
||||||
# Desabilitar autenticação para facilitar testes
|
|
||||||
ENABLE_AUTH=0
|
|
||||||
ENABLE_GUESTS=1
|
|
||||||
|
|
||||||
# Desabilitar transcrissão (não necessário para desenvolvimento)
|
|
||||||
ENABLE_TRANSCRIPTION=0
|
|
||||||
|
|
||||||
# Desabilitar gravação no servidor (usaremos gravação local)
|
|
||||||
ENABLE_RECORDING=0
|
|
||||||
|
|
||||||
# Configurações de vídeo (ajustar conforme necessidade)
|
|
||||||
ENABLE_PREJOIN_PAGE=0
|
|
||||||
START_AUDIO_MUTED=0
|
|
||||||
START_VIDEO_MUTED=0
|
|
||||||
|
|
||||||
# Configurações de segurança
|
|
||||||
ENABLE_XMPP_WEBSOCKET=0
|
|
||||||
ENABLE_P2P=1
|
|
||||||
|
|
||||||
# Limites
|
|
||||||
MAX_NUMBER_OF_PARTICIPANTS=10
|
|
||||||
RESOLUTION_WIDTH=1280
|
|
||||||
RESOLUTION_HEIGHT=720
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Criar diretórios necessários:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p ~/.jitsi-meet-cfg/{web/letsencrypt,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb}
|
|
||||||
```
|
|
||||||
|
|
||||||
6. **Iniciar containers:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
7. **Verificar status:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose ps
|
|
||||||
```
|
|
||||||
|
|
||||||
8. **Ver logs se necessário:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose logs -f
|
|
||||||
```
|
|
||||||
|
|
||||||
9. **Testar acesso:**
|
|
||||||
|
|
||||||
- Acessar: http://localhost:8000
|
|
||||||
- Criar uma sala de teste e verificar se funciona
|
|
||||||
|
|
||||||
**Troubleshooting:**
|
|
||||||
|
|
||||||
- Se houver erro de permissão nos diretórios: `sudo chown -R $USER:$USER ~/.jitsi-meet-cfg`
|
|
||||||
- Se portas estiverem em uso, alterar HTTP_PORT e HTTPS_PORT no .env
|
|
||||||
- Para parar: `docker-compose down`
|
|
||||||
- Para reiniciar: `docker-compose restart`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📦 Etapas no Código - Backend Convex
|
|
||||||
|
|
||||||
### Etapa 2: Atualizar Schema
|
|
||||||
|
|
||||||
**Arquivo:** `packages/backend/convex/schema.ts`
|
|
||||||
|
|
||||||
**Adicionar nova tabela `chamadas`:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
chamadas: defineTable({
|
|
||||||
conversaId: v.id('conversas'),
|
|
||||||
tipo: v.union(v.literal('audio'), v.literal('video')),
|
|
||||||
roomName: v.string(), // Nome único da sala Jitsi
|
|
||||||
criadoPor: v.id('usuarios'), // Anfitrião/criador
|
|
||||||
participantes: v.array(v.id('usuarios')),
|
|
||||||
status: v.union(
|
|
||||||
v.literal('aguardando'),
|
|
||||||
v.literal('em_andamento'),
|
|
||||||
v.literal('finalizada'),
|
|
||||||
v.literal('cancelada')
|
|
||||||
),
|
|
||||||
iniciadaEm: v.optional(v.number()),
|
|
||||||
finalizadaEm: v.optional(v.number()),
|
|
||||||
duracaoSegundos: v.optional(v.number()),
|
|
||||||
gravando: v.boolean(),
|
|
||||||
gravacaoIniciadaPor: v.optional(v.id('usuarios')),
|
|
||||||
gravacaoIniciadaEm: v.optional(v.number()),
|
|
||||||
gravacaoFinalizadaEm: v.optional(v.number()),
|
|
||||||
configuracoes: v.optional(
|
|
||||||
v.object({
|
|
||||||
audioHabilitado: v.boolean(),
|
|
||||||
videoHabilitado: v.boolean(),
|
|
||||||
participantesConfig: v.optional(
|
|
||||||
v.array(
|
|
||||||
v.object({
|
|
||||||
usuarioId: v.id('usuarios'),
|
|
||||||
audioHabilitado: v.boolean(),
|
|
||||||
videoHabilitado: v.boolean(),
|
|
||||||
forcadoPeloAnfitriao: v.optional(v.boolean()) // Se foi forçado pelo anfitrião
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
),
|
|
||||||
criadoEm: v.number()
|
|
||||||
})
|
|
||||||
.index('by_conversa', ['conversaId', 'status'])
|
|
||||||
.index('by_conversa_ativa', ['conversaId', 'status'])
|
|
||||||
.index('by_criado_por', ['criadoPor'])
|
|
||||||
.index('by_status', ['status'])
|
|
||||||
.index('by_room_name', ['roomName']);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Etapa 3: Criar Backend de Chamadas
|
|
||||||
|
|
||||||
**Arquivo:** `packages/backend/convex/chamadas.ts`
|
|
||||||
|
|
||||||
**Funções a implementar:**
|
|
||||||
|
|
||||||
#### Mutations:
|
|
||||||
|
|
||||||
1. `criarChamada` - Criar nova chamada
|
|
||||||
2. `iniciarChamada` - Marcar como em andamento
|
|
||||||
3. `finalizarChamada` - Finalizar e calcular duração
|
|
||||||
4. `adicionarParticipante` - Adicionar participante
|
|
||||||
5. `removerParticipante` - Remover participante
|
|
||||||
6. `toggleAudioVideo` - Anfitrião controla áudio/vídeo de participante
|
|
||||||
7. `atualizarConfiguracaoParticipante` - Atualizar configuração individual
|
|
||||||
8. `iniciarGravacao` - Marcar início de gravação
|
|
||||||
9. `finalizarGravacao` - Marcar fim de gravação
|
|
||||||
|
|
||||||
#### Queries:
|
|
||||||
|
|
||||||
1. `obterChamadaAtiva` - Buscar chamada ativa de uma conversa
|
|
||||||
2. `listarChamadas` - Listar histórico
|
|
||||||
3. `verificarAnfitriao` - Verificar se usuário é anfitrião
|
|
||||||
4. `obterParticipantesChamada` - Listar participantes
|
|
||||||
|
|
||||||
**Tipos TypeScript (sem usar `any`):**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import type { Id } from './_generated/dataModel';
|
|
||||||
import type { QueryCtx, MutationCtx } from './_generated/server';
|
|
||||||
|
|
||||||
type ChamadaTipo = 'audio' | 'video';
|
|
||||||
type ChamadaStatus = 'aguardando' | 'em_andamento' | 'finalizada' | 'cancelada';
|
|
||||||
|
|
||||||
interface ParticipanteConfig {
|
|
||||||
usuarioId: Id<'usuarios'>;
|
|
||||||
audioHabilitado: boolean;
|
|
||||||
videoHabilitado: boolean;
|
|
||||||
forcadoPeloAnfitriao?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConfiguracoesChamada {
|
|
||||||
audioHabilitado: boolean;
|
|
||||||
videoHabilitado: boolean;
|
|
||||||
participantesConfig?: ParticipanteConfig[];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 Etapas no Código - Frontend Svelte
|
|
||||||
|
|
||||||
### Etapa 4: Instalar Dependências
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/package.json`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd apps/web
|
|
||||||
bun add lib-jitsi-meet
|
|
||||||
```
|
|
||||||
|
|
||||||
**Dependências adicionais necessárias:**
|
|
||||||
|
|
||||||
- `lib-jitsi-meet` - Biblioteca oficial Jitsi
|
|
||||||
- (Possivelmente tipos) `@types/lib-jitsi-meet` se disponível
|
|
||||||
|
|
||||||
### Etapa 5: Configurar Variáveis de Ambiente
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/.env`
|
|
||||||
|
|
||||||
```env
|
|
||||||
# Jitsi Meet Configuration (Docker Local)
|
|
||||||
VITE_JITSI_DOMAIN=localhost:8443
|
|
||||||
VITE_JITSI_APP_ID=sgse-app
|
|
||||||
VITE_JITSI_ROOM_PREFIX=sgse
|
|
||||||
VITE_JITSI_USE_HTTPS=false
|
|
||||||
```
|
|
||||||
|
|
||||||
### Etapa 6: Criar Utilitários Jitsi
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/utils/jitsi.ts`
|
|
||||||
|
|
||||||
**Funções:**
|
|
||||||
|
|
||||||
- `gerarRoomName(conversaId: string, tipo: "audio" | "video"): string` - Gerar nome único da sala
|
|
||||||
- `obterConfiguracaoJitsi()` - Retornar configuração do Jitsi baseada em .env
|
|
||||||
- `validarDispositivos()` - Validar disponibilidade de microfone/webcam
|
|
||||||
- `obterDispositivosDisponiveis()` - Listar dispositivos de mídia
|
|
||||||
|
|
||||||
**Tipos (sem `any`):**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface ConfiguracaoJitsi {
|
|
||||||
domain: string;
|
|
||||||
appId: string;
|
|
||||||
roomPrefix: string;
|
|
||||||
useHttps: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DispositivoMedia {
|
|
||||||
deviceId: string;
|
|
||||||
label: string;
|
|
||||||
kind: 'audioinput' | 'audiooutput' | 'videoinput';
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DispositivosDisponiveis {
|
|
||||||
microphones: DispositivoMedia[];
|
|
||||||
speakers: DispositivoMedia[];
|
|
||||||
cameras: DispositivoMedia[];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Etapa 7: Criar Store de Chamadas
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/stores/callStore.ts`
|
|
||||||
|
|
||||||
**Estado gerenciado:**
|
|
||||||
|
|
||||||
- Chamada ativa (se houver)
|
|
||||||
- Estado de mídia (áudio/vídeo ligado/desligado)
|
|
||||||
- Dispositivos selecionados
|
|
||||||
- Status de gravação
|
|
||||||
- Lista de participantes
|
|
||||||
- Duração da chamada
|
|
||||||
- É anfitrião ou não
|
|
||||||
|
|
||||||
**Tipos:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface EstadoChamada {
|
|
||||||
chamadaId: Id<'chamadas'> | null;
|
|
||||||
conversaId: Id<'conversas'> | null;
|
|
||||||
tipo: 'audio' | 'video' | null;
|
|
||||||
roomName: string | null;
|
|
||||||
estaConectado: boolean;
|
|
||||||
audioHabilitado: boolean;
|
|
||||||
videoHabilitado: boolean;
|
|
||||||
gravando: boolean;
|
|
||||||
ehAnfitriao: boolean;
|
|
||||||
participantes: Array<{
|
|
||||||
usuarioId: Id<'usuarios'>;
|
|
||||||
nome: string;
|
|
||||||
avatar?: string;
|
|
||||||
audioHabilitado: boolean;
|
|
||||||
videoHabilitado: boolean;
|
|
||||||
}>;
|
|
||||||
duracaoSegundos: number;
|
|
||||||
dispositivos: {
|
|
||||||
microphoneId: string | null;
|
|
||||||
cameraId: string | null;
|
|
||||||
speakerId: string | null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EventosChamada {
|
|
||||||
'participant-joined': (participant: ParticipanteJitsi) => void;
|
|
||||||
'participant-left': (participantId: string) => void;
|
|
||||||
'audio-mute-status-changed': (isMuted: boolean) => void;
|
|
||||||
'video-mute-status-changed': (isMuted: boolean) => void;
|
|
||||||
'connection-failed': (error: Error) => void;
|
|
||||||
'connection-disconnected': () => void;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Métodos principais:**
|
|
||||||
|
|
||||||
- `iniciarChamada(conversaId, tipo)`
|
|
||||||
- `finalizarChamada()`
|
|
||||||
- `toggleAudio()`
|
|
||||||
- `toggleVideo()`
|
|
||||||
- `iniciarGravacao()`
|
|
||||||
- `finalizarGravacao()`
|
|
||||||
- `atualizarDispositivos()`
|
|
||||||
|
|
||||||
### Etapa 8: Criar Utilitários de Gravação
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/utils/mediaRecorder.ts`
|
|
||||||
|
|
||||||
**Funções:**
|
|
||||||
|
|
||||||
- `iniciarGravacaoAudio(stream: MediaStream): MediaRecorder` - Gravar apenas áudio
|
|
||||||
- `iniciarGravacaoVideo(stream: MediaStream): MediaRecorder` - Gravar áudio + vídeo
|
|
||||||
- `pararGravacao(recorder: MediaRecorder): Promise<Blob>` - Parar e retornar blob
|
|
||||||
- `salvarGravacao(blob: Blob, nomeArquivo: string): void` - Salvar localmente
|
|
||||||
- `obterDuracaoGravacao(recorder: MediaRecorder): number` - Obter duração
|
|
||||||
|
|
||||||
**Tipos:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface OpcoesGravacao {
|
|
||||||
audioBitsPerSecond?: number;
|
|
||||||
videoBitsPerSecond?: number;
|
|
||||||
mimeType?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ResultadoGravacao {
|
|
||||||
blob: Blob;
|
|
||||||
duracaoSegundos: number;
|
|
||||||
nomeArquivo: string;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Etapa 9: Criar Componente CallWindow
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/components/call/CallWindow.svelte`
|
|
||||||
|
|
||||||
**Características:**
|
|
||||||
|
|
||||||
- Janela flutuante redimensionável e arrastável
|
|
||||||
- Integração com lib-jitsi-meet
|
|
||||||
- Container para vídeo dos participantes
|
|
||||||
- Barra de controles
|
|
||||||
- Indicador de gravação
|
|
||||||
- Contador de duração
|
|
||||||
|
|
||||||
**Props (TypeScript estrito):**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface Props {
|
|
||||||
chamadaId: Id<'chamadas'>;
|
|
||||||
conversaId: Id<'conversas'>;
|
|
||||||
tipo: 'audio' | 'video';
|
|
||||||
roomName: string;
|
|
||||||
ehAnfitriao: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Estrutura:**
|
|
||||||
|
|
||||||
- `<script lang="ts">` com tipos explícitos
|
|
||||||
- Uso de `$state`, `$derived`, `$effect` (Svelte 5)
|
|
||||||
- Integração com `callStore`
|
|
||||||
- Eventos do Jitsi tratados tipados
|
|
||||||
|
|
||||||
**Bibliotecas para janela flutuante:**
|
|
||||||
|
|
||||||
- Usar eventos nativos de mouse/touch para drag
|
|
||||||
- CSS para redimensionamento com handles
|
|
||||||
- localStorage para persistir posição/tamanho
|
|
||||||
|
|
||||||
### Etapa 10: Criar Componente CallControls
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/components/call/CallControls.svelte`
|
|
||||||
|
|
||||||
**Controles:**
|
|
||||||
|
|
||||||
- Botão toggle áudio
|
|
||||||
- Botão toggle vídeo
|
|
||||||
- Botão gravação (se anfitrião)
|
|
||||||
- Botão configurações
|
|
||||||
- Botão encerrar chamada
|
|
||||||
- Contador de duração (HH:MM:SS)
|
|
||||||
|
|
||||||
**Props:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface Props {
|
|
||||||
audioHabilitado: boolean;
|
|
||||||
videoHabilitado: boolean;
|
|
||||||
gravando: boolean;
|
|
||||||
ehAnfitriao: boolean;
|
|
||||||
duracaoSegundos: number;
|
|
||||||
onToggleAudio: () => void;
|
|
||||||
onToggleVideo: () => void;
|
|
||||||
onIniciarGravacao: () => void;
|
|
||||||
onPararGravacao: () => void;
|
|
||||||
onAbrirConfiguracoes: () => void;
|
|
||||||
onEncerrar: () => void;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Etapa 11: Criar Componente CallSettings
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/components/call/CallSettings.svelte`
|
|
||||||
|
|
||||||
**Funcionalidades:**
|
|
||||||
|
|
||||||
- Listar microfones disponíveis
|
|
||||||
- Listar webcams disponíveis
|
|
||||||
- Listar alto-falantes disponíveis
|
|
||||||
- Preview de vídeo antes de aplicar
|
|
||||||
- Teste de áudio
|
|
||||||
- Botões aplicar/cancelar
|
|
||||||
|
|
||||||
**Props:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface Props {
|
|
||||||
open: boolean;
|
|
||||||
dispositivoAtual: {
|
|
||||||
microphoneId: string | null;
|
|
||||||
cameraId: string | null;
|
|
||||||
speakerId: string | null;
|
|
||||||
};
|
|
||||||
onClose: () => void;
|
|
||||||
onAplicar: (dispositivos: {
|
|
||||||
microphoneId: string | null;
|
|
||||||
cameraId: string | null;
|
|
||||||
speakerId: string | null;
|
|
||||||
}) => void;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Etapa 12: Criar Componente HostControls
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/components/call/HostControls.svelte`
|
|
||||||
|
|
||||||
**Funcionalidades (apenas para anfitrião):**
|
|
||||||
|
|
||||||
- Lista de participantes
|
|
||||||
- Toggle áudio por participante
|
|
||||||
- Toggle vídeo por participante
|
|
||||||
- Indicador visual de quem está gravando
|
|
||||||
- Status de cada participante
|
|
||||||
|
|
||||||
**Props:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface Props {
|
|
||||||
participantes: Array<{
|
|
||||||
usuarioId: Id<'usuarios'>;
|
|
||||||
nome: string;
|
|
||||||
avatar?: string;
|
|
||||||
audioHabilitado: boolean;
|
|
||||||
videoHabilitado: boolean;
|
|
||||||
forcadoPeloAnfitriao?: boolean;
|
|
||||||
}>;
|
|
||||||
onToggleParticipanteAudio: (usuarioId: Id<'usuarios'>) => void;
|
|
||||||
onToggleParticipanteVideo: (usuarioId: Id<'usuarios'>) => void;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Etapa 13: Criar Componente RecordingIndicator
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/components/call/RecordingIndicator.svelte`
|
|
||||||
|
|
||||||
**Características:**
|
|
||||||
|
|
||||||
- Banner visível no topo da janela
|
|
||||||
- Ícone animado de gravação
|
|
||||||
- Mensagem clara de que está gravando
|
|
||||||
- Informação de quem iniciou (se disponível)
|
|
||||||
|
|
||||||
**Props:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface Props {
|
|
||||||
gravando: boolean;
|
|
||||||
iniciadoPor?: string; // Nome do usuário que iniciou
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Etapa 14: Criar Utilitário de Janela Flutuante
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/utils/floatingWindow.ts`
|
|
||||||
|
|
||||||
**Funções:**
|
|
||||||
|
|
||||||
- `criarDragHandler(element: HTMLElement, handle: HTMLElement): () => void` - Criar handler de arrastar
|
|
||||||
- `criarResizeHandler(element: HTMLElement, handles: HTMLElement[]): () => void` - Criar handler de redimensionar
|
|
||||||
- `salvarPosicaoJanela(id: string, posicao: { x: number; y: number; width: number; height: number }): void` - Salvar no localStorage
|
|
||||||
- `restaurarPosicaoJanela(id: string): { x: number; y: number; width: number; height: number } | null` - Restaurar do localStorage
|
|
||||||
|
|
||||||
**Tipos:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface PosicaoJanela {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LimitesJanela {
|
|
||||||
minWidth: number;
|
|
||||||
minHeight: number;
|
|
||||||
maxWidth?: number;
|
|
||||||
maxHeight?: number;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Etapa 15: Integrar com ChatWindow
|
|
||||||
|
|
||||||
**Arquivo:** `apps/web/src/lib/components/chat/ChatWindow.svelte`
|
|
||||||
|
|
||||||
**Modificações:**
|
|
||||||
|
|
||||||
- Adicionar botão de chamada de áudio
|
|
||||||
- Adicionar botão de chamada de vídeo
|
|
||||||
- Mostrar indicador quando há chamada ativa
|
|
||||||
- Importar e usar CallWindow quando houver chamada
|
|
||||||
|
|
||||||
**Adicionar no topo (junto com outros botões):**
|
|
||||||
|
|
||||||
```svelte
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm btn-circle"
|
|
||||||
onclick={() => iniciarChamada('audio')}
|
|
||||||
title="Ligação de áudio"
|
|
||||||
>
|
|
||||||
<Phone class="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm btn-circle"
|
|
||||||
onclick={() => iniciarChamada('video')}
|
|
||||||
title="Ligação de vídeo"
|
|
||||||
>
|
|
||||||
<Video class="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 Ordem de Implementação Recomendada
|
|
||||||
|
|
||||||
1. ✅ **Etapa 1:** Configurar Docker Jitsi (fora do código)
|
|
||||||
2. ✅ **Etapa 2:** Atualizar schema com tabela chamadas
|
|
||||||
3. ✅ **Etapa 3:** Criar backend chamadas.ts com todas as funções
|
|
||||||
4. ✅ **Etapa 4:** Instalar dependências frontend
|
|
||||||
5. ✅ **Etapa 5:** Configurar variáveis de ambiente
|
|
||||||
6. ✅ **Etapa 6:** Criar utilitários Jitsi (jitsi.ts)
|
|
||||||
7. ✅ **Etapa 7:** Criar store de chamadas (callStore.ts)
|
|
||||||
8. ✅ **Etapa 8:** Criar utilitários de gravação (mediaRecorder.ts)
|
|
||||||
9. ✅ **Etapa 9:** Criar CallWindow básico (apenas estrutura)
|
|
||||||
10. ✅ **Etapa 10:** Integrar lib-jitsi-meet no CallWindow
|
|
||||||
11. ✅ **Etapa 11:** Criar CallControls e integrar
|
|
||||||
12. ✅ **Etapa 12:** Implementar contador de duração
|
|
||||||
13. ✅ **Etapa 13:** Implementar janela flutuante (drag & resize)
|
|
||||||
14. ✅ **Etapa 14:** Criar CallSettings e integração de dispositivos
|
|
||||||
15. ✅ **Etapa 15:** Criar HostControls e lógica de anfitrião
|
|
||||||
16. ✅ **Etapa 16:** Implementar gravação local
|
|
||||||
17. ✅ **Etapa 17:** Criar RecordingIndicator
|
|
||||||
18. ✅ **Etapa 18:** Integrar botões no ChatWindow
|
|
||||||
19. ✅ **Etapa 19:** Testes completos
|
|
||||||
20. ✅ **Etapa 20:** Ajustes finais e tratamento de erros
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛡️ Segurança e Boas Práticas
|
|
||||||
|
|
||||||
### TypeScript
|
|
||||||
|
|
||||||
- ❌ **NUNCA** usar `any`
|
|
||||||
- ✅ Usar tipos explícitos em todas as funções
|
|
||||||
- ✅ Usar tipos inferidos do Convex quando possível
|
|
||||||
- ✅ Criar interfaces para objetos complexos
|
|
||||||
|
|
||||||
### Svelte 5
|
|
||||||
|
|
||||||
- ✅ Usar `$props()` para props
|
|
||||||
- ✅ Usar `$state()` para estado reativo
|
|
||||||
- ✅ Usar `$derived()` para valores derivados
|
|
||||||
- ✅ Usar `$effect()` para side effects
|
|
||||||
|
|
||||||
### Validação
|
|
||||||
|
|
||||||
- ✅ Validar permissões no backend antes de mutações
|
|
||||||
- ✅ Validar entrada de dados
|
|
||||||
- ✅ Tratar erros adequadamente
|
|
||||||
- ✅ Logs de segurança (criação/finalização de chamadas)
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
|
|
||||||
- ✅ Cleanup adequado de event listeners
|
|
||||||
- ✅ Desconectar Jitsi ao fechar janela
|
|
||||||
- ✅ Parar gravação ao finalizar chamada
|
|
||||||
- ✅ Liberar streams de mídia
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 Notas Importantes
|
|
||||||
|
|
||||||
1. **Room Names:** Gerar room names únicos usando conversaId + timestamp + hash
|
|
||||||
2. **Persistência:** Salvar posição/tamanho da janela no localStorage
|
|
||||||
3. **Notificações:** Notificar participantes quando chamada é criada/finalizada
|
|
||||||
4. **Limpeza:** Sempre limpar recursos ao finalizar chamada
|
|
||||||
5. **Erros:** Tratar erros de conexão, permissões de mídia, etc.
|
|
||||||
6. **Acessibilidade:** Adicionar labels, ARIA attributes, suporte a teclado
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 Testes
|
|
||||||
|
|
||||||
### Testes Funcionais
|
|
||||||
|
|
||||||
- [ ] Criar chamada de áudio individual
|
|
||||||
- [ ] Criar chamada de vídeo individual
|
|
||||||
- [ ] Criar chamada em grupo
|
|
||||||
- [ ] Toggle áudio/vídeo
|
|
||||||
- [ ] Anfitrião controlar participantes
|
|
||||||
- [ ] Iniciar/parar gravação
|
|
||||||
- [ ] Contador de duração
|
|
||||||
- [ ] Configuração de dispositivos
|
|
||||||
- [ ] Janela flutuante drag/resize
|
|
||||||
|
|
||||||
### Testes de Segurança
|
|
||||||
|
|
||||||
- [ ] Não anfitrião não pode controlar outros
|
|
||||||
- [ ] Não anfitrião não pode iniciar gravação
|
|
||||||
- [ ] Validação de participantes
|
|
||||||
- [ ] Rate limiting de criação de chamadas
|
|
||||||
|
|
||||||
### Testes de Erros
|
|
||||||
|
|
||||||
- [ ] Conexão perdida
|
|
||||||
- [ ] Sem permissão de mídia
|
|
||||||
- [ ] Dispositivos não disponíveis
|
|
||||||
- [ ] Servidor Jitsi offline
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📚 Referências
|
|
||||||
|
|
||||||
- [Jitsi Meet Docker](https://github.com/jitsi/docker-jitsi-meet)
|
|
||||||
- [lib-jitsi-meet Documentation](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-ljm-api)
|
|
||||||
- [Svelte 5 Documentation](https://svelte.dev/docs)
|
|
||||||
- [Convex Documentation](https://docs.convex.dev)
|
|
||||||
- [WebRTC API](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API)
|
|
||||||
- [MediaRecorder API](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Data de Criação:** 2025-01-XX
|
|
||||||
**Versão:** 1.0
|
|
||||||
**Opção:** Docker Local (Desenvolvimento)
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
# Relatório de Testes - Sistema de Temas Personalizados
|
|
||||||
|
|
||||||
## Data: 2025-01-27
|
|
||||||
|
|
||||||
## Resumo Executivo
|
|
||||||
Foram testados todos os 10 temas disponíveis no sistema SGSE através da aba "Aparência" na página de perfil. Cada tema foi selecionado e validado visualmente através de screenshots.
|
|
||||||
|
|
||||||
## Temas Testados
|
|
||||||
|
|
||||||
### 1. ✅ Tema Roxo (Purple)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema padrão com cores roxa e azul
|
|
||||||
- **Screenshot**: `tema-roxo.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe cores roxas/azuis
|
|
||||||
|
|
||||||
### 2. ✅ Tema Azul (Blue)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema azul clássico e profissional
|
|
||||||
- **Screenshot**: `tema-azul.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe tons de azul
|
|
||||||
|
|
||||||
### 3. ✅ Tema Verde (Green)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema verde natural e harmonioso
|
|
||||||
- **Screenshot**: `tema-verde.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe tons de verde
|
|
||||||
|
|
||||||
### 4. ✅ Tema Laranja (Orange)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema laranja vibrante e energético
|
|
||||||
- **Screenshot**: `tema-laranja.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe tons de laranja
|
|
||||||
|
|
||||||
### 5. ✅ Tema Vermelho (Red)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema vermelho intenso e impactante
|
|
||||||
- **Screenshot**: `tema-vermelho.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe tons de vermelho
|
|
||||||
|
|
||||||
### 6. ✅ Tema Rosa (Pink)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema rosa suave e elegante
|
|
||||||
- **Screenshot**: `tema-rosa.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe tons de rosa
|
|
||||||
|
|
||||||
### 7. ✅ Tema Verde-água (Teal)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema verde-água refrescante
|
|
||||||
- **Screenshot**: `tema-verde-agua.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe tons de verde-água
|
|
||||||
|
|
||||||
### 8. ✅ Tema Escuro (Dark)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema escuro para uso noturno
|
|
||||||
- **Screenshot**: `tema-escuro.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe fundo escuro
|
|
||||||
|
|
||||||
### 9. ✅ Tema Claro (Light)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema claro e minimalista
|
|
||||||
- **Screenshot**: `tema-claro.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe fundo claro
|
|
||||||
|
|
||||||
### 10. ✅ Tema Corporativo (Corporate)
|
|
||||||
- **Status**: Funcionando
|
|
||||||
- **Descrição**: Tema corporativo azul escuro
|
|
||||||
- **Screenshot**: `tema-corporativo.png`
|
|
||||||
- **Observações**: Tema aplicado corretamente, interface exibe tons corporativos
|
|
||||||
|
|
||||||
## Funcionalidades Testadas
|
|
||||||
|
|
||||||
### ✅ Seleção de Temas
|
|
||||||
- Todos os 10 temas podem ser selecionados através dos botões na interface
|
|
||||||
- A seleção é visualmente indicada com "Tema Ativo"
|
|
||||||
- A mudança de tema é aplicada imediatamente na interface
|
|
||||||
|
|
||||||
### ✅ Interface de Seleção
|
|
||||||
- A aba "Aparência" está acessível na página de perfil
|
|
||||||
- Todos os 10 temas são exibidos em cards com preview visual
|
|
||||||
- Cada card mostra o nome, descrição e um gradiente de cores representativo
|
|
||||||
|
|
||||||
### ✅ Aplicação de Temas
|
|
||||||
- Os temas são aplicados dinamicamente ao elemento `<html>` via atributo `data-theme`
|
|
||||||
- As cores são alteradas em toda a interface (sidebar, header, botões, etc.)
|
|
||||||
- A mudança é instantânea, sem necessidade de recarregar a página
|
|
||||||
|
|
||||||
## Screenshots Capturados
|
|
||||||
|
|
||||||
Todos os screenshots foram salvos com os seguintes nomes:
|
|
||||||
- `tema-verde-agua-atual.png` - Estado inicial (tema verde-água)
|
|
||||||
- `tema-roxo.png`
|
|
||||||
- `tema-azul.png`
|
|
||||||
- `tema-verde.png`
|
|
||||||
- `tema-laranja.png`
|
|
||||||
- `tema-vermelho.png`
|
|
||||||
- `tema-rosa.png`
|
|
||||||
- `tema-verde-agua.png`
|
|
||||||
- `tema-escuro.png`
|
|
||||||
- `tema-claro.png`
|
|
||||||
- `tema-corporativo.png`
|
|
||||||
|
|
||||||
## Conclusão
|
|
||||||
|
|
||||||
✅ **Todos os 10 temas estão funcionando corretamente!**
|
|
||||||
|
|
||||||
- Cada tema altera a aparência da interface conforme esperado
|
|
||||||
- As cores são aplicadas consistentemente em todos os componentes
|
|
||||||
- A seleção de temas funciona de forma intuitiva e responsiva
|
|
||||||
- O sistema está pronto para uso em produção
|
|
||||||
|
|
||||||
## Próximos Passos Recomendados
|
|
||||||
|
|
||||||
1. Testar a persistência do tema salvo no banco de dados
|
|
||||||
2. Validar que o tema é aplicado automaticamente ao fazer login
|
|
||||||
3. Verificar que o tema padrão (roxo) é aplicado ao fazer logout
|
|
||||||
4. Testar com diferentes usuários para garantir isolamento de preferências
|
|
||||||
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
# Validação e Correções do Sistema de Temas
|
|
||||||
|
|
||||||
## Correções Implementadas
|
|
||||||
|
|
||||||
### 1. Temas Customizados Melhorados
|
|
||||||
- Adicionadas todas as variáveis CSS necessárias do DaisyUI para cada tema customizado
|
|
||||||
- Incluídas variáveis de arredondamento, animação e bordas
|
|
||||||
- Adicionado `color-scheme` para temas claros/escuros
|
|
||||||
|
|
||||||
### 2. Estrutura Padronizada
|
|
||||||
- Todos os temas customizados seguem o mesmo padrão de variáveis CSS
|
|
||||||
- Temas nativos do DaisyUI (purple/aqua, dark, light) mantidos
|
|
||||||
- Temas customizados (sgse-blue, sgse-green, etc.) com variáveis completas
|
|
||||||
|
|
||||||
### 3. Aplicação de Temas
|
|
||||||
- Função `aplicarTema()` atualizada para aplicar corretamente no elemento HTML
|
|
||||||
- Removido localStorage - tema salvo apenas no banco de dados
|
|
||||||
- Tema padrão aplicado ao fazer logout
|
|
||||||
|
|
||||||
## Como Testar Manualmente
|
|
||||||
|
|
||||||
1. **Fazer Login:**
|
|
||||||
- Email: `dfw@poli.br` / Senha: `Admin@2025`
|
|
||||||
- OU Email: `kilder@kilder.com.br` / Senha: `Mudar@123`
|
|
||||||
|
|
||||||
2. **Acessar Página de Perfil:**
|
|
||||||
- Clique no avatar do usuário no canto superior direito
|
|
||||||
- Selecione "Meu Perfil"
|
|
||||||
- OU acesse diretamente: `/perfil`
|
|
||||||
|
|
||||||
3. **Testar Cada Tema:**
|
|
||||||
- Clique na aba "Aparência"
|
|
||||||
- Teste cada um dos 10 temas:
|
|
||||||
- **Roxo** (purple/aqua) - Padrão
|
|
||||||
- **Azul** (sgse-blue)
|
|
||||||
- **Verde** (sgse-green)
|
|
||||||
- **Laranja** (sgse-orange)
|
|
||||||
- **Vermelho** (sgse-red)
|
|
||||||
- **Rosa** (sgse-pink)
|
|
||||||
- **Verde-água** (sgse-teal)
|
|
||||||
- **Escuro** (dark)
|
|
||||||
- **Claro** (light)
|
|
||||||
- **Corporativo** (sgse-corporate)
|
|
||||||
|
|
||||||
4. **Validar Mudanças:**
|
|
||||||
- Ao clicar em um tema, a interface deve mudar imediatamente
|
|
||||||
- Verificar cores em:
|
|
||||||
- Sidebar
|
|
||||||
- Botões
|
|
||||||
- Cards
|
|
||||||
- Badges
|
|
||||||
- Links
|
|
||||||
- Backgrounds
|
|
||||||
|
|
||||||
5. **Salvar Tema:**
|
|
||||||
- Clique em "Salvar Tema" após selecionar
|
|
||||||
- Faça logout e login novamente
|
|
||||||
- O tema salvo deve ser aplicado automaticamente
|
|
||||||
|
|
||||||
6. **Testar Logout:**
|
|
||||||
- Ao fazer logout, o tema deve voltar ao padrão (roxo)
|
|
||||||
|
|
||||||
## Problemas Identificados e Corrigidos
|
|
||||||
|
|
||||||
1. ✅ Variáveis CSS incompletas nos temas customizados
|
|
||||||
2. ✅ Falta de `color-scheme` nos temas
|
|
||||||
3. ✅ localStorage removido (tema apenas no banco)
|
|
||||||
4. ✅ Tema padrão aplicado ao logout
|
|
||||||
5. ✅ Estrutura padronizada de todos os temas
|
|
||||||
|
|
||||||
## Próximos Passos para Validação
|
|
||||||
|
|
||||||
Se algum tema não estiver funcionando:
|
|
||||||
|
|
||||||
1. Verificar no console do navegador (F12) se há erros
|
|
||||||
2. Verificar o atributo `data-theme` no elemento `<html>` (deve mudar ao selecionar tema)
|
|
||||||
3. Verificar se as variáveis CSS estão sendo aplicadas (DevTools > Elements > Computed)
|
|
||||||
4. Testar em modo anônimo para garantir que não há cache
|
|
||||||
|
|
||||||
## Arquivos Modificados
|
|
||||||
|
|
||||||
- `apps/web/src/app.css` - Temas customizados melhorados
|
|
||||||
- `apps/web/src/lib/utils/temas.ts` - Funções de aplicação de temas
|
|
||||||
- `apps/web/src/routes/+layout.svelte` - Aplicação automática do tema
|
|
||||||
- `apps/web/src/routes/(dashboard)/perfil/+page.svelte` - Interface de seleção
|
|
||||||
- `apps/web/src/lib/components/Sidebar.svelte` - Reset de tema no logout
|
|
||||||
- `packages/backend/convex/schema.ts` - Campo temaPreferido
|
|
||||||
- `packages/backend/convex/usuarios.ts` - Função atualizarTema
|
|
||||||
|
|
||||||
3
packages/backend/.gitignore
vendored
3
packages/backend/.gitignore
vendored
@@ -1,3 +1,2 @@
|
|||||||
.env
|
.env*
|
||||||
.env.local
|
|
||||||
.convex/
|
.convex/
|
||||||
|
|||||||
Reference in New Issue
Block a user