import SwiftUI import WatchKit /// Routine quotidienne sur la montre : les exercices, cochables un par un au fil /// de la séance. Chaque coche part vers l'iPhone, qui la POSTe au serveur — /// l'état est donc partagé avec la page /routine et le widget de la home. struct RoutineView: View { @StateObject private var store = RoutineStore.shared @State private var guided: RoutineBlock? var body: some View { Group { if store.isEmpty { emptyState } else { list } } .navigationTitle("Routine") .sheet(item: $guided) { block in RoutineGuidedView(block: block) } } private var emptyState: some View { VStack(spacing: 8) { Image(systemName: "figure.cooldown") .font(.title2) .foregroundStyle(.secondary) Text("Ouvre l'app Coach sur l'iPhone une fois pour recevoir la routine.") .font(.footnote) .multilineTextAlignment(.center) .foregroundStyle(.secondary) } .padding() } private var list: some View { ScrollView { LazyVStack(spacing: 6, pinnedViews: []) { progressHeader ForEach(store.morningBlocks) { block in blockSection(block) } if !store.otherBlocks.isEmpty { Text("APRÈS LA COURSE") .font(.caption2) .foregroundStyle(.orange) .frame(maxWidth: .infinity, alignment: .leading) .padding(.top, 8) ForEach(store.otherBlocks) { block in blockSection(block) } } if store.lastSyncFailed { Button { store.retry() } label: { Label("Renvoyer vers l'iPhone", systemImage: "arrow.clockwise") .font(.footnote) } .tint(.orange) .padding(.top, 4) } } .padding(.horizontal, 4) } } private var progressHeader: some View { let total = store.totalExercises let count = store.done.count return VStack(spacing: 4) { Text("\(count) / \(total) exercices") .font(.headline) .foregroundStyle(count == total && total > 0 ? .green : .primary) ProgressView(value: total > 0 ? Double(count) / Double(total) : 0) .tint(.cyan) } .padding(.vertical, 4) } /// En-tête de bloc (tap = tout cocher d'un coup), bouton de séance guidée, /// puis ses exercices. private func blockSection(_ block: RoutineBlock) -> some View { let complete = store.isComplete(block) return VStack(spacing: 4) { Button { store.toggleBlock(block) WKInterfaceDevice.current().play(complete ? .click : .success) } label: { HStack(spacing: 6) { Text("\(block.num). \(block.title)") .font(.caption) .fontWeight(.semibold) .multilineTextAlignment(.leading) Spacer(minLength: 0) Text("\(store.doneCount(in: block))/\(block.items.count)") .font(.caption2) .monospacedDigit() .foregroundStyle(complete ? .green : .secondary) } .frame(maxWidth: .infinity, alignment: .leading) } .buttonStyle(.plain) .padding(.top, 6) Button { guided = block } label: { Label("Démarrer", systemImage: "play.fill") .font(.caption2) .frame(maxWidth: .infinity) } .tint(.cyan) ForEach(block.items) { item in exerciseRow(item) } } } private func exerciseRow(_ item: RoutineExercise) -> some View { let isDone = store.done.contains(item.id) return Button { store.toggle(item.id) WKInterfaceDevice.current().play(isDone ? .click : .success) } label: { HStack(spacing: 8) { Image(systemName: isDone ? "checkmark.circle.fill" : "circle") .foregroundStyle(isDone ? .green : .secondary) VStack(alignment: .leading, spacing: 1) { Text(item.name) .font(.footnote) .multilineTextAlignment(.leading) .strikethrough(isDone, color: .secondary) if !item.dose.isEmpty { Text(item.dose) .font(.caption2) .foregroundStyle(.secondary) } } Spacer(minLength: 0) } .frame(maxWidth: .infinity, alignment: .leading) } .buttonStyle(.plain) .padding(.vertical, 6) .padding(.horizontal, 8) .background(Color.gray.opacity(isDone ? 0.10 : 0.18), in: RoundedRectangle(cornerRadius: 8)) .opacity(isDone ? 0.65 : 1) } }