Fix AccountsListView naming and ContentView navigation
This commit is contained in:
parent
5861b34737
commit
739dd17b05
8 changed files with 196 additions and 152 deletions
|
|
@ -1,43 +1,41 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
@StateObject private var accountsStore = AccountsStore.shared
|
||||
@StateObject private var settings = AppSettings.shared
|
||||
@StateObject private var transactionsStore = TransactionsStore.shared
|
||||
@StateObject private var categoryStore = CategoryStore.shared
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
NavigationView {
|
||||
DashboardView()
|
||||
}
|
||||
.tabItem {
|
||||
Label("Dashboard", systemImage: "chart.pie")
|
||||
Label("Dashboard", systemImage: "chart.pie.fill")
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
AccountListView()
|
||||
AccountsListView()
|
||||
}
|
||||
.tabItem {
|
||||
Label("Accounts", systemImage: "creditcard")
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
CategoryListView(type: .expense)
|
||||
}
|
||||
.tabItem {
|
||||
Label("Categories", systemImage: "tag")
|
||||
Label("Accounts", systemImage: "creditcard.fill")
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
ProfileView()
|
||||
}
|
||||
.tabItem {
|
||||
Label("Profile", systemImage: "person")
|
||||
Label("Profile", systemImage: "person.fill")
|
||||
}
|
||||
}
|
||||
.environmentObject(accountsStore)
|
||||
.environmentObject(settings)
|
||||
.environmentObject(transactionsStore)
|
||||
.environmentObject(categoryStore)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
.environmentObject(TransactionsStore.shared)
|
||||
.environmentObject(AccountsStore.shared)
|
||||
.environmentObject(CategoryStore.shared)
|
||||
.environmentObject(AppSettings.shared)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import SwiftUI
|
||||
|
||||
struct AccountCardView: View {
|
||||
@EnvironmentObject private var accountsStore: AccountsStore
|
||||
let account: AccountModel
|
||||
let isSelected: Bool
|
||||
|
||||
|
|
@ -9,18 +10,12 @@ struct AccountCardView: View {
|
|||
HStack {
|
||||
Image(systemName: account.icon)
|
||||
.font(.title2)
|
||||
.foregroundColor(account.type.color)
|
||||
|
||||
Spacer()
|
||||
|
||||
if account.isDefault {
|
||||
Text("Default")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color(uiColor: .systemGray6))
|
||||
.cornerRadius(8)
|
||||
if account.id == accountsStore.defaultAccountId {
|
||||
Image(systemName: "star.fill")
|
||||
.foregroundColor(.yellow)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -29,33 +24,22 @@ struct AccountCardView: View {
|
|||
.font(.headline)
|
||||
|
||||
Text(account.type.rawValue)
|
||||
.font(.subheadline)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Text(account.balance.formatted(.currency(code: account.currency.rawValue)))
|
||||
.font(.title2.bold())
|
||||
.font(.title3.bold())
|
||||
}
|
||||
.padding()
|
||||
.frame(width: 200)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(Color(uiColor: .systemBackground))
|
||||
.shadow(
|
||||
color: isSelected ? Color.accentColor.opacity(0.3) : Color.black.opacity(0.1),
|
||||
radius: isSelected ? 8 : 4
|
||||
)
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.stroke(isSelected ? Color.accentColor : Color.clear, lineWidth: 2)
|
||||
)
|
||||
.frame(width: 160)
|
||||
.background(isSelected ? Color.accentColor.opacity(0.2) : Color(uiColor: .secondarySystemBackground))
|
||||
.cornerRadius(12)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
AccountCardView(
|
||||
account: AccountModel.previewData,
|
||||
isSelected: true
|
||||
)
|
||||
AccountCardView(account: AccountModel.previewData, isSelected: false)
|
||||
.environmentObject(AccountsStore.shared)
|
||||
.padding()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
import SwiftUI
|
||||
|
||||
struct AccountListView: View {
|
||||
@EnvironmentObject private var accountsStore: AccountsStore
|
||||
@State private var showingAddAccount = false
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
ForEach(accountsStore.accounts) { account in
|
||||
NavigationLink(destination: AccountDetailView(account: account)) {
|
||||
AccountRowView(account: account)
|
||||
}
|
||||
}
|
||||
.onDelete(perform: deleteAccount)
|
||||
}
|
||||
.navigationTitle("Accounts")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
showingAddAccount = true
|
||||
} label: {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showingAddAccount) {
|
||||
NavigationView {
|
||||
AddAccountView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func deleteAccount(at offsets: IndexSet) {
|
||||
offsets.forEach { index in
|
||||
accountsStore.deleteAccount(accountsStore.accounts[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationView {
|
||||
AccountListView()
|
||||
.environmentObject(AccountsStore.shared)
|
||||
.environmentObject(AppSettings.shared)
|
||||
}
|
||||
}
|
||||
90
Coinly/Features/Accounts/AccountsListView.swift
Normal file
90
Coinly/Features/Accounts/AccountsListView.swift
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import SwiftUI
|
||||
|
||||
struct AccountsListView: View {
|
||||
@EnvironmentObject private var accountsStore: AccountsStore
|
||||
@EnvironmentObject private var settings: AppSettings
|
||||
@State private var showingAddAccount = false
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
if !accountsStore.accounts.isEmpty {
|
||||
Section {
|
||||
ForEach(accountsStore.accounts) { account in
|
||||
NavigationLink(destination: AccountDetailView(account: account)) {
|
||||
HStack {
|
||||
Image(systemName: account.icon)
|
||||
.font(.title2)
|
||||
.frame(width: 32)
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
Text(account.name)
|
||||
.font(.headline)
|
||||
|
||||
if account.id == accountsStore.defaultAccountId {
|
||||
Image(systemName: "star.fill")
|
||||
.foregroundColor(.yellow)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
|
||||
Text(account.type.rawValue)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(account.balance.formatted(.currency(code: account.currency.rawValue)))
|
||||
.font(.callout.bold())
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
HStack {
|
||||
Text("Total Balance")
|
||||
Spacer()
|
||||
Text(accountsStore.totalBalance.formatted(.currency(code: settings.selectedCurrency.rawValue)))
|
||||
}
|
||||
.textCase(nil)
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Accounts")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
showingAddAccount = true
|
||||
} label: {
|
||||
Image(systemName: "plus.circle.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showingAddAccount) {
|
||||
NavigationView {
|
||||
AddAccountView()
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if accountsStore.accounts.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"No Accounts",
|
||||
systemImage: "creditcard",
|
||||
description: Text("Add your first account to start tracking your finances")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationView {
|
||||
AccountsListView()
|
||||
.environmentObject(AccountsStore.shared)
|
||||
.environmentObject(AppSettings.shared)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
// Path: Features/Accounts/AddAccountView.swift
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AddAccountView: View {
|
||||
|
|
@ -8,30 +6,19 @@ struct AddAccountView: View {
|
|||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@State private var name = ""
|
||||
@State private var type: AccountType = .cash
|
||||
@State private var balance: Double = 0
|
||||
@State private var currency: AppSettings.Currency = .usd
|
||||
@State private var icon = "creditcard"
|
||||
@State private var type = AccountType.cash
|
||||
@State private var balance = 0.0
|
||||
@State private var currency = AppSettings.Currency.usd
|
||||
@State private var isDefault = false
|
||||
@State private var showingTypeSelector = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
TextField("Account Name", text: $name)
|
||||
|
||||
Button {
|
||||
showingTypeSelector = true
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Type")
|
||||
Spacer()
|
||||
HStack {
|
||||
Image(systemName: type.icon)
|
||||
.foregroundColor(type.color)
|
||||
Text(type.rawValue)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
Picker("Type", selection: $type) {
|
||||
ForEach(AccountType.allCases, id: \.self) { type in
|
||||
Text(type.rawValue).tag(type)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -51,19 +38,13 @@ struct AddAccountView: View {
|
|||
.navigationTitle("New Account")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Add") {
|
||||
let account = AccountModel(
|
||||
name: name,
|
||||
balance: balance,
|
||||
type: type,
|
||||
icon: type.icon, // Используем иконку из типа аккаунта
|
||||
icon: type.icon,
|
||||
isDefault: isDefault,
|
||||
currency: currency
|
||||
)
|
||||
|
|
@ -73,18 +54,6 @@ struct AddAccountView: View {
|
|||
.disabled(name.isEmpty)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showingTypeSelector) {
|
||||
NavigationView {
|
||||
SelectAccountTypeView { selectedType in
|
||||
type = selectedType
|
||||
icon = selectedType.icon // Обновляем иконку при выборе типа
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
// Используем текущую валюту из настроек
|
||||
currency = settings.selectedCurrency
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import SwiftUI
|
|||
|
||||
struct EditAccountView: View {
|
||||
@EnvironmentObject private var accountsStore: AccountsStore
|
||||
@EnvironmentObject private var settings: AppSettings
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
let account: AccountModel
|
||||
|
||||
|
|
@ -9,7 +10,6 @@ struct EditAccountView: View {
|
|||
@State private var type: AccountType
|
||||
@State private var balance: Double
|
||||
@State private var currency: AppSettings.Currency
|
||||
@State private var icon: String
|
||||
@State private var isDefault: Bool
|
||||
|
||||
init(account: AccountModel) {
|
||||
|
|
@ -18,7 +18,6 @@ struct EditAccountView: View {
|
|||
_type = State(initialValue: account.type)
|
||||
_balance = State(initialValue: account.balance)
|
||||
_currency = State(initialValue: account.currency)
|
||||
_icon = State(initialValue: account.icon)
|
||||
_isDefault = State(initialValue: account.isDefault)
|
||||
}
|
||||
|
||||
|
|
@ -57,6 +56,7 @@ struct EditAccountView: View {
|
|||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
// В методе Save кнопки:
|
||||
Button("Save") {
|
||||
let updatedAccount = AccountModel(
|
||||
id: account.id,
|
||||
|
|
@ -67,7 +67,11 @@ struct EditAccountView: View {
|
|||
isDefault: isDefault,
|
||||
currency: currency
|
||||
)
|
||||
accountsStore.updateAccount(updatedAccount)
|
||||
if isDefault {
|
||||
accountsStore.setDefaultAccount(updatedAccount)
|
||||
} else {
|
||||
accountsStore.updateAccount(updatedAccount)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
.disabled(name.isEmpty)
|
||||
|
|
@ -80,5 +84,6 @@ struct EditAccountView: View {
|
|||
NavigationView {
|
||||
EditAccountView(account: AccountModel.previewData)
|
||||
.environmentObject(AccountsStore.shared)
|
||||
.environmentObject(AppSettings.shared)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,18 @@ struct AccountModel: Identifiable, Hashable {
|
|||
)
|
||||
}
|
||||
|
||||
func with(isDefault: Bool) -> AccountModel {
|
||||
AccountModel(
|
||||
id: self.id,
|
||||
name: self.name,
|
||||
balance: self.balance,
|
||||
type: self.type,
|
||||
icon: self.icon,
|
||||
isDefault: isDefault,
|
||||
currency: self.currency
|
||||
)
|
||||
}
|
||||
|
||||
static func == (lhs: AccountModel, rhs: AccountModel) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,55 +4,87 @@ class AccountsStore: ObservableObject {
|
|||
static let shared = AccountsStore()
|
||||
|
||||
@Published private(set) var accounts: [AccountModel] = []
|
||||
@Published private(set) var defaultAccountId: String?
|
||||
|
||||
var totalBalance: Double {
|
||||
accounts.reduce(0) { $0 + $1.balance }
|
||||
}
|
||||
|
||||
var defaultAccount: AccountModel? {
|
||||
accounts.first { $0.id == defaultAccountId }
|
||||
}
|
||||
|
||||
private init() {
|
||||
#if DEBUG
|
||||
self.accounts = AccountModel.previewItems
|
||||
if let firstAccount = accounts.first {
|
||||
self.defaultAccountId = firstAccount.id
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func addAccount(_ account: AccountModel) {
|
||||
if account.isDefault {
|
||||
accounts = accounts.map { acc in
|
||||
AccountModel(
|
||||
id: acc.id,
|
||||
name: acc.name,
|
||||
balance: acc.balance,
|
||||
type: acc.type,
|
||||
icon: acc.icon,
|
||||
isDefault: false,
|
||||
currency: acc.currency
|
||||
)
|
||||
defaultAccountId = account.id
|
||||
// Remove default flag from other accounts
|
||||
accounts = accounts.map { existingAccount in
|
||||
existingAccount.with(isDefault: false)
|
||||
}
|
||||
} else if accounts.isEmpty {
|
||||
// Make first account default
|
||||
defaultAccountId = account.id
|
||||
let newAccount = account.with(isDefault: true)
|
||||
accounts.append(newAccount)
|
||||
return
|
||||
}
|
||||
accounts.append(account)
|
||||
objectWillChange.send()
|
||||
}
|
||||
|
||||
func updateAccount(_ account: AccountModel) {
|
||||
if let index = accounts.firstIndex(where: { $0.id == account.id }) {
|
||||
if account.isDefault {
|
||||
accounts = accounts.map { acc in
|
||||
AccountModel(
|
||||
id: acc.id,
|
||||
name: acc.name,
|
||||
balance: acc.balance,
|
||||
type: acc.type,
|
||||
icon: acc.icon,
|
||||
isDefault: false,
|
||||
currency: acc.currency
|
||||
)
|
||||
guard let index = accounts.firstIndex(where: { $0.id == account.id }) else { return }
|
||||
|
||||
if account.isDefault {
|
||||
defaultAccountId = account.id
|
||||
// Remove default flag from other accounts
|
||||
accounts = accounts.map { existingAccount in
|
||||
if existingAccount.id == account.id {
|
||||
return account
|
||||
}
|
||||
return existingAccount.with(isDefault: false)
|
||||
}
|
||||
} else {
|
||||
// If this was the default account, make sure we have a new default
|
||||
if accounts[index].isDefault {
|
||||
if let firstOtherAccount = accounts.first(where: { $0.id != account.id }) {
|
||||
defaultAccountId = firstOtherAccount.id
|
||||
updateAccount(firstOtherAccount.with(isDefault: true))
|
||||
}
|
||||
}
|
||||
accounts[index] = account
|
||||
}
|
||||
objectWillChange.send()
|
||||
}
|
||||
|
||||
func deleteAccount(_ account: AccountModel) {
|
||||
accounts.removeAll { $0.id == account.id }
|
||||
|
||||
// If we deleted the default account, make another one default
|
||||
if account.isDefault {
|
||||
if let firstAccount = accounts.first {
|
||||
defaultAccountId = firstAccount.id
|
||||
updateAccount(firstAccount.with(isDefault: true))
|
||||
} else {
|
||||
defaultAccountId = nil
|
||||
}
|
||||
}
|
||||
|
||||
objectWillChange.send()
|
||||
}
|
||||
|
||||
func setDefaultAccount(_ account: AccountModel) {
|
||||
defaultAccountId = account.id
|
||||
updateAccount(account.with(isDefault: true))
|
||||
}
|
||||
|
||||
func getAccount(withId id: String) -> AccountModel? {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue