import Foundation /// Eine **eigene** Übersicht der App - unabhängig von den Web-Dashboards und nur /// auf diesem Gerät gespeichert. Je Server wählbar, ob die Web-Dashboards /// gespiegelt werden oder diese lokale Übersicht gilt. /// Modus je Server. enum OverviewMode: String, Codable { case mirror // die Web-Dashboards spiegeln (Standard) case local // eigene App-Übersicht } /// Eine Karte der lokalen Übersicht. Einspaltig - die Reihenfolge im Array /// bestimmt die Anordnung, deshalb kein Raster. struct AppCard: Codable, Identifiable, Equatable { let id: UUID var type: String var productId: Int? // Diagramm-Einstellungen (nur fuer bestimmte Diagramme relevant). Optional, // damit aeltere gespeicherte Uebersichten ohne diese Schluessel weiter // dekodieren. Bedeutung wie im Web-Editor: var mitOhneMhd: Bool? // Ablauf-Ring: „Ohne MHD" einbeziehen (nil = ja) var detail: Bool? // Kategorien-Ring: Detailansicht, alle einzeln (nil = nein) var tiefe: Int? // Kategorien-Ring: Tiefe (nil = feinste Ebene) init(id: UUID = UUID(), type: String, productId: Int? = nil, mitOhneMhd: Bool? = nil, detail: Bool? = nil, tiefe: Int? = nil) { self.id = id self.type = type self.productId = productId self.mitOhneMhd = mitOhneMhd self.detail = detail self.tiefe = tiefe } } struct AppDashboard: Codable, Identifiable, Equatable { let id: UUID var name: String var cards: [AppCard] init(id: UUID = UUID(), name: String, cards: [AppCard] = []) { self.id = id self.name = name self.cards = cards } /// Sinnvolle Startkarten, wenn jemand die eigene Übersicht zum ersten Mal /// anlegt - eine brauchbare Zusammenstellung statt einer leeren Seite. static func standard() -> AppDashboard { AppDashboard(name: "Übersicht", cards: [ AppCard(type: "status"), AppCard(type: "checkin-quick"), AppCard(type: "shopping"), AppCard(type: "expiry-all"), AppCard(type: "movements"), ]) } } /// Was ein Server lokal an Übersicht hat: der Modus und die eigenen Dashboards. struct AppOverviewConfig: Codable, Equatable { var mode: OverviewMode = .mirror var dashboards: [AppDashboard] = [] } /// Ablage je Profil-UUID in den UserDefaults, wie `NotificationStore`. enum AppDashboardStore { static let key = "app_overviews" static func loadAll() -> [String: AppOverviewConfig] { guard let data = UserDefaults.standard.data(forKey: key), let dict = try? JSONDecoder().decode([String: AppOverviewConfig].self, from: data) else { return [:] } return dict } static func config(for profileID: UUID) -> AppOverviewConfig { loadAll()[profileID.uuidString] ?? AppOverviewConfig() } static func save(_ config: AppOverviewConfig, for profileID: UUID) { var alle = loadAll() alle[profileID.uuidString] = config guard let data = try? JSONEncoder().encode(alle) else { return } UserDefaults.standard.set(data, forKey: key) } static func remove(for profileID: UUID) { var alle = loadAll() alle.removeValue(forKey: profileID.uuidString) guard let data = try? JSONEncoder().encode(alle) else { return } UserDefaults.standard.set(data, forKey: key) } } /// Verfügbare Kartenarten für die lokale Übersicht - Grundlage des Hinzufügen-Menüs. struct CardKind: Identifiable { let type: String let needsProduct: Bool var id: String { type } var title: String { CardCatalog.titel(type) } var isChart: Bool { CardCatalog.istDiagramm(type) } } enum CardCatalogList { /// Reihenfolge wie sie im Hinzufügen-Menü erscheinen soll. static let all: [CardKind] = [ CardKind(type: "status", needsProduct: false), CardKind(type: "kpi-products", needsProduct: false), CardKind(type: "kpi-units", needsProduct: false), CardKind(type: "checkin-quick", needsProduct: false), CardKind(type: "checkout-quick", needsProduct: false), CardKind(type: "actions", needsProduct: false), CardKind(type: "shopping", needsProduct: false), CardKind(type: "expiry-all", needsProduct: false), CardKind(type: "expiring", needsProduct: false), CardKind(type: "expired", needsProduct: false), CardKind(type: "movements", needsProduct: false), CardKind(type: "product-stock", needsProduct: true), CardKind(type: "product-timeline", needsProduct: true), CardKind(type: "expiry-donut", needsProduct: false), CardKind(type: "category-donut", needsProduct: false), CardKind(type: "category-bars", needsProduct: false), CardKind(type: "stock-timeline", needsProduct: false), CardKind(type: "activity-timeline", needsProduct: false), ] static func kind(for type: String) -> CardKind? { all.first { $0.type == type } } }