Vorschau erstellt
This commit is contained in:
@@ -1,10 +1,114 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>Neuer Serienbrief</h1>
|
||||
<p>Vorlage und Empfänger-CSV auswählen. Die Spaltennamen der CSV müssen mit den Platzhaltern der Vorlage übereinstimmen (erste Zeile = Spaltennamen).</p>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<p>
|
||||
Vorlage und Empfänger-CSV auswählen. Die Spaltennamen der CSV müssen mit den
|
||||
Platzhaltern der Vorlage übereinstimmen (erste Zeile = Spaltennamen).
|
||||
Erstelle vorab eine <strong>Vorschau</strong>, um die Ausgabe mit der ersten
|
||||
Datenzeile zu prüfen.
|
||||
</p>
|
||||
|
||||
{% if preview_error %}
|
||||
<div class="messages">
|
||||
<ul><li>{{ preview_error }}</li></ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form id="job-form" method="post" action="{% url 'job-create' %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn">Erstellen und starten</button>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" id="btn-preview" class="btn btn-secondary">
|
||||
Vorschau (erste Zeile)
|
||||
</button>
|
||||
<button type="submit" class="btn">
|
||||
Job starten
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<section id="preview-section" hidden>
|
||||
<h2>Vorschau</h2>
|
||||
<p id="preview-status" class="muted"></p>
|
||||
<iframe id="preview-frame"
|
||||
title="Vorschau-PDF"
|
||||
style="width:100%; height:70vh; border:1px solid #ddd; border-radius:4px;"></iframe>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const form = document.getElementById("job-form");
|
||||
const btn = document.getElementById("btn-preview");
|
||||
const section = document.getElementById("preview-section");
|
||||
const status = document.getElementById("preview-status");
|
||||
const frame = document.getElementById("preview-frame");
|
||||
const previewUrl = "{% url 'job-preview' %}";
|
||||
const csrfToken = form.querySelector("[name=csrfmiddlewaretoken]").value;
|
||||
|
||||
let lastBlobUrl = null;
|
||||
|
||||
btn.addEventListener("click", async function () {
|
||||
btn.disabled = true;
|
||||
const originalText = btn.textContent;
|
||||
btn.textContent = "Erzeuge Vorschau…";
|
||||
section.hidden = false;
|
||||
status.textContent = "Bitte warten, LibreOffice rendert die erste Zeile…";
|
||||
status.classList.remove("error");
|
||||
|
||||
try {
|
||||
const formData = new FormData(form);
|
||||
const resp = await fetch(previewUrl, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: { "X-CSRFToken": csrfToken },
|
||||
credentials: "same-origin",
|
||||
});
|
||||
|
||||
if (!resp.ok) {
|
||||
// Server hat das Form-Template zurückgegeben → Fehlertext extrahieren
|
||||
const text = await resp.text();
|
||||
const match = text.match(/<li>([\s\S]*?)<\/li>/);
|
||||
const msg = match ? match[1].trim() : `Vorschau fehlgeschlagen (HTTP ${resp.status}).`;
|
||||
status.textContent = msg;
|
||||
status.classList.add("error");
|
||||
frame.removeAttribute("src");
|
||||
return;
|
||||
}
|
||||
|
||||
const ct = resp.headers.get("Content-Type") || "";
|
||||
if (!ct.includes("application/pdf")) {
|
||||
status.textContent = "Unerwartete Antwort vom Server (kein PDF).";
|
||||
status.classList.add("error");
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = await resp.blob();
|
||||
if (lastBlobUrl) URL.revokeObjectURL(lastBlobUrl);
|
||||
lastBlobUrl = URL.createObjectURL(blob);
|
||||
frame.src = lastBlobUrl;
|
||||
|
||||
const extra = resp.headers.get("X-Preview-Extra-Columns");
|
||||
let hint = "Vorschau bereit. Wenn alles passt, oben auf »Job starten« klicken.";
|
||||
if (extra && extra.trim().length > 0) {
|
||||
hint += " Hinweis: CSV enthält Spalten, die im Template nicht verwendet werden: " + extra + ".";
|
||||
}
|
||||
status.textContent = hint;
|
||||
} catch (err) {
|
||||
status.textContent = "Netzwerk- oder Browser-Fehler: " + err.message;
|
||||
status.classList.add("error");
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = originalText;
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.form-actions { display: flex; gap: 0.75rem; margin-top: 1rem; }
|
||||
.btn-secondary { background: #6b7280; }
|
||||
.muted { color: #6b7280; font-size: 0.95rem; }
|
||||
.muted.error { color: #dc2626; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user