|
|
|
@@ -2648,6 +2648,10 @@ let belegFehler = null;
|
|
|
|
|
// Entwurf auf das Ergebnis wartet - die Antwort selbst trägt dafür keine
|
|
|
|
|
// praefix-Information, nur tank_id (bei einem Parse-Ergebnis immer null).
|
|
|
|
|
let belegZielPraefix = null;
|
|
|
|
|
// Offenes Beleg-Fenster: { tankId, kontext, fehler }. Am Rechner wird der Beleg
|
|
|
|
|
// hineingezogen, am Telefon aus der Zwischenablage eingefügt (aus der Mail
|
|
|
|
|
// kopiertes PDF) - der Dateiauswahl-Knopf bleibt als dritter Weg erhalten.
|
|
|
|
|
let belegPopup = null;
|
|
|
|
|
|
|
|
|
|
/* Wischen nach links zum Löschen (Fahrten/Tankvorgänge/Servicebuch), analog
|
|
|
|
|
zu iOS-Listen. swipeZeile() umschließt eine bestehende Listenzeile mit
|
|
|
|
@@ -2995,6 +2999,83 @@ function hinweis(titel, text) {
|
|
|
|
|
sheet = { titel, text, knopf: null, aktion: null };
|
|
|
|
|
render();
|
|
|
|
|
}
|
|
|
|
|
/* ------------------------------------------------------- Beleg-Upload
|
|
|
|
|
Ein Weg für alle drei Quellen (Ziehen, Einfügen, Dateiauswahl). Kein
|
|
|
|
|
serviceRufen() hier: dessen catch loggt nur in die Konsole - siehe den
|
|
|
|
|
ausführlichen Kommentar an der früheren Aufrufstelle, der Grund gilt
|
|
|
|
|
unverändert. */
|
|
|
|
|
function belegDateiVerarbeiten(datei, tankId, kontext) {
|
|
|
|
|
if (!datei) return;
|
|
|
|
|
if (datei.type && datei.type !== "application/pdf") {
|
|
|
|
|
belegFehler = { context: kontext, meldung: "Nur PDF-Belege werden unterstützt" };
|
|
|
|
|
render(); return;
|
|
|
|
|
}
|
|
|
|
|
const leser = new FileReader();
|
|
|
|
|
leser.onload = () => {
|
|
|
|
|
const base64 = leser.result.split(",")[1];
|
|
|
|
|
HASS.callService("pyscript", "audi_dashboard_beleg_hochladen",
|
|
|
|
|
{ pdf_base64: base64, dateiname: datei.name || "beleg.pdf", tank_id: tankId }
|
|
|
|
|
).catch((err) => {
|
|
|
|
|
console.error("audi_dashboard: audi_dashboard_beleg_hochladen", err);
|
|
|
|
|
belegFehler = { context: kontext, meldung: "Beleg konnte nicht hochgeladen werden" };
|
|
|
|
|
if (!tankId) belegEntwurf = null;
|
|
|
|
|
render();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
leser.readAsDataURL(datei);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Nimmt das erste PDF aus einer DataTransfer-/Clipboard-Dateiliste. */
|
|
|
|
|
function belegAusDateiliste(dateien) {
|
|
|
|
|
return [...(dateien || [])].find((d) => d.type === "application/pdf") || null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function belegPopupSchliessenUndLaden(datei) {
|
|
|
|
|
const ctx = belegPopup;
|
|
|
|
|
if (!ctx || !datei) return;
|
|
|
|
|
belegPopup = null;
|
|
|
|
|
belegDateiVerarbeiten(datei, ctx.tankId, ctx.kontext);
|
|
|
|
|
render();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Telefon-Weg: das aus der Mail kopierte PDF liegt in der Zwischenablage.
|
|
|
|
|
navigator.clipboard.read() ist der einzige Weg, ohne dass der Nutzer in ein
|
|
|
|
|
Eingabefeld tippen muss - iOS zeigt dafür seine eigene Einfügen-Abfrage. */
|
|
|
|
|
async function belegAusZwischenablage() {
|
|
|
|
|
const ctx = belegPopup;
|
|
|
|
|
if (!ctx) return;
|
|
|
|
|
if (!navigator.clipboard || !navigator.clipboard.read) {
|
|
|
|
|
belegPopup.fehler = "Dieser Browser kann die Zwischenablage nicht lesen - bitte ziehen oder Datei auswählen.";
|
|
|
|
|
render(); return;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
for (const eintrag of await navigator.clipboard.read()) {
|
|
|
|
|
const typ = (eintrag.types || []).find((t) => t === "application/pdf");
|
|
|
|
|
if (typ) { belegPopupSchliessenUndLaden(new File([await eintrag.getType(typ)], "beleg.pdf", { type: "application/pdf" })); return; }
|
|
|
|
|
}
|
|
|
|
|
belegPopup.fehler = "In der Zwischenablage liegt kein PDF.";
|
|
|
|
|
} catch (err) {
|
|
|
|
|
belegPopup.fehler = "Kein Zugriff auf die Zwischenablage.";
|
|
|
|
|
}
|
|
|
|
|
render();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function vBelegPopup() {
|
|
|
|
|
if (!belegPopup) return "";
|
|
|
|
|
return `<div class="beleg-catcher" data-belegzu></div>
|
|
|
|
|
<div class="beleg-popup" role="dialog" aria-modal="true" aria-label="Beleg hochladen">
|
|
|
|
|
<span class="label">Beleg hochladen</span>
|
|
|
|
|
<div class="beleg-drop" id="belegDrop">
|
|
|
|
|
<strong>PDF hierher ziehen</strong>
|
|
|
|
|
<span>oder aus der Zwischenablage einfügen</span>
|
|
|
|
|
</div>
|
|
|
|
|
${belegPopup.fehler ? `<span class="label" style="margin-top:10px;color:var(--red)">${esc(belegPopup.fehler)}</span>` : ""}
|
|
|
|
|
<button class="aktion primaer" data-belegeinfuegen>Aus Zwischenablage einfügen</button>
|
|
|
|
|
<button class="aktion" data-belegdatei>Datei auswählen</button>
|
|
|
|
|
<button class="aktion" data-belegzu>Abbrechen</button>
|
|
|
|
|
</div>`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function sheetMarkup() {
|
|
|
|
|
if (!sheet) return "";
|
|
|
|
|
const kopf = `<div class="sheet-kopf"><strong>${esc(sheet.titel)}</strong>${sheet.text ? `<span>${esc(sheet.text)}</span>` : ""}</div>`;
|
|
|
|
@@ -3157,7 +3238,7 @@ function render() {
|
|
|
|
|
// faellt sonst bei jedem render(), z. B. beim Umschalten des Filters,
|
|
|
|
|
// auf 0 zurueck).
|
|
|
|
|
const setupKoerperScroll = setupOffen ? (ROOT.querySelector(".setup-koerper") || {}).scrollTop : 0;
|
|
|
|
|
ROOT.getElementById("overlay").innerHTML = sheetMarkup() + vSetupPopup();
|
|
|
|
|
ROOT.getElementById("overlay").innerHTML = sheetMarkup() + vSetupPopup() + vBelegPopup();
|
|
|
|
|
if (setupOffen) {
|
|
|
|
|
const koerper = ROOT.querySelector(".setup-koerper");
|
|
|
|
|
if (koerper) koerper.scrollTop = setupKoerperScroll || 0;
|
|
|
|
@@ -3274,8 +3355,38 @@ function ereignisseVerdrahten() {
|
|
|
|
|
Hintergrund wandert. Beides gehoert zum Vertrag dieser Rolle.
|
|
|
|
|
Der Handler haengt am Dokument, weil Fokus im Shadow DOM auch ausserhalb
|
|
|
|
|
von ROOT liegen kann. */
|
|
|
|
|
/* Ziehen und Ablegen (Rechner) sowie Einfügen per Tastatur, solange das
|
|
|
|
|
Beleg-Fenster offen ist. Die Listener hängen am Dokument, weil ein Zug
|
|
|
|
|
über der Ablagefläche im Shadow DOM sonst schon am Rand verloren geht -
|
|
|
|
|
und weil ohne preventDefault auf dragover der Browser das PDF einfach
|
|
|
|
|
selbst öffnet, statt ein drop-Ereignis zu liefern. */
|
|
|
|
|
document.addEventListener("dragover", (e) => {
|
|
|
|
|
if (!belegPopup) return;
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
const zone = ROOT.getElementById("belegDrop");
|
|
|
|
|
if (zone) zone.classList.add("darueber");
|
|
|
|
|
});
|
|
|
|
|
document.addEventListener("dragleave", (e) => {
|
|
|
|
|
if (!belegPopup || e.relatedTarget) return;
|
|
|
|
|
const zone = ROOT.getElementById("belegDrop");
|
|
|
|
|
if (zone) zone.classList.remove("darueber");
|
|
|
|
|
});
|
|
|
|
|
document.addEventListener("drop", (e) => {
|
|
|
|
|
if (!belegPopup) return;
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
const datei = belegAusDateiliste(e.dataTransfer && e.dataTransfer.files);
|
|
|
|
|
if (datei) { belegPopupSchliessenUndLaden(datei); return; }
|
|
|
|
|
belegPopup.fehler = "Das war kein PDF.";
|
|
|
|
|
render();
|
|
|
|
|
});
|
|
|
|
|
document.addEventListener("paste", (e) => {
|
|
|
|
|
if (!belegPopup) return;
|
|
|
|
|
const datei = belegAusDateiliste(e.clipboardData && e.clipboardData.files);
|
|
|
|
|
if (datei) { e.preventDefault(); belegPopupSchliessenUndLaden(datei); }
|
|
|
|
|
});
|
|
|
|
|
document.addEventListener("keydown", (e) => {
|
|
|
|
|
if (e.key === "Escape") {
|
|
|
|
|
if (belegPopup) { belegPopup = null; render(); e.preventDefault(); return; }
|
|
|
|
|
if (sheet) { sheetSchliessen(); e.preventDefault(); return; }
|
|
|
|
|
if (setupSucheOffen) { setupSucheOffen = null; setupSuchtext = ""; render(); e.preventDefault(); return; }
|
|
|
|
|
if (setupOffen) { setupSchliessen(); e.preventDefault(); return; }
|
|
|
|
@@ -3740,31 +3851,18 @@ function ereignisseVerdrahten() {
|
|
|
|
|
const kontext = nurParsen ? praefix : ziel;
|
|
|
|
|
if (belegFehler && belegFehler.context === kontext) belegFehler = null;
|
|
|
|
|
belegZielPraefix = praefix;
|
|
|
|
|
// Statt direkt die Dateiauswahl zu öffnen: Fenster mit Ablagefläche
|
|
|
|
|
// (Rechner) und Einfügen-Knopf (Telefon, PDF aus der Mail kopiert).
|
|
|
|
|
belegPopup = { tankId, kontext, fehler: null };
|
|
|
|
|
render();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (e.target.closest("[data-belegzu]")) { belegPopup = null; render(); return; }
|
|
|
|
|
if (e.target.closest("[data-belegeinfuegen]")) { belegAusZwischenablage(); return; }
|
|
|
|
|
if (e.target.closest("[data-belegdatei]")) {
|
|
|
|
|
const eingabe = document.createElement("input");
|
|
|
|
|
eingabe.type = "file"; eingabe.accept = "application/pdf";
|
|
|
|
|
eingabe.onchange = () => {
|
|
|
|
|
const datei = eingabe.files[0]; if (!datei) return;
|
|
|
|
|
const leser = new FileReader();
|
|
|
|
|
leser.onload = () => {
|
|
|
|
|
const base64 = leser.result.split(",")[1];
|
|
|
|
|
// Kein serviceRufen() hier: dessen catch loggt nur in die Browser-
|
|
|
|
|
// Konsole (bei allen anderen Aufrufen ok, die reagieren auf
|
|
|
|
|
// datenLaden() statt auf den Rückgabewert) - ein rejizierter
|
|
|
|
|
// Service-Aufruf (Backend nicht erreichbar, Timeout, ...) blieb
|
|
|
|
|
// für den Beleg-Upload sonst komplett unsichtbar in der
|
|
|
|
|
// Oberfläche, obwohl der Backend-seitige Parser-Fehlerpfad selbst
|
|
|
|
|
// schon einen Fehler veröffentlicht (siehe belegverarbeitung.py).
|
|
|
|
|
HASS.callService("pyscript", "audi_dashboard_beleg_hochladen",
|
|
|
|
|
{ pdf_base64: base64, dateiname: datei.name, tank_id: tankId }
|
|
|
|
|
).catch((err) => {
|
|
|
|
|
console.error("audi_dashboard: audi_dashboard_beleg_hochladen", err);
|
|
|
|
|
belegFehler = { context: kontext, meldung: "Beleg konnte nicht hochgeladen werden" };
|
|
|
|
|
if (!tankId) belegEntwurf = null;
|
|
|
|
|
render();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
leser.readAsDataURL(datei);
|
|
|
|
|
};
|
|
|
|
|
eingabe.onchange = () => belegPopupSchliessenUndLaden(eingabe.files[0]);
|
|
|
|
|
eingabe.click();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|