SGG - Sistema de Gestão de Gabinete Schema: v4 + Blocos 2-14 + Hotfix 10.1/10.2/10.1.1 + 11.1/11.2 + Geo 1.0/1.1 + Storage Refactor + Geo Territorial/Completo + CEP + Bloco 12 + Hotfix 12.1 + Bloco 13 + Bloco 14 + Hotfix UX 14.1 + 14.2 + Importação de Cidadãos + Hotfix 14.2.1 (Demanda Rápida: câmera Android + preview) + Hotfix Título de Mídia (protocolo prefixado + migration 019) + Hotfix 14.2.2 (Câmera Android/Samsung A07: inputs estáticos especializados) + Bloco Registro de Evolução da Demanda (Situação inicial/final + migration 020) + Hotfix Complementação Evolução (resultado/status, observação visível, metadados de mídia, permissão equipe_rua, flash de erro, limite de mídias) + Bloco 15 — Central de Comunicação e Marketing (migration 021) + Bloco 16 — Consolidação da Plataforma / Auditoria Geral v1.0 + Hotfix 16.1 — Cadastro Rápido de Cidadão na Demanda Rápida (modal AJAX) + Hotfix 16.1.1 — Correção definitiva do modal Novo Cidadão (cascata CSS × atributo hidden) + Hotfix 16.1.2 — Complementação do Cadastro Rápido de Cidadão + Hotfix 16.1.3 — Atualização do Modelo de Importação de Cidadãos + Hotfix 16.1.5 — Filtros avançados na listagem de cidadãos + Hotfix 16.1.6 — Busca inteligente e localização avançada de cidadão na Demanda Rápida + Hotfix 16.1.7 — Quantidade de cidadãos por página + Hotfix 16.1.7.1 — Aplicação automática da quantidade por página (este release) Bloco 14 — Inteligência do Gabinete: - Nova área /intelligence (protegida por reports.view) que transforma dados existentes em decisão, sem criar cadastros. - Indicadores executivos: demandas abertas/resolvidas/atrasadas/vencendo, visitas, tarefas abertas/vencidas, cidadãos, por mês e por categoria. - Ranking de bairros (reusa Territory::byNeighborhood) e de secretarias/órgãos (novo: total, abertas, resolvidas, atrasadas, tempo médio de resolução). - Produtividade por assessor (demandas atribuídas/resolvidas/atrasadas, visitas, tarefas) e filtro individual por assessor. - Heatmap de demandas (reusa leaflet.heat + Territory::heatPoints; coordenada da demanda, depois do bairro, depois centro de Uberlândia). - Metas mensais simples (demandas resolvidas, visitas, tarefas) com realizado e percentual — nova tabela goals (migration 018). - Botão "🚗 Abrir no Waze" na tela da demanda (coordenada da demanda ou do bairro; some quando não há coordenada). Botão "Ver no mapa" preservado. - Item de menu "Inteligência do Gabinete" na seção Gestão. - Nenhuma tela existente removida; SLA, demanda tradicional, demanda rápida e mapa intactos. Total de tabelas: 53 (52 + goals). Hotfix UX 14.1 — Bottom Navigation profissional (mobile): - Barra inferior redesenhada: Painel · Demandas · [Nova Demanda] · Cidadãos · Mais. - Botão central flutuante (FAB) circular 64px, elevado, sombra, cor do sistema, ícone de raio + texto "Nova Demanda", abrindo /demands/quick (rota existente). - Item "Demanda Rápida" removido da barra (substituído pelo FAB central). - Menu "Mais": bottom sheet que abre de baixo para cima com Perfil, Agenda, Documentos, Aniversários, Mapa, Relatórios, Configurações e Sair — cada item respeita as permissões existentes. Sair via POST+CSRF. - safe-area-inset-bottom (sem sobreposição com notch), área de toque >=44px, suporte a tema claro/escuro. Apenas front-end: sem mudança de banco, rotas, permissões ou regras de negócio. Hotfix UX 14.2 — Refino final mobile/PWA (iPhone/Safari): - Safe-area da barra inferior com max(10px, env(safe-area-inset-bottom)) e espaço final do conteúdo calc(96px + safe-area) — botões finais não ficam mais escondidos atrás da barra nem colidem com os controles do Safari. - Botão central "Nova Demanda" refinado (alinhamento, toque >=64px, sem corte). - Bottom sheet "Mais": altura máxima 80vh com scroll interno, header com título e botão X, fecha por toque fora/ESC/X, respeita a safe-area. Itens novos: Cargos e Permissões e Auditoria (quando permitido). Sair segue POST+CSRF. - Meta tags PWA Apple (apple-mobile-web-app-capable/status-bar-style/ mobile-web-app-capable); viewport-fit=cover preservado sem duplicar. - Apenas layout/CSS/JS. Sem mudança de banco, rotas, permissões ou regras. Data: 2026-06-24 Módulo Importação de Cidadãos por Planilha (CSV): - Nova área /citizens/import (permissão citizens.create) com botão na listagem. - Download de modelo CSV (UTF-8, separador ';', com linha de exemplo). - Upload .csv (máx 5MB, máx 5000 linhas, MIME básico, cabeçalhos obrigatórios). - Validação por linha: nome obrigatório, e-mail, data (DD/MM ou DD/MM/AAAA), bairro existente, telefone normalizado; dedup interno e contra o banco. - Prévia em sessão (sem tabela nova) com resumo e status por linha (OK/ERRO/ DUPLICADO) e motivo. Confirmar importa só os válidos; cancela limpa. - Origem "Importação" garantida com slug='importacao' (busca por slug, insere slug/name/is_active/sort_order). SEM fallback: se não garantir a origem, NÃO importa e mostra erro claro. - Reaproveita Citizen::create e ConsentLog; consent_channel=importacao; auditoria. - CSV nativo (fgetcsv), sem Composer/XLSX/Node. instagram->notes, whatsapp->phone. Bloco — Registro de Evolução da Demanda (este release): - "Mídias vinculadas" da tela da demanda passa a "Situação inicial"; mídias antigas sem classificação são tratadas como 'initial' (backfill migration 020). - Nova seção "Situação final" com Tirar foto / Gravar vídeo / Escolher da galeria, preview, remoção individual e observação da situação final. Reusa EXATAMENTE a arquitetura estática de câmera validada no Samsung A07 (inputs especializados + input final único name="media[]" via DataTransfer; sem input único com capture dinâmico). Mídias salvas com estágio 'final'. - Título das mídias da situação final: "PROTOCOLO — Situação final — TÍTULO" (reusa buildDemandMediaTitle; idempotente, não duplica protocolo). - Observação geral da situação final: registrada no histórico auditável da demanda (Demand::addComment + Audit::log). Descrição individual reusa media_files.description. Nenhum campo semanticamente duplicado. - Upload de "Anexos" movido visualmente para dentro de "Documentos" (tabela e arquivos de attachments preservados). Documentos oferece "Gerar documento desta demanda" e "Adicionar documento/arquivo". Seção isolada "Anexos" some. - Nova coluna media_files.demand_stage VARCHAR(20) DEFAULT 'initial' + índice idx_media_demand_stage (migration 020, idempotente). RBAC, storage externo, Demanda Rápida, câmera Android/iPhone, mapa, Waze, agenda, tarefas e comentários preservados e inalterados. PHP: 8.0+ Banco: MySQL 8.0+ / MariaDB 10.4+ Hotfix 16.1.7.1 — Aplicacao automatica da quantidade por pagina (este release): - Ajuste minimo: UM UNICO arquivo alterado (Views/citizens/index.php). SEM migration, SEM banco, SEM controller, SEM model, SEM rota, SEM permissao e SEM biblioteca externa. - AUDITORIA: o listener de "change" do seletor JA EXISTIA desde o 16.1.7 e o auto-submit ja funcionava. O botao "Aplicar" era apenas fallback sem JavaScript e vinha sendo OCULTADO por JS (go.hidden = true) — no celular ele extrapolava a largura da tela antes de ser escondido. - 4 REMOCOES DE CODIGO: (1) o elemento foi REMOVIDO do HTML, nao ocultado por CSS; (2) a regra CSS orfa .cz-perpage__go; (3) a variavel "go"; (4) a linha go.hidden = true. - O listener de "change" NAO foi tocado nem duplicado: continua sendo o UNICO, garantindo 1 alteracao = 1 submissao. Sem setTimeout, sem workaround. - LARGURA NO MOBILE (media query <=600px): o bloco do seletor caiu de ~318px para ~234px. Num iPhone SE (320px, ~288px uteis) ele ESTOURAVA e agora cabe com folga; tambem cabe em 360px (Samsung A07), 390px e 412px. Preservados min-height:44px, max-width:110px e order:3 — o select segue facil de tocar e "Importar Planilha"/"Novo cidadao" seguem acessiveis. Nenhuma largura fixa em px, nenhum position:fixed/z-index/overflow-x novo: navegacao inferior e safe-area intactas. - SEM JAVASCRIPT: conforme decidido para este hotfix, o fallback com botao NAO foi recriado; sem JS o seletor permanece visivel, porem sem auto-submit. - TESTES: php -l OK, node --check OK, 95 casos / 95 PASS — ausencia do botao no DOM, no CSS e no HTML servido; 1 alteracao = exatamente 1 submissao (e o evento "input" nao submete); as seis transicoes 250->25, 25->50, 50->100, 100->250, 250->500 e 500->25 submetendo com o per_page correto; "page" ausente do formulario (retorno a pagina 1 a partir de um cenario real de 187 paginas); preservacao de busca, bairro, lideranca, consentimento e WhatsApp isolados e combinados; as cinco quantidades ainda renderizando 25/50/100/250/ 500 linhas; contador e intervalo preservados; larguras de 320 a 412px. - NAO-REGRESSAO: diff -rq recursivo acusa 1 arquivo alterado (+ VERSION/ CHANGELOG), 0 novos e 0 removidos — 264 arquivos antes e 264 depois. Citizen.php, CitizenController.php, routes.php, app.css, demands/quick.php, citizens/form.php, citizens/import.php, config.php, .htaccess, database.sql, sw.js e manifest.json byte a byte. Whitelist, padrao 250, filtros, busca, paginacao, contador, chips e toggle de filtros preservados. Demanda Rapida, autocomplete, modal Procurar, "+ Novo", camera, importacao, Central de Comunicacao e PWA intactos. Hotfix 16.1.7 — Quantidade de cidadaos por pagina: - SEM migration, SEM alteracao de banco, SEM rota nova, SEM permissao nova, SEM indice novo e SEM biblioteca externa. Apenas 3 arquivos alterados: Models/Citizen.php, Controllers/CitizenController.php e Views/citizens/index.php. - LIMITE ANTERIOR: 20 registros por pagina. O controller chamava Citizen::paginate($filters, $page) SEM o 3o argumento, herdando o default do model. Com 1.600+ cidadaos reais isso dava ~81 paginas. - PARAMETRO GET: per_page (nao havia convencao previa no sistema). Whitelist explicita Citizen::PER_PAGE_OPTIONS = [25, 50, 100, 250, 500] e padrao Citizen::PER_PAGE_DEFAULT = 250. - VALIDACAO NO SERVIDOR: normalizePerPage() so aceita inteiro ou string de digitos puros e confronta com in_array estrito. Qualquer outro valor — 0, -1, 1, 20, 999, 999999, abc, 1e3, 25.5, 0x19, 250abc, "25 OR 1=1", "25' OR '1'='1", "25;DROP TABLE citizens", array, bool — cai em 250. O pre-carregado deu lugar a input de busca + hidden citizen_id (o backend da demanda recebe citizen_id exatamente como antes): [ Digite nome, apelido ou telefone... ] [ Procurar ] [ + Novo ] * Autocomplete: 2+ caracteres, debounce 300ms, numero de sequencia descartando respostas fora de ordem (race condition), teclado ↑/↓/Enter/ESC. Cada opcao mostra nome (apelido), telefone e bairro • cidade • regiao. Com excesso: "Muitos resultados encontrados. Continue digitando ou use Procurar." * Modal "Procurar": filtros por nome/apelido/telefone, bairro, cidade e regiao (listas oficiais, sem texto livre), 20 por pagina com "Carregar mais" acumulativo e contador "Exibindo X de Y". * Cidadao escolhido em destaque com telefone e territorio + botao "Trocar". - LOCALIZACAO AVANCADA = BUSCAR por informacao territorial. A localizacao da OCORRENCIA nunca e tocada: coordenada do cidadao NAO e copiada para a demanda e o bairro do cidadao NAO e vinculado ao bairro da ocorrencia (comprovado). - "+ NOVO" PRESERVADO (16.1/16.1.1/16.1.2 intactos). Apenas pick() foi reapontado para a funcao compartilhada window.sggCitizenPick; quickStore passou a devolver tambem "meta" (telefone • bairro • cidade • regiao) via Citizen::lookupOne, para exibir o recem-cadastrado ja identificado. Ele ja esta no banco, logo e encontravel pelo autocomplete sem recarregar a pagina. - ERRO DE VALIDACAO: viewData() nao carrega mais lista alguma; selectedCitizen() busca UMA linha por ID (Citizen::lookupOne, escopada por tenant e deleted_at IS NULL) e a tela reexibe o cidadao escolhido. - PERMISSOES: sem citizens.create some o "+ Novo" e a busca segue funcionando (o CSS compartilhado do overlay, incluindo o fix 16.1.1 .qc-overlay[hidden], e declarado no bloco alternativo); sem citizens.view o campo fica desabilitado com aviso e o "+ Novo" permanece. Nenhuma permissao nova. - PERFORMANCE (5.208 cidadaos): abertura da tela passa de 5,5ms/500 linhas/ 23,3KB de