import SwiftUI /// Nur ansehen: Barcode → Artikel, Einzelstück-QR → genau dieses Stück, /// Lagerort-QR → Liste aller Artikel an diesem Ort. Kein Ein-/Auslagern. struct LookupScanView: View { @Environment(\.dismiss) private var dismiss @EnvironmentObject private var display: DisplaySettings @State private var paused = false @State private var torchOn = false @State private var torchLocked = false @State private var error: String? @State private var item: Item? @State private var product: Product? @State private var contents: LocationContents? var body: some View { ScrollView { VStack(spacing: 14) { ScannerView(onCode: { code in Task { await resolve(code) } }, isPaused: $paused, torchOn: $torchOn, torchLocked: $torchLocked) .aspectRatio(4.0 / 3.0, contentMode: .fit) .clipShape(RoundedRectangle(cornerRadius: 16)) .padding(.horizontal) Text("Barcode, Einzelstück- oder Lagerort-QR scannen – nur ansehen") .font(.caption).foregroundStyle(.secondary) .multilineTextAlignment(.center) if let error { Text(error).font(.callout).foregroundStyle(.red) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal) } } .padding(.vertical) } .navigationTitle("Nachschlagen") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("Fertig") { dismiss() } } ToolbarItem(placement: .topBarTrailing) { TorchButton(isOn: $torchOn, locked: $torchLocked) } } .sheet(item: $item, onDismiss: { paused = false }) { it in NavigationStack { ItemEditView(item: it) } } .sheet(item: $product, onDismiss: { paused = false }) { p in NavigationStack { ProductDetailView(product: p) } .environmentObject(display) } .sheet(item: $contents, onDismiss: { paused = false }) { c in NavigationStack { LocationContentsView(contents: c) } .environmentObject(display) } } private func resolve(_ code: String) async { paused = true error = nil // Einzelstück-QR (…/i/)? if let r = code.range(of: "/i/") { let uid = String(code[r.upperBound...]) .trimmingCharacters(in: CharacterSet(charactersIn: "/ ")).uppercased() if !uid.isEmpty, let found = try? await APIClient.shared.itemByUid(uid: uid) { item = found return } } // Lagerort-QR (…/l/)? → alles zeigen, was dort (inkl. Unterorte) liegt. if let r = code.range(of: "/l/") { let loc = String(code[r.upperBound...]) .trimmingCharacters(in: CharacterSet(charactersIn: "/ ")).uppercased() if !loc.isEmpty { do { contents = try await APIClient.shared.locationContents(code: loc) } catch { self.error = "Lagerort nicht gefunden: \(loc)" paused = false } return } } // Sonst als Barcode auflösen. do { let result = try await APIClient.shared.lookup(barcode: code) if let existing = result.existingProduct { product = existing } else { error = "Nicht im Katalog: \(code)" paused = false } } catch { self.error = error.localizedDescription paused = false } } } /// Liste aller Artikel eines gescannten Lagerorts (inkl. Unterorte). struct LocationContentsView: View { let contents: LocationContents @EnvironmentObject private var display: DisplaySettings var body: some View { List { if contents.products.isEmpty { Text("Hier ist nichts eingelagert.").foregroundStyle(.secondary) } else { Section { ForEach(contents.products) { e in NavigationLink { ProductLoaderView(productId: e.productId).environmentObject(display) } label: { HStack { VStack(alignment: .leading, spacing: 2) { Text(e.name) if let brand = e.brand, !brand.isEmpty { Text(brand).font(.caption).foregroundStyle(.secondary) } } Spacer() Text("\(mengeText(e.stock)) \(e.unitLabel)") .foregroundStyle(.secondary) } } } } footer: { Text("Bestand inkl. der Unterorte. Tippen öffnet den Artikel.") } } } .navigationTitle(contents.locationName) .navigationBarTitleDisplayMode(.inline) } private func mengeText(_ v: Double) -> String { v == v.rounded() ? String(Int(v)) : String(format: "%.2f", v) } } /// Lädt einen Artikel per ID nach und zeigt dann seine Detailansicht. struct ProductLoaderView: View { let productId: Int @EnvironmentObject private var display: DisplaySettings @State private var product: Product? @State private var error: String? var body: some View { Group { if let product { ProductDetailView(product: product).environmentObject(display) } else if let error { Text(error).foregroundStyle(.red).padding() } else { ProgressView() } } .task { do { product = try await APIClient.shared.product(id: productId) } catch { self.error = error.localizedDescription } } } }