// // BudgetView.swift // ydnab // // Created by Andrea Franceschini on 23/09/2020. // import SwiftUI import Combine import CoreData /// Shows the budget values for the selected budget, month, and year. struct BudgetView: View { @State var budget: BudgetInfo @Binding var currentBudgetId: UUID? @State private var month: Int = 0 @State private var monthName: String @State private var year: Int = Calendar(identifier: .gregorian).component(.year, from: Date()) @State private var showMonthPicker: Bool = false @State private var showNotImplemented: Bool = false @State private var showQuickBudget: Bool = false @State private var showEditCategoryEntry: Bool = false @State private var editingCategoryEntry: BudgetCategoryEntry? = nil @AppStorage("lastLoadedBudgetId") private var lastLoadedBudgetId = "" @Environment(\.managedObjectContext) var managedObjectContext init(budget: BudgetInfo, currentBudgetId: Binding) { _budget = .init(initialValue: budget) _currentBudgetId = currentBudgetId var cal = Calendar(identifier: .gregorian) cal.locale = Locale(identifier: self._budget.wrappedValue.localeIdentifier) let now = Date() _month = .init(initialValue: cal.component(.month, from: now)) _monthName = .init(initialValue: cal.monthSymbols[self._month.wrappedValue]) _year = .init(initialValue: cal.component(.year, from: now)) } func formatAmount(_ amount: NSNumber) -> String? { let f = NumberFormatter() f.locale = Locale(identifier: budget.localeIdentifier) f.numberStyle = .currency return f.string(from: amount) } // private func findBudgetCategoryEntry(withId: UUID) -> BudgetCategoryEntry? { // return budgetEntries.first(where: { $0.budgetCategoryId == withId }) // } private func zeroBudgetEntries() -> Void { // for section in budget.sections { // for category in section.categories { // if let entry = findBudgetCategoryEntry(withId: category.id) { // entry.amount = 0 // } else { // let newEntry = BudgetCategoryEntry(context: managedObjectContext) // newEntry.budgetId = budget.id // newEntry.budgetCategoryId = category.id // newEntry.month = Int64(month) // newEntry.year = Int64(year) // newEntry.amount = 0 // } // // do { // try managedObjectContext.save() // } catch { // print(error) // } // } // } } @State private var refreshView: Bool = false var body: some View { DynamicFetchView(predicate: NSPredicate(format: "budgetId == %@ AND month == %ld AND year == %ld", budget.id as CVarArg, month, year), sortDescriptors: []) { (budgetEntries: FetchedResults) in VStack { HStack { Button(action: { withAnimation() { showMonthPicker.toggle() } } ) { Text("\(monthName) \(String(year))") Image(systemName: showMonthPicker ? "chevron.up" : "chevron.down") } Spacer() Button(action: { showQuickBudget.toggle() }) { // TODO: Implement this Image(systemName: "bolt.fill") } Button(action: { showNotImplemented.toggle() }) { // TODO: Implement this Image(systemName: "gearshape.fill") }.padding(.leading, 11) } .padding(11) if showMonthPicker { YearAndMonthPicker(month: $month, monthName: $monthName, year: $year, locale: Locale(identifier: budget.localeIdentifier) ) } BudgetViewSummary(budget: $budget, month: $month, monthName: $monthName, year: $year) .padding([.leading, .trailing], 16) List { ForEach(budget.sections) { section in if !section.hidden { Section(header: BudgetSectionCell(section: section, amountText: "", //formatAmount(0) ?? "--", color: 0 < 0 ? Color("negativeAmount") : Color("positiveAmount")) ) { ForEach(section.categories) { category in if !category.hidden { BudgetCategoryCell(category: category, amountText: String(format: "= %@ =", budgetEntries.first(where: { $0.budgetCategoryId == category.id })?.amount ?? -69 ), color: 0 < 0 ? Color("negativeAmount") : Color("positiveAmount") ) } } } } } } .listStyle(PlainListStyle()) // .onChange(of: month) { m in // let entries = budgetEntries // print(entries.map { $0.amount }) // } .navigationBarTitle(budget.name, displayMode: .inline) .toolbar { EditButton() } .onAppear() { currentBudgetId = budget.id lastLoadedBudgetId = budget.id.uuidString } .actionSheet(isPresented: $showQuickBudget) { ActionSheet( title: Text("Quick budget"), message: Text("How would you like to set up your budget for \(monthName) \(String(year))?"), buttons: [ .cancel { print(self.showQuickBudget) }, .default(Text("All categories to zero"), action: zeroBudgetEntries), .default(Text("Values used the previous month"), action: { print(self) }) ] ) } .popover(isPresented: $showEditCategoryEntry) { // BudgetCategoryEntryEditor(amount: $editingCategoryEntry.wrappedValue?.amount) } .alert(isPresented: $showNotImplemented) { Alert(title: Text("Not Implemented!")) } } } } } struct BudgetCategoryEntryEditor: View { @Binding var amount: Decimal? @Binding var locale: Locale @State private var amountText = "" var body: some View { VStack { TextField("Amount", text: $amountText) .keyboardType(.numbersAndPunctuation) .onReceive(Just(amountText)) { newValue in let filtered = newValue.filter { "0123456789.,".contains($0) } if filtered != newValue { amount = Decimal(string: filtered, locale: locale) } } } .onAppear { amountText = "\(amount ?? -69)" } } } struct BudgetView_Previews: PreviewProvider { static let budget: [BudgetSection] = [ BudgetSection(name: "Everyday Expenses", categories: [ BudgetCategory(name: "Groceries"), BudgetCategory(name: "Eating out"), BudgetCategory(name: "Medical"), BudgetCategory(name: "Clothing"), BudgetCategory(name: "Household goods") ]), BudgetSection(name: "Travel", categories: [ BudgetCategory(name: "Transport"), BudgetCategory(name: "Fuel"), BudgetCategory(name: "Accommodation") ]), BudgetSection(name: "Rainy Days", categories: [ BudgetCategory(name: "Emergencies"), BudgetCategory(name: "Car insurance") ]), BudgetSection(name: "Monthly Expenses", categories: [ BudgetCategory(name: "Rent"), BudgetCategory(name: "Mobile") ]), BudgetSection(name: "Savings Goals", categories: [ BudgetCategory(name: "Savings"), BudgetCategory(name: "Holidays") ]) ] @State static var currentBudgetId: UUID? static var previews: some View { NavigationView { BudgetView(budget: BudgetInfo(name: "Default Budget", sections: budget), currentBudgetId: $currentBudgetId) } .preferredColorScheme(.light) } }