iOS: Einzelstueck-Kategoriefilter hierarchisch + mit Oberkategorien
Der Kategoriefilter der Einzelstueckliste zeigt jetzt einen eingerueckten Baum und enthaelt auch die Oberkategorien (z.B. "Klamotten"), nicht nur die Blaetter mit Exemplaren. Nach einer Oberkategorie zu filtern schliesst alle Unter- kategorien ein (ueber category_id statt Namensgleichheit). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,20 +6,72 @@ import SwiftUI
|
||||
struct ItemListView: View {
|
||||
@State private var items: [Item] = []
|
||||
@State private var locations: [StorageLocation] = []
|
||||
@State private var categories: [CategoryItem] = []
|
||||
@State private var query = ""
|
||||
@State private var locationFilter: String? // Lagerort-ID
|
||||
@State private var categoryFilter: String? // Kategoriename
|
||||
@State private var categoryFilter: Int? // Kategorie-ID (Unterkategorien zählen mit)
|
||||
@State private var busy = true
|
||||
@State private var error: String?
|
||||
|
||||
private var categories: [String] {
|
||||
Array(Set(items.compactMap { $0.categoryName })).sorted()
|
||||
private struct TreeEntry: Identifiable {
|
||||
let item: CategoryItem
|
||||
let depth: Int
|
||||
var id: Int { item.id }
|
||||
var label: String { String(repeating: " ", count: depth) + item.name }
|
||||
}
|
||||
|
||||
private var catById: [Int: CategoryItem] {
|
||||
Dictionary(categories.map { ($0.id, $0) }, uniquingKeysWith: { a, _ in a })
|
||||
}
|
||||
|
||||
/// Ist `catId` die Kategorie `target` oder eine ihrer Unterkategorien?
|
||||
private func imTeilbaum(_ catId: Int?, target: Int) -> Bool {
|
||||
let map = catById
|
||||
var cur = catId
|
||||
var g = 0
|
||||
while let c = cur, g < 50 {
|
||||
if c == target { return true }
|
||||
cur = map[c]?.parentId
|
||||
g += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// Kategorien fürs Filter-Menü: alle, die selbst oder als Oberkategorie zu einem
|
||||
/// vorhandenen Einzelstück gehören – so ist z.B. auch „Klamotten" wählbar. Baum
|
||||
/// mit eingerückter Beschriftung (hierarchisch).
|
||||
private var filterKategorien: [TreeEntry] {
|
||||
let map = catById
|
||||
var relevant = Set<Int>()
|
||||
for it in items {
|
||||
var cur = it.categoryId
|
||||
var g = 0
|
||||
while let c = cur, !relevant.contains(c), g < 50 {
|
||||
relevant.insert(c)
|
||||
cur = map[c]?.parentId
|
||||
g += 1
|
||||
}
|
||||
}
|
||||
let liste = categories.filter { relevant.contains($0.id) }
|
||||
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 liste.filter({ $0.parentId == node.id }) { walk(child, depth + 1) }
|
||||
}
|
||||
for root in liste.filter({ $0.parentId == nil || !ids.contains($0.parentId!) }) { walk(root, 0) }
|
||||
return result
|
||||
}
|
||||
|
||||
private var filterKategorieName: String {
|
||||
if let id = categoryFilter, let t = categories.first(where: { $0.id == id }) { return t.name }
|
||||
return "Alle"
|
||||
}
|
||||
|
||||
private var gefiltert: [Item] {
|
||||
let q = query.trimmingCharacters(in: .whitespaces).lowercased()
|
||||
return items.filter { it in
|
||||
if let cat = categoryFilter, it.categoryName != cat { return false }
|
||||
if let cat = categoryFilter, !imTeilbaum(it.categoryId, target: cat) { return false }
|
||||
if let loc = locationFilter, it.locationId != loc { return false }
|
||||
if !q.isEmpty {
|
||||
let heu = [it.productName, it.uid, it.note, it.productBrand]
|
||||
@@ -64,11 +116,11 @@ struct ItemListView: View {
|
||||
check(loc.path(in: locations), locationFilter == loc.id) { locationFilter = loc.id }
|
||||
}
|
||||
}
|
||||
if !categories.isEmpty {
|
||||
if !filterKategorien.isEmpty {
|
||||
Menu("Kategorie") {
|
||||
check("Alle", categoryFilter == nil) { categoryFilter = nil }
|
||||
ForEach(categories, id: \.self) { c in
|
||||
check(c, categoryFilter == c) { categoryFilter = c }
|
||||
ForEach(filterKategorien) { e in
|
||||
check(e.label, categoryFilter == e.item.id) { categoryFilter = e.item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,6 +150,7 @@ struct ItemListView: View {
|
||||
do {
|
||||
items = try await APIClient.shared.allItems()
|
||||
locations = (try? await APIClient.shared.locations()) ?? []
|
||||
categories = (try? await APIClient.shared.categories()) ?? []
|
||||
} catch {
|
||||
self.error = error.localizedDescription
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user