Files
boc/aamos-admin-upgrade/ios/AamosAdmin/ViewModels/DashboardViewModel.swift
T
Bernt 6de2455917 v1.2.0: Add Global Markets footer, translated to 9 languages
- Added GLOBAL_MARKETS_TITLE to all translation files
- Updated footer with 12 markets (4 active + 8 upcoming)
- Translated market section to: zh-cn, zh-tw, ja, ko, th, vi, id, ms, hi
- Built and deployed to production
- CloudFront invalidation: I3RTMXVFDJXWLG3SYX208OP1CC
2026-07-08 19:56:03 +00:00

107 lines
2.6 KiB
Swift

import Foundation
import Combine
// MARK: - Models
struct DashboardMetrics: Codable {
let activeAgents: Int
let tasksCompleted: Int
let tasksFailed: Int
let avgResponseTimeMs: Double
let uptimePercent: Double
let alertCount: Int
static let empty = DashboardMetrics(
activeAgents: 0,
tasksCompleted: 0,
tasksFailed: 0,
avgResponseTimeMs: 0,
uptimePercent: 0,
alertCount: 0
)
}
struct AuditEntry: Identifiable, Codable {
let id: UUID
let timestamp: Date
let actor: String
let action: String
let resource: String
let outcome: AuditOutcome
let detail: String?
enum AuditOutcome: String, Codable {
case success, failure, warning
}
}
// MARK: - ViewModel
@MainActor
final class DashboardViewModel: ObservableObject {
@Published private(set) var metrics: DashboardMetrics = .empty
@Published private(set) var recentAudit: [AuditEntry] = []
@Published private(set) var isLoading: Bool = false
@Published private(set) var error: Error?
private var pollingTask: Task<Void, Never>?
private let pollingInterval: TimeInterval
private let apiService: APIServiceProtocol
init(apiService: APIServiceProtocol = APIService.shared, pollingInterval: TimeInterval = 30) {
self.apiService = apiService
self.pollingInterval = pollingInterval
}
deinit {
pollingTask?.cancel()
}
// MARK: - Public
func loadDashboard() async {
guard !isLoading else { return }
isLoading = true
error = nil
do {
async let fetchedMetrics = apiService.fetchMetrics()
async let fetchedAudit = apiService.fetchRecentAudit(limit: 20)
let (m, a) = try await (fetchedMetrics, fetchedAudit)
metrics = m
recentAudit = a
} catch {
self.error = error
}
isLoading = false
}
func startPolling() {
pollingTask?.cancel()
pollingTask = Task { [weak self] in
guard let self else { return }
await self.loadDashboard()
while !Task.isCancelled {
try? await Task.sleep(for: .seconds(self.pollingInterval))
guard !Task.isCancelled else { break }
await self.loadDashboard()
}
}
}
func stopPolling() {
pollingTask?.cancel()
pollingTask = nil
}
}
// MARK: - API protocol
protocol APIServiceProtocol {
func fetchMetrics() async throws -> DashboardMetrics
func fetchRecentAudit(limit: Int) async throws -> [AuditEntry]
}