updates
This commit is contained in:
@@ -1,153 +1,102 @@
|
||||
import SwiftUI
|
||||
|
||||
|
||||
|
||||
struct MonitorMenuView: View {
|
||||
@ObservedObject var monitor: ServerMonitor
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
|
||||
// Header
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Label("PEGASUS MONITOR", systemImage: "server.rack")
|
||||
.font(.headline)
|
||||
|
||||
Label(
|
||||
monitor.status.title,
|
||||
systemImage: monitor.status.symbol
|
||||
)
|
||||
.foregroundStyle(monitor.status.color)
|
||||
.font(.subheadline.bold())
|
||||
|
||||
Text("192.168.1.2")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(16)
|
||||
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Label("PEGASUS MONITOR", systemImage: "server.rack").font(.headline)
|
||||
Label(monitor.status.title, systemImage: monitor.status.symbol)
|
||||
.foregroundStyle(monitor.status.color)
|
||||
}
|
||||
Spacer()
|
||||
if monitor.offlineCount > 0 {
|
||||
Text("\(monitor.offlineCount) offline").foregroundStyle(.red).font(.caption.bold())
|
||||
}
|
||||
}.padding(14)
|
||||
Divider()
|
||||
|
||||
// Systems: vertical stack
|
||||
ScrollView(.vertical) {
|
||||
LazyVStack(alignment: .leading, spacing: 0) {
|
||||
if let report = monitor.report {
|
||||
if report.groups.isEmpty {
|
||||
Text("No system groups returned by Pegasus.")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(16)
|
||||
}
|
||||
|
||||
ForEach(report.groups) { group in
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text(group.name)
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
Label(
|
||||
group.status.title,
|
||||
systemImage: group.status.symbol
|
||||
)
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
.foregroundStyle(group.status.color)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 11)
|
||||
|
||||
Divider()
|
||||
.padding(.leading, 16)
|
||||
}
|
||||
} else {
|
||||
Text("Waiting for Pegasus health data…")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(16)
|
||||
Text("SERVERS").font(.caption.bold()).foregroundStyle(.secondary).padding(.horizontal, 14).padding(.top, 12)
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 2) {
|
||||
ForEach(monitor.servers) { server in
|
||||
Button {
|
||||
monitor.select(server.id)
|
||||
} label: {
|
||||
HStack {
|
||||
Image(systemName: monitor.results[server.id]?.status.symbol ?? "circle.dotted")
|
||||
.foregroundStyle(server.enabled ? (monitor.results[server.id]?.status.color ?? .secondary) : .gray)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(server.name).fontWeight(server.id == monitor.selectedID ? .bold : .regular)
|
||||
Text(server.host).font(.caption2).foregroundStyle(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
if monitor.results[server.id]?.refreshing == true { ProgressView().controlSize(.mini) }
|
||||
else if let report = monitor.results[server.id]?.report {
|
||||
Text("\(report.counts.ok) OK").font(.caption2).foregroundStyle(.secondary)
|
||||
}
|
||||
if server.id == monitor.selectedID { Image(systemName: "checkmark") }
|
||||
}.padding(.horizontal, 14).padding(.vertical, 8)
|
||||
.contentShape(Rectangle())
|
||||
}.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 420)
|
||||
|
||||
}.frame(maxHeight: 175)
|
||||
Divider()
|
||||
|
||||
// Summary
|
||||
if let report = monitor.report {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text(
|
||||
"\(report.counts.ok) OK · " +
|
||||
"\(report.counts.warn) WARN · " +
|
||||
"\(report.counts.fail) FAIL"
|
||||
)
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
|
||||
Text(
|
||||
monitor.lastChecked.map {
|
||||
"Last checked: \($0.formatted(date: .omitted, time: .shortened))"
|
||||
} ?? "Not checked"
|
||||
)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(16)
|
||||
if let selected = monitor.selected {
|
||||
HStack {
|
||||
Text(selected.name.uppercased()).font(.caption.bold())
|
||||
Spacer()
|
||||
Button { Task { await monitor.refreshSelected() } } label: { Image(systemName: "arrow.clockwise") }
|
||||
.buttonStyle(.plain)
|
||||
}.padding(.horizontal, 14).padding(.vertical, 10)
|
||||
ScrollView {
|
||||
LazyVStack(alignment: .leading, spacing: 0) {
|
||||
if let report = monitor.report {
|
||||
ForEach(report.groups) { group in
|
||||
HStack {
|
||||
Text(group.name).font(.system(size: 12, weight: .medium))
|
||||
Spacer()
|
||||
Label(group.status.title, systemImage: group.status.symbol)
|
||||
.foregroundStyle(group.status.color).font(.caption)
|
||||
}.padding(.horizontal, 14).padding(.vertical, 8)
|
||||
Divider().padding(.leading, 14)
|
||||
}
|
||||
} else {
|
||||
Text(monitor.selectedResult?.error ?? "Waiting for health data…")
|
||||
.font(.caption).foregroundStyle(.secondary).padding(14)
|
||||
}
|
||||
}
|
||||
}.frame(height: 300)
|
||||
}
|
||||
|
||||
if let error = monitor.lastError {
|
||||
Text(error)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.red)
|
||||
.textSelection(.enabled)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
// Vertical actions
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Button("Refresh") {
|
||||
Task { await monitor.refresh() }
|
||||
}
|
||||
.disabled(monitor.isRefreshing)
|
||||
|
||||
Button("Diagnostics") {
|
||||
monitor.showDiagnostics = true
|
||||
}
|
||||
|
||||
Button("Settings") {
|
||||
monitor.showSettings = true
|
||||
}
|
||||
|
||||
Button("Quit") {
|
||||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
let totals = monitor.aggregate
|
||||
Text("ALL SERVERS: \(totals.ok) OK · \(totals.warn) WARN · \(totals.fail) FAIL")
|
||||
.font(.caption.bold()).padding(14)
|
||||
if let error = monitor.selectedResult?.error {
|
||||
Text(error).foregroundStyle(.red).font(.caption).textSelection(.enabled)
|
||||
.padding(.horizontal, 14).padding(.bottom, 8)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(16)
|
||||
}
|
||||
.frame(width: 320)
|
||||
.sheet(isPresented: $monitor.showDiagnostics) {
|
||||
DiagnosticsView(monitor: monitor)
|
||||
}
|
||||
.sheet(isPresented: $monitor.showSettings) {
|
||||
SettingsView(monitor: monitor)
|
||||
.frame(width: 480)
|
||||
}
|
||||
Divider()
|
||||
HStack {
|
||||
Button("Refresh All") { Task { await monitor.refreshAll() } }
|
||||
Spacer()
|
||||
Button("Diagnostics") { openWindow(id: "monitor-diagnostics") }
|
||||
Button("Settings") { openWindow(id: "monitor-settings") }
|
||||
Button("Quit") { NSApp.terminate(nil) }
|
||||
}.buttonStyle(.borderless).padding(14)
|
||||
}.frame(width: 410)
|
||||
}
|
||||
}
|
||||
|
||||
struct DiagnosticsView: View {
|
||||
@ObservedObject var monitor: ServerMonitor
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
HStack {
|
||||
Text("Pegasus Diagnostics").font(.title2.bold())
|
||||
Spacer()
|
||||
Button("Close") { dismiss() }
|
||||
}
|
||||
Text("Latest remote report — refresh from the menu to update")
|
||||
.foregroundStyle(.secondary)
|
||||
Text("\(monitor.selected?.name ?? "Server") — Diagnostics").font(.title2.bold())
|
||||
Text("Select a server in the menu bar to change this report.").font(.caption).foregroundStyle(.secondary)
|
||||
ScrollView {
|
||||
LazyVStack(alignment: .leading, spacing: 10) {
|
||||
ForEach(monitor.report?.checks ?? []) { check in
|
||||
@@ -161,10 +110,7 @@ struct DiagnosticsView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
.padding(20)
|
||||
.frame(width: 650, height: 560)
|
||||
}.textSelection(.enabled)
|
||||
}.padding(20).frame(minWidth: 650, minHeight: 450)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user