iOS: "Nachschlagen"-Scan (nur ansehen)
Neue Kachel im Scannen-Tab: Barcode -> Artikel, Einzelstueck-QR -> genau das Stueck (Kaufdatum, Garantie, Ort ...), rein lesend ohne Ein-/Auslagern. Praktisch mit Einzelstuecken, um schnell nachzuschauen, welches Exemplar man in der Hand haelt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
76
ios/Sources/LookupScanView.swift
Normal file
76
ios/Sources/LookupScanView.swift
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// Nur ansehen: Barcode → Artikel, Einzelstück-QR → genau dieses Stück.
|
||||||
|
/// Kein Ein-/Auslagern – zum schnellen Nachschauen (z.B. wann gekauft, Garantie).
|
||||||
|
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 error: String?
|
||||||
|
@State private var item: Item?
|
||||||
|
@State private var product: Product?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView {
|
||||||
|
VStack(spacing: 14) {
|
||||||
|
ScannerView(onCode: { code in Task { await resolve(code) } },
|
||||||
|
isPaused: $paused, torchOn: $torchOn)
|
||||||
|
.aspectRatio(4.0 / 3.0, contentMode: .fit)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Text("Barcode oder Einzelstück-QR scannen – nur ansehen")
|
||||||
|
.font(.caption).foregroundStyle(.secondary)
|
||||||
|
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func resolve(_ code: String) async {
|
||||||
|
paused = true
|
||||||
|
error = nil
|
||||||
|
// 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 found = try? await APIClient.shared.itemByUid(uid: uid) {
|
||||||
|
item = found
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ struct RootView: View {
|
|||||||
switch route {
|
switch route {
|
||||||
case .checkin: CheckInView()
|
case .checkin: CheckInView()
|
||||||
case .checkout: CheckOutView()
|
case .checkout: CheckOutView()
|
||||||
|
case .lookup: LookupScanView()
|
||||||
case .expiring:
|
case .expiring:
|
||||||
ExpiringView()
|
ExpiringView()
|
||||||
.toolbar {
|
.toolbar {
|
||||||
@@ -93,6 +94,13 @@ struct ScanTabView: View {
|
|||||||
ActionTile(title: "Auslagern", subtitle: "Barcode scannen und Menge entnehmen",
|
ActionTile(title: "Auslagern", subtitle: "Barcode scannen und Menge entnehmen",
|
||||||
systemImage: "arrow.up.to.line")
|
systemImage: "arrow.up.to.line")
|
||||||
}
|
}
|
||||||
|
Button {
|
||||||
|
router.route = .lookup
|
||||||
|
} label: {
|
||||||
|
ActionTile(title: "Nachschlagen",
|
||||||
|
subtitle: "Barcode/QR scannen und nur ansehen",
|
||||||
|
systemImage: "doc.text.magnifyingglass")
|
||||||
|
}
|
||||||
// Ohne Barcode: gerade bei Gegenstaenden (Kleidung, Werkzeug) gibt
|
// Ohne Barcode: gerade bei Gegenstaenden (Kleidung, Werkzeug) gibt
|
||||||
// es keinen Code zum Scannen. Deshalb hier ein offensichtlicher Weg.
|
// es keinen Code zum Scannen. Deshalb hier ein offensichtlicher Weg.
|
||||||
if session.isAdmin {
|
if session.isAdmin {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ enum Route: String {
|
|||||||
case checkin
|
case checkin
|
||||||
case checkout
|
case checkout
|
||||||
case expiring
|
case expiring
|
||||||
|
case lookup
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Router: ObservableObject {
|
final class Router: ObservableObject {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
9B0A1A7C19764857BDC6ED26 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4741D0E95875919C921945CF /* RootView.swift */; };
|
9B0A1A7C19764857BDC6ED26 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4741D0E95875919C921945CF /* RootView.swift */; };
|
||||||
A7017E6C0532A650439AED7B /* BestBeforeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA022A27118BEEEC5912ECDC /* BestBeforeText.swift */; };
|
A7017E6C0532A650439AED7B /* BestBeforeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA022A27118BEEEC5912ECDC /* BestBeforeText.swift */; };
|
||||||
AAF27320D2A67C994C29C26F /* ProductDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F574168AA0F849D46C384EE /* ProductDetailView.swift */; };
|
AAF27320D2A67C994C29C26F /* ProductDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F574168AA0F849D46C384EE /* ProductDetailView.swift */; };
|
||||||
|
B531EEDAFAF442B1E86A9931 /* LookupScanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F22598772AD8B5EB6394F8 /* LookupScanView.swift */; };
|
||||||
D70CB183C868FF0143A6A5E7 /* DisplaySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC97A555856C6C5DD353E25 /* DisplaySettings.swift */; };
|
D70CB183C868FF0143A6A5E7 /* DisplaySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC97A555856C6C5DD353E25 /* DisplaySettings.swift */; };
|
||||||
D7717F44D2CDB75DC2693B59 /* ObjectStockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5BB49FBEFF88768B0BC902 /* ObjectStockView.swift */; };
|
D7717F44D2CDB75DC2693B59 /* ObjectStockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5BB49FBEFF88768B0BC902 /* ObjectStockView.swift */; };
|
||||||
DFA55EF4ACA34537F445E998 /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2F9406BD0D4F0D30FED345 /* Session.swift */; };
|
DFA55EF4ACA34537F445E998 /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2F9406BD0D4F0D30FED345 /* Session.swift */; };
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
121D44B9990DA931A9CD5847 /* ServerProfiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerProfiles.swift; sourceTree = "<group>"; };
|
121D44B9990DA931A9CD5847 /* ServerProfiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerProfiles.swift; sourceTree = "<group>"; };
|
||||||
163F5DCB00387215F0EA1F21 /* NotificationScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationScheduler.swift; sourceTree = "<group>"; };
|
163F5DCB00387215F0EA1F21 /* NotificationScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationScheduler.swift; sourceTree = "<group>"; };
|
||||||
1847E65D41ACEDD01CD108BC /* ServerFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerFetch.swift; sourceTree = "<group>"; };
|
1847E65D41ACEDD01CD108BC /* ServerFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerFetch.swift; sourceTree = "<group>"; };
|
||||||
|
18F22598772AD8B5EB6394F8 /* LookupScanView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LookupScanView.swift; sourceTree = "<group>"; };
|
||||||
1E86178BB91DBB86ADFB284B /* MasterDataViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterDataViews.swift; sourceTree = "<group>"; };
|
1E86178BB91DBB86ADFB284B /* MasterDataViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterDataViews.swift; sourceTree = "<group>"; };
|
||||||
314B1BB7220691A7423E8929 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
|
314B1BB7220691A7423E8929 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
|
||||||
369B9841E43E727ACA2E2A2A /* ProductViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductViews.swift; sourceTree = "<group>"; };
|
369B9841E43E727ACA2E2A2A /* ProductViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductViews.swift; sourceTree = "<group>"; };
|
||||||
@@ -102,6 +104,7 @@
|
|||||||
A6785CD9174067EFBB2B9043 /* ListViews.swift */,
|
A6785CD9174067EFBB2B9043 /* ListViews.swift */,
|
||||||
C35D34107C8CBB8B7C6C6650 /* LocalOverview.swift */,
|
C35D34107C8CBB8B7C6C6650 /* LocalOverview.swift */,
|
||||||
A75926511854BB8EE316ED3A /* LoginView.swift */,
|
A75926511854BB8EE316ED3A /* LoginView.swift */,
|
||||||
|
18F22598772AD8B5EB6394F8 /* LookupScanView.swift */,
|
||||||
1E86178BB91DBB86ADFB284B /* MasterDataViews.swift */,
|
1E86178BB91DBB86ADFB284B /* MasterDataViews.swift */,
|
||||||
314B1BB7220691A7423E8929 /* Models.swift */,
|
314B1BB7220691A7423E8929 /* Models.swift */,
|
||||||
163F5DCB00387215F0EA1F21 /* NotificationScheduler.swift */,
|
163F5DCB00387215F0EA1F21 /* NotificationScheduler.swift */,
|
||||||
@@ -227,6 +230,7 @@
|
|||||||
7738E209D4D32BFD514B76F5 /* ListViews.swift in Sources */,
|
7738E209D4D32BFD514B76F5 /* ListViews.swift in Sources */,
|
||||||
82A0C22D4AD228C36868853A /* LocalOverview.swift in Sources */,
|
82A0C22D4AD228C36868853A /* LocalOverview.swift in Sources */,
|
||||||
E9EA455CACB6ED5951B114C3 /* LoginView.swift in Sources */,
|
E9EA455CACB6ED5951B114C3 /* LoginView.swift in Sources */,
|
||||||
|
B531EEDAFAF442B1E86A9931 /* LookupScanView.swift in Sources */,
|
||||||
96E5A2455B2EC26050E69232 /* MasterDataViews.swift in Sources */,
|
96E5A2455B2EC26050E69232 /* MasterDataViews.swift in Sources */,
|
||||||
6816C33381DF52A96E5BB303 /* Models.swift in Sources */,
|
6816C33381DF52A96E5BB303 /* Models.swift in Sources */,
|
||||||
38133EE3EC920B462BDAA29F /* NotificationScheduler.swift in Sources */,
|
38133EE3EC920B462BDAA29F /* NotificationScheduler.swift in Sources */,
|
||||||
|
|||||||
Reference in New Issue
Block a user