Files
IYmtg/IYmtg_App_iOS/Features/Help/TrainingGuideView.swift
Mike Wichers 5da5614a10 feat: Create dynamic in-app training status guide
Implements a new UI to show recommended image counts for ML training.
Uses color-coded indicators (orange/green/blue) for Functional, Solid,
and High-Accuracy thresholds across all 28 training categories
(Foil, Stamp, and Condition models). Critical damage types (Inking,
Rips, Water Damage) carry higher recommended counts to minimise false
positives on NM grades. Accessible via a "?" toolbar button in Library.
Bumps app version to 1.1.0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 13:21:45 -05:00

105 lines
3.2 KiB
Swift

// IYmtg_App_iOS/Features/Help/TrainingGuideView.swift
import SwiftUI
struct TrainingGuideView: View {
@StateObject private var viewModel = TrainingGuideViewModel()
var body: some View {
List {
Section {
VStack(alignment: .leading, spacing: 6) {
Text("These are the recommended image counts for each ML training category. Collect cropped card photos and drop them into the matching `IYmtg_Training/` subfolder.")
.font(.caption)
.foregroundColor(.secondary)
HStack(spacing: 16) {
LegendChip(color: .orange, label: "Functional")
LegendChip(color: .green, label: "Solid")
LegendChip(color: .blue, label: "High-Accuracy")
}
.padding(.top, 2)
}
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
}
ForEach(viewModel.groups, id: \.self) { group in
Section(header: GroupHeader(title: group)) {
ForEach(viewModel.categories(for: group)) { category in
TrainingCategoryRow(category: category)
}
}
}
}
.navigationTitle("Training Guide")
.listStyle(.insetGrouped)
}
}
// MARK: - Supporting Views
private struct GroupHeader: View {
let title: String
private var icon: String {
switch title {
case "Foil Model": return "sparkles"
case "Stamp Model": return "seal.fill"
case "Condition Model": return "exclamationmark.triangle"
default: return "circle.grid.2x2"
}
}
var body: some View {
Label(title, systemImage: icon)
}
}
private struct TrainingCategoryRow: View {
let category: TrainingCategory
var body: some View {
VStack(alignment: .leading, spacing: 6) {
Text(category.name)
.font(.subheadline)
.bold()
HStack(spacing: 0) {
StatusIndicator(level: "Functional", count: category.functionalCount, color: .orange)
Spacer()
StatusIndicator(level: "Solid", count: category.solidCount, color: .green)
Spacer()
StatusIndicator(level: "High-Accuracy", count: category.highAccuracyCount, color: .blue)
}
}
.padding(.vertical, 4)
}
}
struct StatusIndicator: View {
let level: String
let count: Int
let color: Color
var body: some View {
HStack(spacing: 5) {
Circle()
.fill(color)
.frame(width: 9, height: 9)
Text("\(level): \(count)+")
.font(.caption2)
.foregroundColor(.secondary)
}
}
}
private struct LegendChip: View {
let color: Color
let label: String
var body: some View {
HStack(spacing: 4) {
Circle().fill(color).frame(width: 8, height: 8)
Text(label).font(.caption2).foregroundColor(.secondary)
}
}
}