iOS: Kassenzettel in den Scannen-Tab, Markenblau statt System-Blau, weisse Kacheln, luftigere Pruefen-Liste

- Kassenzettel-Kachel vom Listen- in den Scannen-Tab verschoben (passt thematisch).
- App-Akzent auf Markenblau (#3F51B5/#7C8AE8, wie Web/App-Icon) via .tint; alle
  Color.accentColor -> Color.marke. System-Blau verschwindet.
- Kachel-Buttons/Links auf .buttonStyle(.plain): weisse Schrift/Symbole statt
  getoentem Blau.
- Kassenzettel-Pruefen-Liste: je Artikel eine eigene Section (insetGrouped) mit
  Artikel/Menge/Lagerort in eigenen Zeilen - klar gruppiert, nicht mehr gequetscht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-30 19:05:03 +02:00
parent 648b8294b8
commit 1648d1b917
10 changed files with 85 additions and 64 deletions

View File

@@ -503,12 +503,12 @@ struct KassenzettelMarkierenView: View {
ZStack(alignment: .topLeading) {
Image(uiImage: bild).resizable().scaledToFit()
.frame(width: geo.size.width, height: geo.size.height)
Rectangle().stroke(Color.accentColor, lineWidth: 2)
.background(Color.accentColor.opacity(0.15))
Rectangle().stroke(Color.marke, lineWidth: 2)
.background(Color.marke.opacity(0.15))
.frame(width: rect.width, height: rect.height)
.offset(x: rect.minX, y: rect.minY)
.gesture(zieh(resize: false))
Rectangle().fill(Color.accentColor)
Rectangle().fill(Color.marke)
.frame(width: 26, height: 26)
.offset(x: rect.maxX - 13, y: rect.maxY - 13)
.gesture(zieh(resize: true))
@@ -608,7 +608,14 @@ struct KassenzettelPruefenView: View {
}.padding()
} else {
List {
ForEach($zeilen) { $z in zeile($z) }
ForEach($zeilen) { $z in
Section {
zeile($z)
} header: {
Text(z.text)
.textCase(nil).font(.footnote).foregroundStyle(.secondary)
}
}
Section {
Button { Task { await einlagern() } } label: {
HStack {
@@ -620,6 +627,7 @@ struct KassenzettelPruefenView: View {
Text("Nicht zugeordnete Zeilen werden übersprungen. Neue Artikel werden nicht angelegt.")
}
}
.listStyle(.insetGrouped)
}
}
.onAppear(perform: aufbauen)
@@ -637,47 +645,49 @@ struct KassenzettelPruefenView: View {
} message: { Text(meldung ?? "") }
}
/// Drei Zeilen (Rows) je Artikel: Auswahl, Menge, Lagerort. In einer eigenen
/// Section gruppiert so ist klar, was zusammengehört, und nichts ist gequetscht.
@ViewBuilder private func zeile(_ z: Binding<KassenzettelZeile>) -> some View {
let zeile = z.wrappedValue
VStack(alignment: .leading, spacing: 8) {
Text(zeile.text).font(.caption).foregroundStyle(.secondary).lineLimit(1)
Menu {
ForEach(zeile.kandidaten) { k in
Button("\(k.name) · \(k.score) %") {
z.wrappedValue.artikel = KassenzettelArtikel(id: k.productId, name: k.name, unit: k.checkInUnit)
}
}
Divider()
Button { sucheZiel = ZeilenRef(id: zeile.id) } label: { Label("Suchen…", systemImage: "magnifyingglass") }
if zeile.artikel != nil {
Button(role: .destructive) { z.wrappedValue.artikel = nil } label: {
Label("Nicht zuordnen", systemImage: "xmark")
}
}
} label: {
HStack {
Image(systemName: zeile.artikel == nil ? "questionmark.circle" : "checkmark.circle.fill")
.foregroundStyle(zeile.artikel == nil ? Color.secondary : Color.green)
Text(zeile.artikel?.name ?? " Artikel wählen ")
.foregroundStyle(zeile.artikel == nil ? Color.secondary : Color.primary)
Spacer()
Image(systemName: "chevron.up.chevron.down").font(.caption2).foregroundStyle(.secondary)
// Artikel wählen
Menu {
ForEach(zeile.kandidaten) { k in
Button("\(k.name) · \(k.score) %") {
z.wrappedValue.artikel = KassenzettelArtikel(id: k.productId, name: k.name, unit: k.checkInUnit)
}
}
HStack(spacing: 12) {
Stepper("Menge \(Int(zeile.menge))", value: z.menge, in: 1...99).fixedSize()
Divider()
Button { sucheZiel = ZeilenRef(id: zeile.id) } label: { Label("Suchen…", systemImage: "magnifyingglass") }
if zeile.artikel != nil {
Button(role: .destructive) { z.wrappedValue.artikel = nil } label: {
Label("Nicht zuordnen", systemImage: "xmark")
}
}
} label: {
HStack {
Image(systemName: zeile.artikel == nil ? "questionmark.circle" : "checkmark.circle.fill")
.foregroundStyle(zeile.artikel == nil ? Color.secondary : Color.green)
Text(zeile.artikel?.name ?? " Artikel wählen ")
.foregroundStyle(zeile.artikel == nil ? Color.secondary : Color.primary)
Spacer()
Menu {
Button(" ohne ") { z.wrappedValue.ortId = nil }
ForEach(locations) { loc in Button(loc.name) { z.wrappedValue.ortId = loc.id } }
} label: {
Label(ortName(zeile.ortId), systemImage: "mappin.and.ellipse").font(.callout).lineLimit(1)
}
Button { scanZiel = ZeilenRef(id: zeile.id) } label: {
Image(systemName: "qrcode.viewfinder")
}.buttonStyle(.borderless)
Image(systemName: "chevron.up.chevron.down").font(.caption2).foregroundStyle(.secondary)
}
}.padding(.vertical, 4)
}
// Menge
Stepper("Menge: \(Int(zeile.menge))", value: z.menge, in: 1...99)
// Lagerort + QR-Scan
HStack {
Menu {
Button(" ohne ") { z.wrappedValue.ortId = nil }
ForEach(locations) { loc in Button(loc.name) { z.wrappedValue.ortId = loc.id } }
} label: {
Label(ortName(zeile.ortId), systemImage: "mappin.and.ellipse")
}
Spacer()
Button { scanZiel = ZeilenRef(id: zeile.id) } label: {
Label("QR", systemImage: "qrcode.viewfinder")
}.buttonStyle(.borderless)
}
}
private func ortName(_ id: String?) -> String {