diff --git a/server/pruef-backstage-import.mjs b/server/pruef-backstage-import.mjs
index fc65afd6..532f333e 100644
--- a/server/pruef-backstage-import.mjs
+++ b/server/pruef-backstage-import.mjs
@@ -397,6 +397,65 @@ console.log("\n=== Die Seite ===");
ok(/Backstage/.test(netzSatz),
"unter den Knöpfen steht dauerhaft, woher die Zahlen kommen");
+ /* ---- EINE EXCEL-DATEI IST EINE SACKGASSE MIT WEGWEISER ----------
+ Filipe: "meine excel datei kann ich nicht auswaehlen wieso??"
+
+ Sie liess sich nicht einmal AUSWAEHLEN -- ausgegraut, ohne ein
+ Wort dazu. Jetzt laesst sie sich waehlen und bekommt eine Antwort,
+ die weiterhilft.
+
+ GEPRUEFT WIRD AN EINEM ECHTEN ZIP-KOPF ("PK"), denn genau das ist
+ eine .xlsx. Die Erkennung haengt am INHALT, nicht an der Endung --
+ deshalb heisst die Testdatei hier bewusst ".xlsx" UND traegt den
+ richtigen Kopf; eine Pruefung ueber die Endung waere gruen, ohne
+ die Erkennung je zu beruehren. */
+ await seite.click("#import-oeffnen");
+ await seite.waitForTimeout(300);
+ await seite.setInputFiles("#datei", {
+ name: "backstage.xlsx",
+ mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ buffer: Buffer.from([0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00]),
+ });
+ await seite.waitForTimeout(600);
+ const xlsx = await seite.evaluate(() => ({
+ text: document.getElementById("import-fehler")?.textContent || "",
+ vorschau: document.getElementById("vorschau")?.hidden,
+ los: document.getElementById("import-los")?.disabled,
+ feld: document.getElementById("datei")?.value || "",
+ }));
+ ok(/Excel/.test(xlsx.text), `es steht da, dass es eine Excel-Datei ist (${xlsx.text.slice(0, 50)}…)`);
+ ok(/Backstage-Tabelle einfügen/.test(xlsx.text),
+ "und welcher Knopf stattdessen hilft");
+ ok(/CSV/.test(xlsx.text), "und wie man sie umspeichert");
+ ok(xlsx.vorschau === true && xlsx.los === true,
+ "es entsteht KEINE Vorschau und der Übernehmen-Knopf bleibt gesperrt");
+ ok(xlsx.feld === "",
+ "und das Feld ist wieder leer -- eine Datei, die nicht geht, bleibt nicht stehen");
+ if (process.env.SCHIRM) {
+ await seite.locator("#import-dialog").screenshot(
+ { path: join(HIER, "pruef-import-excel.png") });
+ }
+
+ /* GEGENPROBE: Eine echte Text-Tabelle geht durch denselben Weg
+ weiterhin hindurch. Sonst hiesse das oben nur, dass gar nichts
+ mehr eingelesen wird. */
+ await seite.setInputFiles("#datei", {
+ name: "tage.csv",
+ mimeType: "text/csv",
+ buffer: Buffer.from(`Datum;Diamanten;LIVE-Dauer (Minuten)
+${gestern};1234;90
+`, "utf8"),
+ });
+ await seite.waitForTimeout(900);
+ const csv = await seite.evaluate(() => ({
+ fehler: document.getElementById("import-fehler")?.textContent || "",
+ vorschau: document.getElementById("vorschau")?.hidden,
+ }));
+ ok(csv.vorschau === false && !/Excel/.test(csv.fehler),
+ `eine CSV kommt weiterhin durch (Vorschau da: ${csv.vorschau === false})`);
+ await seite.click("#import-zu");
+ await seite.waitForTimeout(250);
+
/* ---- DER WEG UEBER DIE TIKTOK-DATEI IST WEG (11.09.2026) --------
Filipe: "ich will keine daten von denen kriegen, nur wir."
diff --git a/workspace/app.webmanifest b/workspace/app.webmanifest
index 075b7aee..64591473 100644
--- a/workspace/app.webmanifest
+++ b/workspace/app.webmanifest
@@ -13,25 +13,25 @@
"theme_color": "#06090f",
"icons": [
{
- "src": "/assets/img/app-symbole/workspace-192.png?v=202609111522",
+ "src": "/assets/img/app-symbole/workspace-192.png?v=202609111545",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
- "src": "/assets/img/app-symbole/workspace-512.png?v=202609111522",
+ "src": "/assets/img/app-symbole/workspace-512.png?v=202609111545",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
- "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609111522",
+ "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609111545",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
- "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609111522",
+ "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609111545",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
diff --git a/workspace/assets/js/leistung.js b/workspace/assets/js/leistung.js
index 70e2ab9a..6acb200c 100644
--- a/workspace/assets/js/leistung.js
+++ b/workspace/assets/js/leistung.js
@@ -456,6 +456,41 @@
$('vorschau').hidden = true;
$('import-los').disabled = true;
+ /* WAS IST DAS UEBERHAUPT FUER EINE DATEI? (11.09.2026)
+
+ Filipe: "meine excel datei kann ich nicht auswaehlen wieso??"
+
+ Bis heute stand im Auswahlfeld nur `.csv` -- eine .xlsx war
+ ausgegraut, ohne ein Wort dazu. Eine Sackgasse ohne Wegweiser
+ ist schlimmer als eine Absage mit Begruendung: Man sucht den
+ Fehler bei sich.
+
+ Jetzt laesst sich jede Datei waehlen, und WAS sie ist, sagt der
+ INHALT -- nicht die Endung. Eine umbenannte Datei ist keine
+ andere Datei. Eine .xlsx ist in Wahrheit ein ZIP-Archiv und
+ beginnt mit "PK"; die alte .xls beginnt mit dem
+ OLE-Kennzeichen D0 CF 11 E0.
+
+ WARUM WIR SIE NICHT EINFACH LESEN: Der Leser dahinter versteht
+ Text (Komma, Semikolon, Tabulator). Ein ZIP-Archiv als Text
+ gelesen ergibt Zeichensalat -- und der schlimmste Ausgang waere
+ nicht "geht nicht", sondern eine Vorschau mit Zahlen, die
+ irgendwo aus dem Dateikopf stammen. */
+ const anfang = new Uint8Array(await datei.slice(0, 8).arrayBuffer());
+ const beginnt = (...b) => b.every((x, i) => anfang[i] === x);
+ if (beginnt(0x50, 0x4b) || beginnt(0xd0, 0xcf, 0x11, 0xe0)) {
+ $('import-fehler').innerHTML =
+ 'Das ist eine Excel-Datei. Hier passt eine Text-Tabelle '
+ + '(CSV) hinein. Zwei Wege, beide dauern keine Minute:
'
+ + '1. In Excel alles markieren (Strg+A), kopieren (Strg+C) – '
+ + 'und statt hier den Knopf „Backstage-Tabelle einfügen" nehmen. '
+ + 'Dort einfügen, fertig.
'
+ + '2. Oder in Excel: Datei → Speichern unter und als '
+ + 'Dateityp CSV UTF-8 (durch Trennzeichen getrennt) wählen.';
+ $('datei').value = '';
+ return;
+ }
+
try {
importText = await datei.text();
} catch {
@@ -463,6 +498,13 @@
return;
}
+ /* Eine leere Datei ist kein Lesefehler, sieht aber wie einer aus. */
+ if (!importText.trim()) {
+ $('import-fehler').textContent = 'Die Datei ist leer.';
+ $('datei').value = '';
+ return;
+ }
+
/* ERST DIE VORSCHAU. Ein Import, der sofort schreibt, ist bei
einer falsch zugeordneten Spalte nicht mehr zurückzuholen. */
try {
diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html
index 2aa06edf..c1ac7b6e 100644
--- a/workspace/aufgaben.html
+++ b/workspace/aufgaben.html
@@ -12,19 +12,19 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
-
-
-
-
-
-
+
+
+
+
+
+
-
+