// // FilterView.swift // Feels // // Created by Trey Tartt on 1/12/22. // import SwiftUI import CoreData struct FilterView: View { let weekdays = [("Sun", 1), ("mon", 2), ("tue", 3), ("wed", 4), ("thur", 5), ("fri", 6), ("sat", 7)] let months = [(0, "J"), (1, "F"), (2,"M"), (3,"A"), (4,"M"), (5, "J"), (6,"J"), (7,"A"), (8,"S"), (9,"O"), (10, "N"), (11,"D")] @State private var toggle = true @State private var showFilter = false @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \MoodEntry.forDate, ascending: false)], animation: .spring()) private var items: FetchedResults @StateObject private var viewModel = FilterViewModel() //[ // 2001: [0: [], 1: [], 2: []], // 2002: [0: [], 1: [], 2: []] // ] let columns = [ GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), GridItem(.flexible(minimum: 5, maximum: 50)), ] var body: some View { VStack { filterButon .padding([.leading, .trailing, .top]) statsView .frame(minWidth: 0, maxWidth: .infinity, minHeight: 120, maxHeight: 120) .cornerRadius(25) .padding([.leading, .trailing]) Text(String(localized: "filter_view_total") + ": \(self.viewModel.numberOfRatings)") .font(.title2) if showFilter { filterView } gridView .onAppear(perform: { self.viewModel.filterEntries(startDate: Date(timeIntervalSince1970: 0), endDate: Date()) }) } } private var filterButon: some View { Button(action: { withAnimation{ showFilter.toggle() } }, label: { Text(showFilter ? String(localized: "filter_view_hide_filters") : String(localized: "filter_view_show_filters")) .frame(maxWidth: .infinity) .frame(height: 44) .foregroundColor(Color(UIColor.label)) .background(Color(UIColor.secondarySystemBackground)) .cornerRadius(25) }).frame(maxWidth: .infinity) } struct StatsSubView: View { let data: [MoodEntry] let mood: Mood var body: some View { VStack { Text(String(Stats.getCountFor(moodType: mood, inData: data))) .font(.title) Text(mood.strValue) .foregroundColor(mood.color) } } } private var statsView: some View { ZStack { Color(UIColor.secondarySystemBackground) HStack { Spacer() ForEach(Mood.allValues, id: \.self) { mood in StatsSubView(data: self.viewModel.uncategorizedData, mood: mood) Spacer() } } } .cornerRadius(25) .padding() } private var filterView: some View { VStack { VStack { ZStack { Color(UIColor.secondarySystemBackground) DatePicker( String(localized: "filter_view_begin_date"), selection: $viewModel.entryStartDate, displayedComponents: [.date] ).onChange(of: viewModel.entryStartDate, perform: { value in viewModel.filterEntries(startDate: viewModel.entryStartDate, endDate: viewModel.entryEndDate) }) .padding() } .frame(minWidth: 0, maxWidth: .infinity, minHeight: 44, maxHeight: 44) .cornerRadius(25) .padding([.leading, .trailing]) ZStack { Color(UIColor.secondarySystemBackground) DatePicker( String(localized: "filter_view_end_date"), selection: $viewModel.entryEndDate, displayedComponents: [.date] ).onChange(of: viewModel.entryStartDate, perform: { value in viewModel.filterEntries(startDate: viewModel.entryStartDate, endDate: viewModel.entryEndDate) }) .padding() } .frame(minWidth: 0, maxWidth: .infinity, minHeight: 44, maxHeight: 44) .cornerRadius(25) .padding([.leading, .trailing]) ZStack { Color(UIColor.secondarySystemBackground) HStack { Spacer() ForEach(weekdays.indices, id: \.self) { dayIdx in let day = String(weekdays[dayIdx].0) let value = weekdays[dayIdx].1 Button(day.capitalized, action: { if let index = viewModel.selectedDays.firstIndex(of: value) { viewModel.selectedDays.remove(at: index) } else { viewModel.selectedDays.append(value) } viewModel.filterEntries(startDate: viewModel.entryStartDate, endDate: viewModel.entryEndDate) }) .frame(maxWidth: .infinity) .foregroundColor(viewModel.selectedDays.contains(value) || viewModel.selectedDays.isEmpty ? .green : .red) } Spacer() } } .frame(minWidth: 0, maxWidth: .infinity, minHeight: 44, maxHeight: 44) .cornerRadius(25) .padding([ .leading, .trailing]) } filterButon .padding([.leading, .trailing, .top]) } } private var monthsHeader: some View { LazyVGrid(columns: columns, spacing: 0) { ForEach(months, id: \.self.0) { item in Text(item.1) .textCase(.uppercase) } }.padding([.leading, .trailing, .top]) } private var gridView: some View { VStack { monthsHeader .cornerRadius(25) .padding([.leading, .trailing]) VStack { ScrollView { ForEach(Array(self.viewModel.data.keys.sorted(by: >)), id: \.self) { yearKey in let yearData = self.viewModel.data[yearKey]! Text(String(yearKey)) .font(.title) yearGridView(yearData: yearData, columns: columns) } } .padding() } } } private struct yearGridView: View { let yearData: [Int: [DayChartView]] let columns: [GridItem] var body: some View { ZStack { Color(UIColor.secondarySystemBackground) VStack { LazyVGrid(columns: columns, spacing: 0) { ForEach(Array(yearData.keys.sorted(by: <)), id: \.self) { monthKey in let monthData = yearData[monthKey]! VStack { monthGridView(monthData: monthData) } } } .padding([.leading, .trailing, .top, .bottom]) } } .cornerRadius(25) } } private struct monthGridView: View { let monthData: [DayChartView] var body: some View { VStack { ForEach(monthData, id: \.self) { view in view } } } } } struct FilterView_Previews: PreviewProvider { static var previews: some View { Group { FilterView() FilterView() .preferredColorScheme(.dark) } } }