66128e7a5c
Hasta ahora includeWeekends y activeMealTypes vivían solo en AppSettings y aplicaban a todo el calendario. WeekPlan puede ahora sobreescribirlos para su semana (includeWeekendsOverride / mealTypesOverrideRaw) y WeekSchedule resuelve qué manda en cada semana: su override si lo tiene, los ajustes globales si no. Todo lo que pintaba días y comidas —planificador, exports, compartir texto, widget, watch— pasa por settings.schedule(for:), así que una semana con fin de semana añadido o con desayuno activo se ve igual en todas partes. Crear o editar el override es premium; los ya guardados se respetan aunque la suscripción caduque, para no borrar comidas ya planificadas. La hoja avisa y pide confirmación cuando quitar un día o una comida se lleva por delante algo ya planificado. Refs #32 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013su1ttRiMeMYxkZJ1Y3246
76 lines
2.6 KiB
Swift
76 lines
2.6 KiB
Swift
import Foundation
|
|
import SwiftData
|
|
|
|
// CloudKit-compatible (2.0): no unique constraints, inline defaults everywhere,
|
|
// and the relationship stored as optional (CloudKit requires it). `slots` keeps
|
|
// its name so existing stores lightweight-migrate; use `slotList` in code.
|
|
@Model
|
|
final class WeekPlan {
|
|
var id: UUID = UUID()
|
|
var weekStartDate: Date = Date()
|
|
var createdAt: Date = Date()
|
|
var updatedAt: Date = Date()
|
|
var userRating: Int = 0 // 0 = unrated, 1 = liked, -1 = disliked
|
|
|
|
/// Per-week overrides of the global schedule (2.1.1, premium). nil = follow
|
|
/// `AppSettings`. Optional so existing stores lightweight-migrate and
|
|
/// CloudKit accepts the new attributes.
|
|
var includeWeekendsOverride: Bool?
|
|
/// Comma-separated MealType raw values, chronological. nil = follow settings.
|
|
var mealTypesOverrideRaw: String?
|
|
|
|
@Relationship(deleteRule: .cascade)
|
|
var slots: [MealSlot]? = []
|
|
|
|
/// Non-optional facade over the CloudKit-required optional relationship.
|
|
var slotList: [MealSlot] {
|
|
get { slots ?? [] }
|
|
set { slots = newValue }
|
|
}
|
|
|
|
/// The meal types this week overrides the global setting with, or nil when
|
|
/// it follows `AppSettings.activeMealTypes`.
|
|
var mealTypesOverride: [MealType]? {
|
|
get {
|
|
guard let raw = mealTypesOverrideRaw else { return nil }
|
|
let types = raw.split(separator: ",").compactMap { MealType(rawValue: String($0)) }
|
|
guard !types.isEmpty else { return nil }
|
|
return MealType.allCases.filter { types.contains($0) }
|
|
}
|
|
set {
|
|
guard let newValue else {
|
|
mealTypesOverrideRaw = nil
|
|
return
|
|
}
|
|
let ordered = MealType.allCases.filter { newValue.contains($0) }
|
|
// An empty selection would leave the week with no slots at all;
|
|
// treat it as "no override" instead.
|
|
mealTypesOverrideRaw = ordered.isEmpty ? nil : ordered.map(\.rawValue).joined(separator: ",")
|
|
}
|
|
}
|
|
|
|
var hasScheduleOverride: Bool {
|
|
includeWeekendsOverride != nil || mealTypesOverrideRaw != nil
|
|
}
|
|
|
|
func clearScheduleOverride() {
|
|
includeWeekendsOverride = nil
|
|
mealTypesOverrideRaw = nil
|
|
}
|
|
|
|
init(
|
|
id: UUID = UUID(),
|
|
weekStartDate: Date,
|
|
slots: [MealSlot] = [],
|
|
createdAt: Date = Date(),
|
|
updatedAt: Date = Date()
|
|
) {
|
|
self.id = id
|
|
self.weekStartDate = weekStartDate
|
|
self.slots = slots
|
|
self.createdAt = createdAt
|
|
self.updatedAt = updatedAt
|
|
self.userRating = 0
|
|
}
|
|
}
|