feat: Scorecard-Browser-Ansicht — Statusleiste mit PNG-Download-Knopf

User-Wunsch: 'Fuege auf der Webseite zur Ansicht der Scorecard unten
eine Statusleiste hinzu mit einem Download-Knopf, der dann das Dokument
als PNG oder JPEG herunterlaedt.'

Statusleiste am unteren Viewport-Rand (position: fixed) mit:
- Label links: 'Scorecard · NRW · Drs. 18/17449 · 1080×1350 (Instagram 4:5)'
- Buttons rechts:
  · primaerer 'PNG herunterladen' (Akzent-gruen, scale=2 = 2160×2700 px)
  · sekundaerer 'PDF' (Outline-Style, format=portrait)

Nutzt das bestehende /api/assessment/scorecard.png-Endpoint und das
download-Attribut sorgt fuer den richtigen Dateinamen
('gwoe-scorecard-18-17449.png').

JPEG-Variante ist nicht implementiert — der Endpoint liefert PNG, was
fuer Scorecards mit Text/scharfen Kanten qualitativ besser ist als
JPEG. Falls explizit JPEG gewollt: separater Endpoint noetig.

Body-Padding bottom 80 px auf @media screen, damit die fixed Toolbar
keinen Inhalt verdeckt. PDF-Render ist unbeeinflusst (Toolbar via
@media print { display:none }).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dotty Dotter 2026-05-07 14:16:06 +02:00
parent 0b5dcba8f9
commit 443c9b0874

View File

@ -56,7 +56,7 @@
min-height: 100vh; min-height: 100vh;
overflow: auto !important; overflow: auto !important;
margin: 0; margin: 0;
padding: 20px; padding: 20px 20px 80px; /* bottom-padding fuer die fixed Toolbar */
} }
body { body {
display: flex; display: flex;
@ -76,7 +76,67 @@
position: absolute; position: absolute;
top: 0; left: 0; top: 0; left: 0;
} }
/* Statusleiste am unteren Viewport-Rand mit Download-Button.
Nur im Browser sichtbar; PDF bekommt sie nicht. */
.screen-toolbar {
position: fixed;
bottom: 0; left: 0; right: 0;
background: var(--ink);
color: var(--paper);
padding: 12px 24px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
z-index: 1000;
font-family: 'JetBrains Mono', monospace;
font-size: 13px;
flex-wrap: wrap;
box-shadow: 0 -8px 24px rgba(0,0,0,0.3);
} }
.screen-toolbar .label {
opacity: 0.75;
letter-spacing: 0.04em;
}
.screen-toolbar .label strong {
color: var(--paper);
font-weight: 600;
opacity: 1;
}
.screen-toolbar .actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.screen-download {
background: var(--accent);
color: var(--paper);
padding: 8px 18px;
border-radius: 4px;
font-family: 'JetBrains Mono', monospace;
font-size: 13px;
font-weight: 700;
text-decoration: none;
border: none;
cursor: pointer;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.screen-download:hover {
filter: brightness(1.1);
}
.screen-download.secondary {
background: transparent;
color: var(--paper);
border: 1.5px solid rgba(245, 241, 234, 0.3);
}
.screen-download.secondary:hover {
background: rgba(245, 241, 234, 0.1);
border-color: var(--paper);
}
}
@media print { .screen-toolbar { display: none !important; } }
/* Kopfleiste */ /* Kopfleiste */
.head{ .head{
@ -266,6 +326,27 @@
</div> </div>
</div>{# .card-viewport #} </div>{# .card-viewport #}
{# Statusleiste mit Download-Button (nur im Browser, nicht im PDF). #}
{% set _drs_safe = assessment.drucksache | replace('/', '-') %}
<div class="screen-toolbar">
<div class="label">
<strong>Scorecard</strong> · {{ bundesland }} · {{ assessment.drucksache }}
· 1080×1350 (Instagram 4:5)
</div>
<div class="actions">
<a class="screen-download"
href="/api/assessment/scorecard.png?drucksache={{ assessment.drucksache | urlencode }}&bundesland={{ bundesland | urlencode }}&format=portrait&scale=2"
download="gwoe-scorecard-{{ _drs_safe }}.png">
⬇ PNG herunterladen
</a>
<a class="screen-download secondary"
href="/api/assessment/scorecard.pdf?drucksache={{ assessment.drucksache | urlencode }}&bundesland={{ bundesland | urlencode }}&format=portrait"
download="gwoe-scorecard-{{ _drs_safe }}.pdf">
PDF
</a>
</div>
</div>
<script> <script>
/* Browser-Preview-Skalierung: Card soll 90 % der Viewport-Hoehe einnehmen. /* Browser-Preview-Skalierung: Card soll 90 % der Viewport-Hoehe einnehmen.
Falls die daraus resultierende Breite ueber den Viewport hinausginge Falls die daraus resultierende Breite ueber den Viewport hinausginge