Fix account types: rename cash to wallet, update all related views and fix type references

This commit is contained in:
“SamoilenkoVadym” 2025-03-02 20:58:38 +00:00
parent 79e649756a
commit 072c31192b
5 changed files with 443 additions and 49 deletions

View file

@ -0,0 +1,91 @@
//
// AccountListView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct AccountListView: View {
@EnvironmentObject private var settings: AppSettings
@State private var showingAddAccount = false
@State private var selectedAccountType: AccountType?
private let accounts = [
AccountModel(
name: "Cash Wallet",
type: .wallet,
currency: .usd,
balance: 1000
),
AccountModel(
name: "Main Bank Account",
type: .bankAccount,
currency: .usd,
balance: 5000
),
AccountModel(
name: "Credit Card",
type: .creditCard,
currency: .usd,
balance: 2000,
creditLimit: 10000,
interestRate: 19.99,
dueDate: Calendar.current.date(byAdding: .day, value: 15, to: Date())
)
]
var body: some View {
List {
ForEach(AccountType.allCases, id: \.self) { type in
Section {
let typeAccounts = accounts.filter { $0.type == type }
if typeAccounts.isEmpty {
Button {
selectedAccountType = type
showingAddAccount = true
} label: {
HStack {
Image(systemName: "plus.circle.fill")
.foregroundColor(.accentColor)
Text("Add \(type.rawValue)")
.foregroundColor(.accentColor)
}
}
} else {
ForEach(typeAccounts) { account in
AccountRowView(account: account)
}
}
} header: {
HStack {
Image(systemName: type.icon)
.foregroundColor(type.color)
Text(type.rawValue)
}
}
}
}
.navigationTitle("Accounts")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
showingAddAccount = true
} label: {
Image(systemName: "plus")
}
}
}
.sheet(isPresented: $showingAddAccount) {
if let type = selectedAccountType {
AddAccountView(accountType: type)
} else {
SelectAccountTypeView { selectedType in
selectedAccountType = selectedType
}
}
}
}
}

View file

@ -0,0 +1,128 @@
//
// AddAccountView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct AddAccountView: View {
@Environment(\.dismiss) private var dismiss
@EnvironmentObject private var settings: AppSettings
let accountType: AccountType
@State private var name = ""
@State private var balance = ""
@State private var currency: AppSettings.Currency = .usd
@State private var isActive = true
// Credit Card fields
@State private var creditLimit = ""
@State private var interestRate = ""
@State private var dueDate = Date()
@State private var minimumPayment = ""
// Deposit fields
@State private var depositEndDate = Date()
@State private var depositInterestRate = ""
@State private var isAutoRenewable = false
// Debt fields
@State private var creditorName = ""
@State private var debtInterestRate = ""
private var isValid: Bool {
!name.isEmpty && !balance.isEmpty
}
var body: some View {
NavigationView {
Form {
Section("Basic Information") {
TextField("Account Name", text: $name)
HStack {
Text(settings.currency.symbol)
.foregroundColor(.secondary)
TextField("Balance", text: $balance)
.keyboardType(.decimalPad)
}
Picker("Currency", selection: $currency) {
ForEach(AppSettings.Currency.allCases, id: \.self) { currency in
Text(currency.rawValue).tag(currency)
}
}
}
if accountType == .creditCard {
Section("Credit Card Details") {
HStack {
Text(settings.currency.symbol)
TextField("Credit Limit", text: $creditLimit)
.keyboardType(.decimalPad)
}
TextField("Interest Rate %", text: $interestRate)
.keyboardType(.decimalPad)
DatePicker("Due Date", selection: $dueDate, displayedComponents: .date)
HStack {
Text(settings.currency.symbol)
TextField("Minimum Payment", text: $minimumPayment)
.keyboardType(.decimalPad)
}
}
}
if accountType == .deposit {
Section("Deposit Details") {
DatePicker("End Date", selection: $depositEndDate, displayedComponents: .date)
TextField("Interest Rate %", text: $depositInterestRate)
.keyboardType(.decimalPad)
Toggle("Auto-renewable", isOn: $isAutoRenewable)
}
}
if accountType == .debt {
Section("Debt Details") {
TextField("Creditor Name", text: $creditorName)
TextField("Interest Rate %", text: $debtInterestRate)
.keyboardType(.decimalPad)
}
}
}
.navigationTitle("New \(accountType.rawValue)")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Add") {
// TODO: Add account creation
dismiss()
}
.disabled(!isValid)
}
}
}
}
}
struct AddAccountView_Previews: PreviewProvider {
static var previews: some View {
AddAccountView(accountType: .creditCard)
.environmentObject(AppSettings.shared)
}
}

View file

@ -0,0 +1,72 @@
//
// SelectAccountTypeView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct SelectAccountTypeView: View {
@Environment(\.dismiss) private var dismiss
let onSelect: (AccountType) -> Void
var body: some View {
NavigationView {
List {
ForEach(AccountType.allCases, id: \.self) { type in
Button {
onSelect(type)
dismiss()
} label: {
HStack {
Image(systemName: type.icon)
.foregroundColor(type.color)
.frame(width: 30)
VStack(alignment: .leading) {
Text(type.rawValue)
.font(.headline)
.foregroundColor(.primary)
Text(descriptionFor(type))
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(Color(uiColor: .systemGray4))
}
}
}
}
.navigationTitle("Select Account Type")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
dismiss()
}
}
}
}
}
private func descriptionFor(_ type: AccountType) -> String {
switch type {
case .wallet:
return "Cash and physical money"
case .bankAccount:
return "Regular bank account"
case .creditCard:
return "Credit card with limit"
case .deposit:
return "Savings and deposits"
case .debt:
return "Debts and loans"
}
}
}

View file

@ -1,27 +1,30 @@
//
// AccountType.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import Foundation
import SwiftUI
enum AccountType: String, Codable, CaseIterable {
case wallet = "Wallet" // Наличные/кошелек
case bankAccount = "Bank" // Банковский счет
case creditCard = "Credit" // Кредитная карта
case deposit = "Deposit" // Депозит
case debt = "Debt" // Долг
case wallet = "Wallet"
case bankAccount = "Bank Account"
case creditCard = "Credit Card"
case deposit = "Deposit"
case debt = "Debt"
var icon: String {
switch self {
case .wallet: return "wallet.pass"
case .bankAccount: return "building.columns"
case .creditCard: return "creditcard"
case .deposit: return "banknote"
case .debt: return "exclamationmark.circle"
case .wallet: return "banknote"
case .bankAccount: return "building.columns.fill"
case .creditCard: return "creditcard.fill"
case .deposit: return "vault.fill"
case .debt: return "exclamationmark.circle.fill"
}
}
var color: Color {
switch self {
case .wallet: return Color(uiColor: .systemGreen)
case .bankAccount: return Color(uiColor: .systemBlue)
case .creditCard: return Color(uiColor: .systemPurple)
case .deposit: return Color(uiColor: .systemOrange)
case .debt: return Color(uiColor: .systemRed)
}
}
}
@ -32,51 +35,57 @@ struct AccountModel: Identifiable, Codable {
var type: AccountType
var currency: AppSettings.Currency
var balance: Double
var initialBalance: Double
var isActive: Bool
// Для кредитных карт
// Кредитная карта
var creditLimit: Double?
var interestRate: Double?
var dueDate: Date?
var minimumPayment: Double?
// Для депозитов
var depositInterestRate: Double?
// Депозит
var depositEndDate: Date?
var depositInterestRate: Double?
var isAutoRenewable: Bool?
// Для долгов
var debtInterestRate: Double?
var debtDueDate: Date?
// Долг
var creditorName: String?
var debtInterestRate: Double?
var paymentSchedule: [DebtPayment]?
init(
name: String,
type: AccountType,
currency: AppSettings.Currency,
balance: Double,
initialBalance: Double = 0,
balance: Double = 0,
isActive: Bool = true,
creditLimit: Double? = nil,
interestRate: Double? = nil,
dueDate: Date? = nil,
depositInterestRate: Double? = nil,
minimumPayment: Double? = nil,
depositEndDate: Date? = nil,
depositInterestRate: Double? = nil,
isAutoRenewable: Bool? = nil,
creditorName: String? = nil,
debtInterestRate: Double? = nil,
debtDueDate: Date? = nil,
creditorName: String? = nil
paymentSchedule: [DebtPayment]? = nil
) {
self.id = UUID().uuidString
self.name = name
self.type = type
self.currency = currency
self.balance = balance
self.initialBalance = initialBalance
self.isActive = isActive
self.creditLimit = creditLimit
self.interestRate = interestRate
self.dueDate = dueDate
self.depositInterestRate = depositInterestRate
self.minimumPayment = minimumPayment
self.depositEndDate = depositEndDate
self.debtInterestRate = debtInterestRate
self.debtDueDate = debtDueDate
self.depositInterestRate = depositInterestRate
self.isAutoRenewable = isAutoRenewable
self.creditorName = creditorName
self.debtInterestRate = debtInterestRate
self.paymentSchedule = paymentSchedule
}
// Вычисляемые свойства
@ -86,40 +95,70 @@ struct AccountModel: Identifiable, Codable {
}
var isOverdue: Bool {
guard let dueDate = dueDate ?? debtDueDate else { return false }
guard let dueDate = dueDate else { return false }
return Date() > dueDate
}
// Sample Data
var formattedBalance: String {
balance.formatAsCurrency()
}
// Методы для работы с кредитной картой
mutating func addPurchase(_ amount: Double) -> Bool {
guard type == .creditCard,
let limit = creditLimit,
balance + amount <= limit else {
return false
}
balance += amount
return true
}
mutating func makePayment(_ amount: Double) -> Bool {
guard amount <= balance else { return false }
balance -= amount
return true
}
}
// Структура для платежей по долгу
struct DebtPayment: Codable, Identifiable {
let id: String
var dueDate: Date
var amount: Double
var isPaid: Bool
init(dueDate: Date, amount: Double, isPaid: Bool = false) {
self.id = UUID().uuidString
self.dueDate = dueDate
self.amount = amount
self.isPaid = isPaid
}
}
// Sample Data
extension AccountModel {
static let sampleData = [
AccountModel(
name: "Cash Wallet",
type: .wallet,
currency: .usd,
balance: 500
balance: 1000
),
AccountModel(
name: "Main Bank Account",
type: .bankAccount,
currency: .usd,
balance: 2500
balance: 5000
),
AccountModel(
name: "Credit Card",
type: .creditCard,
currency: .usd,
balance: 1000,
creditLimit: 5000,
interestRate: 19.9,
balance: 2000,
creditLimit: 10000,
interestRate: 19.99,
dueDate: Calendar.current.date(byAdding: .day, value: 15, to: Date())
),
AccountModel(
name: "Savings Deposit",
type: .deposit,
currency: .usd,
balance: 10000,
depositInterestRate: 5.5,
depositEndDate: Calendar.current.date(byAdding: .month, value: 12, to: Date())
)
]
}

View file

@ -0,0 +1,64 @@
//
// AccountRowView.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import SwiftUI
struct AccountRowView: View {
let account: AccountModel
var body: some View {
VStack(spacing: 8) {
// Header
HStack {
Text(account.name)
.font(.headline)
Spacer()
Text(account.currency.symbol)
.font(.subheadline)
.foregroundColor(.secondary)
}
// Balance
HStack {
Text(account.formattedBalance)
.font(.title3)
.fontWeight(.semibold)
Spacer()
if let availableCredit = account.availableCredit {
Text("Available: \(availableCredit.formatAsCurrency())")
.font(.caption)
.foregroundColor(.secondary)
}
}
// Additional Info
if account.type == .creditCard {
HStack {
if let dueDate = account.dueDate {
Label(dueDate.formatted(date: .abbreviated, time: .omitted),
systemImage: "calendar")
.font(.caption)
.foregroundColor(.secondary)
}
if let rate = account.interestRate {
Text("")
.foregroundColor(.secondary)
Label("\(String(format: "%.1f", rate))%",
systemImage: "percent")
.font(.caption)
.foregroundColor(.secondary)
}
}
}
}
.padding(.vertical, 8)
.contentShape(Rectangle())
}
}