diff options
Diffstat (limited to 'DomainDig/ScheduledReportsView.swift')
| -rw-r--r-- | DomainDig/ScheduledReportsView.swift | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/DomainDig/ScheduledReportsView.swift b/DomainDig/ScheduledReportsView.swift new file mode 100644 index 0000000..f97ee77 --- /dev/null +++ b/DomainDig/ScheduledReportsView.swift @@ -0,0 +1,130 @@ +import SwiftUI + +struct ScheduledReportsView: View { + @Environment(\.appDensity) private var appDensity + @State private var settings = ScheduledReportStorage.loadSettings() + @State private var logs = ScheduledReportStorage.loadLogs() + @State private var isGenerating = false + @State private var statusMessage: String? + + var body: some View { + List { + Section("Overview") { + VStack(alignment: .leading, spacing: 8) { + if !FeatureAccessService.hasAccess(to: .automatedMonitoring) { + Text("Scheduled reports require Pro.") + .font(appDensity.font(.caption)) + .foregroundStyle(.secondary) + } + + Toggle("Scheduled Reports", isOn: Binding( + get: { settings.isEnabled }, + set: { newValue in + settings.isEnabled = newValue + saveSettings() + } + )) + .disabled(!FeatureAccessService.hasAccess(to: .automatedMonitoring)) + + Picker("Cadence", selection: Binding( + get: { settings.cadence }, + set: { newValue in + settings.cadence = newValue + saveSettings() + } + )) { + ForEach(ScheduledReportCadence.allCases) { cadence in + Text(cadence.title).tag(cadence) + } + } + + Picker("Format", selection: Binding( + get: { settings.format }, + set: { newValue in + settings.format = newValue + saveSettings() + } + )) { + ForEach([DomainExportFormat.markdown, .pdf, .json]) { format in + Text(format.title).tag(format) + } + } + + LabeledContent( + "Last Generated", + value: settings.lastGeneratedAt?.formatted(date: .abbreviated, time: .shortened) ?? "Never" + ) + + if let statusMessage { + Text(statusMessage) + .font(appDensity.font(.caption)) + .foregroundStyle(.secondary) + } + + Button(isGenerating ? "Generating…" : "Generate Now") { + Task { await generateNow() } + } + .disabled(isGenerating) + } + .padding(.vertical, 4) + } + .listRowBackground(Color(.systemGray6).opacity(0.5)) + + if logs.isEmpty { + Section { + EmptyStateCardView( + title: "No Reports Yet", + message: "Generated reports for your watchlist appear here after a manual or scheduled run.", + suggestion: "Tap Generate Now, or enable Scheduled Reports above.", + systemImage: "doc.text", + showsCardBackground: false + ) + } + .listRowBackground(Color(.systemGray6).opacity(0.5)) + } else { + Section("Recent Reports") { + ForEach(logs) { log in + Button { + share(log) + } label: { + VStack(alignment: .leading, spacing: 2) { + Text("\(log.domainCount) domains • \(log.format.title)") + .foregroundStyle(.primary) + Text(log.generatedAt.formatted(date: .abbreviated, time: .shortened)) + .font(.caption) + .foregroundStyle(.secondary) + } + } + } + } + .listRowBackground(Color(.systemGray6).opacity(0.5)) + } + } + .scrollContentBackground(.hidden) + .background(Color.black) + .navigationTitle("Scheduled Reports") + } + + private func saveSettings() { + ScheduledReportStorage.saveSettings(settings) + ScheduledReportScheduler.shared.syncSchedule() + } + + private func generateNow() async { + isGenerating = true + let outcome = await ScheduledReportService.shared.generateReport(trigger: .manual, requireEnabledSetting: false) + statusMessage = outcome.message + settings = ScheduledReportStorage.loadSettings() + logs = ScheduledReportStorage.loadLogs() + isGenerating = false + } + + private func share(_ log: ScheduledReportLog) { + let url = ScheduledReportStorage.reportsDirectory.appendingPathComponent(log.fileName) + guard let data = try? Data(contentsOf: url) else { + statusMessage = "That report is no longer available on disk." + return + } + ExportPresenter.share(filename: log.fileName, data: data) + } +} |
