diff --git a/Screenshots/edit_server.png b/Screenshots/edit_server.png
new file mode 100644
index 0000000..6d0bf2f
Binary files /dev/null and b/Screenshots/edit_server.png differ
diff --git a/Screenshots/general_view.png b/Screenshots/general_view.png
new file mode 100644
index 0000000..42bc302
Binary files /dev/null and b/Screenshots/general_view.png differ
diff --git a/Screenshots/resources_view.png b/Screenshots/resources_view.png
new file mode 100644
index 0000000..5519ea6
Binary files /dev/null and b/Screenshots/resources_view.png differ
diff --git a/Screenshots/services_view.png b/Screenshots/services_view.png
new file mode 100644
index 0000000..172db7e
Binary files /dev/null and b/Screenshots/services_view.png differ
diff --git a/iKeyMon/Model/Server.swift b/iKeyMon/Model/Server.swift
index a7a4caa..b37b883 100644
--- a/iKeyMon/Model/Server.swift
+++ b/iKeyMon/Model/Server.swift
@@ -13,17 +13,19 @@ struct Server: Identifiable, Codable, Hashable, Equatable {
 
     // runtime-only, skip for Codable / Hashable / Equatable
     var info: ServerInfo? = nil
+    var pingable: Bool = false
 
-    init(id: UUID = UUID(), hostname: String, info: ServerInfo? = nil) {
+    init(id: UUID = UUID(), hostname: String, info: ServerInfo? = nil, pingable: Bool = false) {
         self.id = id
         self.hostname = hostname
         self.info = info
+        self.pingable = pingable
     }
 
     // MARK: - Manual conformance
 
     static func == (lhs: Server, rhs: Server) -> Bool {
-        lhs.id == rhs.id && lhs.hostname == rhs.hostname
+        lhs.id == rhs.id && lhs.hostname == rhs.hostname && lhs.info == rhs.info && lhs.pingable == rhs.pingable
     }
 
     func hash(into hasher: inout Hasher) {
diff --git a/iKeyMon/Model/ServerInfo.swift b/iKeyMon/Model/ServerInfo.swift
index fca7c49..fd60e36 100644
--- a/iKeyMon/Model/ServerInfo.swift
+++ b/iKeyMon/Model/ServerInfo.swift
@@ -5,7 +5,13 @@
 //  Created by tracer on 30.03.25.
 //
 
-struct ServerInfo: Decodable {
+import Foundation
+
+struct ServerInfo: Decodable, Equatable {
+    static func == (lhs: ServerInfo, rhs: ServerInfo) -> Bool {
+        lhs.hostname == rhs.hostname && lhs.serverTime == rhs.serverTime
+    }
+    
     var hostname: String
     var ipAddresses: [String]
 //    var processor: String
@@ -24,6 +30,12 @@ struct ServerInfo: Decodable {
     var memory: Memory
     var swap: Memory
     var diskSpace: DiskSpace
+    var panelVersion: String
+    var panelBuild: String
+    var apiVersion: String
+    var additionalPHPInterpreters: [PHPInterpreter]?
+
+    
 }
 
 extension ServerInfo {
@@ -50,12 +62,33 @@ extension ServerInfo {
         ),
         memory: Memory(free: 1234, used: 4567, total: 123456, percent: 23.45),
         swap: Memory(free: 1234, used: 4567, total: 123456, percent: 23.45),
-        diskSpace:  DiskSpace(free: 1234, used: 4567, total: 123456, percent: 23.45)
+        diskSpace:  DiskSpace(free: 1234, used: 4567, total: 123456, percent: 23.45),
+        panelVersion: "25.0",
+        panelBuild: "3394",
+        apiVersion: "2.0"
     )
     
     var cpuLoadDetail: String {
         "1min: \(load.minute1), 5min: \(load.minute5), 15min: \(load.minute15) (\(load.level))"
     }
+    
+    var formattedServerTime: String {
+        let isoFormatter = ISO8601DateFormatter()
+        let formatter = DateFormatter()
+        formatter.locale = Locale(identifier: "de_DE")
+        formatter.dateStyle = .medium
+        formatter.timeStyle = .short
+
+        if let date = isoFormatter.date(from: serverTime) {
+            return formatter.string(from: date)
+        } else {
+            return serverTime // fallback to raw string if parsing fails
+        }
+    }
+    
+    var formattedVersion: String {
+        "\(panelVersion) (Build \(panelBuild))"
+    }
 }
 
 extension ServerInfo {
@@ -82,5 +115,10 @@ extension ServerInfo {
         self.memory = response.utilization.memory
         self.swap = response.utilization.swap
         self.diskSpace = response.utilization.diskSpace
+        
+        self.panelVersion = response.meta.panelVersion
+        self.panelBuild = response.meta.panelBuild
+        self.apiVersion = response.meta.apiVersion
+        self.additionalPHPInterpreters = response.additionalPHPInterpreters
     }
 }
diff --git a/iKeyMon/ServerAPI.swift b/iKeyMon/ServerAPI.swift
new file mode 100644
index 0000000..c062cd3
--- /dev/null
+++ b/iKeyMon/ServerAPI.swift
@@ -0,0 +1,67 @@
+//
+//  ServerAPI.swift
+//  iKeyMon
+//
+//  Created by tracer on 06.04.25.
+//
+import Foundation
+
+final class ServerAPI {
+    private let hostname: String
+    private let apiKey: String
+
+    init(hostname: String, apiKey: String) {
+        self.hostname = hostname
+        self.apiKey = apiKey
+    }
+
+    init?(server: Server) {
+        guard let apiKey = KeychainHelper.loadApiKey(for: server.hostname)?.trimmingCharacters(in: .whitespacesAndNewlines) else {
+            return nil
+        }
+        self.hostname = server.hostname
+        self.apiKey = apiKey
+    }
+
+    @discardableResult
+    func ping() async -> Bool {
+        guard let url = URL(string: "https://\(hostname)/api/v2/ping") else {
+            return false
+        }
+
+        var request = URLRequest(url: url)
+        request.httpMethod = "GET"
+        request.setValue(apiKey, forHTTPHeaderField: "X-API-KEY")
+
+        do {
+            let (data, _ /*response */) = try await URLSession.shared.data(for: request)
+//            if let httpResponse = response as? HTTPURLResponse {
+//                print("data: \(String(data: data, encoding: .utf8))")
+//            }
+
+            if let result = try? JSONDecoder().decode([String: String].self, from: data), result["response"] == "pong" {
+                return true
+            }
+        } catch {
+            print("❌ Ping error: \(error)")
+        }
+
+        return false
+    }
+    
+    func fetchServerInfo() async throws -> ServerResponse {
+        guard let url = URL(string: "https://\(hostname)/api/v2/server") else {
+            throw URLError(.badURL)
+        }
+
+        var request = URLRequest(url: url)
+        request.httpMethod = "GET"
+        request.setValue(apiKey, forHTTPHeaderField: "X-API-KEY")
+        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+
+        let (data, _) = try await URLSession.shared.data(for: request)
+        let decoded = try JSONDecoder().decode(ServerResponse.self, from: data)
+                
+        return decoded
+    }
+}
diff --git a/iKeyMon/Views/AddServerView.swift b/iKeyMon/Views/AddServerView.swift
deleted file mode 100644
index 40ebb1f..0000000
--- a/iKeyMon/Views/AddServerView.swift
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-//  addServerView.swift
-//  iKeyMon
-//
-//  Created by tracer on 30.03.25.
-//
-
-import SwiftUI
-
-struct AddServerView: View {
-    @Environment(\.dismiss) private var dismiss
-
-    @Binding var servers: [Server]
-    @State private var hostname: String = ""
-    @State private var apiKey: String = ""
-
-    var saveAction: () -> Void
-    
-    var body: some View {
-        VStack {
-            Text("Add New Server")
-                .font(.headline)
-
-            TextField("Hostname", text: $hostname)
-                .textFieldStyle(RoundedBorderTextFieldStyle())
-                .padding(.top)
-
-            SecureField("API Key", text: $apiKey)
-                .textFieldStyle(RoundedBorderTextFieldStyle())
-
-            HStack {
-                Button("Cancel") {
-                    dismiss()
-                }
-                Spacer()
-                Button("Add") {
-                    let newServer = Server(hostname: hostname)
-                    servers.append(newServer)
-                    saveAction()
-                    KeychainHelper.save(apiKey: apiKey, for: hostname)
-                    dismiss()
-                }
-                .disabled(hostname.isEmpty || apiKey.isEmpty)
-            }
-            .padding(.top)
-        }
-        .padding()
-        .frame(width: 300)
-    }
-}
-
-#Preview {
-    AddServerView(servers: .constant([]), saveAction: {})
-}
diff --git a/iKeyMon/Views/Cells/InfoCell.swift b/iKeyMon/Views/Cells/InfoCell.swift
new file mode 100644
index 0000000..4e23236
--- /dev/null
+++ b/iKeyMon/Views/Cells/InfoCell.swift
@@ -0,0 +1,47 @@
+//
+//  InfoBarCell.swift
+//  iKeyMon
+//
+//  Created by tracer on 03.04.25.
+//
+
+//
+//  ResourcesBarRow.swift
+//  iKeyMon
+//
+//  Created by tracer on 31.03.25.
+//
+
+import SwiftUI
+
+struct InfoCell: View {
+    var value: [String]
+    var monospaced: Bool = false
+    var color: Color = .primary
+
+    init(value: [String], monospaced: Bool = false) {
+        self.value = value
+        self.monospaced = monospaced
+    }
+
+    var body: some View {
+        VStack(alignment: .leading) {
+            VStack(alignment: .leading, spacing: 2) {
+                ForEach(value, id: \.self) { item in
+                    Text(item)
+                        .font(monospaced ? .system(.body, design: .monospaced) : .body)
+                }
+            }
+//            if let subtext {
+//                Text(subtext)
+//                    .font(.caption)
+//                    .foregroundColor(.secondary)
+//            }
+        }
+    }
+}
+
+#Preview {
+    InfoCell(value: ["Some Text", "Another Text"])
+}
+
diff --git a/iKeyMon/Views/Rows/LoadBarCell.swift b/iKeyMon/Views/Cells/LoadBarCell.swift
similarity index 100%
rename from iKeyMon/Views/Rows/LoadBarCell.swift
rename to iKeyMon/Views/Cells/LoadBarCell.swift
diff --git a/iKeyMon/Views/Rows/UsageBarCell.swift b/iKeyMon/Views/Cells/UsageBarCell.swift
similarity index 100%
rename from iKeyMon/Views/Rows/UsageBarCell.swift
rename to iKeyMon/Views/Cells/UsageBarCell.swift
diff --git a/iKeyMon/Views/ContentView.swift b/iKeyMon/Views/ContentView.swift
deleted file mode 100644
index d05c2dc..0000000
--- a/iKeyMon/Views/ContentView.swift
+++ /dev/null
@@ -1,96 +0,0 @@
-//
-//  ContentView.swift
-//  iKeyMon
-//
-//  Created by tracer on 30.03.25.
-//
-
-import SwiftUI
-
-
-//struct ContentView: View {
-//    @State private var showingAddSheet = false
-//    @State private var showingEditSheet = false
-//    @State private var editingServer: Server?
-//    
-//    let serversKey = "storedServers"
-//    
-//    @State private var servers: [Server] = {
-//        if let data = UserDefaults.standard.data(forKey: "storedServers"),
-//           let saved = try? JSONDecoder().decode([Server].self, from: data) {
-//            return saved
-//        }
-//        return []
-//    }()
-//
-//    var body: some View {
-//        VStack(alignment: .leading) {
-//            Text("Monitored Servers")
-//                .font(.largeTitle)
-//                .padding(.top)
-//            
-//            List {
-//                ForEach(servers) { server in
-//                    HStack {
-//                        VStack(alignment: .leading) {
-//                            Text(server.hostname)
-//                                .font(.headline)
-//                            if let apiKey = KeychainHelper.loadApiKey(for: server.hostname) {
-//                                Text("API Key: \(apiKey.prefix(8))")
-//                                    .font(.caption)
-//                                    .foregroundColor(.secondary)
-//                            } else {
-//                                Text("No API key found")
-//                                    .font(.caption)
-//                                    .foregroundColor(.red)
-//                            }
-//                        }
-//                        Spacer()
-//                        Button("Edit") {
-//                            editingServer = server
-//                            showingEditSheet = true
-//                        }
-//                        .buttonStyle(BorderlessButtonStyle())
-//                    }
-//                }
-//                .onDelete(perform: deleteServer)
-//            }
-//            
-//            HStack {
-//                Spacer()
-//                Button("Add Server") {
-//                    showingAddSheet = true
-//                }
-//                .padding(.bottom)
-//            }
-//        }
-//        .padding()
-//        .frame(minWidth: 400, minHeight: 300)
-//        .sheet(isPresented: $showingAddSheet) {
-//            AddServerView(servers: $servers, saveAction: saveServers)
-//        }
-//        .sheet(item: $editingServer) { server in
-//            let _ = print(server.hostname)
-//            EditServerView(
-//                server: server,
-//                servers: $servers,
-//                saveAction: saveServers,
-//                dismiss: { self.showingEditSheet = false }
-//            )
-//        }
-//    }
-//    
-//    
-//    private func deleteServer(at offsets: IndexSet) {
-//        for index in offsets {
-//            let hostname = servers[index].hostname
-//            KeychainHelper.deleteApiKey(for: hostname)
-//        }
-//        servers.remove(atOffsets: offsets)
-//        saveServers()
-//    }
-//}
-//
-//#Preview {
-//    ContentView()
-//}
diff --git a/iKeyMon/Views/MainView.swift b/iKeyMon/Views/MainView.swift
index 766acf4..51a5f95 100644
--- a/iKeyMon/Views/MainView.swift
+++ b/iKeyMon/Views/MainView.swift
@@ -8,14 +8,33 @@
 import SwiftUI
 
 struct MainView: View {
+    
     @State var showAddServerSheet: Bool = false
     @State private var serverBeingEdited: Server?
     @State private var serverToDelete: Server?
     @State private var showDeleteConfirmation = false
-    
+    @State private var isFetchingInfo: Bool = false
+    @State private var refreshTimer = Timer.publish(every: 60, on: .main, in: .common).autoconnect()
+    @State private var progress: Double = 0
+    @State private var lastRefresh = Date()
+    @State private var pingTimer: Timer?
+    private let serverOrderKey = "serverOrder"
+
     @State private var servers: [Server] = {
         if let data = UserDefaults.standard.data(forKey: "storedServers"),
            let saved = try? JSONDecoder().decode([Server].self, from: data) {
+
+            if let idStrings = UserDefaults.standard.stringArray(forKey: "serverOrder") {
+                let idMap = idStrings.compactMap(UUID.init)
+                return saved.sorted { a, b in
+                    guard
+                        let i1 = idMap.firstIndex(of: a.id),
+                        let i2 = idMap.firstIndex(of: b.id)
+                    else { return false }
+                    return i1 < i2
+                }
+            }
+
             return saved
         }
         return []
@@ -25,25 +44,29 @@ struct MainView: View {
     @State private var selectedServerID: UUID?
 
     var body: some View {
+            
         NavigationSplitView {
-            List(servers, selection: $selectedServerID) { server in
-                HStack {
-                    Image(systemName: "dot.circle.fill")
-                        .foregroundColor(.green) // ← later update based on ping
-                    Text(server.hostname)
-                }
-                .tag(server)
-                .contextMenu {
-                    Button("Edit") {
-                        print("Editing:", server.hostname)
-                        serverBeingEdited = server
+            List(selection: $selectedServerID) {
+                ForEach(servers) { server in
+                    HStack {
+                        Image(systemName: "dot.circle.fill")
+                            .foregroundColor(server.pingable ? .green : .red)
+                        Text(server.hostname)
                     }
-                    Divider()
-                    Button("Delete", role: .destructive) {
-                        serverToDelete = server
-                        showDeleteConfirmation = true
+                    .tag(server)
+                    .contextMenu {
+                        Button("Edit") {
+                            print("Editing:", server.hostname)
+                            serverBeingEdited = server
+                        }
+                        Divider()
+                        Button("Delete", role: .destructive) {
+                            serverToDelete = server
+                            showDeleteConfirmation = true
+                        }
                     }
                 }
+                .onMove(perform: moveServer)
             }
             .toolbar {
                 ToolbarItem(placement: .primaryAction) {
@@ -56,16 +79,14 @@ struct MainView: View {
             .navigationTitle("Servers")
             .onChange(of: selectedServerID) {
                 if let selectedServerID {
+                    UserDefaults.standard.set(selectedServerID.uuidString, forKey: "selectedServerID")
                     fetchServerInfo(for: selectedServerID)
                 }
             }
         } detail: {
-            let _ = print("selectedServerID: \(selectedServerID ?? UUID())")
-//            if let id = selectedServerID,
             if let selectedServerID,
                let index = servers.firstIndex(where: { selectedServerID == $0.id }) {
-                let _ = print( "index: %d\n", index)
-                ServerDetailView(server: $servers[index])
+                ServerDetailView(server: $servers[index], isFetching: isFetchingInfo)
             } else {
                 ContentUnavailableView("No Server Selected", systemImage: "server.rack")
             }
@@ -78,7 +99,6 @@ struct MainView: View {
             )
         }
         .sheet(item: $serverBeingEdited) { server in
-            let _ = print("serverBeingEdited: \(server)")
             ServerFormView(
                 mode: .edit(server),
                 servers: $servers,
@@ -91,39 +111,44 @@ struct MainView: View {
             }
             Button("Cancel", role: .cancel) {}
         }
+        .onReceive(refreshTimer) { _ in
+            for server in servers {
+                print("fetching server: \(server.hostname)")
+                fetchServerInfo(for: server.id)
+            }
+        }
+        .onAppear {
+            if let storedID = UserDefaults.standard.string(forKey: "selectedServerID"),
+               let uuid = UUID(uuidString: storedID),
+               servers.contains(where: { $0.id == uuid }) {
+                selectedServerID = uuid
+            } else if selectedServerID == nil, let first = servers.first {
+                selectedServerID = first.id
+            }
+            pingAllServers()
+            pingTimer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true) { _ in
+                pingAllServers()
+            }
+        }
+        .frame(minWidth: 800, minHeight: 450)
+
     }
     
     private func fetchServerInfo(for id: UUID) {
-        guard let server = servers.first(where: { $0.id == id }) else {
-            print ("server not found: \(id)")
+        guard let server = servers.first(where: { $0.id == id }),
+              let api = ServerAPI(server: server) else {
             return
         }
-        print("after guard")
 
-        print("fetching server: \(server.hostname)")
-        let apiKey = (KeychainHelper.loadApiKey(for: server.hostname) ?? "")
-            .trimmingCharacters(in: .whitespacesAndNewlines)
-        guard let url = URL(string: "https://\(server.hostname)/api/v2/server") else { return }
-        print("after url")
+        isFetchingInfo = true
 
-        var request = URLRequest(url: url)
-        request.httpMethod = "GET"
-        request.setValue(apiKey, forHTTPHeaderField: "X-API-KEY")
-        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
-
-        print("after request")
         Task {
+            defer { isFetchingInfo = false }
             do {
-                print("inside task")
-                let (data, _) = try await URLSession.shared.data(for: request)
-                let decoded = try JSONDecoder().decode(ServerResponse.self, from: data)
-                print("after decode")
-
+                let info = try await api.fetchServerInfo()
                 if let index = servers.firstIndex(where: { $0.id == id }) {
-                    print("index found: \(index)")
                     var updated = servers[index]
-                    updated.info = ServerInfo(from: decoded)
-                    dump(updated.info)
+                    updated.info = ServerInfo(from: info)
                     servers[index] = updated
                 }
             } catch {
@@ -132,7 +157,72 @@ struct MainView: View {
         }
     }
     
-}
+    private func moveServer(from source: IndexSet, to destination: Int) {
+        servers.move(fromOffsets: source, toOffset: destination)
+        saveServerOrder()
+    }
+    
+    private func saveServerOrder() {
+        let ids = servers.map { $0.id.uuidString }
+        UserDefaults.standard.set(ids, forKey: serverOrderKey)
+    }
+    
+    private struct PingResponse: Codable {
+        let response: String
+    }
+    
+//    func pingServer(_ server: Server) async -> Bool {
+//        let hostname = server.hostname
+//        guard let url = URL(string: "https://\(hostname)/api/v2/ping") else {
+//            return false
+//        }
+//
+//        var request = URLRequest(url: url)
+//        request.httpMethod = "GET"
+//        request.timeoutInterval = 5
+//        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+//
+//        let apiKey = KeychainHelper.loadApiKey(for: hostname)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
+//        request.setValue(apiKey, forHTTPHeaderField: "X-API-KEY")
+//
+//        do {
+//            let (data, response) = try await URLSession.shared.data(for: request)
+//            if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
+//                do {
+//                    let decoded = try JSONDecoder().decode(PingResponse.self, from: data)
+//                    if decoded.response == "pong" {
+//                        return true
+//                    } else {
+//                        print("❌ Unexpected response: \(decoded.response)")
+//                        return false
+//                    }
+//                } catch {
+//                    print("❌ Failed to decode JSON: \(error)")
+//                    return false
+//                }
+//            } else {
+//                return false
+//            }
+//        } catch {
+//            print("[Ping] \(server.hostname): \(error.localizedDescription)")
+//            return false
+//        }
+//    }
+    
+    func pingAllServers() {
+        for (index, server) in servers.enumerated() {
+            Task {
+                let apiKey = KeychainHelper.loadApiKey(for: server.hostname)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
+
+                let api = ServerAPI(hostname: server.hostname, apiKey: apiKey)
+                let pingable = await api.ping()
+
+                servers[index].pingable = pingable
+                }
+            }
+        }
+    }
+
 
 #Preview {
     MainView()
diff --git a/iKeyMon/Views/Rows/ResouceRow.swift b/iKeyMon/Views/Rows/ResouceRow.swift
deleted file mode 100644
index 5c8038d..0000000
--- a/iKeyMon/Views/Rows/ResouceRow.swift
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  ResouceRow.swift
-//  iKeyMon
-//
-//  Created by tracer on 31.03.25.
-//
-
-//import SwiftUI
-//
-//struct ResourceRow: View {
-//    let label: String
-//    let value: String
-//    var subtext: String? = nil
-//
-//    var body: some View {
-//        HStack(spacing: 6) {
-//            Text(label)
-//                .fontWeight(.semibold)
-//
-//            Text(value)
-//                .foregroundColor(.green)
-//                .fontWeight(.semibold)
-//
-//            if let subtext {
-//                Text("(\(subtext))")
-//                    .foregroundColor(.secondary)
-//                    .font(.callout)
-//            }
-//
-//            Spacer()
-//        }
-//        .padding(.vertical, 2)
-//    }
-//}
-//
-//#Preview {
-//    ResourceRow(label: "Test", value: "value", subtext: "some additional info")
-//}
diff --git a/iKeyMon/Views/Rows/TableRowView.swift b/iKeyMon/Views/Rows/TableRowView.swift
index 9dce0a7..aafa070 100644
--- a/iKeyMon/Views/Rows/TableRowView.swift
+++ b/iKeyMon/Views/Rows/TableRowView.swift
@@ -8,18 +8,25 @@
 import SwiftUI
 
 struct TableRowView<Label: View, Value: View>: View {
+    var showDivider: Bool = true
     @ViewBuilder let label: () -> Label
     @ViewBuilder let value: () -> Value
 
     var body: some View {
-        HStack(alignment: .top) {
-            label()
-                .frame(width: 100, alignment: .leading)
-
-            value()
-                .frame(maxWidth: .infinity, alignment: .leading)
+        VStack(spacing: 0) {
+            HStack(alignment: .top) {
+                label()
+                    .frame(width: 180, alignment: .leading)
+                
+                value()
+                    .frame(maxWidth: .infinity, alignment: .leading)
+            }
+            .padding(.vertical, 2)
+        }
+        if showDivider {
+            Divider()
+                .opacity(0.6)
         }
-        .padding(.vertical, 2)
     }
 }
 
diff --git a/iKeyMon/Views/ServerDetailView.swift b/iKeyMon/Views/ServerDetailView.swift
index 9824384..d0b4f7a 100644
--- a/iKeyMon/Views/ServerDetailView.swift
+++ b/iKeyMon/Views/ServerDetailView.swift
@@ -9,27 +9,81 @@ import SwiftUI
 
 struct ServerDetailView: View {
     @Binding var server: Server
-
+    var isFetching: Bool
+    
+    @State private var progress: Double = 0
+    let timer = Timer.publish(every: 1.0 / 60.0, on: .main, in: .common).autoconnect()
+    
     var body: some View {
-        if server.info == nil {
-            ProgressView("Fetching server info...")
-                .frame(maxWidth: .infinity, maxHeight: .infinity)
-        } else {
-            TabView {
-                GeneralView(server: $server)
-                    .tabItem {
-                        Text("General")
+        VStack(spacing: 0) {
+            ProgressView(value: progress)
+                .progressViewStyle(LinearProgressViewStyle())
+                .padding(.horizontal)
+                .frame(height: 2)
+            
+            if server.info == nil {
+                ProgressView("Fetching server info...")
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
+            } else {
+                ZStack(alignment: .topTrailing) {
+                    VStack(spacing: 0) {
+                        Spacer().frame(height: 6)
+                        TabView {
+                            GeneralView(server: $server)
+                                .tabItem {
+                                    Text("General")
+                                }
+                            ResourcesView(server: $server)
+                                .tabItem {
+                                    Text("Resources")
+                                }
+                            ServicesView(server: $server)
+                                .tabItem {
+                                    Text("Services")
+                                }
+                        }
                     }
-                ResourcesView(server: $server)
-                    .tabItem {
-                        Text("Resources")
-                    }
-                ServicesView(server: $server)
-                    .tabItem {
-                        Text("Services")
+                    
+                    if isFetching {
+                        ProgressView()
+                            .scaleEffect(0.5)
+                            .padding()
                     }
+                }
+                .padding(0)
+            }
+        }
+        .onReceive(timer) { _ in
+            withAnimation(.linear(duration: 1.0 / 60.0)) {
+                progress += 1.0 / (60.0 * 60.0)
+                if progress >= 1 { progress = 0 }
             }
-            .padding(0)
         }
     }
 }
+
+#Preview {
+    ServerDetailView(
+        server: .constant(Server(id: UUID(), hostname: "preview.example.com", info: ServerInfo(
+            hostname: "preview.example.com",
+            ipAddresses: ["192.168.1.1", "fe80::1"],
+            cpuCores: 4,
+            serverTime: "2025-04-04T18:00:00+0200",
+            uptime: "3 Days / 12 Hours / 30 Minutes",
+            processCount: 123,
+            apacheVersion: "2.4.58",
+            phpVersion: "8.2.12",
+            mysqlVersion: "8.0.33",
+            mariadbVersion: nil,
+            ports: nil,
+            load: Load(minute1: 0.5, minute5: 0.3, minute15: 0.2, percent: 10.0, cpuCount: 4, level: "low"),
+            memory: Memory(free: 8_000_000_000, used: 4_000_000_000, total: 12_000_000_000, percent: 33.3),
+            swap: Memory(free: 4_000_000_000, used: 1_000_000_000, total: 5_000_000_000, percent: 20.0),
+            diskSpace: DiskSpace(free: 100_000_000_000, used: 50_000_000_000, total: 150_000_000_000, percent: 33.3),
+            panelVersion: "25.0",
+            panelBuild: "3394",
+            apiVersion: "2"
+        ))),
+        isFetching: false
+    )
+}
diff --git a/iKeyMon/Views/ServerFormView.swift b/iKeyMon/Views/ServerFormView.swift
index 11ceb9b..4b5a01f 100644
--- a/iKeyMon/Views/ServerFormView.swift
+++ b/iKeyMon/Views/ServerFormView.swift
@@ -61,7 +61,9 @@ struct ServerFormView: View {
                 }
                 Spacer()
                 Button("Test connection") {
-                    testConnection()
+                    Task {
+                        await testConnection()
+                    }
                 }
                 Button("Save") {
                     saveServer()
@@ -69,7 +71,7 @@ struct ServerFormView: View {
                     saveServers()
                     dismiss()
                 }
-//                .disabled(hostname.isEmpty || apiKey.isEmpty || !con  nectionOK)
+                .disabled(hostname.isEmpty || apiKey.isEmpty || !connectionOK)
             }
             .padding(.top)
         }
@@ -96,43 +98,45 @@ struct ServerFormView: View {
         }
     }
 
-    private func testConnection() {
-        // @State is no reliable source
+    private func testConnection() async {
         let host = hostname.trimmingCharacters(in: .whitespacesAndNewlines)
         let key = apiKey.trimmingCharacters(in: .whitespacesAndNewlines)
 
-        guard let url = URL(string: "https://\(host)/api/v2/ping") else {
-            print("❌ Invalid URL")
-            return
-        }
-
-        var request = URLRequest(url: url)
-        request.httpMethod = "GET"
-        request.setValue(key, forHTTPHeaderField: "X-API-KEY")
-        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
-
-        print("🔍 Headers: \(request.allHTTPHeaderFields ?? [:])")
-
-        Task {
-            do {
-                let (data, response) = try await URLSession.shared.data(for: request)
-                if let httpResponse = response as? HTTPURLResponse {
-                    print("🔄 Status: \(httpResponse.statusCode)")
-                }
-
-                if let result = try? JSONDecoder().decode([String: String].self, from: data),
-                   result["response"] == "pong" {
-                    print("✅ Pong received")
-                    connectionOK = true
-                } else {
-                    print("⚠️ Unexpected response")
-                    connectionOK = false
-                }
-            } catch {
-                print("❌ Error: \(error)")
-                connectionOK = false
-            }
-        }
+        let pinger = ServerAPI(hostname: host, apiKey: key)
+        connectionOK = await pinger.ping()
+//
+//        guard let url = URL(string: "https://\(host)/api/v2/ping") else {
+//            print("❌ Invalid URL")
+//            return
+//        }
+//
+//        var request = URLRequest(url: url)
+//        request.httpMethod = "GET"
+//        request.setValue(key, forHTTPHeaderField: "X-API-KEY")
+//        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+//
+//        print("🔍 Headers: \(request.allHTTPHeaderFields ?? [:])")
+//
+//        Task {
+//            do {
+//                let (data, response) = try await URLSession.shared.data(for: request)
+//                if let httpResponse = response as? HTTPURLResponse {
+//                    print("🔄 Status: \(httpResponse.statusCode)")
+//                }
+//
+//                if let result = try? JSONDecoder().decode([String: String].self, from: data),
+//                   result["response"] == "pong" {
+//                    print("✅ Pong received")
+//                    connectionOK = true
+//                } else {
+//                    print("⚠️ Unexpected response")
+//                    connectionOK = false
+//                }
+//            } catch {
+//                print("❌ Error: \(error)")
+//                connectionOK = false
+//            }
+//        }
     }
     
 //    func testKeyHelpConnection() {
diff --git a/iKeyMon/Views/Tabs/GeneralView.swift b/iKeyMon/Views/Tabs/GeneralView.swift
index 41ac0b1..faca5ac 100644
--- a/iKeyMon/Views/Tabs/GeneralView.swift
+++ b/iKeyMon/Views/Tabs/GeneralView.swift
@@ -11,26 +11,60 @@ struct GeneralView: View {
     @Binding var server: Server
     
     var body: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            Form {
-                Section {
-                    InfoRow(label: "Hostname", value: server.info?.hostname ?? "—")
-                    InfoRow(label: "IP addresses", value: server.info?.ipAddresses ?? [])
-                    //                    InfoRow(label: "Processor", value: info.processor)
-                    //                    InfoRow(label: "CPU cores", value: "\(info.cpuCores)")
-                    //                    InfoRow(label: "System virtualization", value: info.virtualization)
-                    //                    InfoRow(label: "Server time", value: info.serverTime)
-                    //                    InfoRow(label: "Uptime", value: info.uptime)
-                    //                    InfoRow(label: "SSH fingerprint", value: info.sshFingerprint)
-                    //                    InfoRow(label: "DKIM DNS record", value: "Show", color: .yellow)
-                    //                } header: {
-                    Text("General").font(.headline)
+        GeometryReader { geometry in
+            ScrollView {
+                VStack(alignment: .leading, spacing: 6) {
+                    TableRowView {
+                        Text("Hostname")
+                    } value: {
+                        InfoCell(value: [server.hostname])
+                    }
+                    
+                    TableRowView {
+                        Text("IP addresses")
+                    } value: {
+                        InfoCell(value: server.info?.ipAddresses ?? [], monospaced: true)
+                    }
+                    
+                    TableRowView {
+                        Text("Server time")
+                    } value: {
+                        InfoCell(value: [server.info?.formattedServerTime ?? ""], monospaced: true)
+                    }
+
+                    TableRowView {
+                        Text("Uptime")
+                    } value: {
+                        InfoCell(value: [server.info?.uptime ?? ""])
+                    }
+
+                    TableRowView {
+                        Text("KeyHelp version")
+                    } value: {
+                        InfoCell(value: [server.info?.formattedVersion ?? ""], monospaced: true)
+                    }
+
+                    TableRowView {
+                        Text("Sytem PHP version")
+                    } value: {
+                        InfoCell(value: [server.info?.phpVersion ?? ""], monospaced: true)
+                    }
+
+                    TableRowView(showDivider: false) {
+                        Text("Additional PHP interpreters")
+                    } value: {
+                        InfoCell(
+                            value: server.info?.additionalPHPInterpreters?.map { $0.versionFull } ?? [],
+                            monospaced: true
+                        )
+                    }
                 }
+                .padding()
+                .frame(minHeight: geometry.size.height, alignment: .top)
             }
-            .formStyle(.grouped)
-            .padding(0)
+            .padding()
+            .scrollDisabled(true)
         }
-        .frame(maxWidth: .infinity, alignment: .leading)
     }
 }
 
diff --git a/iKeyMon/Views/Tabs/ResourcesView.swift b/iKeyMon/Views/Tabs/ResourcesView.swift
index caed2c5..983e518 100644
--- a/iKeyMon/Views/Tabs/ResourcesView.swift
+++ b/iKeyMon/Views/Tabs/ResourcesView.swift
@@ -62,7 +62,6 @@ struct ResourcesView: View {
                         )
                     }
                 }
-                .frame(maxWidth: .infinity, alignment: .topLeading)
                 .padding()
                 .frame(minHeight: geometry.size.height, alignment: .top)
             }
diff --git a/iKeyMon/iKeyMonApp.swift b/iKeyMon/iKeyMonApp.swift
index 63f2c49..31b741f 100644
--- a/iKeyMon/iKeyMonApp.swift
+++ b/iKeyMon/iKeyMonApp.swift
@@ -16,5 +16,6 @@ struct iKeyMonApp: App {
                     NSApp.terminate(nil)
                 }
         }
+        .windowResizability(.contentMinSize)
     }
 }