import SwiftUI struct AccountEditorView: View { @Environment(\.dismiss) private var dismiss @EnvironmentObject private var accountStore: AccountStore let account: Account? @State private var name = "" @State private var currencyCode = AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext).currency @State private var inputMode: InputMode = .simple @State private var notificationFrequency: NotificationFrequency = .monthly @State private var customFrequencyMonths = 1 @State private var errorMessage: String? private let accountRepository = AccountRepository() var body: some View { NavigationStack { Form { Section { if isEditingDefaultAccount { HStack { Text(Account.defaultAccountName) .foregroundColor(.secondary) Spacer() Image(systemName: "lock.fill") .font(.caption) .foregroundColor(.secondary) } } else { TextField("Account name", text: $name) } Picker("Currency", selection: $currencyCode) { ForEach(CurrencyPicker.commonCodes, id: \.self) { code in Text(code).tag(code) } } } header: { Text("Account") } footer: { if let errorMessage { Text(errorMessage) .foregroundColor(.negativeRed) } else if isEditingDefaultAccount { Text("The Default account name cannot be changed.") } } Section { Picker("Input Mode", selection: $inputMode) { ForEach(InputMode.allCases) { mode in Text(mode.title).tag(mode) } } } header: { Text("Input") } footer: { Text(inputMode.description) } } .navigationTitle(account == nil ? "New Account" : "Edit Account") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button("Cancel") { dismiss() } } ToolbarItem(placement: .navigationBarTrailing) { Button("Save") { saveAccount() } .disabled(!isValid) } } .onAppear { loadAccount() } .onChange(of: name) { _, _ in validateName() } } .presentationDetents([.large]) } private var isEditingDefaultAccount: Bool { account?.isDefaultAccount == true } private var isValid: Bool { if isEditingDefaultAccount { return true // Can only edit currency/inputMode for Default account } let trimmed = name.trimmingCharacters(in: .whitespaces) return !trimmed.isEmpty && errorMessage == nil } private func validateName() { let trimmed = name.trimmingCharacters(in: .whitespaces) if trimmed.isEmpty { errorMessage = nil return } // Check if trying to use reserved "Default" name for a non-default account if trimmed.lowercased() == Account.defaultAccountName.lowercased() { let isCreatingNew = account == nil let isEditingNonDefault = account != nil && !account!.isDefaultAccount if isCreatingNew || isEditingNonDefault { errorMessage = "The name '\(Account.defaultAccountName)' is reserved." return } } // Check for duplicate names if !accountRepository.isNameAvailable(trimmed, excludingAccountId: account?.safeId) { errorMessage = "An account with this name already exists." return } errorMessage = nil } private func loadAccount() { guard let account else { return } name = account.name currencyCode = account.currencyCode ?? currencyCode inputMode = InputMode(rawValue: account.inputMode) ?? .simple notificationFrequency = .monthly customFrequencyMonths = 1 validateName() } private func saveAccount() { if !isEditingDefaultAccount { validateName() guard errorMessage == nil else { return } } let trimmedName = isEditingDefaultAccount ? Account.defaultAccountName : name.trimmingCharacters(in: .whitespaces) let enforcedFrequency: NotificationFrequency = .monthly let enforcedCustomMonths = 1 if let account { accountRepository.updateAccount( account, name: isEditingDefaultAccount ? nil : trimmedName, // Don't update name for Default account currency: currencyCode, inputMode: inputMode, notificationFrequency: enforcedFrequency, customFrequencyMonths: enforcedCustomMonths ) } else { _ = accountRepository.createAccount( name: trimmedName, currency: currencyCode, inputMode: inputMode, notificationFrequency: enforcedFrequency, customFrequencyMonths: enforcedCustomMonths ) } accountStore.persistSelection() dismiss() } } #Preview { AccountEditorView(account: nil) .environmentObject(AccountStore(iapService: IAPService())) }