initial commit

This commit is contained in:
Jon Vanvik 2026-05-31 14:11:29 +02:00
commit 8de962eeb8
44 changed files with 4843 additions and 0 deletions

126
Components.swift Normal file
View file

@ -0,0 +1,126 @@
import SwiftUI
/// Simple load state for screen view models.
enum LoadPhase: Equatable {
case idle
case loading
case loaded
case failed(String)
}
// MARK: - State / severity badges
struct StateBadge: View {
let text: String
let color: Color
var body: some View {
Text(text)
.font(.caption2.weight(.semibold))
.padding(.horizontal, 8)
.padding(.vertical, 3)
.background(color.opacity(0.18))
.foregroundStyle(color)
.clipShape(Capsule())
}
}
extension ONULifecycleState {
var color: Color {
switch self {
case .registered: return .green
case .unspecified: return .mint // also "online" (dev guide Procedure 2)
case .unprovisioned: return .blue
case .deregistered: return .gray
case .dyingGasp, .disallowedError: return .red
case .disabled, .disallowedAdmin, .disallowedRegID: return .orange
case .unknown: return .secondary
}
}
var label: String { self == .unknown ? "Unknown" : rawValue }
}
extension DeviceState {
var color: Color {
switch self {
case .online: return .green
case .offline: return .gray
case .unattached: return .orange
case .preProvisioned: return .blue
case .unknown: return .secondary
}
}
var label: String { self == .unknown ? "Unknown" : rawValue }
}
extension AlarmSeverity {
var color: Color {
switch self {
case .emergency, .alert, .critical: return .red
case .error: return .orange
case .warning: return .yellow
case .notice, .info: return .blue
case .debug: return .gray
case .unknown: return .secondary
}
}
var label: String { self == .unknown ? "unknown" : rawValue }
}
struct SeverityBadge: View {
let severity: AlarmSeverity
var body: some View { StateBadge(text: severity.label, color: severity.color) }
}
// MARK: - Count tile (dashboard)
struct CountTile: View {
let title: String
let value: Int
var systemImage: String? = nil
var tint: Color = .accentColor
var body: some View {
VStack(alignment: .leading, spacing: 6) {
HStack(spacing: 6) {
if let systemImage { Image(systemName: systemImage).foregroundStyle(tint) }
Text(title).font(.caption).foregroundStyle(.secondary)
}
Text("\(value)").font(.system(.title, design: .rounded).weight(.semibold))
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(Color(.secondarySystemBackground))
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
}
}
// MARK: - Loading / error
struct LoadingView: View {
var label: String = "Loading…"
var body: some View {
VStack(spacing: 12) {
ProgressView()
Text(label).font(.subheadline).foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct ErrorStateView: View {
let message: String
var retry: (() -> Void)? = nil
var body: some View {
ContentUnavailableView {
Label("Something went wrong", systemImage: "exclamationmark.triangle")
} description: {
Text(message)
} actions: {
if let retry {
Button("Retry", action: retry).buttonStyle(.borderedProminent)
}
}
}
}