diff --git a/ios/Sources/AssignScanView.swift b/ios/Sources/AssignScanView.swift index 4a598d8..f506658 100644 --- a/ios/Sources/AssignScanView.swift +++ b/ios/Sources/AssignScanView.swift @@ -24,16 +24,7 @@ struct AssignScanView: View { .padding(.horizontal) if let pending { - VStack(spacing: 4) { - 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) + EinzelstueckKarte(item: pending) { self.pending = nil } } else { VStack(spacing: 8) { 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( + _ 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. struct ItemPickerSheet: View { var onPick: (Item) -> Void diff --git a/ios/Sources/ProductPhotoView.swift b/ios/Sources/ProductPhotoView.swift index 1081d38..d67c13d 100644 --- a/ios/Sources/ProductPhotoView.swift +++ b/ios/Sources/ProductPhotoView.swift @@ -93,11 +93,14 @@ final class ProductImageCache { /// Kästchen-Symbol wie bisher. Ergebnisse kommen aus dem gemeinsamen Cache. struct ProductThumb: View { 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? var body: some View { content - .frame(width: 34, height: 34) + .frame(width: groesse, height: groesse) .task(id: productId) { await load() } } @@ -105,11 +108,12 @@ struct ProductThumb: View { if let image { Image(uiImage: image) .resizable().scaledToFill() - .frame(width: 34, height: 34) - .clipShape(RoundedRectangle(cornerRadius: 6)) + .frame(width: groesse, height: groesse) + .clipShape(RoundedRectangle(cornerRadius: groesse > 48 ? 10 : 6)) } else { Image(systemName: "shippingbox") - .font(.body).foregroundStyle(.secondary) + .font(groesse > 48 ? .largeTitle : .body) + .foregroundStyle(.secondary) } }