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>
This commit is contained in:
104
IYmtg_App_iOS/Features/Help/TrainingGuideView.swift
Normal file
104
IYmtg_App_iOS/Features/Help/TrainingGuideView.swift
Normal file
@@ -0,0 +1,104 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user