Swift 98.3%
Shell 1.7%
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2// StatusView.swift — l'état des 13 plateformes Groupe-KA : pastille vert/rouge3// (HEAD sur https://www.<domaine>/), latence, ouverture du site, actualisation4// toutes les 5 minutes (pouls partagé) ou manuelle.5import SwiftUI67struct StatusView: View {8 @EnvironmentObject private var pulse: EcosystemPulse9 @Environment(\.colorScheme) private var scheme1011 var body: some View {12 ScrollView {13 VStack(alignment: .leading, spacing: 14) {14 header15 summary16 LazyVStack(spacing: 9) {17 ForEach(pulse.health) { s in18 row(s)19 }20 }21 Text("Vérification : requête HEAD sur https://<domaine>/ — vert si le serveur répond (200–399, ou 405 quand HEAD est refusé). Actualisé automatiquement toutes les 5 minutes.")22 .font(.caption2).foregroundStyle(.tertiary)23 }24 .padding(18)25 .frame(maxWidth: 720)26 .frame(maxWidth: .infinity)27 }28 .background(KATheme.paper(scheme))29 }3031 private var header: some View {32 HStack(alignment: .firstTextBaseline) {33 Text("Statut des plateformes").font(.title2.weight(.bold))34 Spacer()35 if let d = pulse.lastRefresh {36 Text("Actualisé \(d.formatted(.relative(presentation: .named, unitsStyle: .narrow)))")37 .font(.caption).foregroundStyle(.secondary)38 }39 Button {40 Task { await pulse.refresh() }41 } label: {42 if pulse.refreshing {43 ProgressView().controlSize(.small)44 } else {45 Label("Actualiser", systemImage: "arrow.clockwise").font(.caption)46 }47 }48 .disabled(pulse.refreshing)49 }50 }5152 private var summary: some View {53 HStack(spacing: 12) {54 let up = pulse.servicesUp55 let total = pulse.health.count56 KAStatusDot(up: up == total ? true : (up == 0 ? false : nil))57 Text(up == total58 ? "Les \(total) services sont en ligne."59 : "\(up) service\(up > 1 ? "s" : "") en ligne sur \(total).")60 .font(.subheadline.weight(.semibold))61 Spacer()62 Link("Page de statut officielle", destination: Ecosystem.statusURL)63 .font(.caption.weight(.semibold))64 .foregroundStyle(KATheme.green)65 }66 .padding(13)67 .frame(maxWidth: .infinity, alignment: .leading)68 .kaCard(accent: pulse.servicesUp == pulse.health.count ? KATheme.green : .orange)69 }7071 private func row(_ s: ServiceHealth) -> some View {72 HStack(spacing: 11) {73 KAStatusDot(up: s.up)74 VStack(alignment: .leading, spacing: 2) {75 Text(s.name).font(.subheadline.weight(.bold))76 Text(s.id).font(.system(.caption2, design: .monospaced)).foregroundStyle(.secondary)77 }78 Spacer()79 if let ms = s.latencyMs, s.up != nil {80 Text("\(ms) ms")81 .font(.system(.caption, design: .monospaced).weight(.bold))82 .foregroundStyle(ms < 500 ? KATheme.green : .orange)83 .padding(.horizontal, 8).padding(.vertical, 3)84 .background((ms < 500 ? KATheme.green : Color.orange).opacity(0.12), in: Capsule())85 } else if s.up == nil {86 Text("vérification…").font(.caption2).foregroundStyle(.tertiary)87 }88 Link(destination: URL(string: "https://\(s.id)/")!) {89 Image(systemName: "arrow.up.right.square")90 }91 .foregroundStyle(.secondary)92 .help("Ouvrir https://\(s.id)/")93 }94 .padding(.horizontal, 14).padding(.vertical, 10)95 .frame(maxWidth: .infinity, alignment: .leading)96 .kaCard(accent: s.up == false ? .red : nil)97 }98}99