Add TransactionsStore for state management
This commit is contained in:
parent
2ec0711bde
commit
9b407e7195
5 changed files with 204 additions and 59 deletions
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
21
Coinly/Features/Models/TransactionsStore.swift
Normal file
21
Coinly/Features/Models/TransactionsStore.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
76
Coinly/Features/Transactions/AddTransactionView.swift
Normal file
76
Coinly/Features/Transactions/AddTransactionView.swift
Normal 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 })
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue