import SwiftUI /// Einen Vertrag anlegen oder ändern. struct AboFormular: View { enum Zweck: Equatable, Identifiable { case neu case aendern(Abo) var id: Int { switch self { case .neu: return 0 case .aendern(let abo): return abo.id } } } let zweck: Zweck var fertig: () -> Void @Environment(AppZustand.self) private var zustand @Environment(\.dismiss) private var schliesse @State private var vorgang = Speichervorgang() @State private var name = "" @State private var betragCents = 0 @State private var waehrung = "EUR" @State private var rhythmus: Zahlungsrhythmus = .monatlich @State private var kategorieId: Int? @State private var kontoId: Int? @State private var hatFaelligkeit = false @State private var faelligAm = Date() @State private var erkennungstext = "" @State private var notiz = "" @State private var istAktiv = true @State private var symbol = "refresh-ccw" @State private var farbe = "#6b7480" @State private var fragtNachLoeschen = false @State private var vorbelegt = false var body: some View { NavigationStack { Form { Section { TextField(String(localized: "abo.name"), text: $name) Betragsfeld(titel: String(localized: "abo.betrag"), waehrung: waehrung, cents: $betragCents) Picker(String(localized: "abo.rhythmus"), selection: $rhythmus) { ForEach(Zahlungsrhythmus.allCases) { einer in Text(einer.name).tag(einer) } } } Section { KategorieFeld(titel: String(localized: "buchung.kategorie"), nachschlag: zustand.nachschlag, art: .expense, auswahl: $kategorieId) KontoFeld(titel: String(localized: "abo.konto"), nachschlag: zustand.nachschlag, auswahl: $kontoId) } footer: { Text(String(localized: "abo.kategorieErklaerung")) } Section { Toggle(String(localized: "abo.faelligkeitKennen"), isOn: $hatFaelligkeit) if hatFaelligkeit { DatePicker(String(localized: "abo.naechsteZahlung"), selection: $faelligAm, displayedComponents: .date) } } Section { TextField(String(localized: "abo.erkennungstext"), text: $erkennungstext) } footer: { Text(String(localized: "abo.erkennungErklaerung")) } Section { SymbolUndFarbeFeld(symbol: $symbol, farbe: $farbe) TextField(String(localized: "buchung.notiz"), text: $notiz, axis: .vertical) .lineLimit(1...3) } if case .aendern = zweck { Section { Toggle(String(localized: "abo.laeuftNoch"), isOn: $istAktiv) Button(String(localized: "abo.loeschen"), role: .destructive) { fragtNachLoeschen = true } } footer: { Text(String(localized: "abo.beendenStattLoeschen")) } } if let fehler = vorgang.fehler { Section { FehlerZeile(text: fehler) } } } .navigationTitle(titel) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button(String(localized: "allgemein.abbrechen")) { schliesse() } } ToolbarItem(placement: .confirmationAction) { Button(String(localized: "allgemein.sichern")) { Task { await sichere() } } .disabled(!istVollstaendig || vorgang.laeuft) } } .interactiveDismissDisabled(vorgang.laeuft) .confirmationDialog(String(localized: "abo.loeschenFrage"), isPresented: $fragtNachLoeschen, titleVisibility: .visible) { Button(String(localized: "abo.loeschen"), role: .destructive) { Task { await loesche() } } Button(String(localized: "allgemein.abbrechen"), role: .cancel) {} } message: { Text(String(localized: "abo.loeschenErklaerung")) } .onAppear(perform: belegeVor) } } private var titel: String { switch zweck { case .neu: return String(localized: "abo.neu") case .aendern: return String(localized: "abo.aendern") } } private var istVollstaendig: Bool { !name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && betragCents > 0 } private func belegeVor() { guard !vorbelegt else { return } vorbelegt = true guard case .aendern(let abo) = zweck else { waehrung = zustand.einstellungen.basiswaehrung return } name = abo.name betragCents = abo.amountCents waehrung = abo.currency rhythmus = Zahlungsrhythmus(rawValue: abo.intervalMonths) ?? .monatlich kategorieId = abo.categoryId kontoId = abo.accountId hatFaelligkeit = abo.nextDue != nil faelligAm = abo.nextDue?.datum ?? Date() erkennungstext = abo.payeePattern ?? "" notiz = abo.notes ?? "" istAktiv = abo.isActive symbol = abo.icon farbe = abo.color } private func sichere() async { guard let client = zustand.client else { return } let faellig = hatFaelligkeit ? Tagesdatum(faelligAm) : nil let sauberName = name.trimmingCharacters(in: .whitespacesAndNewlines) let geglueckt = await vorgang.fuehreAus { switch zweck { case .neu: _ = try await client.legeAboAn(AboAnlegen( name: sauberName, icon: symbol, color: farbe, amountCents: betragCents, currency: waehrung, intervalMonths: rhythmus.rawValue, categoryId: kategorieId, accountId: kontoId, nextDue: faellig, payeePattern: leerAlsNil(erkennungstext), notes: leerAlsNil(notiz))) case .aendern(let abo): _ = try await client.aendereAbo(abo.id, AboAendern( name: sauberName, icon: symbol, color: farbe, amountCents: betragCents, intervalMonths: rhythmus.rawValue, categoryId: kategorieId, accountId: kontoId, nextDue: faellig, payeePattern: leerAlsNil(erkennungstext), isActive: istAktiv, notes: leerAlsNil(notiz))) } } if geglueckt { fertig() schliesse() } } private func loesche() async { guard let client = zustand.client, case .aendern(let abo) = zweck else { return } if await vorgang.fuehreAus({ try await client.loescheAbo(abo.id) }) { fertig() schliesse() } } private func leerAlsNil(_ text: String) -> String? { let sauber = text.trimmingCharacters(in: .whitespacesAndNewlines) return sauber.isEmpty ? nil : sauber } }