// ── Reschedule Sheet ────────────────────────────────────────── // NL-style reschedule options: "I slept in", "Push everything", "Skip next" import SwiftUI struct RescheduleSheet: View { @EnvironmentObject var store: TimerStore @Environment(\.dismiss) private var dismiss @State private var customMinutes: Double = 30 var body: some View { NavigationStack { ZStack { CMColors.bg.ignoresSafeArea() ScrollView { VStack(spacing: CMSpacing.xl) { // Smart suggestions (if any) if !store.rescheduleSuggestions.isEmpty { smartSuggestionsSection } // Quick actions quickActionsSection // Custom shift customShiftSection // Skip next skipSection } .padding(.horizontal, CMSpacing.lg) .padding(.bottom, CMSpacing.xxl) } } .navigationTitle("Reschedule") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(CMColors.surface, for: .navigationBar) .toolbarColorScheme(.dark, for: .navigationBar) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Cancel") { dismiss() } .foregroundStyle(CMColors.textSecondary) } } } } // MARK: - Smart Suggestions private var smartSuggestionsSection: some View { VStack(alignment: .leading, spacing: CMSpacing.md) { HStack(spacing: CMSpacing.sm) { Image(systemName: "sparkles") .foregroundStyle(CMColors.accent) Text("SUGGESTED") .font(CMFonts.body(size: 11, weight: .bold)) .foregroundStyle(CMColors.textMuted) .tracking(1.5) } ForEach(store.rescheduleSuggestions) { suggestion in RescheduleOptionButton( title: suggestion.title, subtitle: suggestion.subtitle, icon: suggestion.icon, color: CMColors.accent ) { applyAndDismiss(suggestion.action) } } } } // MARK: - Quick Actions private var quickActionsSection: some View { VStack(alignment: .leading, spacing: CMSpacing.md) { Text("QUICK ACTIONS") .font(CMFonts.body(size: 11, weight: .bold)) .foregroundStyle(CMColors.textMuted) .tracking(1.5) ForEach(RescheduleSuggestion.quickActions) { action in RescheduleOptionButton( title: action.title, subtitle: action.subtitle, icon: action.icon, color: CMColors.text ) { applyAndDismiss(action.action) } } } } // MARK: - Custom Shift private var customShiftSection: some View { VStack(alignment: .leading, spacing: CMSpacing.md) { Text("CUSTOM") .font(CMFonts.body(size: 11, weight: .bold)) .foregroundStyle(CMColors.textMuted) .tracking(1.5) VStack(spacing: CMSpacing.md) { HStack { Text("Shift by") .font(CMFonts.body(size: 14)) .foregroundStyle(CMColors.textSecondary) Spacer() Text("\(Int(customMinutes)) min") .font(CMFonts.mono(size: 18, weight: .bold)) .foregroundStyle(CMColors.accent) } Slider(value: $customMinutes, in: 5...120, step: 5) .tint(CMColors.accent) HStack(spacing: CMSpacing.md) { Button { applyAndDismiss(.pushAll(interval: TimeInterval(customMinutes * 60))) } label: { HStack { Image(systemName: "arrow.right") Text("Push Later") } .font(CMFonts.body(size: 14, weight: .semibold)) .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding(.vertical, CMSpacing.md) .background(CMColors.accent) .clipShape(RoundedRectangle(cornerRadius: CMRadius.sm)) } Button { applyAndDismiss(.pushAll(interval: TimeInterval(-customMinutes * 60))) } label: { HStack { Image(systemName: "arrow.left") Text("Pull Earlier") } .font(CMFonts.body(size: 14, weight: .semibold)) .foregroundStyle(CMColors.text) .frame(maxWidth: .infinity) .padding(.vertical, CMSpacing.md) .background(CMColors.surface) .clipShape(RoundedRectangle(cornerRadius: CMRadius.sm)) .overlay( RoundedRectangle(cornerRadius: CMRadius.sm) .stroke(CMColors.border, lineWidth: 1) ) } } } .padding(CMSpacing.lg) .background(CMColors.surface) .clipShape(RoundedRectangle(cornerRadius: CMRadius.md)) } } // MARK: - Skip private var skipSection: some View { VStack(alignment: .leading, spacing: CMSpacing.md) { if let next = store.nextFiringTimer { RescheduleOptionButton( title: "Skip \"\(next.label)\"", subtitle: "Dismiss your next timer and adjust remaining", icon: "forward.fill", color: CMColors.important ) { applyAndDismiss(.skip(timerId: next.id)) } } } } // MARK: - Helpers private func applyAndDismiss(_ action: RescheduleAction) { HapticEngine.tap() store.applyReschedule(action) dismiss() } } // MARK: - Option Button struct RescheduleOptionButton: View { let title: String let subtitle: String let icon: String let color: Color let action: () -> Void var body: some View { Button(action: action) { HStack(spacing: CMSpacing.md) { Image(systemName: icon) .font(.title3) .foregroundStyle(color) .frame(width: 32, height: 32) VStack(alignment: .leading, spacing: CMSpacing.xxs) { Text(title) .font(CMFonts.body(size: 15, weight: .semibold)) .foregroundStyle(CMColors.text) Text(subtitle) .font(CMFonts.body(size: 12)) .foregroundStyle(CMColors.textMuted) } Spacer() Image(systemName: "chevron.right") .font(.caption) .foregroundStyle(CMColors.textMuted) } .padding(CMSpacing.md) .background(CMColors.surface) .clipShape(RoundedRectangle(cornerRadius: CMRadius.sm)) .overlay( RoundedRectangle(cornerRadius: CMRadius.sm) .stroke(CMColors.border, lineWidth: 1) ) } .buttonStyle(.plain) } }