SPB Git forge

spb/ka-macos

Public
3commits 1branches 0releases
6.1 MBsize
maindefault branch
1 mo agolast push
Swift 98.3% Shell 1.7%
4.0 KB · 99 lines swift
Raw Blame History
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