summaryrefslogtreecommitdiff
path: root/DomainDig/BatchResultsView.swift
diff options
context:
space:
mode:
Diffstat (limited to 'DomainDig/BatchResultsView.swift')
-rw-r--r--DomainDig/BatchResultsView.swift114
1 files changed, 114 insertions, 0 deletions
diff --git a/DomainDig/BatchResultsView.swift b/DomainDig/BatchResultsView.swift
new file mode 100644
index 0000000..e741830
--- /dev/null
+++ b/DomainDig/BatchResultsView.swift
@@ -0,0 +1,114 @@
+import SwiftUI
+
+struct BatchResultsView: View {
+ @Bindable var viewModel: DomainViewModel
+ let title: String
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 12) {
+ HStack(alignment: .top) {
+ SectionTitleView(title: title)
+ Spacer()
+ if viewModel.batchLookupRunning {
+ VStack(alignment: .trailing, spacing: 4) {
+ ProgressView(value: Double(viewModel.batchCompletedCount), total: Double(max(viewModel.batchTotalCount, 1)))
+ .tint(.cyan)
+ .frame(width: 120)
+ Text(viewModel.batchProgressLabel)
+ .font(.system(.caption2, design: .monospaced))
+ .foregroundStyle(.secondary)
+ }
+ } else if !viewModel.batchResults.isEmpty {
+ Text("\(viewModel.batchResults.count) domains")
+ .font(.system(.caption2, design: .monospaced))
+ .foregroundStyle(.secondary)
+ }
+ }
+
+ if viewModel.batchResults.isEmpty {
+ MessageCardView(text: "No batch results yet", isError: false)
+ } else {
+ CardView(allowsHorizontalScroll: false) {
+ ForEach(viewModel.batchResults) { result in
+ if let entry = viewModel.historyEntry(for: result) {
+ NavigationLink {
+ HistoryDetailView(viewModel: viewModel, entry: entry)
+ } label: {
+ BatchResultRowView(result: result)
+ }
+ .buttonStyle(.plain)
+ } else {
+ BatchResultRowView(result: result)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+struct BatchResultRowView: View {
+ let result: BatchLookupResult
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 6) {
+ HStack(alignment: .firstTextBaseline, spacing: 8) {
+ Text(result.domain)
+ .font(.system(.callout, design: .monospaced))
+ .foregroundStyle(.primary)
+ .lineLimit(1)
+ Spacer(minLength: 8)
+ Text(result.quickStatus)
+ .font(.system(.caption2, design: .monospaced))
+ .foregroundStyle(quickStatusColor)
+ .padding(.horizontal, 8)
+ .padding(.vertical, 4)
+ .background(quickStatusColor.opacity(0.16))
+ .clipShape(Capsule())
+ }
+
+ HStack(spacing: 10) {
+ Text(availabilityText)
+ Text(result.primaryIP ?? "No IP")
+ Text(result.timestamp.formatted(date: .abbreviated, time: .shortened))
+ }
+ .font(.system(.caption2, design: .monospaced))
+ .foregroundStyle(.secondary)
+
+ if let errorMessage = result.errorMessage {
+ Text(errorMessage)
+ .font(.system(.caption2, design: .monospaced))
+ .foregroundStyle(.red)
+ }
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .padding(.vertical, 4)
+ }
+
+ private var availabilityText: String {
+ switch result.availability {
+ case .available:
+ return "Available"
+ case .registered:
+ return "Registered"
+ case .unknown, .none:
+ return "Unknown"
+ }
+ }
+
+ private var quickStatusColor: Color {
+ switch result.status {
+ case .pending:
+ return .secondary
+ case .running:
+ return .cyan
+ case .completed:
+ if result.quickStatus == "Changed" {
+ return .yellow
+ }
+ return .green
+ case .failed:
+ return .red
+ }
+ }
+}