import SwiftUI /// Zuordnen per Scan: erst ein Einzelstück (…/i/) scannen, dann einen /// Lagerort (…/l/) – das Stück wird dem Ort zugewiesen. Danach gleich das /// nächste Stück, ohne Tippen. struct AssignScanView: View { @Environment(\.dismiss) private var dismiss @State private var paused = false @State private var torchOn = false @State private var torchLocked = false @State private var pending: Item? // gescanntes Stück, wartet auf Ort @State private var status: String? @State private var error: String? var body: some View { ScrollView { VStack(spacing: 14) { ScannerView(onCode: { code in Task { await handle(code) } }, isPaused: $paused, torchOn: $torchOn, torchLocked: $torchLocked) .aspectRatio(4.0 / 3.0, contentMode: .fit) .clipShape(RoundedRectangle(cornerRadius: 16)) .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.accentColor.opacity(0.15)) .clipShape(RoundedRectangle(cornerRadius: 12)).padding(.horizontal) } else { Text("Schritt 1: Einzelstück-QR scannen") .font(.caption).foregroundStyle(.secondary) } if let status { Text(status).font(.callout).foregroundStyle(.green) .frame(maxWidth: .infinity, alignment: .leading).padding(.horizontal) } if let error { Text(error).font(.callout).foregroundStyle(.red) .frame(maxWidth: .infinity, alignment: .leading).padding(.horizontal) } } .padding(.vertical) } .navigationTitle("Ort zuordnen") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("Fertig") { dismiss() } } ToolbarItem(placement: .topBarTrailing) { TorchButton(isOn: $torchOn, locked: $torchLocked) } } } private func handle(_ code: String) async { // Lagerort-QR (…/l/)? Der Code ist eine 10-stellige Zeichenkette. if let r = code.range(of: "/l/") { let locCode = String(code[r.upperBound...]) .trimmingCharacters(in: CharacterSet(charactersIn: "/ ")) .uppercased() if !locCode.isEmpty { guard let it = pending else { error = "Erst ein Einzelstück scannen."; status = nil; return } do { _ = try await APIClient.shared.updateItem(id: it.id, ItemUpdateRequest( locationId: locCode, shopId: it.shopId, acquiredOn: it.acquiredOn, warrantyUntil: it.warrantyUntil, note: it.note)) status = "\(it.uid) zugeordnet. Nächstes Stück scannen." error = nil pending = nil } catch { self.error = error.localizedDescription } return } } // 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 it = try? await APIClient.shared.itemByUid(uid: uid) { pending = it; status = nil; error = nil return } } error = "Unbekannter Code – bitte einen Einzelstück- oder Lagerort-QR scannen." } }