use renderreport::components::{AuditTable, TableColumn};
use renderreport::prelude::*;
use crate::i18n::I18n;
use crate::output::report_model::*;
pub(super) fn render_diagnosis_section(
mut builder: renderreport::engine::ReportBuilder,
diagnosis: &DiagnosisBlock,
i18n: &I18n,
) -> renderreport::engine::ReportBuilder {
let en = i18n.locale() == "en";
builder = builder.add_component(Section::new(&diagnosis.section_title).with_level(2));
let pattern_intro = format!(
"{}: {}",
diagnosis.pattern_label, diagnosis.pattern_description
);
let callout = if diagnosis.is_systematic {
Callout::warning(&pattern_intro).with_title(&diagnosis.pattern_label)
} else {
Callout::info(&pattern_intro).with_title(&diagnosis.pattern_label)
};
builder = builder.add_component(callout);
if let Some(ref dominant) = diagnosis.dominant_issue {
let spotlight_text = if en {
format!(
"\"{}\" accounts for the majority of critical/high findings.",
dominant
)
} else {
format!(
"\"{}\" verursacht den Großteil der kritischen/hohen Findings.",
dominant
)
};
builder = builder.add_component(Callout::warning(&spotlight_text));
}
if !diagnosis.category_breakdown.is_empty() {
let col_dim = i18n.t("diagnosis-col-category");
let col_count = i18n.t("diagnosis-col-findings");
let col_sev = i18n.t("diagnosis-col-worst-severity");
let table_title = i18n.t("diagnosis-table-categories");
let mut table = AuditTable::new(vec![
TableColumn::new(col_dim),
TableColumn::new(col_count).with_width("15%"),
TableColumn::new(col_sev).with_width("25%"),
])
.with_title(table_title);
for (dim, count, sev_label) in &diagnosis.category_breakdown {
table = table.add_row(vec![dim.clone(), count.to_string(), sev_label.clone()]);
}
builder = builder.add_component(table);
}
if !diagnosis.clusters.is_empty() {
let clusters_title = i18n.t("diagnosis-table-clusters");
let col_cluster = "Cluster";
let col_findings = i18n.t("diagnosis-col-findings");
let col_occ = i18n.t("diagnosis-col-occurrences");
let col_sev = i18n.t("diagnosis-col-max-severity");
let mut table = AuditTable::new(vec![
TableColumn::new(col_cluster),
TableColumn::new(col_findings).with_width("12%"),
TableColumn::new(col_occ).with_width("14%"),
TableColumn::new(col_sev).with_width("18%"),
])
.with_title(clusters_title);
for cluster in &diagnosis.clusters {
table = table.add_row(vec![
cluster.label.clone(),
cluster.finding_count.to_string(),
cluster.occurrence_total.to_string(),
cluster.severity_label.clone(),
]);
}
builder = builder.add_component(table);
}
builder
}
pub(super) fn output_scope_callout(i18n: &I18n) -> Callout {
if i18n.locale() == "en" {
Callout::info(
"This PDF is a condensed decision and prioritization report. It highlights the most important findings, risks and actions, but does not list every technical occurrence. Use the accompanying JSON report for the complete machine-readable issue list with selectors, occurrences and detail data.",
)
.with_title("How to read this report")
} else {
Callout::info(
"Dieser PDF-Report ist eine verdichtete Entscheidungs- und Priorisierungshilfe. Er zeigt die wichtigsten Befunde, Risiken und Maßnahmen, enthält aber nicht jede technische Einzelstelle. Für die vollständige maschinenlesbare Fehlerliste mit Selektoren, Vorkommen und Detaildaten dient der begleitende JSON-Report.",
)
.with_title("Einordnung dieses Reports")
}
}
pub(super) fn format_word_count(n: u32) -> String {
if n >= 1_000 {
format!("{}.{:03}", n / 1_000, n % 1_000)
} else {
n.to_string()
}
}
pub(super) fn business_relevance(page_type: Option<&str>, url: &str, locale: &str) -> &'static str {
let en = locale == "en";
let high = if en { "high" } else { "hoch" };
let medium = if en { "medium" } else { "mittel" };
let low = if en { "low" } else { "niedrig" };
let path = url.to_lowercase();
if path.contains("impressum")
|| path.contains("datenschutz")
|| path.contains("agb")
|| path.contains("imprint")
|| path.contains("privacy")
|| path.contains("terms")
{
return low;
}
if path.ends_with('/') && path.matches('/').count() <= 3 {
return high; }
match page_type {
Some("Marketing / Landing Page") => high,
Some("Transaktional / Utility") | Some("Transactional / Utility") => high,
Some("Editorial / Artikel") | Some("Editorial / Article") => medium,
Some("Strukturierter Wissensinhalt") | Some("Structured knowledge content") => medium,
Some("Navigations- / Hub-Seite") | Some("Navigation / Hub page") => medium,
Some("Medienorientierte Seite") | Some("Media-oriented page") => medium,
Some("Thin / Minimal Content") => low,
_ => medium,
}
}