From 0d5bf274ff1466dfe512e357daef3b234c7a908c Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Sat, 8 Aug 2026 17:20:48 -0300 Subject: [PATCH] =?UTF-8?q?docs(plans):=20C92-C95=20=E2=80=94=20agenda:=20?= =?UTF-8?q?corrigir=20feed=20link,=20sem-filtros,=20combobox=20e=20header?= =?UTF-8?q?=20do=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...c92-corrigir-criacao-link-import-agenda.md | 72 +++++++++++++++ .../c93-gerar-link-import-sem-filtros.md | 70 +++++++++++++++ ...94-agenda-combobox-header-acoes-rapidas.md | 87 +++++++++++++++++++ .../c95-agenda-seletor-modo-visualizacao.md | 81 +++++++++++++++++ 4 files changed, 310 insertions(+) create mode 100644 docs/plans/c92-corrigir-criacao-link-import-agenda.md create mode 100644 docs/plans/c93-gerar-link-import-sem-filtros.md create mode 100644 docs/plans/c94-agenda-combobox-header-acoes-rapidas.md create mode 100644 docs/plans/c95-agenda-seletor-modo-visualizacao.md diff --git a/docs/plans/c92-corrigir-criacao-link-import-agenda.md b/docs/plans/c92-corrigir-criacao-link-import-agenda.md new file mode 100644 index 00000000..5e9ebbf7 --- /dev/null +++ b/docs/plans/c92-corrigir-criacao-link-import-agenda.md @@ -0,0 +1,72 @@ +# C92 — Link de import da agenda: corrigir criação do feed (erro ao gerar) + +Status: rascunho +Atualizado em: 2026-08-08 +Issue: #436 +Priority: P1 +Model: composer-2.5 +Model-local: deepseek-v4-flash-high +Impeccable: A — N/A sem UI (mesmo diálogo; comportamento corrigido) +Canvas UI: N/A — sem UI +Appetite: ~0,5–1 dia eng; um outcome verificável — staff gera o link de import nomeando o feed +Responsável: — + +## Dependência rápida + +- **Sucessor de C16 (#392, _done/in-prod_):** o fluxo de "link de import" foi entregue mas a criação quebra no ar. Plano de C16 imutável; este é um item novo. + +## Intenção + +A sincronização com Google Calendar (C16) foi entregue, mas **ninguém consegue gerar o link**: ao digitar um nome e confirmar, o app responde "Não foi possível criar o feed de calendário." A funcionalidade fica inútil exatamente onde deveria destravar o valor (assinar o recorte da agenda no GCal da equipe). Queremos que gerar o link funcione para todo o staff, com o fluxo real (nomear → link → copiar/revogar) intacto. + +**Causa raiz verificada localmente (repro):** a collection `calendarFeed` marca `secretSlug`/`createdBy` como campos de sistema com acesso de escrita restrito a admin do Payload (`canSetCalendarFeedSystemField`). A server action cria o feed com `overrideAccess: false` e actor `campaignUser` — o create falha (campos "obrigatórios" não graváveis) e o `catch` engole o erro numa mensagem genérica. Repro: create OK com `overrideAccess: true`; falha com `overrideAccess: false` + `user` `campaignUser`. O executor reconfirma e decide o ajuste (acesso do campo vs forma do create) — aqui fica só o sintoma e o conjunto afetado. + +## Persona e fluxo + +- **Persona / contexto:** coordenador ou assessor (staff) na agenda, montando um recorte ("só deputado presente", "só meu município") para a equipe acompanhar no Google Calendar. +- **Job principal:** obter o link de import do filtro atual para colar no GCal. +- **Fluxo desejado:** aplica filtro → "Link de import" → nomeia o feed → recebe URL → copia/revoga. **Hoje morre no passo "nomeia".** +- **Anti-goals de produto:** nenhum novo — manter C16: sem sync bidirecional; Teqo SoT; fail-closed do acesso no read do feed. + +## Objetivo e aceite + +- Coordenador, assessor e candidato conseguem **gerar o link de import** após nomear o feed (sem erro). +- O link gerado abre o feed correto do recorte (endpoint `/campanha/agenda/ical/[secret]` responde). +- Listar feeds ativos e **revogar** continuam funcionando. +- O erro real não é mais engolido numa mensagem genérica sem rastro — o fallback segue amigável, mas sem mascarar quebra de acesso/configuração. +- Não vaza municípios fora do escopo do criador (invariante de C16 preservado). + +## Dados (intenção) + +- **Vou apresentar dados?** Não. +- **Decisões desbloqueadas:** "posso assinar o recorte da agenda no Google?" — desbloqueada ao funcionar. +- **Forma:** N/A. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/utilities/access/calendarFeeds.ts` (acesso de escrita dos campos de sistema `secretSlug`/`createdBy`), `src/app/(campaign)/campanha/actions/calendarFeed.ts` (`createCalendarFeedLinkRecord`, `overrideAccess`/`user`), `src/collections/CalendarFeed.ts` (definição dos campos de sistema), com repro provável em `src/utilities/calendarFeed.ts`. +- **Precedente a olhar:** como outras server actions da campanha gravam campos `systemStampedActorField` com actor `campaignUser` (ex. `supporter`/`activity`) — o padrão que já funciona no repo. +- **Risco de acoplamento:** acesso é área "nunca economizar" (AGENTS.md) — mudança mínima e com teste de acesso (staff cria; leader/especulador não; read do feed segue fail-closed). + +## Dependências + +- Nenhuma dura. Sequencia antes de C93 (sem criação funcionando, falar de "sem filtros" é prematuro). + +## Fora de escopo + +- Permitir gerar sem filtros → **C93**. +- Layout do botão/diálogo → **C94**. + +## Rabbit holes de produto + +- **"Completar" o fix virando refactor de access.** Se alguém "só arrumar": mexer em todos os campos de sistema e padrões de access do repo. **Corte neste item:** ajuste local no `calendarFeed` (campo/action), validado por repro e teste de acesso. + +## Questões em aberto (produto) + +- Nenhuma — bug com causa raiz reproduzida; decisão é de execução. + +## Referências + +- GitHub Issue #392 (C16, intenção imutável) +- `docs/plans/sync-teqo-google-calendar.md` / `-impl.md` +- Repro local: create OK com `overrideAccess:true`; falha com `overrideAccess:false` + `user: campaignUser` (`calendarFeed` → campos `secretSlug`/`createdBy` com `setAccess` só admin) diff --git a/docs/plans/c93-gerar-link-import-sem-filtros.md b/docs/plans/c93-gerar-link-import-sem-filtros.md new file mode 100644 index 00000000..9c3a9bbf --- /dev/null +++ b/docs/plans/c93-gerar-link-import-sem-filtros.md @@ -0,0 +1,70 @@ +# C93 — Gerar link de import sem filtros (agenda completa do escopo) + +Status: rascunho +Atualizado em: 2026-08-08 +Issue: #437 +Priority: P2 +Model: composer-2.5 +Model-local: deepseek-v4-flash-high +Impeccable: B — encaixe no botão "Link de import" (ativa ação hoje desabilitada; sem rearranjo de layout) +Canvas UI: coberto pelo plan-c94-ui-draft.canvas.tsx (mesmo botão, estado-alvo do C94) +Appetite: ~0,5 dia eng; um outcome verificável — com zero filtros, gera-se o link da agenda completa do meu escopo +Responsável: — + +## Dependência rápida + +- **Sucessor de C16 (#392, _done/in-prod_):** a intenção de C16 condicionava o link a um filtro ativo; produto agora quer o caso "sem filtros". Plano de C16 imutável. +- **Dura (soft): [C92](c92-corrigir-criacao-link-import-agenda.md)** — sem a criação funcionando, esse fluxo não se valida. + +## Intenção + +Hoje o botão "Link de import" fica **desabilitado sem filtros**: quem quer simplesmente assinar **toda** a agenda da campanha no Google não consegue. Na mesa do coordenador, "agenda completa do meu escopo" é um desejo legítimo — o time inteiro acompanhando tudo que está em Teqo, sem montar filtro nenhum. Queremos destravar o feed **sem filtro**: o recorte passa a ser tudo que o criador tem permissão de ver (portfólio acessível), mantendo revogação e fail-closed iguais. + +## Persona e fluxo + +- **Persona / contexto:** coordenador quer o calendário completo da campanha (ou assessor, todo o portfólio dos municípios que administra) sempre atualizado no GCal da equipe. +- **Job principal:** gerar o link de import da agenda inteira — sem precisar inventar um filtro. +- **Fluxo desejado:** abre a agenda (sem filtros) → "Link de import" habilitado → nomeia → copia/revoga. **Hoje:** botão cinza, sem ação. +- **Anti-goals de produto:** feed "sem filtro" não é um backdoor — continua valendo o escopo do criador e a fail-closed (perdeu acesso a um município → feed para de incluir; criador desativado → feed para de servir). + +## Objetivo e aceite + +- Com **nenhum filtro ativo**, o botão "Link de import" permite gerar o link. +- O feed sem filtro cobre a agenda completa **dentro do escopo de leitura do criador** (coordinator/candidate = tudo; advisor = municípios administrados). +- Revogar/listar e o endpoint de leitura seguem com acesso fail-closed (invariante de C16). +- Sem PII nova; sem Consent novo (mesma base de C16). + +## Dados (intenção) + +- **Vou apresentar dados?** Não. +- **Decisões desbloqueadas:** "posso assinar a agenda completa do meu escopo?" — sim. +- **Forma:** N/A. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/components/campaign/activity/ActivityAgendaFilters.tsx` (gate `hasFilters` → disabled do botão) e `src/utilities/calendarFeed.ts` / action `src/app/(campaign)/campanha/actions/calendarFeed.ts` (construir feed sem filtros = só o escopo do criador). +- **Precedente a olhar:** intenção e impl de C16 (`docs/plans/sync-teqo-google-calendar*`), que já previam `filterMunicipality` vazio = "todos os municípios do escopo do criador" (descrição do campo na collection). +- **Risco de acoplamento:** o layout do botão muda no C94 — este item só destrava o **estado permitido**; o executor deve reconciliar com o estado final do C94. + +## Dependências + +- C92 (soft — validação pós-fix). + +## Fora de escopo + +- Layout do botão / ícone no header / ação rápida → **C94**. +- Escolher "tudo" vs "recorte" por UX (pedido de confirmação) → não; o feed sem filtro é direto. + +## Rabbit holes de produto + +- **Feed sem filtro virando "exportar tudo para sempre".** Se alguém "só completar": janela temporal ilimitada ou sem fail-closed. **Corte neste item:** mantém janela deslizante e access por leitura (decisões de C16 intactas). + +## Questões em aberto (produto) + +- **Emparelhar com o estado-alvo do C94?** O botão do C94 vira ícone no header. **Recomendação:** C93 pode entrar antes (habilita o botão atual) e o C94 herda o estado habilitado; sem ordem obrigatória. _(assumido — validar)_ + +## Referências + +- GitHub Issue #392 (C16, intenção imutável) +- Canvas UI do C94: `plan-c94-ui-draft.canvas.tsx` +- Campo `filterMunicipality` da collection `calendarFeed`: "Se vazio, inclui todos os municípios do escopo do criador" (base pronta para o sem-filtro) diff --git a/docs/plans/c94-agenda-combobox-header-acoes-rapidas.md b/docs/plans/c94-agenda-combobox-header-acoes-rapidas.md new file mode 100644 index 00000000..15f4e926 --- /dev/null +++ b/docs/plans/c94-agenda-combobox-header-acoes-rapidas.md @@ -0,0 +1,87 @@ +# C94 — Agenda: filtro combobox único, header compacto (ícones) e ações no FAB mobile + +Status: rascunho +Atualizado em: 2026-08-08 +Issue: #438 +Priority: P1 +Model: cursor-grok-4.5-medium +Model-local: deepseek-v4-high +Impeccable: C — redesign local da toolbar/header da agenda (`/campanha/agenda`) +Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-Code-teqo/canvases/plan-c94-ui-draft.canvas.tsx +Appetite: ~1–1,5 dia eng; um outcome verificável — toolbar consolidada (combobox único + ícones) e uma só entrada "Nova atividade"/"Link de import" por viewport +Responsável: — + +## Intenção + +A barra da agenda (C15) foi construída como 3 seletor­es avulsos (Município, Tag, "Deputado presente") + botões de ação, e hoje **duplica** caminhos: no mobile o usuário vê dois botões de criação na página **e** as mesmas ações no FAB; o "Link de import" vive dentro do card de filtros. Resultado: superfície sem hierarquia e fluxo redundante. Queremos consolidar: + +1. **Um filtro combobox único** (chassis omnibox já usado em `/campanha/municipios` e nas listas) no lugar dos 3 seletor­es — com chips removíveis e sugestões agrupadas. +2. **Mobile:** tirar os botões de criação da página; a entrada vira **ação rápida no FAB**. "Link de import" também entra como ação rápida. +3. **Desktop:** um **ícone "+" (Nova atividade)** logo à direita do combobox, e o **"Link de import" como botão-ícone no header do app** (barra superior junto do AI chat e das notificações, agenda-contextual). + +## Persona e fluxo + +- **Persona / contexto:** coordenador e assessor de mesa (desktop) e de campo (mobile) usando a agenda para planejar e acompanhar compromissos. +- **Job principal:** filtrar e criar/assinar de forma consistente, com um caminho só por gesto. +- **Fluxo desejado (desktop):** combobox filtra (chips) → "+" ao lado abre o formulário de nova atividade com o recorte → ícone de calendário abre o diálogo de "Link de import" (mantém nomear → copiar → revogar). +- **Fluxo desejado (mobile):** combobox filtra → FAB abre o drawer → ações: "Nova atividade", "Link de import", "Planejar giro" (as existentes de lista permanecem). +- **Anti-goals de produto:** manter a criação inline por clique no slot (C91) e o fluxo de giro (compositor) intactos; não virar um header de segundo nível com stack de sub-nav. + +### Esboço de fluxo (C) + +```text +desktop: [header app: título agenda … AI chat][Link de import][notif.] [toolbar: combobox][carrinho: giro][+ nova atividade] +mobile : [top bar app: título agenda … Link de import/FAB][notif.] + FAB → drawer [Nova atividade][Link de import][Planejar giro] +``` + +## Objetivo e aceite + +- Os 3 filtros (Município, Tag, Deputado presente) ficam **num combobox único** com chips removíveis e "Limpar" (chassis omnibox), substituindo os seletor­es nativos e o botão "Limpar filtros". +- **Toolbar da página (desktop):** `[combobox] [ícone carrinho: Planejar giro] [ícone "+": Nova atividade]` — decisão de produto confirmada no gate (`combobox→giro→+`). +- **"Link de import" no header do app (desktop), não na toolbar:** vira **botão-ícone na barra superior** (`CampaignDesktopHeader`), no cluster à direita onde já ficam o botão do Sollinha/AI chat e o de notificações — **agenda-contextual** (só aparece na rota `/campanha/agenda`). O diálogo nomear→copiar→revogar permanece igual. +- **Mobile:** nenhum botão de criação no corpo da página; "Nova atividade" e "Link de import" como ações rápidas no drawer do FAB (com o filtro atual); "Link de import" abre o mesmo diálogo como **sheet** (decisão confirmada no gate). +- O estado do filtro segue refletido na URL (padrão de navegação das listas); reload e share continuam consistentes. +- Sem regressão: criação inline no slot (C91), giros, revogação de feeds, leader lockdown. + +## Dados (intenção) + +- **Vou apresentar dados?** Não (filtro = navegação/URL, não apresentação de dados). +- **Decisões desbloqueadas:** "vejo/dou manutenção ao recorte da agenda com um gesto só". +- **Forma:** adiada (chips/sugestões do omnibox já é o padrão do repo). + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/components/campaign/activity/ActivityAgendaFilters.tsx` (vira o adaptador do combobox), `src/utilities/activityOmnibox.ts` (precedente de chips/sugestões para o estado de agenda — reuso ou adaptação), `src/components/campaign/shared/CampaignListOmnibox.tsx` (chassis), `src/app/(campaign)/campanha/(app)/agenda/page.tsx` (toolbar `[combobox][giro][+]`), e o registro de ações rápidas (`src/lib/activityQuickActions.ts` / `campaignQuickAction*`) para "Link de import" no mobile. +- **Header do app (desktop):** o "Link de import" entra como controle **agenda-contextual** em `src/components/campaign/shell/CampaignDesktopHeader.tsx` (cluster à direita, ao lado de `CampaignAIHeaderButton` e do sino). O mesmo mecanismo de chrome por-página usado hoje pelo título (`CampaignPageChromeDisplay`/contexto) serve de pista para o slot contextual — o executor decide o encaixe menos invasivo (slot no header vs renderizar no shell pela rota). +- **Precedente a olhar:** B127/B138 (omnibox nas listas), B126/B132 (FAB de ações rápidas), B120 (combobox mobile em municípios). +- **Risco de acoplamento:** o header do app é **compartilhado por todas as rotas** `/campanha` — qualquer controle novo precisa ser agenda-contextual para não poluir as demais telas; coordenar com C95 (mesmo header, mesmo slot). C91 inline create só adiciona overlay; sem conflito de intent. Coordenar com C92/C93 (dialog de feed) para o ícone não ser porta morta. + +## Dependências + +- C92 (soft — o ícone de feeed só vale com criação funcionando). + +## Fora de escopo + +- Gerar feed sem filtros → **C93** (o ícone reflete apenas o estado habilitado/desabilitado do momento). +- Redesign do calendário em si (C15) e do overlay de criação inline (C91). +- Mudanças no compositor de giro. + +## Rabbit holes de produto + +- **Combobox vira "omnibox de busca global" da agenda.** Se alguém "só completar": busca por texto livre, presets de janela, múltiplos municípios simultâneos. **Corte neste item:** Município (único) + Tag + Deputado presente, como hoje; sem busca textual nem multi-seleção. +- **Header vira barra de navegação.** Manter no máximo 3 controles + o botão de giro secundário; nada de stacks de sub-nav. + +## Questões em aberto (produto) + +- **"Planejar giro" no desktop?** **Decidido no gate:** vira **ícone carrinho**, na toolbar, entre o combobox e o "+" (ordem `[combobox][giro][+]`). Fora de questão. +- **"Deputado presente" como chip?** **Decidido no gate:** chip bool no combobox. Fora de questão. +- **Mobile: rápida de feed mantém o diálogo atual?** **Decidido no gate:** sim, como **sheet**. Fora de questão. +- **Onde mora o "Link de import" desktop?** **Decidido no gate:** no **header do app** (cluster à direita, junto do AI chat/notificações), agenda-contextual — não na toolbar. Fora de questão. +- **Chip de município permite trocar sem remover?** **Opções:** A) com sugestões marcando o ativo ao reabrir | B) só remove→re-adiciona. **Recomendação:** indistinguível ao usuário fora do detalhe; deixa o executor escolher a mecânica barata que satisfaz (A). _(assumido — validar)_ +- **Ordem no header do app (desktop):** **Decidido no gate** — `[título …][Semana ▾ (C95)][Link de import][Notificações][IA]`. Fora de questão. + +## Referências + +- GitHub Issue #392 (C16 — sync/feed), #390 (C15 — agenda), #428 (C91 — criar inline) +- Canvas UI (gate): `/home/fsolla/.cursor/projects/home-fsolla-Code-teqo/canvases/plan-c94-ui-draft.canvas.tsx` +- Precedente: `CampaignListOmnibox` (B127), `atividades-omnibox-presets-busca` (B138), FAB de ações rápidas (B126/B132) diff --git a/docs/plans/c95-agenda-seletor-modo-visualizacao.md b/docs/plans/c95-agenda-seletor-modo-visualizacao.md new file mode 100644 index 00000000..78f17720 --- /dev/null +++ b/docs/plans/c95-agenda-seletor-modo-visualizacao.md @@ -0,0 +1,81 @@ +# C95 — Agenda: seletor do modo de visualização no header do app (mobile e desktop) + +Status: rascunho +Atualizado em: 2026-08-08 +Issue: #439 +Priority: P2 +Model: composer-2.5 +Model-local: deepseek-v4-flash-high +Impeccable: B — encaixe no header global do shell (controle compacto; sem redesign) +Canvas UI: coberto/atualizado com o plan-c94-ui-draft.canvas.tsx (header do app com o seletor) +Appetite: ~0,5–1 dia eng; um outcome verificável — trocar o modo de visualização (semana/dia/mês/lista) por um seletor único no header do app, sem aumentar o header +Responsável: — + +## Dependência rápida + +- **Com [C94](c94-agenda-combobox-header-acoes-rapidas.md) (coordenar):** os dois tocam o **header do app** (`CampaignDesktopHeader`/`CampaignMobileTopBar`) — C94 coloca o "Link de import" lá; C95 coloca o seletor de vista.**Sequenciar com C94** (slot agenda-contextual é construído por um, reutilizado pelo outro), mas sem ordem dura fixa. + +## Intenção + +Hoje o troca-modo do calendário (semana/dia/mês/lista) vive nos **botões embutidos** do próprio FullCalendar (cada modo com seu botão), no meio da página. Os controles do calendário devem morar no **header do app** — a barra superior do `/campanha` onde já ficam o título da página, o botão do Sollinha/AI chat e o de notificações — tanto em desktop quanto em mobile. Em vez de um botão por modo, **um seletor único** ("Semana ▾"), compacto o bastante para **não aumentar o tamanho do header**. No mobile, a mesma barra superior (título + sino) ganha o seletor no mesmo gesto. + +## Persona e fluxo + +- **Persona / contexto:** coordenador/assessor alternando entre vista de dia, semana, mês ou lista conforme o gesto: no campo (mobile) quer o dia; na mesa (desktop) quer a semana com o mês à esquerda para saltar. +- **Job principal:** trocar o modo de visualização com um controle só, **no header do app** (onde já olha título/sino/AI), sem perder o estado do filtro nem o recorte. +- **Fluxo desejado:** header do app → seletor "Semana ▾" → escolhe "Mês" → calendário muda a vista; escolha vale para a sessão e sobrevive ao reload (vira parte do estado da tela, junto do filtro). +- **Anti-goals de produto:** não duplicar com os botões do FullCalendar (remover `end` do `headerToolbar`); não empilhar segunda linha de controles; não esconder o modo ativo; **não aumentar a altura/forma do header**. + +### Esboço de fluxo (B) + +```text +[header] seletor "Semana ▾" → [Dia|Semana|Mês|Lista] → calendário muda → escolha persiste +``` + +## Objetivo e aceite + +- Um **seletor único** de modo de visualização no **header do app** (`CampaignDesktopHeader` no desktop; `CampaignMobileTopBar` no mobile), **agenda-contextual** (só na rota `/campanha/agenda`), substituindo os 4 botões do FullCalendar. +- O seletor **não aumenta a altura do header** (mantém `min-h-11` desktop / `min-h-14` mobile; só insere o controle compacto na linha existente, junto do título/sino/AI chat). +- Modos disponíveis: Dia (`timeGridDay`), Semana (`timeGridWeek`), Mês (`dayGridMonth`) e Lista (`listMonth`) — os mesmos de hoje. +- A escolha persiste por sessão/reload (estado da tela, junto do filtro) e respeita o recorte do filtro. +- Sem regressão: navegação prev/next/hj continua no FullCalendar; criação inline (C91) intacta; leader lockdown; demais telas `/campanha` sem o seletor (contextual). + +## Dados (intenção) + +- **Vou apresentar dados?** Não. +- **Decisões desbloqueadas:** "estou olhando o recorte do jeito certo para o meu gesto". +- **Forma:** adiada (select compacto vs segmented são igualmente válidos; cabe ao executor, respeitando "não crescer o header"). + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/components/campaign/shell/CampaignDesktopHeader.tsx` + `src/components/campaign/shell/CampaignMobileTopBar.tsx` (slot do seletor, agenda-contextual; o mecanismo de chrome por-página do `CampaignPageChromeDisplay`/contexto é a pista para o encaixe), `src/components/campaign/activity/ActivityAgenda.tsx` (remover botões de modo do `headerToolbar`; conectar o seletor ao `changeView` via `calendarRef`), e o estado de vista eventualmente junto ao `ActivityAgendaState`/URL (persistência). +- **Precedente a olhar:** C91 (`agenda-criar-evento-inline.md`) mexe no mesmo FullCalendar; o "Link de import" do C94 entra no mesmo header do app — coordenar slot/ordem. +- **Risco de acoplamento:** o header do app é **compartilhado por todas as rotas** `/campanha` — o seletor precisa ser agenda-contextual para não poluir as demais telas. A lógica responsiva que hoje troca semana↔dia ao redimensionar (narrow) precisa reconciliar com um modo **escolhido pelo usuário** — a escolha do usuário deve vencer (detalhe de interação, não de layout). + +## Dependências + +- C94 (coordenar — mesmo header do app / slot agenda-contextual; serializar na fila). + +## Fora de escopo + +- Redesign do FullCalendar (C15); criação inline (C91). +- Mover prev/next/"Hoje" para o header (mantém dentro do calendário). +- Persistência entre dispositivos/contas (só sessão/tela). +- O slot agenda-contextual do header em si — construído junto do "Link de import" (C94) e reutilizado aqui. + +## Rabbit holes de produto + +- **Seletor vira barra de controles.** Se alguém "só completar": arrastar prev/next/título/modo tudo pro header do app. **Corte neste item:** só o seletor de modo; navegação permanece no calendário. +- **Crescer o header.** O gatilho "só mais um controle" vira segunda linha ou aumenta `min-h`. **Corte:** um controle único compacto, dentro da linha existente do header do app. + +## Questões em aberto (produto) + +- **Controle: select texto vs segmented de ícones.** **Opções:** A) select/dropdown compacto (rótulo do modo atual + ▾) | B) segmented com 4 ícones. **Recomendação:** A — é o que cabe na linha única sem crescer; 4 segmentos não. _(assumido — validar)_ +- **Posição no header do app (desktop).** **Decidido no gate:** cluster direito na ordem `[Semana ▾][Link de import][Notificações][IA]` — o seletor é o primeiro do cluster da agenda. No mobile, junto ao sino, à direita do título. Fora de questão. +- **Escolha do usuário × auto-responsivo.** **Opções:** A) escolha do usuário vence e não é trocada por resize (só o default muda por viewport) | B) resize continua forçando de volta. **Recomendação:** A — o usuário que escolheu mês não deve ser jogado de volta ao fechar o teclado. _(assumido — validar)_ + +## Referências + +- GitHub Issue #390 (C15 — agenda FullCalendar), #428 (C91 — criar inline) +- Canvas UI (gate): `/home/fsolla/.cursor/projects/home-fsolla-Code-teqo/canvases/plan-c94-ui-draft.canvas.tsx` (header-alvo com o seletor) +- `ActivityAgenda.tsx` (`headerToolbar` `end: 'timeGridWeek,timeGridDay,dayGridMonth,listMonth'`; `MOBILE_BREAKPOINT_PX`)