From cf625f339176bd5af9d5f361190fb6b9a8472a64 Mon Sep 17 00:00:00 2001 From: rene Date: Sat, 30 May 2026 12:31:59 +0200 Subject: [PATCH] Ausgaben-Kategorien dynamisch vom Backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ExpenseCategory DTO + GET /api/expenses/categories beim Öffnen der Liste sowie bei Refresh. Falls der Endpunkt noch nicht ausgerollt ist (oder fehlschlägt), Fallback auf eine lokale Default-Liste mit den aktuellen sechs Kategorien. AddExpenseSheet bekommt die Kategorien als Parameter, statt eigene Liste zu führen — Source of Truth ist jetzt das Backend. --- BanYaroGo/API/DTOs.swift | 6 ++++ BanYaroGo/Views/AusgabenView.swift | 56 ++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/BanYaroGo/API/DTOs.swift b/BanYaroGo/API/DTOs.swift index 5f2a8a4..753b477 100644 --- a/BanYaroGo/API/DTOs.swift +++ b/BanYaroGo/API/DTOs.swift @@ -109,6 +109,12 @@ struct ExpenseCreateBody: Encodable { let notiz: String? } +struct ExpenseCategory: Decodable, Identifiable { + let id: String + let label: String + let color: String? +} + // MARK: - Gassi-Zeiten struct GassiZeit: Decodable, Identifiable { diff --git a/BanYaroGo/Views/AusgabenView.swift b/BanYaroGo/Views/AusgabenView.swift index 8569418..bbb4bd3 100644 --- a/BanYaroGo/Views/AusgabenView.swift +++ b/BanYaroGo/Views/AusgabenView.swift @@ -2,10 +2,22 @@ import SwiftUI struct AusgabenView: View { @State private var expenses: [Expense] = [] + @State private var categories: [ExpenseCategory] = [] @State private var isLoading = false @State private var errorMessage: String? @State private var showAdd = false + /// Lokale Fallback-Liste, falls der Backend-Endpunkt /api/expenses/categories + /// noch nicht ausgerollt ist. Sollte im Normalfall überschrieben werden. + private static let defaultCategories: [ExpenseCategory] = [ + ExpenseCategory(id: "futter", label: "Futter", color: nil), + ExpenseCategory(id: "tierarzt", label: "Tierarzt", color: nil), + ExpenseCategory(id: "zubehoer", label: "Zubehör", color: nil), + ExpenseCategory(id: "versicherung", label: "Versicherung", color: nil), + ExpenseCategory(id: "sitter", label: "Sitter", color: nil), + ExpenseCategory(id: "sonstiges", label: "Sonstiges", color: nil) + ] + private static let dateFormatter: DateFormatter = { let f = DateFormatter() f.locale = Locale(identifier: "de_DE") @@ -30,10 +42,27 @@ struct AusgabenView: View { } } .sheet(isPresented: $showAdd) { - AddExpenseSheet { Task { await load() } } + AddExpenseSheet(categories: categories.isEmpty ? Self.defaultCategories : categories) { + Task { await load() } + } } - .task { await load() } - .refreshable { await load() } + .task { + await loadCategories() + await load() + } + .refreshable { + await loadCategories() + await load() + } + } + + private func loadCategories() async { + if let fetched: [ExpenseCategory] = try? await APIClient.shared.get("/api/expenses/categories"), + !fetched.isEmpty { + categories = fetched + } else if categories.isEmpty { + categories = Self.defaultCategories + } } @ViewBuilder @@ -139,9 +168,10 @@ private struct ExpenseRow: View { private struct AddExpenseSheet: View { @Environment(\.dismiss) private var dismiss + let categories: [ExpenseCategory] let onSaved: () -> Void - @State private var kategorie = "futter" + @State private var kategorie: String @State private var betrag = "" @State private var date = Date() @State private var notiz = "" @@ -150,23 +180,19 @@ private struct AddExpenseSheet: View { @State private var isSaving = false @State private var errorMessage: String? - /// Backend-Whitelist: tierarzt, futter, zubehoer, versicherung, sitter, sonstiges - private let kategorien: [(key: String, label: String)] = [ - ("futter", "Futter"), - ("tierarzt", "Tierarzt"), - ("zubehoer", "Zubehör"), - ("versicherung", "Versicherung"), - ("sitter", "Sitter"), - ("sonstiges", "Sonstiges") - ] + init(categories: [ExpenseCategory], onSaved: @escaping () -> Void) { + self.categories = categories + self.onSaved = onSaved + _kategorie = State(initialValue: categories.first?.id ?? "sonstiges") + } var body: some View { NavigationStack { Form { Section("Kategorie") { Picker("Kategorie", selection: $kategorie) { - ForEach(kategorien, id: \.key) { entry in - Text(entry.label).tag(entry.key) + ForEach(categories) { entry in + Text(entry.label).tag(entry.id) } } }