Clayton BorgesDESENVOLVEDOR
← ← Todos os Projetos

Hábitos – Rastreador de Hábitos e Terapia

2026
Full-Stack Web Application / Personal Tool

Rastreador compartilhado de hábitos e progresso terapêutico para paciente e terapeuta. App full-stack Next.js 16 com interfaces baseadas em perfil, calendário semanal de hábitos, rastreamento de metas com observações clínicas e tracker de candidaturas com gate de responsabilidade comportamental — tudo com auth JWT customizado, Prisma e PostgreSQL.

Visão Geral

Hábitos é um app full-stack de rastreamento de hábitos e terapia criado para um cenário real específico: um paciente em terapia gerenciando medicações diárias, rotina de sono, autocuidado e uma busca ativa de emprego — com seu terapeuta precisando de visibilidade estruturada sobre o progresso semanal sem exigir um canal de comunicação separado. A interface do paciente é um calendário semanal com hábitos fixos diários (medicações, horário de acordar, dormir) e tarefas personalizadas; a interface do terapeuta adiciona uma view Planejador que exibe as notas do paciente em cada meta junto a um espaço para observações clínicas e ajustes planejados para a semana seguinte.

Problema

Pacientes em terapia geralmente rastreiam o progresso por check-ins verbais ou documentos compartilhados informais sem estrutura para hábitos diários, histórico de metas ou comparações semana a semana. Um paciente gerenciando medicações, padrões de sono e uma busca de emprego simultaneamente tem variáveis demais para um rastreamento informal. O terapeuta não tem visibilidade passiva sobre a semana do paciente — só vê o que o paciente lembra de relatar na sessão.

Objetivo

Construir um app compartilhado de dois usuários que: (1) dê ao paciente um calendário semanal estruturado com responsabilidade por hábitos; (2) dê ao terapeuta uma view somente-leitura do progresso de metas com espaço para observações clínicas; (3) aplique responsabilidade comportamental pelo gate de candidaturas; (4) persista estado de sessão após refresh sem biblioteca de auth de terceiros; (5) faça deploy com migrações de schema sem downtime no Vercel.

Meu Papel
  • ◈Desenvolvedor solo — arquitetura, full-stack, UX, modelagem de dados, deployment
Stack
Next.jsReact 19TypeScriptPrismaPostgreSQLJWTbcryptjsTailwind CSS v4shadcn/uidnd-kitdate-fnsVercel
Decisões Técnicas
  1. 01Server Components do App Router como camada de busca de dados — as páginas consultam o Prisma diretamente e passam dados como props para Client Components. Isso elimina estados de loading no client para renders iniciais e mantém a verificação de auth no servidor, de modo que uma requisição não autenticada é redirecionada antes de qualquer HTML ser enviado.
  2. 02Auth JWT customizado com jose (sem NextAuth) — sessões são JWTs HS256 em cookie httpOnly, SameSite=Lax com TTL de 24h. A decisão de construir o auth do zero foi deliberada: o app tem exatamente dois usuários e uma biblioteca de terceiros adicionaria abstração e configuração desnecessárias.
  3. 03Prisma migrate deploy executa automaticamente em cada build do Vercel — o script de build é prisma generate && prisma migrate deploy && next build, garantindo que o schema de produção esteja sempre sincronizado com o código sem etapa manual de migração.
  4. 04Padrão upsert do GoalProgress — em vez de registros separados de paciente e terapeuta por meta por semana, uma única linha GoalProgress armazena tanto patientNotes quanto therapistNotes. O endpoint /api/progresso faz findOrCreate por (goalId + weekStart), permitindo que os dois usuários escrevam no mesmo registro sem coordenação.
  5. 05Ordenação de horários após meia-noite — horários 00:00–05:59 recebem chave de ordenação h + 24, então um horário de dormir às 02:00 aparece corretamente após 23:59. Sem isso, hábitos de sono de notívagos seriam agrupados no bloco de madrugada.
  6. 06Setter de horário semanal em lote — um único PATCH /api/semanas/[scheduleId] usa prisma.task.updateMany para propagar um horário para todos os 7 dias de uma vez, evitando 7 chamadas de API separadas do client.
  7. 07Gate de candidaturas aplicado no nível dos dados — a tarefa de candidatura não pode ser marcada como concluída até que 3 candidaturas tenham sido registradas no dia. O botão fica desabilitado com contador ('X vagas restantes'). Responsabilidade comportamental embutida no modelo de dados, não apenas na UI.
  8. 08dnd-kit instalado e Task.sortOrder scaffolded mas não conectado — a biblioteca de drag-and-drop está no package.json e o modelo Task tem um integer sortOrder com suporte completo a PATCH, deliberadamente scaffolded para um recurso futuro de reordenação sem adicionar complexidade ao release atual.
  9. 09Fix de font-size para iOS Safari aplicado globalmente — todos os inputs têm style={{ fontSize: '16px' }} inline, a única forma confiável de evitar o auto-zoom do iOS Safari ao focar que quebraria o layout do calendário mobile.
O Que Aprendi
  • →Construir auth JWT do zero com jose não é significativamente mais difícil do que configurar o NextAuth para um app de base de usuários pequena — e produz um modelo mental muito mais simples de como as sessões funcionam, tornando o debugging de issues de auth direto.
  • →O padrão upsert do GoalProgress (find-or-create por chave composta) é limpo para co-autoria de registros entre perfis, mas exige que a API seja verdadeiramente idempotente — chamá-la duas vezes para o mesmo (goalId, weekStart) não deve sobrescrever o campo do outro perfil, o que exige usar update com apenas o campo que mudou, não um replace completo.
  • →Executar prisma migrate deploy no build do Vercel é confiável para evolução de schema sem downtime, mas exige que todas as migrations sejam retrocompatíveis — uma migration destrutiva que passa localmente bloqueará o deploy de produção sem rollback automático.
  • →Projetar para um usuário real específico — não uma persona — produz decisões de UX muito mais intencionais. A ordenação pós-meia-noite, o setter semanal em lote e o gate de candidaturas vieram diretamente de observar o fluxo real do paciente, não de produto abstrato.
  • →Server Components funcionam melhor quando a forma dos dados corresponde à árvore de renderização. Qualquer incompatibilidade — filtrar ou transformar dados no client — é melhor resolvida retornando mais dados do servidor e filtrando no Client Component, não adicionando rotas de API extras.
Status

Ativo — em uso diário pelo paciente e terapeuta para quem foi construído