iOS: Produktliste - Kategoriefilter nach Typ + Sortierung
Kategoriefilter zeigt nur noch Kategorien des Listentyps (Lebensmittel-Liste nur Food-Kategorien, Gegenstaende-Liste nur Objekt-Kategorien) - vorher konnte man sinnlos ueberkreuz filtern. Neue Sortierung (Name A-Z/Z-A, Zuletzt geaendert) per Menue-Picker; Product traegt dafuer updated_at. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -201,15 +201,42 @@ struct ProductListView: View {
|
||||
@State private var categories: [CategoryItem] = []
|
||||
/// nil = alle, 0 = ohne Kategorie, sonst die ID (Unterkategorien zaehlen mit).
|
||||
@State private var categoryId: Int?
|
||||
@State private var sortMode: SortMode = .nameAsc
|
||||
@State private var busy = false
|
||||
@State private var showNew = false
|
||||
|
||||
enum SortMode: String, CaseIterable, Identifiable {
|
||||
case nameAsc = "Name A–Z"
|
||||
case nameDesc = "Name Z–A"
|
||||
case updated = "Zuletzt geändert"
|
||||
var id: String { rawValue }
|
||||
}
|
||||
|
||||
/// Auf den festen Typ gefiltert (clientseitig, wie im Web).
|
||||
private var sichtbar: [Product] {
|
||||
guard let fixedType else { return products }
|
||||
return products.filter { ($0.isObject ? "object" : "food") == fixedType }
|
||||
}
|
||||
|
||||
/// Sichtbare Produkte in der gewählten Reihenfolge.
|
||||
private var sortiert: [Product] {
|
||||
switch sortMode {
|
||||
case .nameAsc:
|
||||
return sichtbar.sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
|
||||
case .nameDesc:
|
||||
return sichtbar.sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedDescending }
|
||||
case .updated:
|
||||
// ISO-Strings gleichen Formats sind chronologisch vergleichbar; neueste zuerst.
|
||||
return sichtbar.sorted { ($0.updatedAt ?? "") > ($1.updatedAt ?? "") }
|
||||
}
|
||||
}
|
||||
|
||||
/// Nur Kategorien des Listentyps (Lebensmittel-Liste zeigt nur Food-Kategorien usw.).
|
||||
private var typKategorien: [CategoryItem] {
|
||||
guard let fixedType else { return categories }
|
||||
return categories.filter { ($0.tracking ?? "food") == fixedType }
|
||||
}
|
||||
|
||||
private var titel: String {
|
||||
switch fixedType {
|
||||
case "food": return "Lebensmittel"
|
||||
@@ -243,10 +270,16 @@ struct ProductListView: View {
|
||||
Text(filterLabel).foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
Picker(selection: $sortMode) {
|
||||
ForEach(SortMode.allCases) { Text($0.rawValue).tag($0) }
|
||||
} label: {
|
||||
Label("Sortierung", systemImage: "arrow.up.arrow.down")
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
}
|
||||
|
||||
Section {
|
||||
ForEach(sichtbar) { product in
|
||||
ForEach(sortiert) { product in
|
||||
NavigationLink {
|
||||
ProductDetailView(product: product)
|
||||
} label: {
|
||||
@@ -308,17 +341,18 @@ struct ProductListView: View {
|
||||
}
|
||||
|
||||
private func categoryTree() -> [TreeEntry] {
|
||||
let ids = Set(categories.map(\.id))
|
||||
let liste = typKategorien
|
||||
let ids = Set(liste.map(\.id))
|
||||
var result: [TreeEntry] = []
|
||||
|
||||
func walk(_ node: CategoryItem, _ depth: Int) {
|
||||
result.append(TreeEntry(item: node, depth: depth))
|
||||
for child in categories.filter({ $0.parentId == node.id }) {
|
||||
for child in liste.filter({ $0.parentId == node.id }) {
|
||||
walk(child, depth + 1)
|
||||
}
|
||||
}
|
||||
// Waisen (Oberkategorie geloescht) gelten als oberste Ebene.
|
||||
for root in categories.filter({ $0.parentId == nil || !ids.contains($0.parentId!) }) {
|
||||
for root in liste.filter({ $0.parentId == nil || !ids.contains($0.parentId!) }) {
|
||||
walk(root, 0)
|
||||
}
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user