Files
RouterOS/RouterOSAssistant/Features/Wizard/Steps/Setup/ReviewApplyView.swift
T
KayandClaude Sonnet 5 31607b7270 M17-M19: Bekannte Router, Live-Traffic-Anzeige, Übersicht-Animation+Drag
M17: "Bekannte Router" im Verbinden-Tab (SavedRouter/SavedRoutersStore),
Standort-Freitextfeld, Scroll-Cap ab 4 Einträgen. Bugfix: Umbenennen-
TextField steckte in einem sich selbst deaktivierenden Button.

M18: Live-Traffic-Punkt an Interfaces (InterfaceTrafficMonitor, eigene
SSH-Verbindung, monitor-traffic-Polling). Dabei zwei reale CLI-Parser-Bugs
gefunden und gefixt: running/disabled-Flags werden als Buchstaben vor dem
ersten Feld codiert, nicht als key=value; monitor-traffic liefert
"50.7kbps" statt einer reinen Zahl.

M19: Übersicht-Tab — animierte Flussrichtung auf allen Verbindungslinien
(TimelineView+dashPhase), frei verschiebbare Knoten mit Live-folgenden
Linien, Zurücksetzen-Button.

Zusätzlich (noch nicht live getestet, nur Build+Unit-Tests grün):
LAN-Port-Konflikt-Prüfung im Einrichten-Assistenten mit doppelter
Sicherheitsbestätigung, "Fertig"-Button nach erfolgreichem Anwenden.

82 Tests grün. HANDOFF.md/README.md/Manual.md/CHATLOG.md aktualisiert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDmUd93KxsYGr2kLTotWnG
2026-09-15 21:40:46 +02:00

86 lines
3.2 KiB
Swift

import SwiftUI
struct ReviewApplyView: View {
@ObservedObject var viewModel: SetupViewModel
let credentials: RouterOSCredentials?
@State private var showCliDetails = false
var body: some View {
Form {
Section("Geplante Änderungen") {
ForEach(viewModel.plannedCommands) { command in
VStack(alignment: .leading, spacing: 2) {
Text(command.summary)
if showCliDetails {
Text(command.cliLine)
.font(.system(.caption, design: .monospaced))
.foregroundStyle(.secondary)
}
}
}
Toggle("Details anzeigen (RouterOS-Befehle)", isOn: $showCliDetails)
}
Section {
Text("Vor dem Anwenden wird automatisch eine Sicherung der aktuellen Konfiguration erstellt (Tab \"Sicherungen\"). Ein garantiertes automatisches Zurückrollen bei Verbindungsabbruch gibt es nicht — bei Problemen die Sicherung im Tab \"Sicherungen\" verwenden oder den Router lokal (Ethernet/Konsole) wiederherstellen.")
.font(.caption)
.foregroundStyle(.secondary)
}
if !viewModel.applyLog.isEmpty {
Section("Ablauf") {
ForEach(viewModel.applyLog, id: \.self) { line in
Text(line).font(.caption)
}
}
}
if viewModel.didApplySuccessfully {
Section {
Label("Änderungen erfolgreich angewendet", systemImage: "checkmark.circle.fill")
.foregroundStyle(.green)
}
}
Section {
HStack {
Button("Zurück") { viewModel.goBack() }
.disabled(viewModel.isApplying)
Spacer()
if viewModel.didApplySuccessfully {
Button("Fertig") { viewModel.finish() }
.keyboardShortcut(.defaultAction)
} else {
Button {
if let credentials {
viewModel.apply(credentials: credentials)
}
} label: {
if viewModel.isApplying {
ProgressView()
} else {
Text("Jetzt anwenden")
}
}
.disabled(credentials == nil || viewModel.isApplying)
}
}
}
}
.formStyle(.grouped)
.navigationTitle("Übersicht & Anwenden")
.alert(
"Anwenden fehlgeschlagen",
isPresented: Binding(
get: { viewModel.applyError != nil },
set: { _ in }
)
) {
Button("OK") {}
} message: {
Text(viewModel.applyError ?? "")
}
}
}