397 lines
12 KiB
HTML
397 lines
12 KiB
HTML
|
|
{% extends "v2/base.html" %}
|
|||
|
|
|
|||
|
|
{% block title %}Scorecard-Werkstatt — GWÖ-Antragsprüfer{% endblock %}
|
|||
|
|
|
|||
|
|
{% set v2_active_nav = "" %}
|
|||
|
|
|
|||
|
|
{% block head_extra %}
|
|||
|
|
<style>
|
|||
|
|
.ws-shell {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: 1fr 480px;
|
|||
|
|
gap: 18px;
|
|||
|
|
height: calc(100vh - 100px);
|
|||
|
|
min-height: 700px;
|
|||
|
|
}
|
|||
|
|
@media (max-width: 1100px) {
|
|||
|
|
.ws-shell { grid-template-columns: 1fr; height: auto; }
|
|||
|
|
}
|
|||
|
|
.ws-preview {
|
|||
|
|
background: var(--ecg-bg-subtle);
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
padding: 14px;
|
|||
|
|
overflow: auto;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 10px;
|
|||
|
|
}
|
|||
|
|
.ws-preview iframe {
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
background: #fff;
|
|||
|
|
/* iframe-Skalierung: 1080×1350 → ein bisschen verkleinert anzeigen,
|
|||
|
|
Skalierung pro Format konfigurierbar via JS */
|
|||
|
|
transform-origin: top left;
|
|||
|
|
background: #fff;
|
|||
|
|
}
|
|||
|
|
.ws-frame-wrap {
|
|||
|
|
overflow: hidden;
|
|||
|
|
background: #fff;
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
.ws-controls {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 14px;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
padding-right: 6px;
|
|||
|
|
}
|
|||
|
|
.ws-section {
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
padding: 12px 14px;
|
|||
|
|
background: var(--ecg-card-bg);
|
|||
|
|
}
|
|||
|
|
.ws-section h3 {
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 10px;
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
letter-spacing: 0.07em;
|
|||
|
|
color: var(--ecg-blue);
|
|||
|
|
margin: 0 0 8px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
}
|
|||
|
|
.ws-row {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 8px;
|
|||
|
|
align-items: center;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
margin-bottom: 6px;
|
|||
|
|
}
|
|||
|
|
.ws-row label {
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: var(--ecg-dark);
|
|||
|
|
opacity: 0.75;
|
|||
|
|
min-width: 90px;
|
|||
|
|
}
|
|||
|
|
.ws-row select,
|
|||
|
|
.ws-row input[type="text"] {
|
|||
|
|
flex: 1;
|
|||
|
|
padding: 4px 8px;
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
border-radius: 3px;
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 11px;
|
|||
|
|
background: var(--paper);
|
|||
|
|
color: var(--ecg-dark);
|
|||
|
|
}
|
|||
|
|
.ws-format-pills {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 4px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
.ws-format-pills button {
|
|||
|
|
padding: 4px 10px;
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
border-radius: 3px;
|
|||
|
|
background: var(--paper);
|
|||
|
|
color: var(--ecg-dark);
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 11px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
.ws-format-pills button.active {
|
|||
|
|
background: var(--ecg-blue);
|
|||
|
|
color: #fff;
|
|||
|
|
border-color: var(--ecg-blue);
|
|||
|
|
}
|
|||
|
|
textarea.ws-css-editor {
|
|||
|
|
width: 100%;
|
|||
|
|
min-height: 320px;
|
|||
|
|
flex: 1;
|
|||
|
|
padding: 10px 12px;
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
border-radius: 3px;
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 11px;
|
|||
|
|
background: #1f1f1f;
|
|||
|
|
color: #d8e4d8;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
resize: vertical;
|
|||
|
|
tab-size: 2;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
.ws-css-actions {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 6px;
|
|||
|
|
margin-top: 6px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
.ws-css-actions button {
|
|||
|
|
padding: 6px 14px;
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
border-radius: 3px;
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 11px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
background: var(--paper);
|
|||
|
|
color: var(--ecg-dark);
|
|||
|
|
}
|
|||
|
|
.ws-css-actions button.primary {
|
|||
|
|
background: var(--ecg-blue);
|
|||
|
|
border-color: var(--ecg-blue);
|
|||
|
|
color: #fff;
|
|||
|
|
font-weight: 700;
|
|||
|
|
}
|
|||
|
|
.ws-meta {
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 10px;
|
|||
|
|
color: var(--ecg-dark);
|
|||
|
|
opacity: 0.65;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
}
|
|||
|
|
.ws-embed-row code {
|
|||
|
|
display: block;
|
|||
|
|
padding: 8px 10px;
|
|||
|
|
background: var(--ecg-bg-subtle);
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
border-radius: 3px;
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 10px;
|
|||
|
|
word-break: break-all;
|
|||
|
|
user-select: all;
|
|||
|
|
}
|
|||
|
|
.ws-toolbar {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 6px;
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
.ws-toolbar button {
|
|||
|
|
padding: 3px 9px;
|
|||
|
|
border: 1px solid var(--ecg-border);
|
|||
|
|
border-radius: 3px;
|
|||
|
|
background: var(--paper);
|
|||
|
|
color: var(--ecg-dark);
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 10px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
.ws-toolbar span.zoom-info {
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 10px;
|
|||
|
|
color: var(--ecg-dark);
|
|||
|
|
opacity: 0.6;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
{% endblock %}
|
|||
|
|
|
|||
|
|
{% block main %}
|
|||
|
|
<div style="padding:0 0 1rem;">
|
|||
|
|
<h1 style="font-family:var(--font-display);font-size:22px;color:var(--ecg-teal);margin:0 0 4px;">Scorecard-Werkstatt</h1>
|
|||
|
|
<p style="font-size:12px;font-family:var(--font-mono);color:var(--ecg-dark);opacity:0.7;">
|
|||
|
|
Live-Editor für Scorecard-Layout. Drucksache + Format wählen, CSS im rechten Editor anpassen,
|
|||
|
|
Apply drücken — Vorschau aktualisiert ohne neue PNG-Render. Auch als Embed-Quelle für
|
|||
|
|
eingebettete Übersichten in anderen Kontexten nutzbar.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="ws-shell">
|
|||
|
|
|
|||
|
|
{# ── Vorschau links ──────────────────────────────────────────────── #}
|
|||
|
|
<div class="ws-preview">
|
|||
|
|
<div class="ws-toolbar">
|
|||
|
|
<button onclick="wsZoomFit()">Fit</button>
|
|||
|
|
<button onclick="wsZoom(0.4)">40 %</button>
|
|||
|
|
<button onclick="wsZoom(0.5)">50 %</button>
|
|||
|
|
<button onclick="wsZoom(0.65)">65 %</button>
|
|||
|
|
<button onclick="wsZoom(0.8)">80 %</button>
|
|||
|
|
<button onclick="wsZoom(1.0)">100 %</button>
|
|||
|
|
<span class="zoom-info" id="ws-zoom-info">—</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="ws-frame-wrap" id="ws-frame-wrap">
|
|||
|
|
<iframe id="ws-frame" src="about:blank" title="Scorecard-Vorschau"></iframe>
|
|||
|
|
</div>
|
|||
|
|
<div class="ws-meta" id="ws-meta">Drucksache + Format wählen.</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{# ── Controls + CSS-Editor rechts ────────────────────────────────── #}
|
|||
|
|
<div class="ws-controls">
|
|||
|
|
|
|||
|
|
<div class="ws-section">
|
|||
|
|
<h3>Inhalt</h3>
|
|||
|
|
<div class="ws-row">
|
|||
|
|
<label>Drucksache</label>
|
|||
|
|
<select id="ws-drs" onchange="wsRender()">
|
|||
|
|
{% for d in drucksachen %}
|
|||
|
|
<option value="{{ d.drucksache }}|{{ d.bundesland }}">
|
|||
|
|
{{ d.bundesland }} · {{ d.drucksache }} — {{ d.title }}
|
|||
|
|
</option>
|
|||
|
|
{% endfor %}
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
<div class="ws-row">
|
|||
|
|
<label>Format</label>
|
|||
|
|
<div class="ws-format-pills">
|
|||
|
|
<button data-format="portrait" class="active" onclick="wsSetFormat('portrait')">Portrait 4:5 (1080×1350)</button>
|
|||
|
|
<button data-format="square" onclick="wsSetFormat('square')">Square 1:1 (1080×1080)</button>
|
|||
|
|
<button data-format="og" onclick="wsSetFormat('og')">OG 16:8 (1200×630)</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="ws-section" style="display:flex;flex-direction:column;flex:1;min-height:280px;">
|
|||
|
|
<h3>CSS-Override (live)</h3>
|
|||
|
|
<textarea class="ws-css-editor" id="ws-css" placeholder="/* CSS-Selektoren in den iframe-Kontext.
|
|||
|
|
Beispiel:
|
|||
|
|
|
|||
|
|
.portrait-title { font-size: 28pt; }
|
|||
|
|
.portrait-score-num { font-size: 150pt; }
|
|||
|
|
.cell.r-0 { background: #c8c8c2; }
|
|||
|
|
*/"></textarea>
|
|||
|
|
<div class="ws-css-actions">
|
|||
|
|
<button class="primary" onclick="wsApplyCss()">Anwenden ⏎</button>
|
|||
|
|
<button onclick="wsResetCss()">Zurücksetzen</button>
|
|||
|
|
<button onclick="wsCopyCss()">CSS kopieren</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="ws-meta" style="margin-top:6px;">
|
|||
|
|
Tipp: Strg/⌘+Enter wendet das CSS sofort an. Reset entfernt nur den Override —
|
|||
|
|
die Server-CSS bleibt.
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="ws-section ws-embed-row">
|
|||
|
|
<h3>Direkt-Link / Embed</h3>
|
|||
|
|
<code id="ws-embed-url">—</code>
|
|||
|
|
<div class="ws-meta" style="margin-top:6px;">
|
|||
|
|
Iframe-Snippet:
|
|||
|
|
</div>
|
|||
|
|
<code id="ws-embed-iframe" style="margin-top:4px;">—</code>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{% endblock %}
|
|||
|
|
|
|||
|
|
{% block body_scripts %}
|
|||
|
|
<script>
|
|||
|
|
(function () {
|
|||
|
|
var drsSel = document.getElementById('ws-drs');
|
|||
|
|
var iframe = document.getElementById('ws-frame');
|
|||
|
|
var frameWrap = document.getElementById('ws-frame-wrap');
|
|||
|
|
var cssEditor = document.getElementById('ws-css');
|
|||
|
|
var meta = document.getElementById('ws-meta');
|
|||
|
|
var embedUrl = document.getElementById('ws-embed-url');
|
|||
|
|
var embedIframe = document.getElementById('ws-embed-iframe');
|
|||
|
|
var zoomInfo = document.getElementById('ws-zoom-info');
|
|||
|
|
|
|||
|
|
var currentFormat = 'portrait';
|
|||
|
|
var formatDims = {
|
|||
|
|
portrait: [1080, 1350],
|
|||
|
|
square: [1080, 1080],
|
|||
|
|
og: [1200, 630],
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
function getCurrent() {
|
|||
|
|
var v = drsSel.value || '';
|
|||
|
|
var parts = v.split('|');
|
|||
|
|
return { drs: parts[0] || '', bl: parts[1] || 'NRW' };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
window.wsSetFormat = function (f) {
|
|||
|
|
currentFormat = f;
|
|||
|
|
document.querySelectorAll('.ws-format-pills button').forEach(function (b) {
|
|||
|
|
b.classList.toggle('active', b.dataset.format === f);
|
|||
|
|
});
|
|||
|
|
wsRender();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
window.wsRender = function () {
|
|||
|
|
var c = getCurrent();
|
|||
|
|
if (!c.drs) return;
|
|||
|
|
var src = '/v2/scorecard'
|
|||
|
|
+ '?drucksache=' + encodeURIComponent(c.drs)
|
|||
|
|
+ '&bundesland=' + encodeURIComponent(c.bl)
|
|||
|
|
+ '&format=' + currentFormat
|
|||
|
|
+ '&_=' + Date.now();
|
|||
|
|
iframe.src = src;
|
|||
|
|
var dims = formatDims[currentFormat];
|
|||
|
|
iframe.width = dims[0];
|
|||
|
|
iframe.height = dims[1];
|
|||
|
|
iframe.style.width = dims[0] + 'px';
|
|||
|
|
iframe.style.height = dims[1] + 'px';
|
|||
|
|
meta.textContent = c.bl + ' · ' + c.drs + ' · ' + currentFormat
|
|||
|
|
+ ' (' + dims[0] + '×' + dims[1] + ')';
|
|||
|
|
var publicUrl = window.location.origin + '/v2/scorecard'
|
|||
|
|
+ '?drucksache=' + encodeURIComponent(c.drs)
|
|||
|
|
+ '&bundesland=' + encodeURIComponent(c.bl)
|
|||
|
|
+ '&format=' + currentFormat;
|
|||
|
|
embedUrl.textContent = publicUrl;
|
|||
|
|
embedIframe.textContent = '<iframe src="' + publicUrl + '" '
|
|||
|
|
+ 'width="' + dims[0] + '" height="' + dims[1] + '" '
|
|||
|
|
+ 'frameborder="0"></iframe>';
|
|||
|
|
setTimeout(wsZoomFit, 100);
|
|||
|
|
iframe.addEventListener('load', wsApplyCss, { once: true });
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
window.wsApplyCss = function () {
|
|||
|
|
var doc = iframe.contentDocument;
|
|||
|
|
if (!doc) return;
|
|||
|
|
var styleId = 'ws-override-style';
|
|||
|
|
var existing = doc.getElementById(styleId);
|
|||
|
|
if (existing) existing.remove();
|
|||
|
|
var style = doc.createElement('style');
|
|||
|
|
style.id = styleId;
|
|||
|
|
style.textContent = cssEditor.value;
|
|||
|
|
doc.head.appendChild(style);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
window.wsResetCss = function () {
|
|||
|
|
cssEditor.value = '';
|
|||
|
|
wsApplyCss();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
window.wsCopyCss = function () {
|
|||
|
|
if (navigator.clipboard && cssEditor.value) {
|
|||
|
|
navigator.clipboard.writeText(cssEditor.value);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
window.wsZoom = function (factor) {
|
|||
|
|
iframe.style.transform = 'scale(' + factor + ')';
|
|||
|
|
var dims = formatDims[currentFormat];
|
|||
|
|
frameWrap.style.width = (dims[0] * factor) + 'px';
|
|||
|
|
frameWrap.style.height = (dims[1] * factor) + 'px';
|
|||
|
|
zoomInfo.textContent = Math.round(factor * 100) + '%';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
window.wsZoomFit = function () {
|
|||
|
|
var avail = document.querySelector('.ws-preview').clientWidth - 30;
|
|||
|
|
var dims = formatDims[currentFormat];
|
|||
|
|
var factor = Math.min(1.0, avail / dims[0]);
|
|||
|
|
factor = Math.round(factor * 100) / 100;
|
|||
|
|
wsZoom(factor);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
cssEditor.addEventListener('keydown', function (e) {
|
|||
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
wsApplyCss();
|
|||
|
|
}
|
|||
|
|
if (e.key === 'Tab') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
var s = this.selectionStart;
|
|||
|
|
this.value = this.value.substring(0, s) + ' ' + this.value.substring(this.selectionEnd);
|
|||
|
|
this.selectionStart = this.selectionEnd = s + 2;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
wsRender();
|
|||
|
|
})();
|
|||
|
|
</script>
|
|||
|
|
{% endblock %}
|