Ort zuordnen: Karte zeigt, was man da in der Hand haelt

Bisher stand dort nur "Einzelstück R9D55SDC". Die UID sagt aber nichts darueber,
welches Stueck das ist - gerade bei Kleidung, wo das NFC-Tag eingenaeht und
unsichtbar ist. Unter dem Sucher lag dafuer die halbe Seite ungenutzt.

Die Karte zeigt jetzt Vorschaubild, Artikelname, Marke und die Notiz, darunter
als beschriftete Zeilen die Einzelstuecknummer und den aktuellen Lagerort. Der
Ort ist beim Zuordnen die nuetzlichste Angabe ueberhaupt: man sieht sofort, ob
das Stueck schon irgendwo liegt.

Der Block war mit 50 Zeilen zu gross fuer den ohnehin langen body und steht
jetzt als EinzelstueckKarte fuer sich.

ProductThumb bekommt dafuer einen Groessenparameter (Vorgabe 34 wie bisher, also
keine Aenderung an den Listen) und bei grossen Bildern einen passenderen
Eckenradius und ein groesseres Ersatzsymbol.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-12 14:05:47 +02:00
co-authored by Claude Opus 5
parent a220823882
commit ba84e4ea01
2 changed files with 70 additions and 14 deletions
+62 -10
View File
@@ -24,16 +24,7 @@ struct AssignScanView: View {
.padding(.horizontal) .padding(.horizontal)
if let pending { if let pending {
VStack(spacing: 4) { EinzelstueckKarte(item: pending) { self.pending = nil }
Text("Einzelstück \(pending.uid)").font(.headline)
Text("Jetzt den Lagerort-QR scannen")
.font(.caption).foregroundStyle(.secondary)
Button("Abbrechen") { self.pending = nil }
.font(.caption).padding(.top, 2)
}
.padding().frame(maxWidth: .infinity)
.background(Color.marke.opacity(0.15))
.clipShape(RoundedRectangle(cornerRadius: 12)).padding(.horizontal)
} else { } else {
VStack(spacing: 8) { VStack(spacing: 8) {
Text("Schritt 1: Einzelstück-QR scannen – oder ohne Code:") Text("Schritt 1: Einzelstück-QR scannen – oder ohne Code:")
@@ -125,6 +116,67 @@ struct AssignScanView: View {
} }
} }
/// Was gerade in der Hand liegt und auf einen Lagerort wartet.
///
/// Die UID allein sagt nichts darueber, was man da haelt - gerade bei Kleidung,
/// wo das NFC-Tag eingenaeht und unsichtbar ist. Unter dem Sucher ist reichlich
/// Platz, also zeigt die Karte Bild, Name, Marke und den aktuellen Ort.
private struct EinzelstueckKarte: View {
let item: Item
var onAbbrechen: () -> Void
var body: some View {
VStack(alignment: .leading, spacing: 10) {
HStack(alignment: .top, spacing: 12) {
ProductThumb(productId: item.productId, groesse: 64)
VStack(alignment: .leading, spacing: 3) {
Text(item.productName ?? "Unbekannter Artikel")
.font(.headline)
if let marke = item.productBrand, !marke.isEmpty {
Text(marke).font(.subheadline).foregroundStyle(.secondary)
}
if let notiz = item.note, !notiz.isEmpty {
Text(notiz).font(.caption).foregroundStyle(.secondary).lineLimit(2)
}
}
Spacer(minLength: 0)
}
Divider()
zeile("Einzelstücknummer") {
Text(item.uid).font(.callout.monospaced())
}
zeile("Derzeit") {
Text(item.locationName ?? "ohne Lagerort").multilineTextAlignment(.trailing)
}
Divider()
HStack {
Text("Jetzt den Lagerort-QR scannen")
.font(.caption).foregroundStyle(.secondary)
Spacer()
Button("Abbrechen", action: onAbbrechen).font(.caption)
}
}
.padding().frame(maxWidth: .infinity)
.background(Color.marke.opacity(0.15))
.clipShape(RoundedRectangle(cornerRadius: 12)).padding(.horizontal)
}
private func zeile<Inhalt: View>(
_ titel: String, @ViewBuilder inhalt: () -> Inhalt
) -> some View {
HStack {
Text(titel).foregroundStyle(.secondary)
Spacer()
inhalt()
}
.font(.callout)
}
}
/// Einzelstück aus einer durchsuchbaren Liste wählen – für Stücke ohne eigenen QR. /// Einzelstück aus einer durchsuchbaren Liste wählen – für Stücke ohne eigenen QR.
struct ItemPickerSheet: View { struct ItemPickerSheet: View {
var onPick: (Item) -> Void var onPick: (Item) -> Void
+8 -4
View File
@@ -93,11 +93,14 @@ final class ProductImageCache {
/// Kästchen-Symbol wie bisher. Ergebnisse kommen aus dem gemeinsamen Cache. /// Kästchen-Symbol wie bisher. Ergebnisse kommen aus dem gemeinsamen Cache.
struct ProductThumb: View { struct ProductThumb: View {
let productId: Int let productId: Int
/// Kantenlaenge. Listen lassen sie weg; wo mehr Platz ist (Ort zuordnen),
/// hilft ein groesseres Bild beim Wiedererkennen.
var groesse: CGFloat = 34
@State private var image: UIImage? @State private var image: UIImage?
var body: some View { var body: some View {
content content
.frame(width: 34, height: 34) .frame(width: groesse, height: groesse)
.task(id: productId) { await load() } .task(id: productId) { await load() }
} }
@@ -105,11 +108,12 @@ struct ProductThumb: View {
if let image { if let image {
Image(uiImage: image) Image(uiImage: image)
.resizable().scaledToFill() .resizable().scaledToFill()
.frame(width: 34, height: 34) .frame(width: groesse, height: groesse)
.clipShape(RoundedRectangle(cornerRadius: 6)) .clipShape(RoundedRectangle(cornerRadius: groesse > 48 ? 10 : 6))
} else { } else {
Image(systemName: "shippingbox") Image(systemName: "shippingbox")
.font(.body).foregroundStyle(.secondary) .font(groesse > 48 ? .largeTitle : .body)
.foregroundStyle(.secondary)
} }
} }