Der lokale Uebersicht-Editor kann jetzt pro Diagramm-Karte einstellen, wie im Web-Editor: - Ablauf-Ring: 'Ohne MHD einbeziehen' - Kategorien-Ring: 'Detailansicht' (alle einzeln) + 'Kategorie-Tiefe' AppCard speichert die Optionen (optional -> alte Uebersichten dekodieren weiter) und reicht sie an DashboardCardView durch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
131 lines
4.9 KiB
Swift
131 lines
4.9 KiB
Swift
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 } }
|
|
}
|