Files
Vorrania/ios/Sources/AssignScanView.swift
Scarriffle 761c131286 iOS: Scanner – Pinch-Zoom und schlaueres Blitzlicht
- Zwei-Finger-Pinch zoomt im Kamerabild naeher an einen Code heran (digital,
  auf Faktor 6 begrenzt).
- Blitz geht nach dem ersten erfolgreichen Scan wieder aus. Per Langdruck auf
  den Blitz-Knopf bleibt er dauerhaft an (gesperrt, gelb); Tippen schaltet aus
  und hebt die Sperre auf. Gilt fuer alle Scan-Bildschirme.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 22:42:12 +02:00

92 lines
4.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
/// Zuordnen per Scan: erst ein Einzelstück (/i/<UID>) scannen, dann einen
/// Lagerort (/l/<ID>) 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/<ID>)?
if let r = code.range(of: "/l/") {
let idStr = String(code[r.upperBound...])
.trimmingCharacters(in: CharacterSet(charactersIn: "/ "))
if let locId = Int(idStr) {
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: locId, 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/<UID>)?
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."
}
}