/** * TrackerMap * Copyright (C) 2021-2022 Iván Ávalos , Henoch Ojeda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import SwiftUI import shared import SwiftUIX import Tabler extension EventInformation: Identifiable {} struct UnitReportsView: View { @StateObject var unitReportsViewModel: UnitReportsViewModel var unit: UnitInformation init (unit: UnitInformation) { self.unit = unit self._unitReportsViewModel = StateObject( wrappedValue: UnitReportsViewModel(deviceId: unit.device.id)) // Source: https://stackoverflow.com/a/63607411 UITableView.appearance().contentInset.top = -35 UITableView.appearance().contentInset.bottom = -25 } private var eventsConfig: TablerListConfig { TablerListConfig(gridItems: eventsGridItems) } private var eventsGridItems: [GridItem] = [ GridItem(.flexible(minimum: 100), alignment: .leading), GridItem(.flexible(minimum: 100), alignment: .leading), GridItem(.flexible(minimum: 100, maximum: 150), alignment: .leading), GridItem(.flexible(minimum: 150, maximum: 250), alignment: .leading), ] @ViewBuilder private func eventsHeader(_ ctx: Binding>) -> some View { Text("events-table-datetime") Text("events-table-event") Text("events-table-geofence") Text("events-table-address") } @ViewBuilder private func eventsRow(_ element: EventInformation) -> some View { if let eventTime = element.event.eventTime { Text(Formatter.companion.formatDate(str: eventTime)) } else { Text("") } if let eventType = element.event.type { switch EventInformation.companion.stringToReportType(s: eventType) { case .deviceOnline: Text("event-device-online") case .deviceUnknown: Text("event-device-unknown") case .deviceOffline: Text("event-device-offline") case .deviceInactive: Text("event-device-inactive") case .deviceMoving: Text("event-device-moving") case .deviceStopped: Text("event-device-stopped") case .deviceOverspeed: Text("event-device-overspeed") case .deviceFuelDrop: Text("event-device-fuel-drop") case .commandResult: Text("event-command-result") case .geofenceEnter: Text("event-geofence-enter") case .geofenceExit: Text("event-geofence-exit") case .alarm: Text("event-alarm") case .ignitionOn: Text("event-ignition-on") case .ignitionOff: Text("event-ignition-off") case .maintenance: Text("event-maintenance") case .textMessage: Text("event-text-message") case .driverChanged: Text("event-driver-changed") default: Text("event-unknown") } } else { Text("") } if let geofenceName = element.geofence?.name { Text(geofenceName) } else { Text("") } if let positionAddress = element.position?.address { Text(positionAddress) } else { Text("") } } var body: some View { VStack { if unitReportsViewModel.reportType == .events { if let report = unitReportsViewModel.report as? ReportController.ReportEventsReport { SidewaysScroller(minWidth: 1000) { TablerList(eventsConfig, headerContent: eventsHeader, rowContent: eventsRow, results: report.events) } } else { Spacer() } } else { MapView(layer: $unitReportsViewModel.layer, markers: $unitReportsViewModel.markers, geofences: $unitReportsViewModel.flatGeofences, selected: $unitReportsViewModel.selectedMarker, isReport: true) } // MARK: - Report type Picker(selection: $unitReportsViewModel.reportType) { Text("report-positions").tag(ReportController.ReportType.positions) Text("report-events").tag(ReportController.ReportType.events) Text("report-stops").tag(ReportController.ReportType.stops) } label: { EmptyView() } .pickerStyle(.segmented) .padding() Form { // MARK: - Report period Section { HStack { Text("report-period") Spacer() Picker(selection: $unitReportsViewModel.periodType, label: Text("report-period")) { Text("period-today").tag(ReportDates.PeriodTypes.today) Text("period-last-24").tag(ReportDates.PeriodTypes.last24) Text("period-yesterday").tag(ReportDates.PeriodTypes.yesterday) Text("period-this-week").tag(ReportDates.PeriodTypes.thisWeek) Text("period-last-7").tag(ReportDates.PeriodTypes.last7) Text("period-this-month").tag(ReportDates.PeriodTypes.thisMonth) Text("period-last-30").tag(ReportDates.PeriodTypes.last30) Text("period-custom").tag(ReportDates.PeriodTypes.custom) } .pickerStyle(.menu) } if unitReportsViewModel.periodType == .custom { DatePicker(selection: $unitReportsViewModel.fromDate, in: ...Date()) { Text("period-from") } DatePicker(selection: $unitReportsViewModel.toDate, in: ...Date()) { Text("period-to") } } } // MARK: - Report actions Section { Button { unitReportsViewModel.saveXlsxReport() } label: { Text("report-save") }.frame(maxWidth: .infinity) Button { unitReportsViewModel.shareXlsxReport() } label: { Text("report-share") }.frame(maxWidth: .infinity) } } .frame(maxHeight: 150) .sheet(isPresented: $unitReportsViewModel.showShareDialog) { ShareView(activityItems: $unitReportsViewModel.activityItems) } .fileExporter(isPresented: $unitReportsViewModel.showExportDialog, documents: [unitReportsViewModel.saveDocument], contentType: .xlsx) { result in switch result { case .success (let url): print ("Saved to \(url)") case .failure (let error): print (error.localizedDescription) } } }.onAppear { unitReportsViewModel.fetchReport() } } }