|
| 1 | +import SwiftUI |
| 2 | + |
| 3 | +func benchmarkValueText(_ v: StandardValue?, _ unit: String) -> String { |
| 4 | + guard let v else { return "-" } |
| 5 | + switch v { |
| 6 | + case .scalar(let d): |
| 7 | + if unit == "seconds" { let s = Int(d); return String(format: "%d:%02d", s/60, s%60) } |
| 8 | + return "\(Int(d)) \(unit == "kg" ? "kg" : unit == "rounds" ? "rds" : unit)" |
| 9 | + case .range(let lo, let hi): |
| 10 | + if hi >= 99 { return "\(lo)+ \(unit)" } |
| 11 | + if lo == hi { return "\(lo) \(unit)" } |
| 12 | + return "\(lo)-\(hi) \(unit)" |
| 13 | + } |
| 14 | +} |
| 15 | + |
| 16 | +struct BenchmarkDetailView: View { |
| 17 | + @Environment(DataStore.self) private var store |
| 18 | + @AppStorage("op.gender") private var gender: Gender = .male |
| 19 | + @AppStorage("op.level") private var userLevel: Int = 3 |
| 20 | + let benchmark: Benchmark |
| 21 | + let categoryName: String |
| 22 | + @State private var showLog = false |
| 23 | + |
| 24 | + var body: some View { |
| 25 | + ScrollView { |
| 26 | + VStack(alignment: .leading, spacing: 18) { |
| 27 | + VStack(alignment: .leading, spacing: 6) { |
| 28 | + SectionLabel(text: categoryName) |
| 29 | + Text(benchmark.name).font(.display(28)).foregroundStyle(Theme.text) |
| 30 | + HStack(spacing: 8) { |
| 31 | + Chip(text: testLabel, color: Theme.primary) |
| 32 | + if let d = benchmark.description { Text(d).font(.body(12)).foregroundStyle(Theme.textFaint) } |
| 33 | + } |
| 34 | + } |
| 35 | + Picker("", selection: $gender) { Text("Male").tag(Gender.male); Text("Female").tag(Gender.female) }.pickerStyle(.segmented) |
| 36 | + |
| 37 | + VStack(alignment: .leading, spacing: 6) { |
| 38 | + SectionLabel(text: "Standards by level") |
| 39 | + ForEach(store.levels.reversed()) { lv in |
| 40 | + HStack(spacing: 12) { |
| 41 | + Circle().fill(Theme.levelColor(lv.number)).frame(width: 8, height: 8) |
| 42 | + Text(lv.name).font(.body(14, .medium)).foregroundStyle(Theme.text) |
| 43 | + Spacer() |
| 44 | + if let bwm = benchmark.bwMultiplier?[lv.id]?[gender.rawValue] { |
| 45 | + Text(String(format: "%.2gx BW", bwm)).font(.body(11)).foregroundStyle(Theme.textFaint) |
| 46 | + } |
| 47 | + Text(benchmarkValueText(benchmark.standards[lv.id]?[gender.rawValue], benchmark.unit)) |
| 48 | + .font(.body(15, .semibold)).foregroundStyle(Theme.levelColor(lv.number)) |
| 49 | + } |
| 50 | + .padding(.vertical, 8) |
| 51 | + .overlay(alignment: .bottom) { Rectangle().fill(Theme.stroke).frame(height: 1) } |
| 52 | + } |
| 53 | + }.card(padding: 16) |
| 54 | + |
| 55 | + Button { Haptics.tap(); showLog = true } label: { |
| 56 | + Label("Log a test", systemImage: "square.and.pencil") |
| 57 | + .font(.body(15, .semibold)).foregroundStyle(Color.black) |
| 58 | + .frame(maxWidth: .infinity).padding(.vertical, 14) |
| 59 | + .background(Theme.primary, in: RoundedRectangle(cornerRadius: 16, style: .continuous)) |
| 60 | + }.buttonStyle(.plain) |
| 61 | + Spacer(minLength: 12) |
| 62 | + } |
| 63 | + .padding(20) |
| 64 | + } |
| 65 | + .background(Theme.bg.ignoresSafeArea()) |
| 66 | + .sheet(isPresented: $showLog) { |
| 67 | + LogEntrySheet(type: "Benchmark", name: benchmark.name, code: nil, level: userLevel, gender: gender, |
| 68 | + resultPlaceholder: placeholder).presentationDetents([.medium]) |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + private var testLabel: String { |
| 73 | + switch benchmark.testType { case "1rm": return "1 Rep Max"; case "max_reps": return "Max Reps"; case "time": return "For Time"; case "amrap": return "AMRAP"; default: return benchmark.testType } |
| 74 | + } |
| 75 | + private var placeholder: String { |
| 76 | + switch benchmark.testType { case "time": return "e.g. 7:30"; case "1rm": return "e.g. 120 kg"; default: return benchmark.unit == "reps" ? "e.g. 12 reps" : "e.g. \(benchmark.unit)" } |
| 77 | + } |
| 78 | +} |
0 commit comments