perf(mangel): Foto-Kompression vor Upload in beiden Clients ergänzt
CI / backend-tests (push) Successful in 3m17s
CI / frontend-build (push) Successful in 22s

PWA lud Mangel-Fotos bislang komplett unkomprimiert hoch (rohes File aus
capture-Input) - Canvas-basierte Verkleinerung/Neukodierung (max. 1600px,
JPEG q0.7) vor dem Enqueue ergänzt. Flutter-Client nutzte nur imageQuality
(reine JPEG-Kompression ohne Auflösungs-Downscale) - maxWidth/maxHeight
ergänzt. Beides zahlt sich bei schlechter Feld-Verbindung aus.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDMvneg6wKfXdXvTYNd3dN
This commit is contained in:
patrickandClaude Sonnet 5 committed 2026-09-12 22:23:31 +02:00
1 parent ecca012197
commit c8d9480cf9
4 files changed
+254 -2

No files matched your search

+204
View File
@@ -14795,3 +14795,207 @@ Keine Commits in dieser Session.
- frontend/DEVLOG.md | 387 ++++++++++++
---
## 2026-09-12 22:01 – 22:11 (9m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
- ecca012 fix(flutter): Sync-Abbruch-Bug in Offline-Queues behoben, API-URL konfigurierbar
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:12 – 22:12 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:14 – 22:15 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:15 – 22:15 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:15 – 22:15 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:16 – 22:17 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:17 – 22:17 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:18 – 22:18 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:19 – 22:19 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:20 – 22:20 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:21 – 22:21 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
## 2026-09-12 22:21 – 22:21 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- arbeitskacheln/15_mobile.md | 12 +++++++
- flutter_app/lib/main.dart | 12 ++++---
- flutter_app/lib/offline/mangel_queue.dart | 14 +++++++-
- flutter_app/lib/offline/queue.dart | 20 ++++++++++-
- flutter_app/test/mangel_queue_test.dart | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- flutter_app/test/offline_queue_test.dart | 6 ++++
---
@@ -61,7 +61,17 @@ class _MangelMeldenScreenState extends State<MangelMeldenScreen> {
}
Future<void> _fotoAufnehmen() async {
final bild = await ImagePicker().pickImage(source: ImageSource.camera, imageQuality: 70);
// Fund (Foto-Kompression-Check): `imageQuality` allein komprimiert nur die
// JPEG-Qualität, verkleinert aber NICHT die Auflösung - ein 12MP-Kamerafoto
// bleibt trotzdem mehrere MB groß. `maxWidth`/`maxHeight` skaliert zusätzlich
// herunter, was bei schlechter Feld-Verbindung den eigentlichen Unterschied
// macht (1600px reicht für einen Mangel-Nachweis völlig aus).
final bild = await ImagePicker().pickImage(
source: ImageSource.camera,
imageQuality: 70,
maxWidth: 1600,
maxHeight: 1600,
);
if (bild == null || !mounted) return;
setState(() => _foto = File(bild.path));
}
+34
View File
@@ -0,0 +1,34 @@
// Fund (Foto-Kompression-Check, Flutter-Pendant hat imageQuality+maxWidth/
// maxHeight): die PWA lud Mangel-Fotos bislang komplett unkomprimiert hoch
// (rohes File-Objekt aus <input type="file" capture>) - ein 12MP-Kamerafoto
// sind mehrere MB, die im Feld über schlechte Verbindung übertragen werden
// müssen. Canvas-basierte Verkleinerung/Neukodierung vor dem Enqueue schließt
// diese Lücke, ohne eine zusätzliche Bibliothek zu brauchen.
const MAX_ABMESSUNG = 1600;
const JPEG_QUALITAET = 0.7;
export async function komprimiereFoto(datei: File): Promise<File> {
if (!datei.type.startsWith("image/")) return datei;
const bitmap = await createImageBitmap(datei).catch(() => null);
if (!bitmap) return datei; // z.B. HEIC ohne Browser-Decoder-Unterstützung
const skalierung = Math.min(1, MAX_ABMESSUNG / Math.max(bitmap.width, bitmap.height));
const breite = Math.round(bitmap.width * skalierung);
const hoehe = Math.round(bitmap.height * skalierung);
const canvas = document.createElement("canvas");
canvas.width = breite;
canvas.height = hoehe;
const kontext = canvas.getContext("2d");
if (!kontext) return datei;
kontext.drawImage(bitmap, 0, 0, breite, hoehe);
const blob = await new Promise<Blob | null>((resolve) =>
canvas.toBlob(resolve, "image/jpeg", JPEG_QUALITAET)
);
if (!blob || blob.size >= datei.size) return datei; // kein Gewinn, Original behalten
const name = datei.name.replace(/\.[^.]+$/, "") + ".jpg";
return new File([blob], name, { type: "image/jpeg" });
}
+5 -1
View File
@@ -4,6 +4,7 @@ import { useSearchParams } from "react-router-dom";
import { apiRequest } from "../api/client";
import { useAuth } from "../auth/AuthContext";
import { DokumentePanel } from "../components/DokumentePanel";
import { komprimiereFoto } from "../offline/fotoKomprimierung";
import * as mangelQueue from "../offline/mangelQueue";
import type { MangelQueueItem } from "../offline/mangelQueue";
import { useOnlineStatus } from "../offline/useOnlineStatus";
@@ -173,7 +174,10 @@ export function MangelListePage() {
type="file"
accept="image/*"
capture="environment"
onChange={(e) => setFoto(e.target.files?.[0] ?? null)}
onChange={async (e) => {
const datei = e.target.files?.[0] ?? null;
setFoto(datei ? await komprimiereFoto(datei) : null);
}}
/>
<button className="btn btn-primary" onClick={melden} disabled={wirdGemeldet}>
Melden {!online && "(wird bei Verbindung übertragen)"}