// IYmtg_App_iOS/Features/Help/Models/TrainingStatus.swift import SwiftUI struct TrainingCategory: Identifiable { let id = UUID() let name: String let group: String let functionalCount: Int let solidCount: Int let highAccuracyCount: Int } class TrainingGuideViewModel: ObservableObject { @Published var categories: [TrainingCategory] = [ // MARK: Foil Model (Image Classification) // 13 classes — false negatives cause wrong foil label; all classes need balanced data. .init(name: "NonFoil", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Traditional", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Etched", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "PreModern", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Textured", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Galaxy", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Surge", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Oil Slick", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Step & Compleat", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Halo", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Confetti", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Neon Ink", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Fracture", group: "Foil Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), // MARK: Stamp Model (Image Classification) // Detects promo stamp presence. Binary classification — easy to train quickly. .init(name: "Stamped (Promo)", group: "Stamp Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), .init(name: "Clean (No Stamp)", group: "Stamp Model", functionalCount: 15, solidCount: 50, highAccuracyCount: 100), // MARK: Condition Model (Object Detection — requires bounding-box annotations in Create ML) // Higher minimums than image classification because each image needs manual annotation. // Surface defects .init(name: "Light Scratches", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), .init(name: "Clouding", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), .init(name: "Dirt", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), .init(name: "Dents", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), // Edge defects .init(name: "Whitening (Edges)", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), .init(name: "Chipping (Edges)", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), .init(name: "Corner Wear", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), // Structure defects .init(name: "Creases", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), .init(name: "Shuffle Bend", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), .init(name: "Binder Dents", group: "Condition Model", functionalCount: 20, solidCount: 50, highAccuracyCount: 100), // Critical damage — a single false positive will downgrade a NM card to Damaged. // Train these more aggressively to minimise false positives. .init(name: "Water Damage", group: "Condition Model", functionalCount: 20, solidCount: 75, highAccuracyCount: 150), .init(name: "Inking", group: "Condition Model", functionalCount: 20, solidCount: 75, highAccuracyCount: 150), .init(name: "Rips", group: "Condition Model", functionalCount: 20, solidCount: 75, highAccuracyCount: 150), ] var groups: [String] { var seen = Set() return categories.compactMap { seen.insert($0.group).inserted ? $0.group : nil } } func categories(for group: String) -> [TrainingCategory] { categories.filter { $0.group == group } } }