diff --git a/docs/plans/lembranca-status-liderancas.md b/docs/plans/lembranca-status-liderancas.md new file mode 100644 index 00000000..f716a549 --- /dev/null +++ b/docs/plans/lembranca-status-liderancas.md @@ -0,0 +1,83 @@ +# Status de apoio "Lembrança" em lideranças + +Status: registrado +Atualizado em: 2026-08-11 +Issue: #661 +Priority: P1 +Model: composer-2.5 +Impeccable: B — encaixe no seletor/badge de status existentes +Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-36/canvases/plan-c119-ui-draft.canvas.tsx +Appetite: ~1 dia eng; um estado novo nos lugares onde status já existe +Responsável: — + +## Intenção + +No campo existe um perfil de liderança que não cabe em nenhum dos quatro status atuais: ela **não se compromete** com a campanha — já está comprometida com outra campanha ou tem outro impedimento — mas é **favorável** e vai trazer alguns votos por respeito/admiração ao mandato. + +Hoje o assessor que encontra esse caso só tem duas saídas ruins: marcar "Em disputa" (status pendente, que promete trabalho de abordagem que não existe) ou "Negativo" (que apaga a informação mais valiosa — ela é favorável e conta no planejamento). O status "Lembrança" é o registro de que a conversa terminou bem: não vamos conquistar o compromisso, mas podemos contar com os votos dela. + +## Persona e fluxo + +- **Persona / contexto:** assessor ou coordenador no campo (celular ou desktop), classificando lideranças após visitas e conversas. +- **Job principal:** registrar em um clique que a liderança não se compromete, mas é favorável — para a equipe parar de gastar esforço de abordagem e ainda contabilizá-la no planejamento. +- **Fluxo desejado:** abre a lista de lideranças → toca no badge de status da célula → escolhe "Lembrança" no popover → o badge muda e o valor salva sozinho (o auto-save da célula já existe) → a liderança sai dos "pendentes de abordagem" e segue visível como favorável nos filtros e nas leituras do Sollinha. +- **Anti-goals de produto:** não é um "negativo leve" — a liderança é favorável e os votos dela importam; não vira um estado que o assessor marque por dó. Não cria segundo eixo de classificação. + +### Esboço de fluxo (B) + +```text +[lista de lideranças — célula "Status"] → [toque no badge → popover com 5 opções] +→ [escolhe "Lembrança"] → [badge muda + salva sozinho] +→ [sai dos pendentes de abordagem] → [segue nos filtros/leituras como favorável] +``` + +## Objetivo e aceite + +- O assessor consegue marcar "Lembrança" em **todas** as superfícies onde os quatro status atuais aparecem (célula da lista, ficha, wizard, admin, filtro da lista de pessoas). +- Liderança "Lembrança" **não** aparece nos pendentes de abordagem — nem na ferramenta do Sollinha ("quais lideranças precisam ser abordadas") nem no critério de pendência usado por ela. (Guardrail: mesmo padrão de exclusão do "Negativo".) +- O badge "Lembrança" é visualmente distinto dos quatro atuais. +- O filtro por status (lista de lideranças e lista de pessoas) inclui o novo valor. +- O Sollinha reconhece e consegue responder sobre o status (rótulo na leitura de lideranças; pendências declaram o critério sem o novo estado). +- Nenhuma leitura existente quebra: agregações, filtros e tools continuam cobrindo o novo valor sem comportamento acidental (ex.: vazar para pendências). + +## Dados (intenção) + +- **Vou apresentar dados?** Não — o item adiciona um valor de classificação que já flui pelas listas existentes (filtro/facets). Nenhuma superfície de dado nova neste item. +- **Decisões desbloqueadas:** assessor/coordenador decide, por liderança, que ela é "favorável sem compromisso" em vez de pendente ou negativa. Essa classificação alimenta leituras futuras (planejamento, pendências) — sem métrica nova aqui. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/collections/Leadership.ts` (campo `supportStatus`, `type: 'select'`, enum no Postgres — precisa de migration); `src/lib/schemas/leadership.ts` (enum zod); `src/utilities/leadership/leadershipLabels.ts` (rótulos); `src/components/campaign/leadership/SupportStatusBadge.tsx` e `LeadershipListSupportStatusControl.tsx` (lista); formulários `LeadershipForm`/`LeadershipInternalForm`/`WizardLeadershipForm`; filtro de status em `src/utilities/people/peopleData.ts` (lista de pessoas); tools do Sollinha `src/utilities/ai/tools/getLeaderships.ts` (rótulos) e `getPendingLeaderships.ts` (critério de pendência — o novo valor não pode entrar no pré-filtro), além do texto do critério no `systemPrompt.ts`. +- **Precedente a olhar:** planos `simplificar-modelo-lideranca.md` e `autosave-status-lista-liderancas.md`; critério de pendência documentado em `sollinha-liderancas-pendentes-abordagem.md` (o critério "negativo nunca entra" é o guardrail a espelhar para "lembrança nunca entra"). +- **Risco de acoplamento:** o status é um enum no banco (migration de `ALTER TYPE`), consumido por lista, formulários, pessoas, admin e tools do Sollinha — qualquer superfície que itere os valores por engano precisa ser encontrada. Liderança é campo staff-only; leader lockdown não é afetado. + +## Dependências + +- Nenhuma dura. Suave: concluir após o C116–C118 (pessoas) se a fila assim o der, para não disputar as mesmas superfícies de lista. + +## Fora de escopo + +- Mudança de semântica dos status existentes (ex.: renomear "A abordar", reordenar) — outro item. +- Fluxo ou mecanismo novo para votos de "Lembrança" — decidido: votos declarados vão no mesmo campo já existente, sem fluxo próprio. +- Mudanças na UI além do encaixe do novo valor (sem redesign do seletor/badge). + +## Rabbit holes de produto + +- **Segundo eixo de classificação.** Se alguém "só completar" e achar que "favorável sem compromisso" precisa de um campo extra (ex.: intensidade, motivo), explode para matriz de combinações. **Corte neste item:** um valor a mais no enum existente; motivo/nota já tem `notes`. +- **Lembrança nos pendentes.** Incluir o novo estado no critério de "precisa ser abordada" geraria fila de trabalho que não existe. **Corte:** espelhar a exclusão do "Negativo" (fail-closed na query, não só na UI). +- **Tratamento de votos especializado.** "Contar votos por admiração" pode puxar lógica de pledge nova. **Corte neste item:** decidido no gate — os votos de uma "Lembrança" usam o mesmo campo de votos declarados já existente, sem fluxo próprio; nada de mecanismo novo. + +## Questões em aberto (produto) + +- **"Lembrança" pode ter votos registrados (pledge) no planejamento?** **Decidido (gate 2026-08-11):** sim — os votos vão no **mesmo campo já existente para votos declarados**, sem fluxo próprio. A liderança "Lembrança" se comporta como qualquer outra quanto a votos; o status é só classificação. +- **Onde "Lembrança" entra na ordem do seletor?** **Decidido (gate 2026-08-11):** entre "Em disputa" e "Negativo" — estado resolvido, não estado de engajamento. +- **"Lembrança" deve aparecer como opção em filtros rápidos/contadores de status?** **Decidido (gate 2026-08-11):** sim — como qualquer outro valor; filtros derivam dos valores existentes. +- **"Lembrança" conta como pendente de abordagem?** **Decidido (gate 2026-08-11):** não — espelha a exclusão do "Negativo" (fail-closed na query, não só na UI). + +## Referências + +- GitHub Issue #661 +- Canvas UI (gate): [plan-c119-ui-draft.canvas.tsx](/home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-36/canvases/plan-c119-ui-draft.canvas.tsx) +- `src/collections/Leadership.ts` (campo `supportStatus`), `src/lib/schemas/leadership.ts`, `src/utilities/leadership/leadershipLabels.ts` +- `docs/plans/sollinha-liderancas-pendentes-abordagem.md` (critério de pendência), `docs/plans/autosave-status-lista-liderancas.md` +- `src/utilities/ai/tools/getPendingLeaderships.ts` (pré-filtro de pendência) diff --git a/docs/plans/sollinha-drawer-fecha-link-mobile.md b/docs/plans/sollinha-drawer-fecha-link-mobile.md new file mode 100644 index 00000000..5de7fd73 --- /dev/null +++ b/docs/plans/sollinha-drawer-fecha-link-mobile.md @@ -0,0 +1,78 @@ +# Sollinha mobile: fechar o drawer ao navegar por link + +Status: registrado +Atualizado em: 2026-08-11 +Issue: #660 +Priority: P2 +Model: composer-2.5 +Impeccable: B — comportamento do drawer existente, sem nova superfície +Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-36/canvases/plan-b198-ui-draft.canvas.tsx +Appetite: ~0,5 dia eng; um encaixe no renderer de links do chat +Responsável: — + +## Intenção + +No celular, o Sollinha abre como drawer de 90% da tela. Quando o usuário toca num link entregue na resposta (ex.: "ver liderança", "ver município"), a navegação acontece — mas o drawer continua aberto cobrindo o destino. O usuário chega na página sem ver nada dela, tendo que fechar o drawer manualmente para só então olhar o conteúdo que o link prometia. + +A conversa já é preservada (o layout fica montado na navegação interna e o contexto vive na sessão da janela) — o que falta é só o drawer sair do caminho no momento do toque. + +## Persona e fluxo + +- **Persona / contexto:** assessor/liderança no celular usando o Sollinha em campo; toca num link interno para ir ver o recurso citado. +- **Job principal:** navegar para o destino do link com o drawer fechado, sem perder a conversa. +- **Fluxo desejado:** Sollinha responde com um link interno → toque no link → drawer fecha e a navegação segue no mesmo tab → página de destino visível → abrir o Sollinha de novo (FAB/header) → conversa e estado exatamente onde estavam (comportamento atual de restauração, inalterado). +- **Anti-goals de produto:** fechar o drawer não pode descartar a conversa (o contexto deve continuar salvo); o desktop não muda; link externo (nova aba) não fecha nada na aba de origem. + +### Esboço de fluxo (B) + +```text +[chat aberto no drawer — resposta com link /campanha…] → [toque no link] +→ [drawer fecha + navegação no mesmo tab] → [destino visível] +→ [abrir Sollinha de novo] → [conversa restaurada no estado anterior] +``` + +## Objetivo e aceite + +- Em mobile, tocar em um link interno (`/campanha…`) na resposta do Sollinha fecha o drawer enquanto navega — o usuário aterrissa no destino com a página visível. +- Reabrir o Sollinha na mesma janela restaura a conversa exatamente onde estava (comportamento atual preservado — nada de contexto perdido). +- Desktop inalterado: o painel lateral continua aberto ao navegar por link (comportamento atual). +- Link externo (http(s), abre em nova aba) não fecha o drawer da aba de origem. +- O estado persistido de "aberto/fechado" continua correto após o fechamento por navegação (reload não reabre o drawer sozinho). + +## Dados (intenção) + +Dados: N/A — sem superfície de dados; é comportamento de navegação de UI. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/components/campaign/shell/ai/CampaignAIChat.tsx` — o renderer de markdown (`markdownComponents.a`) que hoje decide link interno (`next/link`) vs externo (`externalLinkTarget`); hoje é um módulo estático sem acesso ao contexto do chat. O drawer (`CampaignAIDrawer.tsx`) reage ao `open` do `CampaignAISidebarContext` — fechar é chamar `setOpen(false)` no toque (provavelmente só quando `isMobile`). A persistência (`src/lib/sollinhaChatSession.ts`) e a restauração (`CampaignAISidebarContext.tsx`) não devem precisar mudar. +- **Precedente a olhar:** planos `sollinha-links-como-links.md` (B187/B188 — decisão de link interno vs externo) e `sollinha-contexto-sessao-janela.md` (B188 — persistência da conversa). +- **Risco de acoplamento:** o contexto `open` é compartilhado entre drawer (mobile) e painel (desktop) — o fechamento precisa ser condicionado ao viewport mobile para não fechar o painel no desktop. A marcação de intenção de usuário (`userToggledOpenRef`) distingue abertura por gesto de abertura por settle — o fechamento por navegação não deve se passar por decisão do usuário de forma que reabra em reload (o restore só reabre se `open: true` persistido, então fechar programaticamente já é seguro — validar na implementação). + +## Dependências + +- Nenhuma. + +## Fora de escopo + +- Fechar o drawer ao navegar por chips/sugestões ou pela busca global — só os links das respostas do Sollinha. +- Alterar a persistência da conversa ou o comportamento do painel desktop. +- Animar/fazer transição do fechamento — o drawer já fecha com o mecanismo existente. + +## Rabbit holes de produto + +- **Fechar para tudo.** Se alguém "só completar" e fechar o drawer em qualquer link (inclusive externos, que abrem em nova aba), a aba de origem perde o lugar de retomada da conversa. **Corte:** só links internos (navegação no mesmo tab) fecham; externos deixam o chat montado. +- **Reescrever a restauração.** A conversa já persiste e restaura; mexer no mecanismo de sessão para "resolver" o fechamento seria reabrir a raça de restore (OPS22/B188). **Corte:** fechar é só `setOpen(false)` no toque; sessão intocada. + +## Questões em aberto (produto) + +- **Links externos também devem fechar o drawer?** **Decidido (gate 2026-08-11):** não — o destino abre em nova aba; a aba de origem fica com o chat aberto para retomar a conversa. Só links internos (`/campanha…`) fecham. +- **O fechamento deve acontecer no clique?** **Decidido (gate 2026-08-11):** sim — o drawer sai do caminho no toque, antes de a página carregar. + +## Referências + +- GitHub Issue #660 +- Canvas UI (gate): [plan-b198-ui-draft.canvas.tsx](/home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-36/canvases/plan-b198-ui-draft.canvas.tsx) +- `src/components/campaign/shell/ai/CampaignAIChat.tsx` (markdownComponents.a), `CampaignAIDrawer.tsx`, `CampaignAISidebarContext.tsx` +- `src/lib/ai/markdownLinks.ts` (decisão link interno/externo), `src/lib/sollinhaChatSession.ts` +- `docs/plans/sollinha-links-como-links.md`, `docs/plans/sollinha-contexto-sessao-janela.md`