From f61865a3c274bc1be02c91bf8ec065590d594a56 Mon Sep 17 00:00:00 2001 From: flemming-it Date: Thu, 18 Jun 2026 14:41:07 +0200 Subject: [PATCH] feat(ui): add legend card below the heatmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The four-line description above the heatmap was concise but opaque: 'X: log10 €/year. Y: 0–5. Punktgröße.' said the right words, but a reviewer who isn't already methodologically oriented can't tell what they're looking at. Adds a dedicated Legende-Karte directly under the heatmap. Four rows, each: leading symbol (↔ ↕ ● ◐), axis-or-quantity label, prominent name (Schaden / Nutzen / Betroffenheit / Evidenz-Stufe), and a short prose explanation tying the visual back to the Studie: ↔ Abszisse (X) Schaden — log10 €/Jahr, SKM §3.1 ↕ Ordinate (Y) Nutzen — 7-dim. Komposit §3.2 ● Punktgröße Betroffenheit — sqrt scale, DESTATIS-/IHK ◐ Tier-Farbe Evidenz-Stufe — Regel der niedrigsten §6.7 Top description tightened to one line of context only: 'Schaden × Nutzen × Betroffenheit pro Norm. Klick auf einen Punkt öffnet das Detail …'. No redundancy with the legend, no jargon up top. Signed-off-by: flemming-it --- app/lib/pages/heatmap_page.dart | 139 +++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 3 deletions(-) diff --git a/app/lib/pages/heatmap_page.dart b/app/lib/pages/heatmap_page.dart index 9fd69ce..f6ebf48 100644 --- a/app/lib/pages/heatmap_page.dart +++ b/app/lib/pages/heatmap_page.dart @@ -50,9 +50,9 @@ class _HeatmapPageState extends State { Text('Heatmap', style: t.displaySmall), const SizedBox(height: ReclaimSpace.xs), Text( - 'X: jährliche Bürokratiekosten (€, log10). ' - 'Y: Nutzen-Score 0–5. Punktgröße: Betroffenheit. ' - 'Klick auf einen Punkt → Detail mit Pflichten + Quellen.', + 'Schaden × Nutzen × Betroffenheit pro Norm. ' + 'Klick auf einen Punkt öffnet das Detail mit ' + 'Pflichten, Quellen und Gesetzestext.', style: t.bodyLarge?.copyWith( color: ReclaimColors.mute, ), @@ -77,6 +77,8 @@ class _HeatmapPageState extends State { ), ), ), + const SizedBox(height: ReclaimSpace.md), + _LegendCard(), const SizedBox(height: ReclaimSpace.lg), Wrap( spacing: ReclaimSpace.md, @@ -126,3 +128,134 @@ class _HeatmapPageState extends State { ); } } + +/// Axis + circle legend below the heatmap. Spells out what is +/// plotted and how — the four-line top description deliberately +/// stays short and high-level, this card carries the precise +/// reading instructions a reviewer needs to defend the plot +/// against "what does that even mean?" +class _LegendCard extends StatelessWidget { + @override + Widget build(BuildContext context) { + final t = Theme.of(context).textTheme; + return ReclaimCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Legende', style: t.headlineSmall), + const SizedBox(height: ReclaimSpace.md), + _LegendRow( + symbol: '↔', + axis: 'Abszisse (X)', + label: 'Schaden', + description: + 'Bürokratiekosten pro Jahr in Euro, logarithmisch ' + 'skaliert (10⁴ = 10 k, 10⁶ = 1 Mio, 10⁹ = 1 Mrd). ' + 'Berechnet nach Standardkostenmodell ' + 'P × F × T × h (Studie §3.1).', + ), + const SizedBox(height: ReclaimSpace.sm), + _LegendRow( + symbol: '↕', + axis: 'Ordinate (Y)', + label: 'Nutzen', + description: + 'Komposit-Score von 0–5, Mittelwert aus den sieben ' + 'Dimensionen Schutz · Markt · Rechtssicherheit · ' + 'EU-Binnenmarkt · Sicherheit · Umwelt · Einnahmen ' + '(Studie §3.2). Jede Dimension einzeln ' + 'quellen-begründet.', + ), + const SizedBox(height: ReclaimSpace.sm), + _LegendRow( + symbol: '●', + axis: 'Punktgröße', + label: 'Betroffenheit', + description: + 'Anzahl der Adressaten (KMU, Bürger, Behörden), ' + 'wurzel-skaliert. Mehr Adressaten → größerer Kreis. ' + 'Quellen: DESTATIS Unternehmensregister, ' + 'IHK-/Handwerkskammer-Mitgliederzahlen.', + ), + const SizedBox(height: ReclaimSpace.sm), + _LegendRow( + symbol: '◐', + axis: 'Tier-Farbe', + label: 'Evidenz-Stufe', + description: + 'Petrol → warm-orange markiert die Stufe (T1–T4) der ' + 'schwächsten Eingangs-Variable. Eine SKM-Zahl mit ' + 'T4-Schätzwert für h trägt die ganze Zahl als T4 ' + '(Regel der niedrigsten Stufe, Studie §6.7).', + ), + ], + ), + ); + } +} + +class _LegendRow extends StatelessWidget { + const _LegendRow({ + required this.symbol, + required this.axis, + required this.label, + required this.description, + }); + + final String symbol; + final String axis; + final String label; + final String description; + + @override + Widget build(BuildContext context) { + final t = Theme.of(context).textTheme; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 28, + child: Text( + symbol, + style: t.titleMedium?.copyWith( + color: ReclaimColors.signal, + fontSize: 18, + ), + ), + ), + SizedBox( + width: 140, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + axis, + style: t.labelSmall?.copyWith( + color: ReclaimColors.mute, + letterSpacing: 0.4, + ), + ), + Text( + label, + style: t.titleMedium, + ), + ], + ), + ), + const SizedBox(width: ReclaimSpace.md), + Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 2), + child: Text( + description, + style: t.bodyLarge?.copyWith( + color: ReclaimColors.mute, + height: 1.5, + ), + ), + ), + ), + ], + ); + } +}