import SwiftData import SwiftUI import UIKit struct ResultEditorView: View { enum Mode { case recognized case manual var title: String { switch self { case .recognized: return "Review Result" case .manual: return "Manual Entry" } } } @Environment(\.modelContext) private var modelContext @ObservedObject var flowModel: ScanFlowModel let mode: Mode var body: some View { Group { if let session = flowModel.currentSession { Form { if let image = session.thumbnailJPEGData.flatMap(UIImage.init(data:)) { Section { Image(uiImage: image) .resizable() .scaledToFit() .frame(maxWidth: .infinity) .clipShape(RoundedRectangle(cornerRadius: 18)) } } Section("Recognition") { LabeledContent("Source", value: draftBinding.wrappedValue.source.title) LabeledContent("Confidence", value: draftBinding.wrappedValue.confidence.title) Text(draftBinding.wrappedValue.confidence.helperText) .font(.footnote) .foregroundStyle(.secondary) Text(draftBinding.wrappedValue.source.detail) .font(.footnote) .foregroundStyle(.secondary) } Section("Card details") { TextField("Card name", text: draftBinding.cardName) .textInputAutocapitalization(.words) TextField("Card number", text: draftBinding.cardNumber) .textInputAutocapitalization(.never) TextField("Set", text: draftBinding.setIdentifier) .textInputAutocapitalization(.words) Picker("Rarity", selection: draftBinding.rarity) { ForEach(CardRarity.allCases) { rarity in Text(rarity.rawValue).tag(rarity.rawValue) } } } if !draftBinding.wrappedValue.notes.isEmpty { Section("Hints") { ForEach(Array(draftBinding.wrappedValue.notes.enumerated()), id: \.offset) { _, note in Text(note) .font(.subheadline) } } } if !draftBinding.wrappedValue.rawText.isEmpty { Section("OCR fragments") { Text(draftBinding.wrappedValue.rawText) .font(.footnote.monospaced()) .foregroundStyle(.secondary) .textSelection(.enabled) } } Section { Button("Confirm and Log") { confirmDraft() } .buttonStyle(.borderedProminent) Button(mode == .recognized ? "Manual Entry" : "Re-scan") { if mode == .recognized { flowModel.openManualEntryFromResult() } else { flowModel.returnToScan() } } if mode == .recognized { Button("Re-scan") { flowModel.returnToScan() } .foregroundStyle(.red) } } } } else { ContentUnavailableView("No scan loaded", systemImage: "rectangle.and.text.magnifyingglass", description: Text("Capture or import a card image first.")) } } .navigationTitle(mode.title) .navigationBarTitleDisplayMode(.inline) } private var draftBinding: Binding { Binding( get: { flowModel.currentSession?.draft ?? .manualPrefill() }, set: { flowModel.updateDraft($0) } ) } private func confirmDraft() { let draft = draftBinding.wrappedValue modelContext.insert(ConfirmedScanRecord(draft: draft)) flowModel.returnToScan(message: "Saved to temporary local log.") } } private extension Binding where Value == CardRecognitionDraft { var cardName: Binding { Binding( get: { wrappedValue.cardName }, set: { var draft = wrappedValue draft.cardName = $0 wrappedValue = draft } ) } var cardNumber: Binding { Binding( get: { wrappedValue.cardNumber }, set: { var draft = wrappedValue draft.cardNumber = $0 wrappedValue = draft } ) } var setIdentifier: Binding { Binding( get: { wrappedValue.setIdentifier }, set: { var draft = wrappedValue draft.setIdentifier = $0 wrappedValue = draft } ) } var rarity: Binding { Binding( get: { wrappedValue.rarity }, set: { var draft = wrappedValue draft.rarity = $0 wrappedValue = draft } ) } }