From 9b407e7195b69f2c5cdb89acb9f54cfc54311872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CSamoilenkoVadym=E2=80=9D?= <“samoylenko.vadym@gmail.com”> Date: Sun, 2 Mar 2025 18:45:26 +0000 Subject: [PATCH] Add TransactionsStore for state management --- Coinly/App/ContentView.swift | 17 +-- Coinly/Features/Dashboard/DashboardView.swift | 108 ++++++++++++++---- .../Features/Models/TransactionsStore.swift | 21 ++++ .../Transactions/AddTransactionView.swift | 76 ++++++++++++ .../Transactions/TransactionsView.swift | 41 +++---- 5 files changed, 204 insertions(+), 59 deletions(-) create mode 100644 Coinly/Features/Models/TransactionsStore.swift create mode 100644 Coinly/Features/Transactions/AddTransactionView.swift diff --git a/Coinly/App/ContentView.swift b/Coinly/App/ContentView.swift index c41cb08..46ba200 100644 --- a/Coinly/App/ContentView.swift +++ b/Coinly/App/ContentView.swift @@ -1,16 +1,11 @@ -// -// ContentView.swift -// Coinly -// -// Created by Vadym Samoilenko on 02/03/2025. -// - import SwiftUI struct ContentView: View { + @State private var transactions = TransactionModel.sampleData + var body: some View { TabView { - DashboardView() + DashboardView(transactions: transactions) .tabItem { Label("Dashboard", systemImage: "chart.pie.fill") } @@ -27,9 +22,3 @@ struct ContentView: View { } } } - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -} diff --git a/Coinly/Features/Dashboard/DashboardView.swift b/Coinly/Features/Dashboard/DashboardView.swift index f78959a..23037ab 100644 --- a/Coinly/Features/Dashboard/DashboardView.swift +++ b/Coinly/Features/Dashboard/DashboardView.swift @@ -1,35 +1,93 @@ -// -// DashboardView.swift -// Coinly -// -// Created by Vadym Samoilenko on 02/03/2025. -// - - import SwiftUI struct DashboardView: View { + let transactions: [TransactionModel] + + var totalBalance: Double { + transactions.reduce(0) { $0 + $1.signedAmount } + } + + var income: Double { + transactions.filter { !$0.isExpense }.reduce(0) { $0 + $1.amount } + } + + var expenses: Double { + transactions.filter { $0.isExpense }.reduce(0) { $0 + $1.amount } + } + var body: some View { NavigationView { - VStack { - Text("Total Balance") - .font(.subheadline) - .foregroundColor(.gray) - - Text("$1,234.56") - .font(.title) - .fontWeight(.bold) - - Spacer() + ScrollView { + VStack(spacing: 20) { + // Balance Card + VStack(spacing: 8) { + Text("Total Balance") + .font(.subheadline) + .foregroundColor(.gray) + + Text(String(format: "$ %.2f", totalBalance)) + .font(.title) + .fontWeight(.bold) + } + .frame(maxWidth: .infinity) + .padding() + .background(Color(.systemBackground)) + .cornerRadius(12) + .shadow(radius: 2) + + // Income/Expense Summary + HStack(spacing: 16) { + // Income Card + VStack(spacing: 8) { + Text("Income") + .font(.subheadline) + .foregroundColor(.gray) + Text(String(format: "$ %.2f", income)) + .font(.headline) + .foregroundColor(.green) + } + .frame(maxWidth: .infinity) + .padding() + .background(Color(.systemBackground)) + .cornerRadius(12) + .shadow(radius: 2) + + // Expense Card + VStack(spacing: 8) { + Text("Expenses") + .font(.subheadline) + .foregroundColor(.gray) + Text(String(format: "$ %.2f", expenses)) + .font(.headline) + .foregroundColor(.red) + } + .frame(maxWidth: .infinity) + .padding() + .background(Color(.systemBackground)) + .cornerRadius(12) + .shadow(radius: 2) + } + + // Recent Transactions + VStack(alignment: .leading, spacing: 12) { + Text("Recent Transactions") + .font(.headline) + + ForEach(Array(transactions.prefix(3))) { transaction in + TransactionRowView(transaction: transaction) + Divider() + } + } + .frame(maxWidth: .infinity) + .padding() + .background(Color(.systemBackground)) + .cornerRadius(12) + .shadow(radius: 2) + } + .padding() } - .padding() .navigationTitle("Dashboard") + .background(Color(.systemGroupedBackground)) } } } - -struct DashboardView_Previews: PreviewProvider { - static var previews: some View { - DashboardView() - } -} \ No newline at end of file diff --git a/Coinly/Features/Models/TransactionsStore.swift b/Coinly/Features/Models/TransactionsStore.swift new file mode 100644 index 0000000..097f5d5 --- /dev/null +++ b/Coinly/Features/Models/TransactionsStore.swift @@ -0,0 +1,21 @@ +// +// TransactionsStore.swift +// Coinly +// +// Created by Vadym Samoilenko on 02/03/2025. +// + + +import Foundation + +class TransactionsStore: ObservableObject { + @Published var transactions: [TransactionModel] = TransactionModel.sampleData + + func addTransaction(_ transaction: TransactionModel) { + transactions.insert(transaction, at: 0) + } + + func deleteTransaction(at indexSet: IndexSet) { + transactions.remove(atOffsets: indexSet) + } +} \ No newline at end of file diff --git a/Coinly/Features/Transactions/AddTransactionView.swift b/Coinly/Features/Transactions/AddTransactionView.swift new file mode 100644 index 0000000..a3607da --- /dev/null +++ b/Coinly/Features/Transactions/AddTransactionView.swift @@ -0,0 +1,76 @@ +import SwiftUI + +struct AddTransactionView: View { + @Environment(\.dismiss) private var dismiss + @State private var amount: String = "" + @State private var note: String = "" + @State private var category: String = "Food" + @State private var date = Date() + @State private var type: TransactionType = .expense + + let categories = ["Food", "Transport", "Entertainment", "Shopping", "Salary", "Other"] + let addTransaction: (TransactionModel) -> Void + + var body: some View { + NavigationView { + Form { + Section("Amount") { + TextField("Amount", text: $amount) + .keyboardType(.decimalPad) + } + + Section("Type") { + Picker("Type", selection: $type) { + Text("Expense").tag(TransactionType.expense) + Text("Income").tag(TransactionType.income) + } + .pickerStyle(.segmented) + } + + Section("Details") { + Picker("Category", selection: $category) { + ForEach(categories, id: \.self) { category in + Text(category).tag(category) + } + } + + DatePicker("Date", selection: $date, displayedComponents: .date) + + TextField("Note", text: $note) + } + } + .navigationTitle("New Transaction") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { + dismiss() + } + } + + ToolbarItem(placement: .confirmationAction) { + Button("Add") { + if let amountDouble = Double(amount) { + let transaction = TransactionModel( + amount: amountDouble, + date: date, + type: type, + category: category, + note: note.isEmpty ? nil : note + ) + addTransaction(transaction) + dismiss() + } + } + .disabled(amount.isEmpty) + } + } + } + } +} + +struct AddTransactionView_Previews: PreviewProvider { + static var previews: some View { + AddTransactionView(addTransaction: { _ in }) + } +} diff --git a/Coinly/Features/Transactions/TransactionsView.swift b/Coinly/Features/Transactions/TransactionsView.swift index d579040..ff8fad4 100644 --- a/Coinly/Features/Transactions/TransactionsView.swift +++ b/Coinly/Features/Transactions/TransactionsView.swift @@ -1,35 +1,36 @@ -// -// TransactionsView.swift -// Coinly -// -// Created by Vadym Samoilenko on 02/03/2025. -// - - import SwiftUI struct TransactionsView: View { + @State private var transactions = TransactionModel.sampleData + @State private var showingAddTransaction = false + + func addTransaction(_ transaction: TransactionModel) { + transactions.insert(transaction, at: 0) + } + var body: some View { NavigationView { List { - Text("Transaction 1") - Text("Transaction 2") - Text("Transaction 3") + ForEach(transactions) { transaction in + TransactionRowView(transaction: transaction) + } + .onDelete(perform: deleteTransactions) } .navigationTitle("Transactions") .toolbar { - Button(action: { - // Add transaction action - }) { + Button { + showingAddTransaction = true + } label: { Image(systemName: "plus") } } + .sheet(isPresented: $showingAddTransaction) { + AddTransactionView(addTransaction: addTransaction) + } } } -} - -struct TransactionsView_Previews: PreviewProvider { - static var previews: some View { - TransactionsView() + + private func deleteTransactions(at offsets: IndexSet) { + transactions.remove(atOffsets: offsets) } -} \ No newline at end of file +}