initial version

This commit is contained in:
alexandrev-tibco
2026-01-15 09:24:06 +01:00
parent bab350dd22
commit 7988257399
139 changed files with 13149 additions and 3233 deletions
@@ -0,0 +1,121 @@
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
private let accountRepository = AccountRepository()
var body: some View {
NavigationStack {
Form {
Section {
TextField("Account name", text: $name)
Picker("Currency", selection: $currencyCode) {
ForEach(CurrencyPicker.commonCodes, id: \.self) { code in
Text(code).tag(code)
}
}
} header: {
Text("Account")
}
Section {
Picker("Input Mode", selection: $inputMode) {
ForEach(InputMode.allCases) { mode in
Text(mode.title).tag(mode)
}
}
} header: {
Text("Input")
} footer: {
Text(inputMode.description)
}
Section {
Picker("Reminder Frequency", selection: $notificationFrequency) {
ForEach(NotificationFrequency.allCases) { frequency in
Text(frequency.displayName).tag(frequency)
}
}
if notificationFrequency == .custom {
Stepper(
"Every \(customFrequencyMonths) month\(customFrequencyMonths > 1 ? "s" : "")",
value: $customFrequencyMonths,
in: 1...24
)
}
} header: {
Text("Account Reminders")
} footer: {
Text("Reminders apply to the whole account.")
}
}
.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()
}
}
.presentationDetents([.large])
}
private var isValid: Bool {
!name.trimmingCharacters(in: .whitespaces).isEmpty
}
private func loadAccount() {
guard let account else { return }
name = account.name
currencyCode = account.currencyCode ?? currencyCode
inputMode = InputMode(rawValue: account.inputMode) ?? .simple
notificationFrequency = account.frequency
customFrequencyMonths = Int(account.customFrequencyMonths)
}
private func saveAccount() {
if let account {
accountRepository.updateAccount(
account,
name: name.trimmingCharacters(in: .whitespaces),
currency: currencyCode,
inputMode: inputMode,
notificationFrequency: notificationFrequency,
customFrequencyMonths: customFrequencyMonths
)
} else {
_ = accountRepository.createAccount(
name: name.trimmingCharacters(in: .whitespaces),
currency: currencyCode,
inputMode: inputMode,
notificationFrequency: notificationFrequency,
customFrequencyMonths: customFrequencyMonths
)
}
accountStore.persistSelection()
dismiss()
}
}
#Preview {
AccountEditorView(account: nil)
.environmentObject(AccountStore(iapService: IAPService()))
}
@@ -0,0 +1,78 @@
import SwiftUI
struct AccountsView: View {
@EnvironmentObject private var iapService: IAPService
@EnvironmentObject private var accountStore: AccountStore
@StateObject private var accountRepository = AccountRepository()
@State private var showingAddAccount = false
@State private var selectedAccount: Account?
@State private var showingPaywall = false
var body: some View {
ZStack {
AppBackground()
List {
Section {
ForEach(accountRepository.accounts) { account in
Button {
selectedAccount = account
} label: {
HStack {
VStack(alignment: .leading) {
Text(account.name)
.font(.headline)
Text(account.currencyCode ?? AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext).currency)
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
if accountStore.selectedAccount?.id == account.id && !accountStore.showAllAccounts {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.appPrimary)
}
}
}
}
} header: {
Text("Accounts")
} footer: {
Text(iapService.isPremium ? "Create multiple accounts for business, family, or goals." : "Free users can create one account.")
}
}
.scrollContentBackground(.hidden)
}
.navigationTitle("Accounts")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
if accountStore.canAddAccount() {
showingAddAccount = true
} else {
showingPaywall = true
}
} label: {
Image(systemName: "plus")
}
}
}
.sheet(isPresented: $showingAddAccount) {
AccountEditorView(account: nil)
}
.sheet(isPresented: $showingPaywall) {
PaywallView()
}
.sheet(item: $selectedAccount) { account in
AccountEditorView(account: account)
}
.onAppear {
accountRepository.fetchAccounts()
}
}
}
#Preview {
AccountsView()
.environmentObject(IAPService())
.environmentObject(AccountStore(iapService: IAPService()))
}