diff --git a/ios/Sources/ItemViews.swift b/ios/Sources/ItemViews.swift index 577401d..80655a9 100644 --- a/ios/Sources/ItemViews.swift +++ b/ios/Sources/ItemViews.swift @@ -76,6 +76,8 @@ struct DateRow: View { struct ProductItemsSection: View { let product: Product var onChanged: () async -> Void + /// Bis zur Liste schließen (für „Anlegen & fertig"). + var onFinish: (() -> Void)? = nil @State private var items: [Item] = [] @State private var showAdd = false @@ -102,7 +104,8 @@ struct ProductItemsSection: View { .task(id: product.id) { await reload() } .sheet(isPresented: $showAdd) { NavigationStack { - ItemAddSheet(productId: product.id) { await refresh() } + ItemAddSheet(productId: product.id, onDone: { await refresh() }, + onFinish: onFinish.map { finish in { showAdd = false; finish() } }) } } } @@ -436,6 +439,8 @@ struct ItemEditView: View { struct ItemAddSheet: View { let productId: Int var onDone: () async -> Void + /// Wenn gesetzt: „Anlegen & fertig" schließt bis zur Liste (nicht nur das Sheet). + var onFinish: (() -> Void)? = nil @Environment(\.dismiss) private var dismiss @@ -514,7 +519,11 @@ struct ItemAddSheet: View { if let error { Section { Text(error).foregroundStyle(.red).font(.callout) } } Section { - Button(busy ? "Anlegen…" : "Anlegen") { Task { await create() } }.disabled(busy) + Button(busy ? "Anlegen…" : "Anlegen") { Task { await create(finish: false) } }.disabled(busy) + if onFinish != nil { + Button(busy ? "Anlegen…" : "Anlegen & fertig") { Task { await create(finish: true) } } + .disabled(busy) + } } } .navigationTitle("Einzelstücke anlegen") @@ -575,7 +584,7 @@ struct ItemAddSheet: View { : "Übernommen: \(teile.joined(separator: ", "))." } - private func create() async { + private func create(finish: Bool) async { busy = true; defer { busy = false }; error = nil do { var sid = shopId @@ -598,7 +607,7 @@ struct ItemAddSheet: View { filename: docName.isEmpty ? "beleg" : docName, contentType: docType) } await onDone() - dismiss() + if finish, let onFinish { onFinish() } else { dismiss() } } catch { self.error = error.localizedDescription } } } diff --git a/ios/Sources/ProductDetailView.swift b/ios/Sources/ProductDetailView.swift index 46d24bd..1569dcb 100644 --- a/ios/Sources/ProductDetailView.swift +++ b/ios/Sources/ProductDetailView.swift @@ -5,8 +5,10 @@ struct ProductDetailView: View { let product: Product @EnvironmentObject private var display: DisplaySettings + @Environment(\.dismiss) private var dismiss @State private var current: Product + @State private var showLeaveConfirm = false @State private var lots: [Lot] = [] @State private var busy = false @State private var error: String? @@ -40,6 +42,29 @@ struct ProductDetailView: View { _current = State(initialValue: product) } + /// Ungespeicherte Änderungen an den Artikel-Stammdaten? (Foto und Lagerort- + /// Bedarf speichern sofort und zählen hier nicht.) + private var isDirty: Bool { + if name != current.name { return true } + if brand != (current.brand ?? "") { return true } + if categoryId != current.categoryId { return true } + if current.isObject { + if !current.isIndividual, shopId != current.shopId { return true } + if productUrl != (current.productUrl ?? "") { return true } + if fieldValues != Self.stringValues(current.fieldValues) { return true } + } else { + if packageSize != (current.packageSize.map { formatAmount($0) } ?? "") { return true } + if packageLabel != (current.packageLabel ?? "") { return true } + if datePrecision != (current.datePrecision == "month" ? "month" : "day") { return true } + if groupId != current.groupId { return true } + } + return false + } + + private func attemptLeave() { + if isDirty { showLeaveConfirm = true } else { dismiss() } + } + var body: some View { Form { if let status { @@ -52,7 +77,8 @@ struct ProductDetailView: View { ProductPhotoSection(productId: current.id) if current.isObject && current.isIndividual { - ProductItemsSection(product: current, onChanged: { await reload() }) + ProductItemsSection(product: current, onChanged: { await reload() }, + onFinish: { dismiss() }) } else if current.isObject { ObjectStockSection(product: current, locations: locations, lots: lots, onChanged: { await reload() }) @@ -193,6 +219,21 @@ struct ProductDetailView: View { } .navigationTitle(current.name) .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button { attemptLeave() } label: { Label("Zurück", systemImage: "chevron.left") } + } + } + .confirmationDialog("Ungespeicherte Änderungen", isPresented: $showLeaveConfirm, + titleVisibility: .visible) { + Button("Speichern") { Task { await save(); dismiss() } } + Button("Verwerfen", role: .destructive) { dismiss() } + Button("Abbrechen", role: .cancel) {} + } message: { + Text("Änderungen an Name/Marke/Kategorie sind noch nicht gespeichert. " + + "Foto und Lagerort-Bedarf werden sofort gespeichert.") + } .sheet(item: $editLot) { lot in NavigationStack { LotEditView(lot: lot, product: current) {