Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions Sleepypod/Views/Schedule/PhaseBlockView.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,87 @@
import SwiftUI

// MARK: - Compact Phase Card (horizontal scroll)

struct PhaseBlockCompactView: View {
@Environment(ScheduleManager.self) private var scheduleManager
@Environment(SettingsManager.self) private var settingsManager
let phase: SchedulePhase

private var tempColor: Color { TempColor.forOffset(phase.offset) }

var body: some View {
VStack(spacing: 8) {
// Icon + time
Image(systemName: phase.icon)
.font(.system(size: 14))
.foregroundColor(tempColor)

Text(phase.name)
.font(.system(size: 10, weight: .semibold))
.foregroundColor(.white)
.lineLimit(1)

Text(formatTime(phase.time))
.font(.system(size: 9))
.foregroundColor(Theme.textSecondary)

// Temp + controls
HStack(spacing: 6) {
Button {
Haptics.light()
Task { await scheduleManager.updatePhaseTemperature(time: phase.time, delta: -1) }
} label: {
Image(systemName: "minus")
.font(.system(size: 10, weight: .bold))
.foregroundColor(Theme.textSecondary)
.frame(width: 22, height: 22)
.background(Theme.cardElevated)
.clipShape(Circle())
}
.buttonStyle(.plain)

Text(TemperatureConversion.displayTemp(phase.temperatureF, format: settingsManager.temperatureFormat))
.font(.system(size: 14, weight: .bold, design: .rounded))
.foregroundColor(tempColor)
.frame(width: 40)
.contentTransition(.numericText())

Button {
Haptics.light()
Task { await scheduleManager.updatePhaseTemperature(time: phase.time, delta: 1) }
} label: {
Image(systemName: "plus")
.font(.system(size: 10, weight: .bold))
.foregroundColor(Theme.textSecondary)
.frame(width: 22, height: 22)
.background(Theme.cardElevated)
.clipShape(Circle())
}
.buttonStyle(.plain)
}
}
.frame(width: 100)
.padding(.vertical, 10)
.padding(.horizontal, 6)
.background(Theme.card)
.clipShape(RoundedRectangle(cornerRadius: 12))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(tempColor.opacity(0.2), lineWidth: 1)
)
}

private func formatTime(_ time: String) -> String {
let parts = time.split(separator: ":")
guard parts.count == 2, let hour = Int(parts[0]), let minute = Int(parts[1]) else { return time }
let period = hour >= 12 ? "PM" : "AM"
let displayHour = hour == 0 ? 12 : (hour > 12 ? hour - 12 : hour)
return "\(displayHour):\(String(format: "%02d", minute)) \(period)"
}
}

// MARK: - Full Phase Card (legacy, kept for reference)

struct PhaseBlockView: View {
@Environment(ScheduleManager.self) private var scheduleManager
@Environment(SettingsManager.self) private var settingsManager
Expand Down
89 changes: 49 additions & 40 deletions Sleepypod/Views/Schedule/ScheduleScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,55 +22,64 @@ struct ScheduleScreen: View {
// Schedule toggle
scheduleToggle

// Manual set points (advanced)
Button {
Haptics.light()
withAnimation(.easeInOut(duration: 0.2)) { showAdvanced.toggle() }
} label: {
HStack {
Text("Manual Set Points")
.font(.caption.weight(.medium))
.foregroundColor(Theme.textMuted)
Spacer()
Image(systemName: "chevron.right")
.font(.caption2)
.foregroundColor(Theme.textMuted)
.rotationEffect(.degrees(showAdvanced ? 90 : 0))
}
}
.buttonStyle(.plain)
// Phase detail — horizontal scroller behind disclosure
if scheduleManager.schedules != nil && !scheduleManager.phases.isEmpty {
VStack(spacing: 10) {
Button {
Haptics.light()
withAnimation(.easeInOut(duration: 0.2)) { showAdvanced.toggle() }
} label: {
HStack {
Text("Set Points")
.font(.caption.weight(.medium))
.foregroundColor(Theme.textSecondary)
Text("(\(scheduleManager.phases.count))")
.font(.caption2)
.foregroundColor(Theme.textMuted)
Spacer()
Image(systemName: "chevron.right")
.font(.caption2)
.foregroundColor(Theme.textMuted)
.rotationEffect(.degrees(showAdvanced ? 90 : 0))
}
}
.buttonStyle(.plain)

if showAdvanced {
// Horizontal scrolling phase cards
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
ForEach(scheduleManager.phases) { phase in
PhaseBlockCompactView(phase: phase)
}
}
.padding(.horizontal, 2)
}
}

// Phase blocks
if scheduleManager.schedules != nil {
VStack(spacing: 12) {
ForEach(scheduleManager.phases) { phase in
PhaseBlockView(phase: phase)
// Clear schedule
if showAdvanced {
Button {
Haptics.medium()
showClearConfirm = true
} label: {
HStack(spacing: 6) {
Image(systemName: "trash")
Text("Clear Schedule")
}
.font(.caption.weight(.medium))
.foregroundColor(Theme.error)
}
.buttonStyle(.plain)
}
}
} else if scheduleManager.isLoading {
LoadingView(message: "Loading schedule…")
} else {
} else if scheduleManager.schedules == nil {
Text("No schedule data")
.foregroundColor(Theme.textSecondary)
.padding(40)
}

// Clear schedule
if scheduleManager.schedules != nil && !scheduleManager.phases.isEmpty {
Button {
Haptics.medium()
showClearConfirm = true
} label: {
HStack(spacing: 6) {
Image(systemName: "trash")
Text("Clear Schedule")
}
.font(.caption.weight(.medium))
.foregroundColor(Theme.error)
}
.buttonStyle(.plain)
.padding(.top, 8)
}
}
.padding(.horizontal, 16)
.padding(.bottom, 20)
Expand Down
Loading