Add transaction filtering by period, type and categories

This commit is contained in:
“SamoilenkoVadym” 2025-03-02 18:54:18 +00:00
parent 30afdbb5f5
commit df59aab471
3 changed files with 166 additions and 6 deletions

View file

@ -0,0 +1,57 @@
//
// TransactionFilter.swift
// Coinly
//
// Created by Vadym Samoilenko on 02/03/2025.
//
import Foundation
struct TransactionFilter {
enum Period {
case all
case today
case week
case month
func filter(_ date: Date) -> Bool {
let calendar = Calendar.current
let now = Date()
switch self {
case .all:
return true
case .today:
return calendar.isDate(date, inSameDayAs: now)
case .week:
let weekAgo = calendar.date(byAdding: .day, value: -7, to: now)!
return date >= weekAgo
case .month:
let monthAgo = calendar.date(byAdding: .month, value: -1, to: now)!
return date >= monthAgo
}
}
}
var period: Period = .all
var selectedCategories: Set<String> = []
var transactionType: TransactionType? = nil
func applies(to transaction: TransactionModel) -> Bool {
// Check period
guard period.filter(transaction.date) else { return false }
// Check categories
if !selectedCategories.isEmpty && !selectedCategories.contains(transaction.category) {
return false
}
// Check type
if let type = transactionType, transaction.type != type {
return false
}
return true
}
}

View file

@ -0,0 +1,71 @@
import SwiftUI
struct TransactionFilterView: View {
@Environment(\.dismiss) private var dismiss
@Binding var filter: TransactionFilter
var body: some View {
NavigationView {
Form {
// Period Filter
Section("Time Period") {
Picker("Period", selection: $filter.period) {
Text("All Time").tag(TransactionFilter.Period.all)
Text("Today").tag(TransactionFilter.Period.today)
Text("This Week").tag(TransactionFilter.Period.week)
Text("This Month").tag(TransactionFilter.Period.month)
}
}
// Type Filter
Section("Transaction Type") {
Picker("Type", selection: Binding(
get: { filter.transactionType ?? .expense },
set: { filter.transactionType = $0 }
)) {
Text("All").tag(TransactionType.expense)
Text("Income").tag(TransactionType.income)
Text("Expense").tag(TransactionType.expense)
}
}
// Categories Filter
Section("Categories") {
ForEach(CategoryModel.categories) { category in
Toggle(isOn: Binding(
get: { filter.selectedCategories.contains(category.name) },
set: { isSelected in
if isSelected {
filter.selectedCategories.insert(category.name)
} else {
filter.selectedCategories.remove(category.name)
}
}
)) {
HStack {
Image(systemName: category.icon)
.foregroundColor(Color(category.color))
Text(category.name)
}
}
}
}
}
.navigationTitle("Filters")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Reset") {
filter = TransactionFilter()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
dismiss()
}
}
}
}
}
}

View file

@ -3,28 +3,60 @@ import SwiftUI
struct TransactionsView: View {
@ObservedObject var store: TransactionsStore
@State private var showingAddTransaction = false
@State private var showingFilters = false
@State private var filter = TransactionFilter()
var filteredTransactions: [TransactionModel] {
store.transactions.filter { filter.applies(to: $0) }
}
var body: some View {
NavigationView {
List {
ForEach(store.transactions) { transaction in
ForEach(filteredTransactions) { transaction in
TransactionRowView(transaction: transaction)
}
.onDelete { indexSet in
store.deleteTransaction(at: indexSet)
// Преобразуем индексы отфильтрованного списка в индексы полного списка
let transactionsToDelete = indexSet.map { filteredTransactions[$0] }
for transaction in transactionsToDelete {
if let index = store.transactions.firstIndex(where: { $0.id == transaction.id }) {
store.deleteTransaction(at: IndexSet([index]))
}
}
}
}
.navigationTitle("Transactions")
.toolbar {
Button {
showingAddTransaction = true
} label: {
Image(systemName: "plus")
ToolbarItem(placement: .navigationBarLeading) {
Button {
showingFilters = true
} label: {
Image(systemName: "line.3.horizontal.decrease.circle")
.foregroundColor(hasActiveFilters ? .blue : .gray)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button {
showingAddTransaction = true
} label: {
Image(systemName: "plus")
}
}
}
.sheet(isPresented: $showingAddTransaction) {
AddTransactionView(addTransaction: store.addTransaction)
}
.sheet(isPresented: $showingFilters) {
TransactionFilterView(filter: $filter)
}
}
}
private var hasActiveFilters: Bool {
filter.period != .all ||
!filter.selectedCategories.isEmpty ||
filter.transactionType != nil
}
}