Add TransactionsStore for state management

This commit is contained in:
“SamoilenkoVadym” 2025-03-02 18:45:26 +00:00
parent 2ec0711bde
commit 9b407e7195
5 changed files with 204 additions and 59 deletions

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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)
}
}

View file

@ -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 })
}
}

View file

@ -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)
}
}
}