Commit graph

230 commits

Author SHA1 Message Date
Francisco Solla
5fa4e22c9d feat(C119): status de apoio 'Lembrança' em lideranças — enum, badge e exclusão de pendências 2026-08-11 14:52:49 -03:00
Francisco Solla
9789bf36e6
Merge pull request #673 from fsolla/C114-google-calendar-agenda-da-campanha-espelhada-com-notifi
feat(C114): agenda da campanha espelhada no Google Calendar com notificação (push Teqo → Google)
2026-08-11 17:17:17 +00:00
Francisco Solla
d56f2250c6
Merge pull request #675 from fsolla/D12-flake-e2e-getbylabel-atividade-relacionada-resolve-2-ele
fix(e2e): first() no getByLabel('Atividade relacionada') sob duplicação transitória (D12 #641)
2026-08-11 17:11:46 +00:00
Francisco Solla
70070db8dc feat(C114): agenda da campanha espelhada no Google Calendar com notificação (push Teqo → Google)
Espelho oficial da agenda em calendário Google compartilhado via API
(service account, jose + fetch): reconciliação por passada com id
determinístico teqo+base32hex (upsert idempotente, sem estado por evento),
hooks afterChange/afterDelete na activity (só campos espelhados, nunca
lança), auto-resync quando calendarId muda, retry na view da agenda,
pill de estado no header + FAB + diálogo (4 estados). Cancelado no Teqo
deleta o evento no Google. Sem participantes no v1 (decisão de produto).
Sem migration nova de tabela (hand-written). Closes #635
2026-08-11 13:53:51 -03:00
Francisco Solla
a740f1eb16 fix(e2e): first() no getByLabel('Atividade relacionada') sob duplicação transitória (flake pré-existente, precedente cd469857)
D12 (#641): strict-mode violation (2 triggers idênticos) só em runs
completos sob carga. Estado estável tem 1 match (verificado em browser);
o 2º match é duplicação transitória RSC-pending, mesma classe de cd469857.
Mesmo alvo em campaignActivity:52 (único outro uso do label em e2e).
2026-08-11 13:21:21 -03:00
Francisco Solla
c83bd779d2
Merge pull request #665 from fsolla/C112-multiplos-telefones-por-pessoa
C112 — Múltiplos telefones por pessoa
2026-08-11 14:27:47 +00:00
Francisco Solla
6de8fad8f6 feat(C112): múltiplos telefones por pessoa — Contact.phones array (ordem = prioridade)
- Contact.phone (text único) → phones (array field Payload, subcampo value):
  fonte única; principal = phones[0]; beforeValidate normaliza, filtra vazios,
  rejeita inválido e duplicado DENTRO da ficha (C111 entre fichas preservado)
- migration add_contact_phones_array: backfill phone → phones[0] (gen_random_uuid)
  antes do drop da coluna + índice em value (hand-edit documentado; snapshot .json
  gerado mantém as colunas C99/C100 já existentes)
- primaryPhoneOf / phoneValuesOf / reorderWithPrimaryPhone em src/lib/phone.ts
  (ordem = prioridade; inline edit = seta o principal preservando o resto)
- findOrCreateContactByPhone aceita phones[] (dedupe pelo primário; reuse faz
  append dos faltantes; locks em todos os números digitados)
- sync conta→ficha (CampaignUser): phone da conta faz APPEND quando a ficha não
  tem (nunca reordena nem remove); tombstone LGPD substitui TODOS os telefones
- convite redemption: telefone digitado vira principal, demais preservados
- import CSV: preview sinaliza compartilhado também por número secundário;
  bulk insere contact_phones (JOIN nas leituras, uuid no insert)
- busca por telefone (apoiadores, pickers, combobox) casa qualquer número
- UI: PhonesFieldEditor compartilhado (repetível mascarado, remover, ↑/↓,
  modo inline com Salvar) nas 5 superfícies — liderança (form/ficha/lista),
  apoiador (form/ficha), dobradinha (ficha), assessor (ficha), wizard
- UI pública (petição/WhatsApp) intocada: zod e forms ficam com phone;
  as ações mapeiam phone → phones[0]
- testes: spec novo campaignMultiPhones (aceite completo), specs C111/C99
  atualizados, e2e wizard/apoiador ajustados
2026-08-11 10:47:02 -03:00
Francisco Solla
29ce38bd29 Merge remote-tracking branch 'origin/main' into OPS36-e2e-journeys-sessao-compartilhada-arquivos-pesados
# Conflicts:
#	docs/CHANGELOG-AGENTS.md
2026-08-11 10:31:04 -03:00
Francisco Solla
a0fe13c3d1 chore(OPS36): journeys com sessão compartilhada nos e2e pesados — seed do campaign-token via payload.login sem login de browser
- Sessão por arquivo: coordinator/leader compartilhados por worker (ownership
  própria, cleanup no afterAll); beforeEach semeia o cookie campaign-token no
  context fresh de cada teste; advisors permanecem por teste (OPS29/B176)
- Premissa da intenção corrigida por medição: getPayload é memoizado por
  worker (364 ms cold, 0 ms warm); o overhead real era login de browser
  (3.6 s quente / 30.3 s 1º hit vs 112-134 ms de seed) + criação de usuário
- Sem mode: 'serial' (divergência declarada): seed por cookie dispensa o
  reuso de contexto e evita skips em cascata no relatório
- Endurece 7 interações client-side com toPass (classe B13/B17 exposta pela
  remoção do warm-up do login) e 4 lookups do strip de ações com .first()
  (duplicado transitório RSC)
- Medição antes/depois registrada no CHANGELOG; débito OPS39 (prewarm de
  home-search/mudar-tendencia) registrado com depends no OPS30
2026-08-11 09:35:14 -03:00
Francisco Solla
5577d31c38 fix(C113): feed iCal da agenda vivo — contrato de frescor no lugar do snapshot congelado
Spike no build de produção provou que o Next 15 não congela route handlers GET
(rota dinâmica por default); o freeze era o contrato de resposta
(public, max-age=3600 sem validadores) permitindo caches compartilhados
reterem o snapshot do primeiro fetch. Fix: force-dynamic explícito +
buildICalFeedResponse com public no-cache, ETag forte sha256, Last-Modified,
304 só por If-None-Match (nunca por data — blind spot de exclusão), dica
X-PUBLISHED-TTL no corpo do VCALENDAR (RFC 7986) e 404 no-store para
revogação fail-closed no CDN. Testes unit/int/e2e (dev + prod build).
2026-08-11 08:42:42 -03:00
Francisco Solla
02abd7de89 feat(B196): omnibox colado ao header e card mobile mais denso (Closes #607)
Chassis de lista (vale para todas as listas, gate A):
- scrollport: max-md:has-[.campaign-list-omnibox-form]:pt-0 — o containing
  block do sticky passa a começar no topo; a barra cola no header (sem vão
  de 16px, nada passa por cima no scroll)
- barra densa: campo min-h-8 py-0 + X size-8 → ~40px no total; anel de
  foco escopado a md (mobile sem anel de cor; caret é o indicador)
- trailing cluster desktop-only por padrão (edgeToEdge removido)
- pt-4 md:pt-0 nas 6 listas com linha de ações antes do form (incl. organizacoes)

Card mobile (B193 densificado):
- nome 18px leading-tight com território a ~2px
- chips como blocos label-acima (Classe/Tendência/Nível, gate B) via ChipBlock
- avatares numa linha por grupo com overlap proporcional (flex-1 cells,
  sem cap/wrap/…, overflow-hidden nas bordas) — prop wrap → overlapRow
- MissingAdvisorBadge com wrap para não clipar na célula do grupo
- densidade vertical gap-2.5/py-3; toques min-h-11 preservados

e2e B196 (6 testes) + regressão B184/B183/B193 verde.
2026-08-11 07:55:50 -03:00
Francisco Solla
07dd68511f chore(OPS36): journeys com sessão compartilhada nos e2e pesados — seed do campaign-token via payload.login sem login de browser
- Sessão por arquivo: coordinator/leader compartilhados por worker (ownership
  própria, cleanup no afterAll); beforeEach semeia o cookie campaign-token no
  context fresh de cada teste; advisors permanecem por teste (OPS29/B176)
- Premissa da intenção corrigida por medição: getPayload é memoizado por
  worker (364 ms cold, 0 ms warm); o overhead real era login de browser
  (3.6 s quente / 30.3 s 1º hit vs 112-134 ms de seed) + criação de usuário
- Sem mode: 'serial' (divergência declarada): seed por cookie dispensa o
  reuso de contexto e evita skips em cascata no relatório
- Endurece 7 interações client-side com toPass (classe B13/B17 exposta pela
  remoção do warm-up do login)
- Medição antes/depois registrada no CHANGELOG; débito OPS39 (prewarm de
  home-search/mudar-tendencia) registrado com depends no OPS30
2026-08-11 07:41:14 -03:00
Francisco Solla
419ba39ecc
Merge pull request #639 from fsolla/B197-ocultar-a-coluna-de-e-mail-por-padrao-nas-listas-de-pes
B197 — Ocultar a coluna de e-mail por padrão nas listas de pessoas
2026-08-11 01:39:57 +00:00
Francisco Solla
02a33f1bc0
Merge pull request #638 from fsolla/OPS35+-restaurar-pin-de-visibilidade-css-das-colunas-de-rede
test(OPS35+): restaurar pin de browser da visibilidade CSS das colunas de rede em Territórios
2026-08-11 01:23:44 +00:00
Francisco Solla
3e9390230b feat(B197): e-mail oculto por padrão nas listas de pessoas — default B17 nas 5 listas + coluna própria em pessoas/apoiadores + picker em assessores 2026-08-10 21:57:52 -03:00
Francisco Solla
91ae6b5408 test(OPS35+): restaurar pin de browser da visibilidade CSS das colunas de rede em Territórios (Closes #616) 2026-08-10 21:28:52 -03:00
Francisco Solla
c4865135b4
Merge pull request #633 from fsolla/C110+-ux-pos-critique-refinamentos-do-preview-do-arrasto-da-
C110+ — UX pós-critique: preview do arrasto — indicadores ancorados ao frame, gradiente deduplicado, e2e prev
2026-08-11 00:14:04 +00:00
Francisco Solla
9cb8fef98d feat(C110+): preview do arrasto — indicadores ancorados ao frame, gradiente deduplicado, e2e do direction prev
- scene posicionado único (.activity-agenda-swipe-scene) envolve frame + dots/bars:
  percentuais resolvem contra o box do frame, não o content (chevron/label inclusos)
- width: 100% no scene (fix do /simplify: align-items flex-end do prev colapsava a 1px)
- --swipe-frame-columns deduplica o gradiente de 14.285% (2 call sites)
- unit test estrutural do preview (month/day/week/list, chevron por direção)
- e2e novo: direction prev com frame visível (pina o fix do colapso)
2026-08-10 20:38:04 -03:00
Francisco Solla
6fe38b332c test(B195): migrar e2e B85 do cadastro de demanda ao campo único 2026-08-10 20:15:05 -03:00
Francisco Solla
712d40f83f feat(B195): finalizar wizard Registrar pedido — campo único + título derivado por IA + edição no detalhe 2026-08-10 20:15:05 -03:00
Francisco Solla
1f31d1b628 test(OPS35): modo e2e full-stack sem browser (paradigma HTTP) + migração da família Territórios (Closes #600) 2026-08-10 19:30:29 -03:00
Francisco Solla
0047d2637c ops28(OPS28): worktrees rodam o seed mínimo no provisionamento — e2e local espelha a CI
- scripts/worktree.mjs: provision() aplica migrate + db:seed:minimal nos
  bancos dev e test (paridade com a CI, que roda o par antes do e2e);
  --no-migrate pula os dois (o seed depende do catálogo migrado); fallback
  sem Docker também migra+seeda o teqo/teqo_test compartilhado (com warning
  de sobrescrita dos pins).
- Guard de regressão tests/e2e/seedParity.setup.e2e.spec.ts no projeto setup:
  falha cedo com mensagem clara se o banco de teste não estiver seedado;
  evidência = users do seed (MINIMAL_CAMPAIGN_USERS), não os pins de priority
  — o cleanup dos fixtures e2e reseta priority por round-robin persistente.
- scripts/lib/test-affected-core.mjs: scripts/worktree.mjs em HIGH_RISK_EXACT.
- Docs: AGENTS.md, skill local-database, CHANGELOG-AGENTS.md, impl plan.

Closes #584
2026-08-10 16:11:27 -03:00
Francisco Solla
4baa617c01
Merge pull request #610 from fsolla/C110-agenda-mobile-feedback-visual-do-arrasto-reveal-do-peri
C110 — feedback visual do arrasto na agenda mobile (reveal do período adjacente + commit/snap-back)
2026-08-10 15:02:53 -03:00
Francisco Solla
3b81431d39 refactor(C110): simplify — helpers de dominância/clamp, seed e2e generalizado, CSS sem regras mortas 2026-08-10 14:42:54 -03:00
Francisco Solla
b23a323f83 feat(C110): feedback visual do arrasto na agenda mobile — reveal do período adjacente + commit/snap-back 2026-08-10 13:51:55 -03:00
Francisco Solla
cc7dff88c4 feat(C100): lista unificada de pessoas em /campanha/pessoas
Merge por Contact (uma linha por pessoa física) de lideranças,
dobradinhas e staff, com capacidades como colunas; omnibox com
capacidade/município/apoio + filtros salvos (2º call site do B18 via
factory createSavedFilterStore); apagar pessoa com manifest de
confirmação e cascata transacional; mobile edge-to-edge (omnibox sem
bordas, cards com divisórias, salvar filtro no header). Sem migration.

Closes #495
2026-08-10 13:45:46 -03:00
Francisco Solla
89e7292529
Merge pull request #580 from fsolla/OPS22-ci-main-e2e-da-agenda-mobile-vermelho-desde-o-merge-do
OPS22 — fix: settle do chat desktop não vaza drawer para a visita mobile da mesma aba (CI main / deploy desbloqueado)
2026-08-10 13:35:07 -03:00
Francisco Solla
0fefbd0490
Merge pull request #604 from fsolla/OPS29-busca-global-sem-sugestoes-mostra-regiao-em-branco-est
fix(OPS29): estado vazio honesto na busca global sem sugestões curadas
2026-08-10 13:31:03 -03:00
Francisco Solla
5bc154dd20 fix(OPS22): settle do chat desktop não vaza drawer para a visita mobile da mesma aba — CI main verde (deploy desbloqueado) 2026-08-10 13:12:12 -03:00
Francisco Solla
fb7f85cd59 fix(OPS29): estado vazio honesto na busca global sem sugestões curadas (Closes #585)
- HomeSearchResultsShell renderiza 'Nenhuma sugestão ainda — priorize
  municípios na planilha de projeção' quando o suggest responde success
  sem hits (Início e FAB overlay compartilham o shell; SSR initialSuggest
  e POST cobertos); gateada por !isFetching para não falar sob o skeleton
- B126 e2e determinístico: pin um município priority alta claimado;
  testes OPS29 novos usam advisor sem carteira (scoped, imune a alta
  global de workers paralelos); settle de animação do overlay no B126
- unit: 3 casos novos no shell + overlap skeleton/mensagem
2026-08-10 12:54:16 -03:00
Francisco Solla
2b8213eff6 test(B193): adapta o e2e C109 do main ao contrato do card novo — rodapé expandível após registrar
O teste C109 (merge do main via rebase) esperava o CTA 'Registrar atualização'
sempre visível no card; o card B193 troca o rodapé para a linha expandível
'Última atualização' após o primeiro registro (CTA dentro da expansão). A
cobertura da C109 (abrir/registrar/fechar/reabrir + sheet de nível) é mantida.
2026-08-10 11:23:49 -03:00
Francisco Solla
5d38bed12f test(B193): e2e robustos — cards ancorados pelo link exato do nome e C106 com timestamps determinísticos
- '?q=' casa prefixos de nome ('Coribe' também retorna 'São Félix do Coribe'):
  os cards B193 são ancorados por has:{ link do nome exato }.
- C106: os 8 updates seedados podiam compartilhar o mesmo now() de transação,
  tornando o sort -createdAt instável (flake '#8 no topo'); createdAt
  explícito e crescente.
2026-08-10 11:23:49 -03:00
Francisco Solla
b210939226 refactor(B193): simplify fixes pós-revisão — mapper único de updates, API de trigger uniforme, grupos de relação extraídos, hardening do sheet host
- Extrai toMunicipalityUpdateViewModel/loadUpdateAuthorNames compartilhados
  (feed + batch da lista); remove avatarClassName morto.
- MunicipalityRelationTriggerProps compartilhado nos 3 wrappers; triggerClassName
  no ExpectedVotesControl; ChipLabel/RelationGroup/CityDash no card.
- Sheet host: cleanup de unmount no overlay (sem drawer órfão), registerFooter
  valida open, branch nulo do handleOpenChange limpa footer; chrome freeze
  documentado.
- A11y/UX: ícone de alerta no rodapé frio (WCAG 1.4.1), iniciais de avatar
  aria-hidden, chip sem_base sem target morto, label da cidade com Tendência,
  aria-label da barra anuncia os 3 cenários, card layout 'colocação · votos'.
- E2E: settleStream no B184 (flake da cópia transitória do streaming), cards
  ancorados por hasText (q= casa prefixos), cor da borda via classe do token.
2026-08-10 11:23:49 -03:00
Francisco Solla
3d8dbae61f feat(B193): card de município mobile denso com edit-where-you-see e rodapé de atualização expandível
- Card denso (wireframe): posição de voto à direita, barra de cenários com
  marcador do ativo, chips classe/tendência/nível (label à esquerda), 3 grupos
  de avatares com wrap (um por entidade), rodapé Última atualização + chevron
  que expande revelando o último card de atualização + CTA registrar; sem
  atualização → CTA direto; prioridade = borda lateral direita 6px primary.
- Controles ganham prop trigger/triggerClassName (precedente RelationChipCell);
  MunicipalityRelationAvatarStack ganha modo wrap.
- C109 (sheet quebra ao abrir) corrigida: sessões do sheet host keyed por id
  (imune à identidade dos callbacks recriados por render) e footer customizado
  renderizado pelo host dentro da árvore do Drawer (portal perdia o Dialog
  context — reproduzido digitando no sheet).
- Dado: MunicipalityListViewModel.lastUpdate (batch por página, scoped).
- E2E novos (barra/votos, chip nível, borda prioridade, expansão/CTA vazio).
Sem migration/access/Consent.
2026-08-10 11:23:49 -03:00
Francisco Solla
972c6ac8ba
Merge pull request #577 from fsolla/D7-sticky-dos-headers-da-tabela-desktop-quebrado-b41-morto-t
fix(D7): sticky dos headers das tabelas desktop volta a engajar (overflow-x-clip)
2026-08-10 09:52:54 -03:00
Francisco Solla
2fa38a08f5 style(B192): format (prettier) 2026-08-10 09:13:38 -03:00
Francisco Solla
382c4c46ec feat(B192): follow-ups sugeridos após cada resposta do Sollinha (bloco no prompt + chips extraídos) 2026-08-10 09:13:38 -03:00
Francisco Solla
00a4b7f0c7 fix(D7): sticky dos headers das tabelas desktop volta a engajar (overflow-x-clip)
O wrapper do ui/Table (`overflow-x-auto`, depois `overflow-x-hidden` via
container queries) é um scroll container nos dois eixos — o overflow-y computa
para `auto` — e o th `position: sticky; top: 0` declarado pelo B41 resolvia
contra ele, que nunca rola (o scroll vertical real é o CampaignContentScroll da
shell). A feature estava silenciosamente morta desde o B41.

`overflow-x: clip` (permitido ao lado de `overflow-y: visible`, sem promover
visible→auto) NÃO cria scroll container: o th volta a resolver contra o
scrollport da shell e engaja. Troca nas duas listas com sticky declarado
(MunicipalityList, TerritoryList), preservando o fallback `overflow-x-auto`
para browsers sem container queries. As demais listas (lideranças, demandas,
dobradinhas, apoiadores) estouram em 1440px e dependem do scroller — não
tocadas.

Validado nos 3 engines (Chromium/Firefox/WebKit): th 174 → 69 (topo do content
box do scrollport, padding da shell acima — mesmo ponto onde o omnibox mobile
do B184 cola). E2E novo pina o engajamento com asserção bilateral + guarda de
computed style; unit pinado no novo contrato; JSDoc do containerClassName
atualizado. Closes #543.
2026-08-10 09:11:38 -03:00
Francisco Solla
e9d7f0da39
Merge pull request #567 from fsolla/C109-quick-edit-sheet-mobile-da-lista-de-municipios-quebra-a
fix(campanha): quick-edit sheet mobile com footer custom quebrava ao abrir (C109)
2026-08-10 08:49:39 -03:00
Francisco Solla
5ad2f7c722 fix(campanha): quick-edit sheet mobile com footer custom quebrava ao abrir (C109)
O footer custom ('Registrar atualização' B42, 'Registrar movimento' E14) era
portalado da célula para o DrawerFooter do shared-sheet: createPortal preserva
o contexto React da origem (sem Drawer.Root), então o DrawerCloseButton
(base-ui DialogClose) crashava em useDialogRootContext() e o sheet nunca
renderizava.

O footer agora viaja no chrome do CampaignListSheetHost (mesmo mecanismo de
title/description) e é renderizado inline no DrawerFooter — contexto correto
por construção. O elemento continua criado na célula (estado do form e
associação form attribute preservados). footerPortalRef/attachFooterPortal/
hasCustomFooter removidos; fallback sem provider e popover intactos.

Cobertura: unit do shared path (abre/fecha/reabre + submit via form attribute)
e e2e mobile no describe B42 (abre, registra, fecha — incluindo o sheet do
nível E14).
2026-08-10 08:11:05 -03:00
Francisco Solla
7bdd7f91b1
Merge pull request #559 from fsolla/B187-sollinha-links-com-aparencia-de-link-nas-respostas
B187 — Sollinha: links com aparência de link nas respostas
2026-08-10 07:34:36 -03:00
Francisco Solla
1016668265 feat(B187): links com aparência de link nas respostas do Sollinha
Estilo de links no markdown do chat (primária + sublinhado + hover/foco),
externos abrem em aba nova; compõe com o next/link interno do B188.
Sem migration; e2e + unit cobrem a superfície.
2026-08-10 07:20:44 -03:00
Francisco Solla
1937688fc5
Merge pull request #555 from fsolla/C106-atualizacoes-mobile-feed-sem-moldura-omnibox-sticky-sem
C106 — Atualizações mobile: feed sem moldura (omnibox sticky sem label, criar no header, cards edge-to-edge)
2026-08-10 04:31:17 -03:00
Francisco Solla
74ccce432d
Merge pull request #554 from fsolla/B191-sollinha-acoes-rapidas-de-abertura-no-chat-chips-de-per
feat(B191): chips de pergunta de abertura no chat Sollinha
2026-08-10 04:15:38 -03:00
Francisco Solla
40f38eeb8c feat(C106): cards do feed edge-to-edge com linha + empty state (padrão B184/C107 compartilhado)
- Adota o mecanismo canônico que o irmão B184/C107 estabeleceu em main
  (campaignListOmniboxFormClassName + campaignMobileHeaderIconClassName):
  a hipótese original de CSS de página virava twin — rebase resolveu
  mantendo a versão do irmão em CampaignUpdatesFilters.
- C106 entrega o que o irmão não cobriu: cards edge-to-edge (-mx-4,
  border-b last:border-b-0, padrão exato de MunicipalityListMobileCards),
  neutralização do leak do @layer base ul (my-0 list-none [&>li]:mt-0) e
  copy única do empty state.
- E2E campaignUpdatesMobile: label oculta, form sticky, icon abre o bottom
  sheet, cards edge-to-edge, empty state, regressão desktop.
- Impl plan + changelog atualizados com a decisão revisada.
2026-08-10 04:12:33 -03:00
Francisco Solla
7eaa65ce03 feat(C106): atualizações mobile sem moldura — omnibox sticky, criar no header, cards edge-to-edge
- CSS de página (padrão C101): .campaign-updates-filter-strip sticky sob a
  top bar com linha separadora, :has() zera o padding-top do scroll container
- Chassis B127 ganha o hook estável data-slot=campaign-omnibox-field (sem
  mudança de comportamento)
- 'Nova atualização' vira icon no header via SetCampaignHeaderAction (slot
  C94/C95), gate de resolução 100% CSS (md:hidden); mesmo modal C89
- Cards edge-to-edge (-mx-4) sem borda, divisor de linha; corrigido o leak do
  @layer base editorial (ul my-6 ml-6 [&>li]:mt-2) que afetava a única lista
  campaign em <ul>
- Empty state com copy única descrevendo a ação
- E2E campaignUpdatesMobile (mobile + regressão desktop); impl plan + changelog
2026-08-10 04:12:23 -03:00
Francisco Solla
04951f6772 feat(B191): chips de pergunta de abertura no chat Sollinha (curados por papel e viewport)
- src/lib/sollinhaOpeningQuestions.ts: catálogo puro de chips de abertura
  (staff: 4 perguntas ancoradas nas tools atuais; leader: conjunto seguro
  sem tool eleitoral — fail-closed B180)
- ChatChipGroup: primitivo presentacional reutilizável (slot compartilhado
  com o B192)
- role do usuário flui do layout server pelo shell até o context do chat
- slot acima do input renderiza só com conversa vazia (messages 0 + ready);
  tocar envia via sendMessage e os chips somem no primeiro envio
- unit tests do catálogo + e2e (staff desktop 4 / mobile 3 / leader seguro)
2026-08-10 03:49:08 -03:00
fsolla
437b5f0c2f test(C103): mobile agenda e2e clica o slot diretamente (C104 all-day lane mudou o grid) 2026-08-10 03:34:33 -03:00
Francisco Solla
acd522f035 feat(C103): agenda mobile — formulário de criação usável (sheet do topo, sem labels, salvar fixo, seletor em bottom sheet) 2026-08-10 03:34:33 -03:00
Francisco Solla
3ca200e7b0
Merge pull request #549 from fsolla/B184-municipios-mobile-lista-sem-moldura-omnibox-sticky-x-de
B184 — Municípios mobile: lista sem moldura (omnibox sticky, X de limpar no input, salvar filtro no header, cards edge-to-edge)
2026-08-10 03:30:25 -03:00