From c501595c6a6713762fa6bc456f193ad9dcdd5259 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Tue, 15 Sep 2026 09:26:05 -0300 Subject: [PATCH] =?UTF-8?q?feat(C163):=20total=20populacional=20e=20revis?= =?UTF-8?q?=C3=A3o=20de=20apresenta=C3=A7=C3=A3o=20do=20relat=C3=B3rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../changelog/2026-09-15-c163-ui-relatorio.md | 1 + scripts/lib/cityReportRender.mjs | 34 ++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 docs/changelog/2026-09-15-c163-ui-relatorio.md diff --git a/docs/changelog/2026-09-15-c163-ui-relatorio.md b/docs/changelog/2026-09-15-c163-ui-relatorio.md new file mode 100644 index 00000000..c3732e64 --- /dev/null +++ b/docs/changelog/2026-09-15-c163-ui-relatorio.md @@ -0,0 +1 @@ +**C163 — total populacional e revisão de apresentação do relatório (2026-09-15):** a demografia complementar ganhou o item **"População total (Censo 2022) — 59.605"** (o total que fecha cor/raça e renda). No PDF, passe de UI mantendo todos os links das fontes: âncoras **sublinhadas** em azul, **zebra** nas linhas de tabela, caption das tabelas maior, seções com régua superior e mais respiro, e remoção de títulos duplicados (o título de tabela era impresso duas vezes — h4 + caption; blocos com título idêntico ao da seção, como "Conjuntura", perderam o h4 redundante). Página 1 intocada e ainda fechando em uma página; unit 3118/3118, `tsc`/eslint verdes; PDF de Itamaraju em 9 páginas. diff --git a/scripts/lib/cityReportRender.mjs b/scripts/lib/cityReportRender.mjs index 662f8f11..412befe3 100644 --- a/scripts/lib/cityReportRender.mjs +++ b/scripts/lib/cityReportRender.mjs @@ -112,6 +112,7 @@ const PRINT_CSS = ` .stats .stat-hint { display: block; font-weight: 400; color: #71717a; font-size: 7.4pt; } .source { color: #71717a; font-size: 7.2pt; margin: 1.4mm 0 0; } .source a { color: #1d4ed8; word-break: break-all; } + a { color: #1d4ed8; text-decoration: underline; text-decoration-color: #bfdbfe; text-underline-offset: 1.5px; } .callout { border-left: 2.2mm solid #b45309; background: #fffbeb; padding: 2.4mm 3mm; border-radius: 0 1.6mm 1.6mm 0; } .callout.gap { border-left-color: #71717a; background: #f4f4f5; } .callout.decision { border-left-color: #15803d; background: #f0fdf4; } @@ -129,16 +130,18 @@ const PRINT_CSS = ` table { width: 100%; border-collapse: collapse; font-size: 8pt; } table.table-fixed { table-layout: fixed; } table.table-fixed td, table.table-fixed th { overflow-wrap: anywhere; } - caption { caption-side: top; text-align: left; font-weight: 600; font-size: 8.6pt; padding-bottom: 1.2mm; color: #18181b; } - th { text-align: left; background: #f4f4f5; border-bottom: .8px solid #d4d4d8; padding: 1.4mm 1.4mm; font-size: 7pt; text-transform: uppercase; letter-spacing: .03em; color: #52525b; } - td { border-bottom: .6px solid #e4e4e7; padding: 1.4mm 1.4mm; vertical-align: top; } + caption { caption-side: top; text-align: left; font-weight: 600; font-size: 9.4pt; padding-bottom: 1.8mm; color: #18181b; } + th { text-align: left; background: #f4f4f5; border-bottom: .8px solid #d4d4d8; padding: 1.6mm 1.4mm; font-size: 7pt; text-transform: uppercase; letter-spacing: .03em; color: #52525b; } + td { border-bottom: .6px solid #e4e4e7; padding: 1.6mm 1.4mm; vertical-align: top; } + tbody tr:nth-child(even) { background: #fafafa; } td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; } .table-note { color: #71717a; font-size: 7.2pt; margin: 1mm 0 0; } .prose p { margin: 0 0 1.4mm; } .page-break { break-before: page; } .index ol { columns: 2; margin: 0; padding-left: 0; list-style: none; color: #3f3f46; font-size: 8.6pt; } - .report-section { margin-bottom: 6mm; } - .report-section > h3 { font-size: 11pt; margin: 0 0 2mm; break-after: avoid; } + .report-section { margin-bottom: 7mm; } + .report-section > h3 { font-size: 12pt; margin: 0 0 2.6mm; padding-top: 2.4mm; border-top: .8px solid #d4d4d8; break-after: avoid; } + .report-section:first-of-type > h3 { border-top: none; padding-top: 0; } .block + .block { margin-top: 3mm; } .block { break-inside: avoid; } .block.table-block { break-inside: auto; } @@ -260,8 +263,23 @@ const renderSources = (block) => { return `

Limites

` } -const renderBlockHtml = (block) => { - const title = block.title ? `

${htmlEscape(block.title)}

` : '' +const normalizeHeading = (value) => + String(value ?? '') + .replace(/^\d+\.\s*/, '') + .replace(/\s+/g, ' ') + .trim() + .toLowerCase() + +const renderBlockHtml = (block, sectionTitle = null) => { + // Tables carry their title in the caption; a block title equal to the section + // heading is duplication (the section h3 right above already says it). + const duplicatesSection = + Boolean(block.title && sectionTitle) && + normalizeHeading(block.title) === normalizeHeading(sectionTitle) + const title = + block.title && block.kind !== 'table' && !duplicatesSection + ? `

${htmlEscape(block.title)}

` + : '' let body switch (block.kind) { case 'kpis': @@ -344,7 +362,7 @@ export const renderReportHtml = (report) => { const sections = report.sections .map((section) => { - const blocks = section.blocks.map(renderBlockHtml).join('') + const blocks = section.blocks.map((block) => renderBlockHtml(block, section.title)).join('') return `
${`

${htmlEscape(section.title)}

`}${blocks}
` }) .join('')