import SwiftUI struct ChallengeDetailView: View { let challenge: Challenge @StateObject private var viewModel = ChallengeViewModel() var body: some View { ScrollView { VStack(alignment: .leading, spacing: 16) { challengeHeader challengeInfoSection challengeDescription progressSection participationSection leaderboardSection } .padding(.horizontal) .padding(.bottom, 32) } .navigationTitle(challenge.title) .navigationBarTitleDisplayMode(.inline) .onAppear { Task { await viewModel.selectChallenge(id: challenge.id) await viewModel.fetchLeaderboard(challengeId: challenge.id) } } } private var challengeHeader: some View { VStack(spacing: 12) { HStack { Image(systemName: challenge.challengeType.icon) .font(.system(size: 40)) .foregroundColor(challenge.challengeType.color) VStack(alignment: .leading, spacing: 4) { Text(challenge.challengeType.displayName) .font(.title2) .fontWeight(.bold) Text("\(challenge.targetValue) \(challenge.targetUnit) goal") .font(.subheadline) .foregroundColor(.secondary) } Spacer() } Divider() HStack(spacing: 20) { infoItem(label: "Status", value: challenge.status.rawValue.capitalized) infoItem(label: "Participants", value: "\(challenge.participantCount)") infoItem(label: "Days Left", value: "\(challenge.daysRemaining)") } } .padding() .background(Color.secondary.opacity(0.1)) .cornerRadius(12) } private func infoItem(label: String, value: String) -> some View { VStack(alignment: .leading, spacing: 2) { Text(label) .font(.caption) .foregroundColor(.secondary) Text(value) .font(.subheadline) .fontWeight(.medium) } } private var challengeInfoSection: some View { VStack(alignment: .leading, spacing: 8) { Text("Challenge Details") .font(.headline) VStack(alignment: .leading, spacing: 6) { detailRow(label: "Type", value: challenge.challengeType.displayName) detailRow(label: "Metric", value: challenge.targetMetric.displayName) detailRow(label: "Target", value: "\(challenge.targetValue) \(challenge.targetUnit)") detailRow(label: "Start", value: formatDate(challenge.startDate)) detailRow(label: "End", value: formatDate(challenge.endDate)) detailRow(label: "Creator", value: challenge.createdByName) } } } private func detailRow(label: String, value: String) -> some View { HStack { Text(label) .font(.subheadline) .foregroundColor(.secondary) .frame(width: 100, alignment: .leading) Text(value) .font(.subheadline) .fontWeight(.medium) } } private var challengeDescription: some View { VStack(alignment: .leading, spacing: 4) { Text("About This Challenge") .font(.headline) Text(challenge.description) .font(.subheadline) .foregroundColor(.secondary) if let rules = challenge.rules { Divider() .padding(.vertical, 4) Text("Rules") .font(.headline) Text(rules) .font(.subheadline) .foregroundColor(.secondary) } } } private var progressSection: some View { VStack(alignment: .leading, spacing: 12) { Text("Your Progress") .font(.headline) if challenge.participationStatus == .participating { VStack(spacing: 8) { HStack { Text("\(challenge.progressPercentage, specifier: "%.0f")%") .font(.title2) .fontWeight(.bold) .foregroundColor(challenge.challengeType.color) Spacer() Text("\(challenge.userProgress ?? 0) / \(challenge.targetValue) \(challenge.targetUnit)") .font(.subheadline) .foregroundColor(.secondary) } ProgressView(value: challenge.progressPercentage / 100) .tint(challenge.challengeType.color) .frame(height: 8) } } else { Text(challenge.participationStatus == .invited ? "You've been invited — join to start tracking progress." : "Join this challenge to track your progress.") .font(.subheadline) .foregroundColor(.secondary) } } } private var participationSection: some View { VStack(alignment: .leading, spacing: 12) { Text("Participation") .font(.headline) HStack(spacing: 12) { switch challenge.participationStatus { case .participating: Button { Task { await viewModel.leaveChallenge(id: challenge.id) } } label: { Label("Leave Challenge", systemImage: "flag.on.flag.fill") .foregroundColor(.red) } .frame(maxWidth: .infinity) .padding(.vertical, 8) .background(Color.red.opacity(0.15)) .cornerRadius(8) case .invited: Button { Task { await viewModel.joinChallenge(id: challenge.id) } } label: { Label("Accept & Join", systemImage: "checkmark.circle.fill") .foregroundColor(.green) } .frame(maxWidth: .infinity) .padding(.vertical, 8) .background(Color.green.opacity(0.15)) .cornerRadius(8) case .notParticipating: Button { Task { await viewModel.joinChallenge(id: challenge.id) } } label: { Label("Join Challenge", systemImage: "flag.fill") .foregroundColor(.blue) } .frame(maxWidth: .infinity) .padding(.vertical, 8) .background(Color.blue.opacity(0.15)) .cornerRadius(8) } } } } } private var leaderboardSection: some View { VStack(alignment: .leading, spacing: 8) { Text("Leaderboard") .font(.headline) if viewModel.leaderboard.isEmpty { Text("No participants yet.") .font(.subheadline) .foregroundColor(.secondary) .padding(.vertical, 8) } else { ForEach(Array(viewModel.leaderboard.prefix(10), id: \.id)) { entry in HStack(spacing: 12) { Text("\(entry.position)") .font(.caption) .fontWeight(.bold) .frame(width: 24) Circle() .fill(Color.secondary.opacity(0.2)) .frame(width: 28, height: 28) Text(entry.participantName) .font(.subheadline) Spacer() Text("\(entry.progressPercentage, specifier: "%.0f")%") .font(.caption) .fontWeight(.medium) .foregroundColor(entry.position <= 3 ? .orange : .secondary) } } } } } private func formatDate(_ date: Date) -> String { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .none return formatter.string(from: date) } } #Preview { NavigationView { ChallengeDetailView(challenge: sampleChallenge) } } private var sampleChallenge: Challenge { Challenge( id: "1", title: "Monthly 100km Challenge", description: "Run 100km this month. Track your distance and compete with friends!", challengeType: .distance, status: .active, startDate: Date().addingTimeInterval(-7 * 24 * 3600), endDate: Date().addingTimeInterval(23 * 24 * 3600), targetMetric: .distance, targetValue: 100, targetUnit: "km", participantCount: 47, rules: "All runs count. GPS-tracked activities only.", imageUrl: nil, createdBy: "user1", createdByName: "Sarah Chen", clubId: nil, participationStatus: .participating, userProgress: 42.5, createdAt: Date().addingTimeInterval(-7 * 24 * 3600) ) }