From 4371c1cc0c5539ac3dafdf7c747844d1bccbdbdd Mon Sep 17 00:00:00 2001 From: Trey t Date: Mon, 12 Jan 2026 20:46:44 -0600 Subject: [PATCH] feat(wizard): add PlanningModeStep component Co-Authored-By: Claude Opus 4.5 --- .../Views/Wizard/Steps/PlanningModeStep.swift | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 SportsTime/Features/Trip/Views/Wizard/Steps/PlanningModeStep.swift diff --git a/SportsTime/Features/Trip/Views/Wizard/Steps/PlanningModeStep.swift b/SportsTime/Features/Trip/Views/Wizard/Steps/PlanningModeStep.swift new file mode 100644 index 0000000..51d74ad --- /dev/null +++ b/SportsTime/Features/Trip/Views/Wizard/Steps/PlanningModeStep.swift @@ -0,0 +1,115 @@ +// +// PlanningModeStep.swift +// SportsTime +// +// Step 1 of the trip wizard - select planning mode. +// + +import SwiftUI + +struct PlanningModeStep: View { + @Environment(\.colorScheme) private var colorScheme + @Binding var selection: PlanningMode? + + var body: some View { + VStack(alignment: .leading, spacing: Theme.Spacing.md) { + StepHeader( + title: "How do you want to plan?", + subtitle: "Choose your starting point" + ) + + VStack(spacing: Theme.Spacing.sm) { + ForEach(PlanningMode.allCases) { mode in + PlanningModeCard( + mode: mode, + isSelected: selection == mode, + onTap: { selection = mode } + ) + } + } + } + .padding(Theme.Spacing.lg) + .background(Theme.cardBackground(colorScheme)) + .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.large)) + .overlay { + RoundedRectangle(cornerRadius: Theme.CornerRadius.large) + .stroke(Theme.surfaceGlow(colorScheme), lineWidth: 1) + } + } +} + +// MARK: - Planning Mode Card + +private struct PlanningModeCard: View { + @Environment(\.colorScheme) private var colorScheme + let mode: PlanningMode + let isSelected: Bool + let onTap: () -> Void + + var body: some View { + Button(action: onTap) { + HStack(spacing: Theme.Spacing.md) { + Image(systemName: mode.iconName) + .font(.title2) + .foregroundStyle(isSelected ? Theme.warmOrange : Theme.textSecondary(colorScheme)) + .frame(width: 32) + + VStack(alignment: .leading, spacing: 2) { + Text(mode.displayName) + .font(.headline) + .foregroundStyle(Theme.textPrimary(colorScheme)) + + Text(mode.description) + .font(.caption) + .foregroundStyle(Theme.textMuted(colorScheme)) + } + + Spacer() + + if isSelected { + Image(systemName: "checkmark.circle.fill") + .foregroundStyle(Theme.warmOrange) + } + } + .padding(Theme.Spacing.md) + .background(isSelected ? Theme.warmOrange.opacity(0.1) : Color.clear) + .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) + .overlay( + RoundedRectangle(cornerRadius: Theme.CornerRadius.medium) + .stroke(isSelected ? Theme.warmOrange : Theme.textMuted(colorScheme).opacity(0.3), lineWidth: isSelected ? 2 : 1) + ) + } + .buttonStyle(.plain) + } +} + +// MARK: - Step Header (Reusable) + +struct StepHeader: View { + @Environment(\.colorScheme) private var colorScheme + let title: String + var subtitle: String? = nil + + var body: some View { + VStack(alignment: .leading, spacing: 4) { + Text(title) + .font(.title3) + .fontWeight(.semibold) + .foregroundStyle(Theme.textPrimary(colorScheme)) + + if let subtitle { + Text(subtitle) + .font(.subheadline) + .foregroundStyle(Theme.textSecondary(colorScheme)) + } + } + } +} + +// MARK: - Preview + +#Preview { + PlanningModeStep(selection: .constant(.dateRange)) + .padding() + .themedBackground() +}