connection updates
This commit is contained in:
@@ -2962,6 +2962,8 @@ struct WorkbenchAdministrationView: View {
|
||||
// Phase 25/26: bounded, in-session samples. No credentials or SQL persisted.
|
||||
@State private var diagnosticSamples: [DiagnosticSample] = []
|
||||
@State private var showingTrends = false
|
||||
@State private var selectedLogEntry: DBSynLogDetail?
|
||||
@State private var showingFullError = false
|
||||
|
||||
private var supportsTrends: Bool {
|
||||
page == .slowQueries || page == .connectionStatistics ||
|
||||
@@ -3077,6 +3079,7 @@ struct WorkbenchAdministrationView: View {
|
||||
} description: {
|
||||
Text(errorMessage)
|
||||
} actions: {
|
||||
Button("View Full Error…") { showingFullError = true }
|
||||
Button("Retry") {
|
||||
Task { await refresh() }
|
||||
}
|
||||
@@ -3237,6 +3240,12 @@ struct WorkbenchAdministrationView: View {
|
||||
.sheet(isPresented: $showingTrends) {
|
||||
WorkbenchDiagnosticTrendsView(page: page, samples: diagnosticSamples)
|
||||
}
|
||||
.sheet(item: $selectedLogEntry) { entry in
|
||||
DBSynLogDetailSheet(title: "MySQL Server Log Entry", details: entry.text)
|
||||
}
|
||||
.sheet(isPresented: $showingFullError) {
|
||||
DBSynLogDetailSheet(title: "MySQL Error Details", details: errorMessage ?? "No error available.")
|
||||
}
|
||||
.sheet(isPresented: $showingGrants) {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Grants: \(selectedAccount ?? "")")
|
||||
@@ -3345,6 +3354,11 @@ struct WorkbenchAdministrationView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
if page == .serverLogs, let errorMessage {
|
||||
Button("Full Error…") { showingFullError = true }
|
||||
.help(errorMessage)
|
||||
}
|
||||
|
||||
if page != .dashboard {
|
||||
TextField("Filter results", text: $searchText)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
@@ -3452,6 +3466,40 @@ struct WorkbenchAdministrationView: View {
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.accessibilityIdentifier("scrollable-dashboard-phase51")
|
||||
case .serverLogs:
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack {
|
||||
Text("Select an entry to view the complete log message and copy it.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
Button("Copy All Logs") {
|
||||
let all = filteredRows.map { logText(for: $0) }.joined(separator: "\n\n---\n\n")
|
||||
NSPasteboard.general.clearContents()
|
||||
NSPasteboard.general.setString(all, forType: .string)
|
||||
}
|
||||
.disabled(filteredRows.isEmpty)
|
||||
}
|
||||
.padding(12)
|
||||
List(Array(filteredRows.enumerated()), id: \.offset) { _, row in
|
||||
Button {
|
||||
selectedLogEntry = DBSynLogDetail(text: logText(for: row))
|
||||
} label: {
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text(logValue("DATA", in: row))
|
||||
.lineLimit(2)
|
||||
.foregroundStyle(.primary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Text([logValue("LOGGED", in: row), logValue("PRIO", in: row), logValue("ERROR_CODE", in: row)]
|
||||
.filter { !$0.isEmpty }.joined(separator: " · "))
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
case .serverCounters, .connectionStatistics, .innodbMetrics:
|
||||
WorkbenchLiveMonitorScreen(
|
||||
page: page,
|
||||
@@ -3468,6 +3516,16 @@ struct WorkbenchAdministrationView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func logValue(_ name: String, in row: [String]) -> String {
|
||||
guard let index = result.columns.firstIndex(where: { $0.caseInsensitiveCompare(name) == .orderedSame }),
|
||||
index < row.count else { return "" }
|
||||
return row[index]
|
||||
}
|
||||
|
||||
private func logText(for row: [String]) -> String {
|
||||
zip(result.columns, row).map { "\($0.0): \($0.1)" }.joined(separator: "\n")
|
||||
}
|
||||
|
||||
private var footer: some View {
|
||||
HStack {
|
||||
if let lastUpdated {
|
||||
@@ -7219,3 +7277,48 @@ private struct DBSynConnectionManager: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Copyable administration log details
|
||||
|
||||
private struct DBSynLogDetail: Identifiable {
|
||||
let id = UUID()
|
||||
let text: String
|
||||
}
|
||||
|
||||
private struct DBSynLogDetailSheet: View {
|
||||
let title: String
|
||||
let details: String
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
HStack {
|
||||
Label(title, systemImage: "doc.text.magnifyingglass")
|
||||
.font(.title2.bold())
|
||||
Spacer()
|
||||
Button("Copy Full Text") {
|
||||
NSPasteboard.general.clearContents()
|
||||
NSPasteboard.general.setString(details, forType: .string)
|
||||
}
|
||||
.keyboardShortcut("c", modifiers: [.command, .shift])
|
||||
Button("Close") { dismiss() }
|
||||
.keyboardShortcut(.cancelAction)
|
||||
}
|
||||
Text("Select text to copy, or use Copy Full Text for the complete error and metadata.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
ScrollView([.vertical, .horizontal]) {
|
||||
Text(details)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
.textSelection(.enabled)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(12)
|
||||
}
|
||||
.background(Color(nsColor: .textBackgroundColor))
|
||||
.overlay(RoundedRectangle(cornerRadius: 6).stroke(Color(nsColor: .separatorColor)))
|
||||
}
|
||||
.padding(20)
|
||||
.frame(minWidth: 720, minHeight: 420)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user