// // DebugPollPreviewView.swift // SportsTime // // Debug-only preview of the poll detail + voting flow with hardcoded data. // #if DEBUG import SwiftUI struct DebugPollPreviewView: View { @Environment(\.dismiss) private var dismiss @Environment(\.colorScheme) private var colorScheme @State private var showVotingSheet = false @State private var selectedTrip: Trip? private let poll: TripPoll private let votes: [PollVote] init() { let p = DebugShareExporter.buildSamplePoll() self.poll = p self.votes = DebugShareExporter.buildSampleVotes(for: p) } private var results: PollResults { PollResults(poll: poll, votes: votes) } var body: some View { ScrollView { VStack(spacing: Theme.Spacing.lg) { // Share Code Card shareCodeCard // Voting Status votingStatusCard // Results resultsSection // Trip Previews tripPreviewsSection } .padding(.horizontal, Theme.Spacing.md) .padding(.vertical, Theme.Spacing.lg) } .background(Theme.backgroundGradient(colorScheme)) .navigationTitle(poll.title) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Done") { dismiss() } } ToolbarItem(placement: .primaryAction) { Button { if let url = poll.shareURL as URL? { UIPasteboard.general.string = url.absoluteString } } label: { Image(systemName: "square.and.arrow.up") } } } .sheet(isPresented: $showVotingSheet) { PollVotingView(poll: poll, existingVote: nil) } .sheet(item: $selectedTrip) { trip in NavigationStack { TripDetailView(trip: trip, allowCustomItems: true) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Done") { selectedTrip = nil } } } } } } // MARK: - Share Code @ViewBuilder private var shareCodeCard: some View { VStack(spacing: Theme.Spacing.md) { ZStack { Circle() .fill(Theme.warmOrange.opacity(0.15)) .frame(width: 56, height: 56) Image(systemName: "link.circle.fill") .font(.title2) .foregroundStyle(Theme.warmOrange) } VStack(spacing: Theme.Spacing.xs) { Text("Share Code") .font(.subheadline) .foregroundStyle(Theme.textSecondary(colorScheme)) Text(poll.shareCode) .font(.system(.largeTitle, design: .monospaced).weight(.bold)) .foregroundStyle(Theme.warmOrange) .tracking(4) .lineLimit(1) .minimumScaleFactor(0.7) } Button { UIPasteboard.general.string = poll.shareCode } label: { Label("Copy Code", systemImage: "doc.on.doc") .font(.subheadline.weight(.medium)) .foregroundStyle(Theme.warmOrange) .padding(.horizontal, Theme.Spacing.md) .padding(.vertical, Theme.Spacing.sm) .background(Theme.warmOrange.opacity(0.1)) .clipShape(Capsule()) } } .frame(maxWidth: .infinity) .padding(Theme.Spacing.lg) .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.large)) .overlay( RoundedRectangle(cornerRadius: Theme.CornerRadius.large) .strokeBorder(Theme.warmOrange.opacity(0.2), lineWidth: 1) ) } // MARK: - Voting Status @ViewBuilder private var votingStatusCard: some View { HStack(spacing: Theme.Spacing.md) { ZStack { Circle() .fill(Theme.warmOrange.opacity(0.15)) .frame(width: 44, height: 44) Image(systemName: "hand.raised.fill") .font(.title3) .foregroundStyle(Theme.warmOrange) } VStack(alignment: .leading, spacing: 2) { Text("Cast your vote") .font(.headline) .foregroundStyle(Theme.textPrimary(colorScheme)) HStack(spacing: Theme.Spacing.xs) { Image(systemName: "person.2.fill") .font(.caption2) Text("\(votes.count) votes") .font(.subheadline) } .foregroundStyle(Theme.textSecondary(colorScheme)) } Spacer() Button { showVotingSheet = true } label: { Text("Vote") .font(.subheadline.weight(.semibold)) .foregroundStyle(.white) .padding(.horizontal, Theme.Spacing.md) .padding(.vertical, Theme.Spacing.sm) .background(Theme.warmOrange) .clipShape(Capsule()) } } .padding(Theme.Spacing.md) .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) } // MARK: - Results @ViewBuilder private var resultsSection: some View { VStack(alignment: .leading, spacing: Theme.Spacing.md) { HStack(spacing: Theme.Spacing.sm) { Image(systemName: "chart.bar.fill") .foregroundStyle(Theme.warmOrange) Text("Results") .font(.headline) .foregroundStyle(Theme.textPrimary(colorScheme)) } VStack(spacing: Theme.Spacing.sm) { ForEach(Array(results.tripScores.enumerated()), id: \.element.tripIndex) { index, item in let trip = results.poll.tripSnapshots[item.tripIndex] let rank = index + 1 DebugResultRow( rank: rank, tripName: trip.displayName, score: item.score, percentage: results.scorePercentage(for: item.tripIndex), isLeader: rank == 1 && item.score > 0 ) } } } .padding(Theme.Spacing.md) .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) } // MARK: - Trip Previews @ViewBuilder private var tripPreviewsSection: some View { VStack(alignment: .leading, spacing: Theme.Spacing.md) { HStack(spacing: Theme.Spacing.sm) { Image(systemName: "map.fill") .foregroundStyle(Theme.warmOrange) Text("Trip Options") .font(.headline) .foregroundStyle(Theme.textPrimary(colorScheme)) Spacer() Text("\(poll.tripSnapshots.count) trips") .font(.caption) .foregroundStyle(Theme.textMuted(colorScheme)) } ForEach(Array(poll.tripSnapshots.enumerated()), id: \.element.id) { index, trip in Button { selectedTrip = trip } label: { DebugTripPreviewCard(trip: trip, index: index + 1) } .buttonStyle(.plain) } } } } // MARK: - Debug Result Row private struct DebugResultRow: View { @Environment(\.colorScheme) private var colorScheme let rank: Int let tripName: String let score: Int let percentage: Double var isLeader: Bool = false private var rankIcon: String { switch rank { case 1: return "trophy.fill" case 2: return "medal.fill" case 3: return "medal.fill" default: return "\(rank).circle.fill" } } private var rankColor: Color { switch rank { case 1: return Theme.warmOrange case 2: return .gray case 3: return .brown default: return .secondary } } var body: some View { HStack(spacing: Theme.Spacing.sm) { ZStack { Circle() .fill(rankColor.opacity(0.15)) .frame(width: 36, height: 36) Image(systemName: rankIcon) .font(.subheadline) .foregroundStyle(rankColor) } VStack(alignment: .leading, spacing: 4) { Text(tripName) .font(.subheadline.weight(isLeader ? .semibold : .regular)) .foregroundStyle(Theme.textPrimary(colorScheme)) GeometryReader { geometry in ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: 4) .fill(Theme.cardBackgroundElevated(colorScheme)) .frame(height: 6) RoundedRectangle(cornerRadius: 4) .fill(rankColor) .frame(width: max(geometry.size.width * percentage, percentage > 0 ? 6 : 0), height: 6) } } .frame(height: 6) } Text("\(score)") .font(.subheadline.weight(.medium).monospacedDigit()) .foregroundStyle(rankColor) .padding(.horizontal, 10) .padding(.vertical, 4) .background(rankColor.opacity(0.1)) .clipShape(Capsule()) } .padding(.vertical, Theme.Spacing.xs) } } // MARK: - Debug Trip Preview Card private struct DebugTripPreviewCard: View { @Environment(\.colorScheme) private var colorScheme let trip: Trip let index: Int var body: some View { HStack(spacing: 12) { VStack(alignment: .leading, spacing: 8) { HStack { Text("Option \(index)") .font(.caption) .fontWeight(.semibold) .foregroundStyle(.white) .padding(.horizontal, 8) .padding(.vertical, 4) .background(Theme.warmOrange) .clipShape(Capsule()) Text(trip.displayName) .font(.headline) .foregroundStyle(.primary) } HStack { Label(trip.formattedDateRange, systemImage: "calendar") Spacer() Label("\(trip.tripDuration) days", systemImage: "clock") } .font(.caption) .foregroundStyle(.secondary) HStack { Label("\(trip.stops.count) stops", systemImage: "mappin.and.ellipse") Spacer() Label("\(trip.stops.flatMap { $0.games }.count) games", systemImage: "sportscourt") } .font(.caption) .foregroundStyle(.secondary) } Image(systemName: "chevron.right") .font(.caption) .fontWeight(.semibold) .foregroundStyle(.tertiary) } .padding() .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: 12)) .overlay( RoundedRectangle(cornerRadius: 12) .strokeBorder(Color.secondary.opacity(0.1), lineWidth: 1) ) } } #Preview { NavigationStack { DebugPollPreviewView() } } #endif