// Auteur : Simon-Pierre Boucher — contact@spboucher.ai // StatusView.swift — l'état des 13 plateformes Groupe-KA : pastille vert/rouge // (HEAD sur https://www./), latence, ouverture du site, actualisation // toutes les 5 minutes (pouls partagé) ou manuelle. import SwiftUI struct StatusView: View { @EnvironmentObject private var pulse: EcosystemPulse @Environment(\.colorScheme) private var scheme var body: some View { ScrollView { VStack(alignment: .leading, spacing: 14) { header summary LazyVStack(spacing: 9) { ForEach(pulse.health) { s in row(s) } } Text("Vérification : requête HEAD sur https:/// — vert si le serveur répond (200–399, ou 405 quand HEAD est refusé). Actualisé automatiquement toutes les 5 minutes.") .font(.caption2).foregroundStyle(.tertiary) } .padding(18) .frame(maxWidth: 720) .frame(maxWidth: .infinity) } .background(KATheme.paper(scheme)) } private var header: some View { HStack(alignment: .firstTextBaseline) { Text("Statut des plateformes").font(.title2.weight(.bold)) Spacer() if let d = pulse.lastRefresh { Text("Actualisé \(d.formatted(.relative(presentation: .named, unitsStyle: .narrow)))") .font(.caption).foregroundStyle(.secondary) } Button { Task { await pulse.refresh() } } label: { if pulse.refreshing { ProgressView().controlSize(.small) } else { Label("Actualiser", systemImage: "arrow.clockwise").font(.caption) } } .disabled(pulse.refreshing) } } private var summary: some View { HStack(spacing: 12) { let up = pulse.servicesUp let total = pulse.health.count KAStatusDot(up: up == total ? true : (up == 0 ? false : nil)) Text(up == total ? "Les \(total) services sont en ligne." : "\(up) service\(up > 1 ? "s" : "") en ligne sur \(total).") .font(.subheadline.weight(.semibold)) Spacer() Link("Page de statut officielle", destination: Ecosystem.statusURL) .font(.caption.weight(.semibold)) .foregroundStyle(KATheme.green) } .padding(13) .frame(maxWidth: .infinity, alignment: .leading) .kaCard(accent: pulse.servicesUp == pulse.health.count ? KATheme.green : .orange) } private func row(_ s: ServiceHealth) -> some View { HStack(spacing: 11) { KAStatusDot(up: s.up) VStack(alignment: .leading, spacing: 2) { Text(s.name).font(.subheadline.weight(.bold)) Text(s.id).font(.system(.caption2, design: .monospaced)).foregroundStyle(.secondary) } Spacer() if let ms = s.latencyMs, s.up != nil { Text("\(ms) ms") .font(.system(.caption, design: .monospaced).weight(.bold)) .foregroundStyle(ms < 500 ? KATheme.green : .orange) .padding(.horizontal, 8).padding(.vertical, 3) .background((ms < 500 ? KATheme.green : Color.orange).opacity(0.12), in: Capsule()) } else if s.up == nil { Text("vérification…").font(.caption2).foregroundStyle(.tertiary) } Link(destination: URL(string: "https://\(s.id)/")!) { Image(systemName: "arrow.up.right.square") } .foregroundStyle(.secondary) .help("Ouvrir https://\(s.id)/") } .padding(.horizontal, 14).padding(.vertical, 10) .frame(maxWidth: .infinity, alignment: .leading) .kaCard(accent: s.up == false ? .red : nil) } }