iOS: Umschalter Lebensmittel/Gegenstand im Artikelformular
Gleiche Idee wie im Web: ein Typ-Umschalter filtert die Kategorieauswahl, statt Werkzeug & Co. zwischen den Lebensmittel-Kategorien zu suchen. Im Anlege-Formular blenden sich zudem die Lebensmittel-Felder (Gruppe, Einheit, MHD) aus, wenn "Gegenstand" gewaehlt ist. Die Detailansicht filtert die Kategorieliste auf den Typ des Artikels. CategoryPicker bekam dafuer einen optionalen Typ-Filter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,8 @@ import SwiftUI
|
||||
struct CategoryPicker: View {
|
||||
let categories: [CategoryItem]
|
||||
@Binding var selection: Int?
|
||||
/// Nur Kategorien dieses Typs zeigen ("food"/"object"); nil = alle.
|
||||
var tracking: String? = nil
|
||||
|
||||
var body: some View {
|
||||
Picker("Kategorie", selection: $selection) {
|
||||
@@ -18,17 +20,23 @@ struct CategoryPicker: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var gefiltert: [CategoryItem] {
|
||||
guard let tracking else { return categories }
|
||||
return categories.filter { $0.tracking == tracking }
|
||||
}
|
||||
|
||||
private func flattened() -> [(item: CategoryItem, depth: Int)] {
|
||||
let ids = Set(categories.map(\.id))
|
||||
let quelle = gefiltert
|
||||
let ids = Set(quelle.map(\.id))
|
||||
var result: [(CategoryItem, Int)] = []
|
||||
|
||||
func walk(_ node: CategoryItem, _ depth: Int) {
|
||||
result.append((node, depth))
|
||||
for child in categories.filter({ $0.parentId == node.id }) {
|
||||
for child in quelle.filter({ $0.parentId == node.id }) {
|
||||
walk(child, depth + 1)
|
||||
}
|
||||
}
|
||||
for root in categories.filter({ $0.parentId == nil || !ids.contains($0.parentId!) }) {
|
||||
for root in quelle.filter({ $0.parentId == nil || !ids.contains($0.parentId!) }) {
|
||||
walk(root, 0)
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -84,11 +84,12 @@ struct ProductDetailView: View {
|
||||
}
|
||||
|
||||
Section {
|
||||
CategoryPicker(categories: categories, selection: $categoryId)
|
||||
CategoryPicker(categories: categories, selection: $categoryId,
|
||||
tracking: current.tracking)
|
||||
} footer: {
|
||||
Text(current.isObject
|
||||
? "Kategorie bestimmt die Verwaltungsart (Gegenstand) und die eigenen Felder."
|
||||
: "Kategorie: nur für den Überblick in der Artikelliste.")
|
||||
? "Nur Gegenstands-Kategorien. Bestimmt die eigenen Felder."
|
||||
: "Nur Lebensmittel-Kategorien.")
|
||||
}
|
||||
|
||||
if current.isObject {
|
||||
|
||||
@@ -70,6 +70,8 @@ struct ProductFormView: View {
|
||||
@State private var datePrecision = "day"
|
||||
@State private var selectedGroupId: Int?
|
||||
@State private var selectedCategoryId: Int?
|
||||
// Umschalter Lebensmittel/Gegenstand: filtert die Kategorieauswahl.
|
||||
@State private var modus = "object"
|
||||
@State private var groups: [GroupItem] = []
|
||||
@State private var categories: [CategoryItem] = []
|
||||
@State private var units: [Unit] = []
|
||||
@@ -89,43 +91,61 @@ struct ProductFormView: View {
|
||||
LabeledField(label: "Marke", text: $brand)
|
||||
}
|
||||
Section {
|
||||
CategoryPicker(categories: categories, selection: $selectedCategoryId)
|
||||
Picker("Typ", selection: $modus) {
|
||||
Text("Lebensmittel").tag("food")
|
||||
Text("Gegenstand").tag("object")
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
CategoryPicker(categories: categories, selection: $selectedCategoryId, tracking: modus)
|
||||
} footer: {
|
||||
Text("Kategorie: nur für den Überblick in der Artikelliste.")
|
||||
Text(modus == "object"
|
||||
? "Gegenstand: Menge je Lagerort und eigene Felder."
|
||||
: "Lebensmittel: Chargen mit Mindesthaltbarkeit.")
|
||||
}
|
||||
Section {
|
||||
Picker("Gruppe", selection: $selectedGroupId) {
|
||||
Text("– keine –").tag(Int?.none)
|
||||
ForEach(groups) { gruppe in
|
||||
Text(gruppe.name).tag(Int?.some(gruppe.id))
|
||||
.onChange(of: modus) { neu in
|
||||
// Passt die gewählte Kategorie nicht mehr zum Typ, Auswahl leeren.
|
||||
if let cid = selectedCategoryId,
|
||||
categories.first(where: { $0.id == cid })?.tracking != neu {
|
||||
selectedCategoryId = nil
|
||||
}
|
||||
}
|
||||
// Lebensmittel-Felder (Gruppe, Einheit, MHD) nur im Lebensmittel-Modus.
|
||||
// Fuer Gegenstaende irrelevant - sie zaehlen in Stueck je Lagerort.
|
||||
if modus == "food" {
|
||||
Section {
|
||||
Picker("Gruppe", selection: $selectedGroupId) {
|
||||
Text("– keine –").tag(Int?.none)
|
||||
ForEach(groups) { gruppe in
|
||||
Text(gruppe.name).tag(Int?.some(gruppe.id))
|
||||
}
|
||||
}
|
||||
if session.isAdmin {
|
||||
Button {
|
||||
newGroupShown = true
|
||||
} label: {
|
||||
Label("Neue Gruppe anlegen", systemImage: "plus")
|
||||
}
|
||||
}
|
||||
} footer: {
|
||||
Text("Gruppe: zählt Bestände mehrerer Marken zusammen. Der EAN-Code "
|
||||
+ "dieses Artikels erscheint danach automatisch bei der Gruppe.")
|
||||
}
|
||||
Section("Einheit") {
|
||||
Picker("Basiseinheit", selection: $baseUnit) {
|
||||
ForEach(baseUnits, id: \.0) { Text($0.1).tag($0.0) }
|
||||
}
|
||||
QuantityField(label: "Packungsgröße", text: $packageSize,
|
||||
suffix: DisplaySettings.baseUnitLabel(baseUnit))
|
||||
Picker("Bezeichnung", selection: $packageLabel) {
|
||||
ForEach(labels, id: \.self) { Text($0.isEmpty ? "Packung (Standard)" : $0).tag($0) }
|
||||
}
|
||||
}
|
||||
if session.isAdmin {
|
||||
Button {
|
||||
newGroupShown = true
|
||||
} label: {
|
||||
Label("Neue Gruppe anlegen", systemImage: "plus")
|
||||
Section("MHD") {
|
||||
Picker("Angabe", selection: $datePrecision) {
|
||||
Text("Tagesdatum").tag("day")
|
||||
Text("nur Monat/Jahr").tag("month")
|
||||
}
|
||||
}
|
||||
} footer: {
|
||||
Text("Gruppe: zählt Bestände mehrerer Marken zusammen. Der EAN-Code "
|
||||
+ "dieses Artikels erscheint danach automatisch bei der Gruppe.")
|
||||
}
|
||||
Section("Einheit") {
|
||||
Picker("Basiseinheit", selection: $baseUnit) {
|
||||
ForEach(baseUnits, id: \.0) { Text($0.1).tag($0.0) }
|
||||
}
|
||||
QuantityField(label: "Packungsgröße", text: $packageSize,
|
||||
suffix: DisplaySettings.baseUnitLabel(baseUnit))
|
||||
Picker("Bezeichnung", selection: $packageLabel) {
|
||||
ForEach(labels, id: \.self) { Text($0.isEmpty ? "Packung (Standard)" : $0).tag($0) }
|
||||
}
|
||||
}
|
||||
Section("MHD") {
|
||||
Picker("Angabe", selection: $datePrecision) {
|
||||
Text("Tagesdatum").tag("day")
|
||||
Text("nur Monat/Jahr").tag("month")
|
||||
}
|
||||
}
|
||||
if let error {
|
||||
Section { Text(error).foregroundStyle(.red).font(.callout) }
|
||||
@@ -142,6 +162,11 @@ struct ProductFormView: View {
|
||||
groups = (try? await APIClient.shared.groups()) ?? []
|
||||
categories = (try? await APIClient.shared.categories()) ?? []
|
||||
units = (try? await APIClient.shared.units()) ?? []
|
||||
// Kam eine Kategorie aus dem Barcode-Vorschlag, den Typ dazu setzen.
|
||||
if let cid = selectedCategoryId,
|
||||
let t = categories.first(where: { $0.id == cid })?.tracking {
|
||||
modus = t
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $newGroupShown) {
|
||||
NavigationStack {
|
||||
|
||||
Reference in New Issue
Block a user