iOS: Mengen-Sheet auf Form-Ebene praesentieren (erstes Oeffnen bleibt offen)
Das Menge-hinzufuegen/umlagern/entfernen-Sheet ging beim ersten Antippen sofort wieder zu: das spaet fertige Laden der Entnahme-Statistik (.task im Abschnitt) riss das gerade praesentierte Sheet weg. ObjectStockSection ist jetzt zustands- los (bekommt removals + Sheet-Binding); Sheet und Statistik-Laden liegen in der Detailansicht auf der stabilen Form-Ebene bzw. im reload(). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,16 +4,11 @@ import SwiftUI
|
|||||||
/// Lebensmittel. Erlaubt Hinzufügen, Umlagern (ohne Grund) und Entfernen (mit
|
/// Lebensmittel. Erlaubt Hinzufügen, Umlagern (ohne Grund) und Entfernen (mit
|
||||||
/// Pflicht-Grund) und zeigt eine kleine Entnahme-Statistik. Wird als Abschnitt
|
/// Pflicht-Grund) und zeigt eine kleine Entnahme-Statistik. Wird als Abschnitt
|
||||||
/// in die Produkt-Detailansicht eingebettet.
|
/// in die Produkt-Detailansicht eingebettet.
|
||||||
struct ObjectStockSection: View {
|
/// Welches Mengen-Sheet gerade offen ist. Wird auf der stabilen Form-Ebene der
|
||||||
let product: Product
|
/// Detailansicht präsentiert (nicht im Abschnitt selbst), sonst schloss sich das
|
||||||
let locations: [StorageLocation]
|
/// Fenster beim ersten Öffnen sofort wieder (das spät fertige Laden der
|
||||||
let lots: [Lot]
|
/// Entnahme-Statistik hat das frische Sheet weggerissen).
|
||||||
var onChanged: () async -> Void
|
enum ObjectStockSheet: Identifiable {
|
||||||
|
|
||||||
@State private var removals: RemovalSummary?
|
|
||||||
@State private var sheet: StockSheet?
|
|
||||||
|
|
||||||
enum StockSheet: Identifiable {
|
|
||||||
case add
|
case add
|
||||||
case relocate(from: String?)
|
case relocate(from: String?)
|
||||||
case remove(loc: String?)
|
case remove(loc: String?)
|
||||||
@@ -26,6 +21,13 @@ struct ObjectStockSection: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ObjectStockSection: View {
|
||||||
|
let product: Product
|
||||||
|
let locations: [StorageLocation]
|
||||||
|
let lots: [Lot]
|
||||||
|
let removals: RemovalSummary?
|
||||||
|
@Binding var sheet: ObjectStockSheet?
|
||||||
|
|
||||||
private var einheit: String { product.unitName.isEmpty ? "Stück" : product.unitName }
|
private var einheit: String { product.unitName.isEmpty ? "Stück" : product.unitName }
|
||||||
private func ortName(_ id: String?) -> String {
|
private func ortName(_ id: String?) -> String {
|
||||||
guard let id else { return "Ohne Lagerort" }
|
guard let id else { return "Ohne Lagerort" }
|
||||||
@@ -63,23 +65,6 @@ struct ObjectStockSection: View {
|
|||||||
.buttonStyle(.borderless)
|
.buttonStyle(.borderless)
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
}
|
}
|
||||||
// Sheet an den Abschnitt hängen (nicht an die Group) – sonst ging das
|
|
||||||
// Fenster bei manchen iOS-Versionen kurz auf und sofort wieder zu.
|
|
||||||
.sheet(item: $sheet) { welche in
|
|
||||||
NavigationStack {
|
|
||||||
switch welche {
|
|
||||||
case .add:
|
|
||||||
ObjectAddSheet(product: product, locations: locations, einheit: einheit,
|
|
||||||
perform: { await afterAction() })
|
|
||||||
case .relocate(let from):
|
|
||||||
ObjectRelocateSheet(product: product, locations: locations, initialFrom: from,
|
|
||||||
perform: { await afterAction() })
|
|
||||||
case .remove(let loc):
|
|
||||||
ObjectRemoveSheet(product: product, locations: locations, einheit: einheit,
|
|
||||||
initialLoc: loc, perform: { await afterAction() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let removals, !removals.stats.isEmpty {
|
if let removals, !removals.stats.isEmpty {
|
||||||
Section("Bereits entnommen") {
|
Section("Bereits entnommen") {
|
||||||
@@ -90,17 +75,6 @@ struct ObjectStockSection: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task(id: product.id) { await loadRemovals() }
|
|
||||||
.onChange(of: lots) { _ in Task { await loadRemovals() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadRemovals() async {
|
|
||||||
removals = try? await APIClient.shared.productRemovals(id: product.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func afterAction() async {
|
|
||||||
await onChanged()
|
|
||||||
await loadRemovals()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ struct ProductDetailView: View {
|
|||||||
@State private var fieldValues: [String: String] = [:]
|
@State private var fieldValues: [String: String] = [:]
|
||||||
|
|
||||||
@State private var editLot: Lot?
|
@State private var editLot: Lot?
|
||||||
|
// Mengen-Bestand (Menge je Lagerort): Sheet + Entnahme-Statistik werden hier
|
||||||
|
// gehalten und auf Form-Ebene präsentiert, damit das Sheet stabil aufgeht.
|
||||||
|
@State private var objectSheet: ObjectStockSheet?
|
||||||
|
@State private var objectRemovals: RemovalSummary?
|
||||||
|
|
||||||
private let labels = ["", "Glas", "Tüte", "Flasche", "Dose", "Tube", "Becher", "Beutel", "Karton"]
|
private let labels = ["", "Glas", "Tüte", "Flasche", "Dose", "Tube", "Becher", "Beutel", "Karton"]
|
||||||
|
|
||||||
@@ -92,7 +96,7 @@ struct ProductDetailView: View {
|
|||||||
} else if current.isObject && !current.isBulk {
|
} else if current.isObject && !current.isBulk {
|
||||||
// Menge je Lagerort (Verbrauchsgegenstände laufen als Charge, s.u.).
|
// Menge je Lagerort (Verbrauchsgegenstände laufen als Charge, s.u.).
|
||||||
ObjectStockSection(product: current, locations: locations, lots: lots,
|
ObjectStockSection(product: current, locations: locations, lots: lots,
|
||||||
onChanged: { await reload() })
|
removals: objectRemovals, sheet: $objectSheet)
|
||||||
} else {
|
} else {
|
||||||
// Lebensmittel und Verbrauchsgegenstände: Vorrat aus den Chargen.
|
// Lebensmittel und Verbrauchsgegenstände: Vorrat aus den Chargen.
|
||||||
Section("Bestand") {
|
Section("Bestand") {
|
||||||
@@ -287,6 +291,25 @@ struct ProductDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Mengen-Sheets auf der stabilen Form-Ebene (nicht im Abschnitt), sonst
|
||||||
|
// schloss sich das Fenster beim ersten Öffnen sofort wieder.
|
||||||
|
.sheet(item: $objectSheet) { welche in
|
||||||
|
NavigationStack {
|
||||||
|
switch welche {
|
||||||
|
case .add:
|
||||||
|
ObjectAddSheet(product: current, locations: locations,
|
||||||
|
einheit: current.unitName.isEmpty ? "Stück" : current.unitName,
|
||||||
|
perform: { await reload() })
|
||||||
|
case .relocate(let from):
|
||||||
|
ObjectRelocateSheet(product: current, locations: locations, initialFrom: from,
|
||||||
|
perform: { await reload() })
|
||||||
|
case .remove(let loc):
|
||||||
|
ObjectRemoveSheet(product: current, locations: locations,
|
||||||
|
einheit: current.unitName.isEmpty ? "Stück" : current.unitName,
|
||||||
|
initialLoc: loc, perform: { await reload() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.task {
|
.task {
|
||||||
fillForm()
|
fillForm()
|
||||||
await reload()
|
await reload()
|
||||||
@@ -355,6 +378,12 @@ struct ProductDetailView: View {
|
|||||||
} else {
|
} else {
|
||||||
fieldDefs = []
|
fieldDefs = []
|
||||||
}
|
}
|
||||||
|
// Entnahme-Statistik nur für „Menge je Lagerort"-Gegenstände.
|
||||||
|
if current.isObject && !current.isBulk && !current.isIndividual {
|
||||||
|
objectRemovals = try? await APIClient.shared.productRemovals(id: current.id)
|
||||||
|
} else {
|
||||||
|
objectRemovals = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verwaltungsart (individual/bulk) mitschicken, wenn sie umgestellt wurde.
|
/// Verwaltungsart (individual/bulk) mitschicken, wenn sie umgestellt wurde.
|
||||||
|
|||||||
Reference in New Issue
Block a user