import SwiftUI /// Eine Budget-Zeile setzen – für den Monat oder für die Vorlage. /// /// Dieselbe Maske für beides, weil das Backend dieselben drei Angaben /// entgegennimmt (`planned_cents`, `modus`, `zuschlag_cents`) und sie über /// `uebernimm` durch dieselbe Regel schickt. Was sich unterscheidet, ist nur /// der Endpunkt und der Satz darunter. struct BudgetZeileBearbeiten: View { enum Ziel: Equatable { /// Wirkt nur auf diesen Monat; er gilt danach als angepasst. case monat(String) /// Wirkt auf alle Monate, die nicht eigens angepasst sind. case vorlage } let ziel: Ziel let kategorie: Budgetkategorie let waehrung: String var fertig: () -> Void @Environment(AppZustand.self) private var zustand @Environment(\.dismiss) private var schliesse @State private var vorgang = Speichervorgang() @State private var istBerechnet = false @State private var geplantCents = 0 @State private var zuschlagCents = 0 @State private var vorbelegt = false var body: some View { NavigationStack { Form { Section { HStack(spacing: 10) { Symbolplakette(symbol: kategorie.icon, farbe: kategorie.color) Text(kategorie.name) .font(.headline) } } Section { Toggle(String(localized: "budget.ausVertraegenRechnen"), isOn: $istBerechnet) } footer: { Text(istBerechnet ? String(localized: "budget.berechnetErklaerung") : String(localized: "budget.festErklaerung")) } Section { if istBerechnet { // Bei „berechnet" ist die hingeschriebene Zahl nicht // der Soll-Betrag, sondern nur der Zuschlag obendrauf. LabeledContent(String(localized: "budget.ausVertraegen"), value: Geldbetrag(cents: kategorie.ausVertraegenCents, waehrung: waehrung).formatiert()) Betragsfeld(titel: String(localized: "budget.zuschlag"), waehrung: waehrung, cents: $zuschlagCents) LabeledContent(String(localized: "budget.ergibt"), value: Geldbetrag(cents: kategorie.ausVertraegenCents + zuschlagCents, waehrung: waehrung).formatiert()) .fontWeight(.medium) } else { Betragsfeld(titel: String(localized: "budget.geplant"), waehrung: waehrung, cents: $geplantCents) } } if let fehler = vorgang.fehler { Section { FehlerZeile(text: fehler) } } } .navigationTitle(zielTitel) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button(String(localized: "allgemein.abbrechen")) { schliesse() } } ToolbarItem(placement: .confirmationAction) { Button(String(localized: "allgemein.sichern")) { Task { await sichere() } } .disabled(vorgang.laeuft) } } .interactiveDismissDisabled(vorgang.laeuft) .onAppear { guard !vorbelegt else { return } vorbelegt = true istBerechnet = kategorie.istBerechnet geplantCents = kategorie.plannedCents zuschlagCents = kategorie.zuschlagCents } } } private var zielTitel: String { switch ziel { case .monat: return String(localized: "budget.monatSetzen") case .vorlage: return String(localized: "budget.vorlageSetzen") } } private func sichere() async { guard let client = zustand.client else { return } let modus = istBerechnet ? "berechnet" : "fest" // Bei „berechnet" bleibt planned_cents unberührt: Das Backend liest // es dann nicht, und es zu überschreiben verlöre den Wert, auf den // ein Zurückschalten auf „fest" zurückfallen soll. let geplant = istBerechnet ? nil : geplantCents let zuschlag = istBerechnet ? zuschlagCents : nil let geglueckt = await vorgang.fuehreAus { switch ziel { case .monat(let label): try await client.setzeBudgetMonat(label, kategorie: kategorie.categoryId, geplantCents: geplant, modus: modus, zuschlagCents: zuschlag) case .vorlage: try await client.setzeBudgetVorlage(kategorie: kategorie.categoryId, geplantCents: geplant, modus: modus, zuschlagCents: zuschlag) } } if geglueckt { fertig() schliesse() } } }