Show only the day's docket on the Today board and add ad hoc Now workouts
- Due-filter schedules: daily always, fixed days by weekday, one-offs on their date; rest days get an empty state - Workouts with no due schedule row (ad hoc or off-day starts) now render as their own board rows - The + sheet is now "New Workout" with a "When" picker; "Now" (offered when adding on today) skips the schedule and starts the workout directly - Remove the times-per-week scheduling mode everywhere (enum, document, entity, mappers, planner, seeds, tests)
This commit is contained in:
@@ -376,7 +376,6 @@ struct ScheduleDocument: Codable, Sendable, Equatable, Identifiable {
|
||||
var goal: String? // GoalKind raw value; nil = unassigned
|
||||
var recurrence: String // ScheduleRecurrence raw value
|
||||
var weekdays: [Int]? // Calendar weekday numbers 1 (Sun) – 7 (Sat); .fixedDays only
|
||||
var timesPerWeek: Int? // .frequency only
|
||||
var date: Date? = nil // the one-off day; .once only
|
||||
var order: Int
|
||||
var createdAt: Date
|
||||
@@ -394,9 +393,9 @@ extension ScheduleDocument {
|
||||
/// The recurrence, defaulting to `.daily` on an unknown raw value.
|
||||
var recurrenceEnum: ScheduleRecurrence { ScheduleRecurrence(rawValue: recurrence) ?? .daily }
|
||||
|
||||
/// Human-readable recurrence line ("Jul 12, 2026" / "Daily" / "Mon & Thu" / "2× per week").
|
||||
/// Human-readable recurrence line ("Jul 12, 2026" / "Daily" / "Mon & Thu").
|
||||
var recurrenceSummary: String {
|
||||
recurrenceEnum.summary(weekdays: weekdays, timesPerWeek: timesPerWeek, date: date)
|
||||
recurrenceEnum.summary(weekdays: weekdays, date: date)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +317,6 @@ final class Schedule {
|
||||
var goalRaw: String?
|
||||
var recurrenceRaw: String = ScheduleRecurrence.daily.rawValue
|
||||
var weekdays: [Int]?
|
||||
var timesPerWeek: Int?
|
||||
var date: Date?
|
||||
var order: Int = 0
|
||||
var createdAt: Date = Date()
|
||||
@@ -325,7 +324,7 @@ final class Schedule {
|
||||
var jsonRelativePath: String = ""
|
||||
|
||||
init(id: String, routineID: String, routineName: String, goalRaw: String?,
|
||||
recurrenceRaw: String, weekdays: [Int]?, timesPerWeek: Int?, date: Date? = nil,
|
||||
recurrenceRaw: String, weekdays: [Int]?, date: Date? = nil,
|
||||
order: Int, createdAt: Date, updatedAt: Date, jsonRelativePath: String) {
|
||||
self.id = id
|
||||
self.routineID = routineID
|
||||
@@ -333,7 +332,6 @@ final class Schedule {
|
||||
self.goalRaw = goalRaw
|
||||
self.recurrenceRaw = recurrenceRaw
|
||||
self.weekdays = weekdays
|
||||
self.timesPerWeek = timesPerWeek
|
||||
self.date = date
|
||||
self.order = order
|
||||
self.createdAt = createdAt
|
||||
@@ -352,8 +350,8 @@ final class Schedule {
|
||||
}
|
||||
|
||||
/// Human-readable recurrence line — shares its formatting with `ScheduleDocument`
|
||||
/// via `ScheduleRecurrence.summary(weekdays:timesPerWeek:date:)`.
|
||||
/// via `ScheduleRecurrence.summary(weekdays:date:)`.
|
||||
var recurrenceSummary: String {
|
||||
recurrenceEnum.summary(weekdays: weekdays, timesPerWeek: timesPerWeek, date: date)
|
||||
recurrenceEnum.summary(weekdays: weekdays, date: date)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,14 +198,13 @@ extension GoalKind {
|
||||
|
||||
/// How often a schedule expects its routine. Raw values are persisted in ScheduleDocument.
|
||||
enum ScheduleRecurrence: String, CaseIterable, Codable, Sendable {
|
||||
case once, daily, fixedDays, frequency
|
||||
case once, daily, fixedDays
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .once: "Once"
|
||||
case .daily: "Daily"
|
||||
case .fixedDays: "Fixed Days"
|
||||
case .frequency: "Times per Week"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,10 +212,10 @@ enum ScheduleRecurrence: String, CaseIterable, Codable, Sendable {
|
||||
extension ScheduleRecurrence {
|
||||
/// Human-readable recurrence description shared by `ScheduleDocument` and the
|
||||
/// `Schedule` cache entity, so both render identically. `weekdays` uses Calendar
|
||||
/// weekday numbers (1 = Sun … 7 = Sat); `timesPerWeek` is the frequency count;
|
||||
/// `date` is the one-off day (`.once` only). A degenerate case (fixed days with no
|
||||
/// weekdays, a nil count or date) falls back to the recurrence's own `displayName`.
|
||||
func summary(weekdays: [Int]?, timesPerWeek: Int?, date: Date?) -> String {
|
||||
/// weekday numbers (1 = Sun … 7 = Sat); `date` is the one-off day (`.once` only).
|
||||
/// A degenerate case (fixed days with no weekdays, a nil date) falls back to the
|
||||
/// recurrence's own `displayName`.
|
||||
func summary(weekdays: [Int]?, date: Date?) -> String {
|
||||
switch self {
|
||||
case .once:
|
||||
return date?.formatDate() ?? displayName // "Jul 12, 2026" / "Once"
|
||||
@@ -230,8 +229,6 @@ extension ScheduleRecurrence {
|
||||
(1...7).contains(n) ? symbols[n - 1] : nil
|
||||
}
|
||||
return names.isEmpty ? displayName : Self.joined(names)
|
||||
case .frequency:
|
||||
return "\(timesPerWeek ?? 0)× per week"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ extension ScheduleDocument {
|
||||
self.init(schemaVersion: Self.currentSchemaVersion, id: schedule.id,
|
||||
routineID: schedule.routineID, routineName: schedule.routineName,
|
||||
goal: schedule.goalRaw, recurrence: schedule.recurrenceRaw,
|
||||
weekdays: schedule.weekdays, timesPerWeek: schedule.timesPerWeek,
|
||||
weekdays: schedule.weekdays,
|
||||
date: schedule.date,
|
||||
order: schedule.order, createdAt: schedule.createdAt, updatedAt: schedule.updatedAt)
|
||||
}
|
||||
@@ -255,7 +255,7 @@ enum CacheMapper {
|
||||
} else {
|
||||
schedule = Schedule(id: doc.id, routineID: doc.routineID, routineName: doc.routineName,
|
||||
goalRaw: doc.goal, recurrenceRaw: doc.recurrence, weekdays: doc.weekdays,
|
||||
timesPerWeek: doc.timesPerWeek, date: doc.date, order: doc.order,
|
||||
date: doc.date, order: doc.order,
|
||||
createdAt: doc.createdAt, updatedAt: doc.updatedAt,
|
||||
jsonRelativePath: relativePath)
|
||||
context.insert(schedule)
|
||||
@@ -265,7 +265,6 @@ enum CacheMapper {
|
||||
schedule.goalRaw = doc.goal
|
||||
schedule.recurrenceRaw = doc.recurrence
|
||||
schedule.weekdays = doc.weekdays
|
||||
schedule.timesPerWeek = doc.timesPerWeek
|
||||
schedule.date = doc.date
|
||||
schedule.order = doc.order
|
||||
schedule.createdAt = doc.createdAt
|
||||
|
||||
@@ -66,10 +66,10 @@ enum ScreenshotSeed {
|
||||
|
||||
// ---- Schedules (the Today board's rows, spanning two goal groups) -------
|
||||
let schedulePlans: [(routine: Routine, goal: GoalKind, recurrence: ScheduleRecurrence,
|
||||
weekdays: [Int]?, timesPerWeek: Int?)] = [
|
||||
(created[0], .strength, .fixedDays, [2, 5], nil), // Upper Body · Mon & Thu
|
||||
(created[1], .strength, .frequency, nil, 2), // Lower Body · 2× per week
|
||||
(created[2], .mobility, .daily, nil, nil), // Core · daily
|
||||
weekdays: [Int]?)] = [
|
||||
(created[0], .strength, .fixedDays, [2, 5]), // Upper Body · Mon & Thu
|
||||
(created[1], .strength, .fixedDays, [3, 7]), // Lower Body · Tue & Sat
|
||||
(created[2], .mobility, .daily, nil), // Core · daily
|
||||
]
|
||||
// Schedules predate the history below, so the Progress tab's adherence
|
||||
// tracks cover the seeded weeks instead of starting "today".
|
||||
@@ -78,7 +78,7 @@ enum ScreenshotSeed {
|
||||
context.insert(Schedule(
|
||||
id: ULID.make(), routineID: plan.routine.id, routineName: plan.routine.name,
|
||||
goalRaw: plan.goal.rawValue, recurrenceRaw: plan.recurrence.rawValue,
|
||||
weekdays: plan.weekdays, timesPerWeek: plan.timesPerWeek, order: i,
|
||||
weekdays: plan.weekdays, order: i,
|
||||
createdAt: scheduledSince, updatedAt: scheduledSince, jsonRelativePath: ""))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user