208 lines
7.0 KiB
Markdown
208 lines
7.0 KiB
Markdown
# FightRate — Plano Data-First para histórico, gráficos e UI profissional
|
||
|
||
## Situação crítica
|
||
|
||
O feedback atual mostra dois bugs de produto, não apenas problema visual:
|
||
|
||
1. O gráfico tem espaço vazio demais e exige rolar horizontalmente por muito tempo até encontrar leituras.
|
||
2. O histórico não está confiável: dados parecem não salvar ou não ficam acessíveis depois.
|
||
|
||
Conclusão: dados e persistência devem voltar a ser prioridade absoluta antes de polimento visual.
|
||
|
||
## Objetivo
|
||
|
||
Transformar o FightRate em um app data-first:
|
||
|
||
- Cada batimento recebido deve ser persistido localmente.
|
||
- Cada treino deve ter uma janela temporal confiável.
|
||
- Histórico deve mostrar dados reais recuperáveis.
|
||
- Gráficos devem usar domínio baseado apenas nas amostras reais disponíveis.
|
||
- Não pode existir scroll infinito/área vazia até achar leitura.
|
||
- Interface deve priorizar dados e navegação histórica, seguindo o design system Combat Lab.
|
||
|
||
## Skills aplicadas
|
||
|
||
- `fightrate-quality-loop`: spec → auditoria → implementação em fatias → build.
|
||
- `systematic-debugging`: encontrar causa raiz antes de corrigir.
|
||
- `test-driven-development`: criar testes de regressão para domínio de gráfico/histórico.
|
||
- `design-system` e `ui-ux-pro-max`: estado vazio útil, chart/data UX, consistência visual.
|
||
|
||
## Hipóteses a investigar
|
||
|
||
### H1 — Domínio X do Vico está incluindo janela cheia, não amostras reais
|
||
|
||
Sintoma: usuário rola para a direita “para sempre” até encontrar leituras.
|
||
|
||
Possível causa:
|
||
|
||
- O gráfico usa `windowStartMillis`/`windowEndMillis` grandes, mas as amostras estão concentradas em pequeno trecho.
|
||
- O X é calculado relativo à janela selecionada, então se a leitura está no fim da janela, sobra um enorme vazio à esquerda/direita.
|
||
- O scroll/zoom inicial pode estar mostrando início/fim errado.
|
||
|
||
Correção esperada:
|
||
|
||
- Domínio padrão do gráfico deve ser `min(sample.timestamp)` → `max(sample.timestamp)`.
|
||
- A janela selecionada filtra dados, mas não força o domínio visual a incluir todo o período vazio.
|
||
- Exibir texto com a janela consultada e o intervalo real encontrado.
|
||
|
||
### H2 — Histórico busca janela errada ou estado não atualiza após persistência
|
||
|
||
Sintoma: dados históricos não aparecem/salvam.
|
||
|
||
Possíveis causas:
|
||
|
||
- `publishLiveSample` salva, mas histórico não recarrega após cada nova amostra.
|
||
- `selectedPeriod` usa uma janela curta ou errada.
|
||
- Sessão finalizada tem samples com `sessionId` nulo.
|
||
- `finishCurrentSession` não associa amostras sem sessão dentro da janela.
|
||
- UI exibe `historySamples`, mas dados reais ficam apenas em `visibleSamples`/`dailySamples`.
|
||
|
||
Correções esperadas:
|
||
|
||
- Confirmar que `HeartRateSampleStore.save` recebe `sessionId` ativo.
|
||
- Confirmar que `samplesForSession` funciona depois do finish.
|
||
- Confirmar que `samplesBetween(start, end)` recupera dados persistidos.
|
||
- Atualizar estado de histórico depois de salvar amostra e depois de finalizar treino.
|
||
|
||
### H3 — UI não oferece navegação histórica suficiente
|
||
|
||
Sintoma: “os dados devem poder ser vistos de qualquer maneira”.
|
||
|
||
Correção esperada:
|
||
|
||
- Histórico com modos claros:
|
||
- Últimos 10 min
|
||
- Última hora
|
||
- Hoje
|
||
- Sessão atual/finalizada
|
||
- Se janela consultada não tem dados, mostrar “nenhum dado nessa janela” e sugerir janela que tem dados.
|
||
- Se existe sessão finalizada, exibir relatório e gráfico daquela sessão.
|
||
|
||
## Arquitetura desejada
|
||
|
||
### Domínio puro novo/revisado
|
||
|
||
Criar `ChartDataWindow.kt` se necessário.
|
||
|
||
Responsabilidades:
|
||
|
||
- Receber samples + janela consultada.
|
||
- Retornar:
|
||
- `visibleSamples`: somente amostras reais e úteis.
|
||
- `dataStartMillis` / `dataEndMillis`: domínio real do gráfico.
|
||
- `queryStartMillis` / `queryEndMillis`: janela consultada.
|
||
- `status`: Empty, SinglePoint, Ready.
|
||
- resumo: duração real, média, min, max, densidade.
|
||
- Não depender de Compose/Vico/Android.
|
||
|
||
### Persistência
|
||
|
||
Reutilizar `HeartRateSampleStore`.
|
||
|
||
Necessário auditar/corrigir:
|
||
|
||
- insert de samples;
|
||
- active session id;
|
||
- associação de sample com session id;
|
||
- queries por sessão;
|
||
- queries por período;
|
||
- refresh após write.
|
||
|
||
Não fazer migração destrutiva.
|
||
|
||
### ViewModel
|
||
|
||
- Não mover BLE para ViewModel.
|
||
- Garantir que o estado de histórico seja atualizado após persistência.
|
||
- Expor chart windows já preparados para UI.
|
||
- Evitar que UI calcule regras complexas.
|
||
|
||
### UI / Gráfico
|
||
|
||
- `HeartRateLineChart` deve receber domínio real de dados.
|
||
- X deve ser relativo a `dataStartMillis`, não ao começo da janela vazia.
|
||
- Se `samples.size < 2`, não chamar Vico.
|
||
- Mostrar estado vazio editorial.
|
||
- Mostrar uma linha de contexto:
|
||
- “Consultado: Hoje”
|
||
- “Dados encontrados: 14:03–14:17”
|
||
- Zoom/scroll habilitado, mas sem espaço fantasma.
|
||
- Não usar Canvas custom chart.
|
||
|
||
## Testes obrigatórios
|
||
|
||
### `ChartDataWindowTest`
|
||
|
||
1. Janela grande com dados no fim não gera domínio vazio gigante.
|
||
2. Sem dados retorna Empty.
|
||
3. Um ponto retorna SinglePoint.
|
||
4. Dados fora de ordem são ordenados.
|
||
5. Dados filtrados preservam timestamp real.
|
||
6. Downsampling mantém primeiro e último ponto.
|
||
|
||
### Store/persistência
|
||
|
||
Se for viável em teste local unitário:
|
||
|
||
1. Salvar sample e recuperar por período.
|
||
2. Salvar sample em sessão ativa e recuperar por session id.
|
||
3. Finalizar sessão e recuperar samples da sessão.
|
||
|
||
Se não for viável sem Robolectric/instrumented test, adicionar diagnóstico via código e validar pelo fluxo ViewModel/build.
|
||
|
||
## Implementação em fatias
|
||
|
||
### Fase 1 — Auditoria e causa raiz
|
||
|
||
- Ler `HeartRateSampleStore`.
|
||
- Ler `LiveHeartRateViewModel`.
|
||
- Ler `LiveHeartRateUiState`.
|
||
- Ler `HomeScreen` chart/history.
|
||
- Localizar onde sample é salvo.
|
||
- Localizar onde `historySamples` é atualizado.
|
||
- Localizar como Vico define X/domain.
|
||
|
||
### Fase 2 — Domínio de janela de gráfico
|
||
|
||
- Criar `ChartDataWindow.kt`.
|
||
- Criar `ChartDataWindowTest.kt`.
|
||
- Garantir domínio real `dataStartMillis`→`dataEndMillis`.
|
||
- Garantir estado Empty/SinglePoint/Ready.
|
||
|
||
### Fase 3 — Persistência e refresh
|
||
|
||
- Corrigir sample/session association se necessário.
|
||
- Corrigir reload de histórico depois de salvar/finalizar.
|
||
- Adicionar método de query se faltar.
|
||
|
||
### Fase 4 — Gráfico profissional
|
||
|
||
- Atualizar `HeartRateLineChart` para usar domínio real.
|
||
- Remover scroll fantasma.
|
||
- Adicionar zoom/scroll Vico com domínio real.
|
||
- Remover render de gráfico vazio.
|
||
- Mostrar resumo consultado vs encontrado.
|
||
|
||
### Fase 5 — Interface data-first
|
||
|
||
- Histórico como centro de dados, não card secundário.
|
||
- Sessões e dados recentes com hierarquia clara.
|
||
- Evitar dezenas de cards iguais.
|
||
- Estados vazios úteis e específicos.
|
||
|
||
### Fase 6 — Validação final
|
||
|
||
- `./gradlew :app:testDebugUnitTest :app:assembleDebug`
|
||
- Atualizar Obsidian roadmap.
|
||
- Enviar APK Telegram.
|
||
|
||
## Critérios de sucesso
|
||
|
||
- Usuário abre Histórico e vê dados reais sem procurar em espaço vazio.
|
||
- O gráfico começa/termina no intervalo real dos dados encontrados.
|
||
- Sem dados: nenhum gráfico é desenhado.
|
||
- Dados salvos são recuperáveis por período e por sessão.
|
||
- Testes de domínio passam.
|
||
- Build completo passa.
|
||
- APK enviado.
|