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
El panel de avisos solo se alcanzaba desde el menu "..." de la barra, asi
que los incumplimientos pasaban desapercibidos. Ahora, cuando la semana
rompe reglas, aparece un banner bajo el selector de semana con el numero
de comidas afectadas, un resumen de donde estan (Lun - Cena, Mie -
Comida) y un boton Ver que abre el panel con la explicacion y el arreglo.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
- RuleViolation.reasons: el motor devuelve ahora QUE regla rompe cada
asignacion (repetido en la semana, maximo por semana superado, no
consecutivo, no mismo dia, solo comida/cena, solo entre semana o fin
de semana) con su etiqueta y limite
- Panel de avisos: bloque con cada regla rota en lenguaje claro + una
sugerencia de que hacer, y boton nuevo "Cambiar plato" que abre el
selector de ese hueco (antes solo se podia quitar o ignorar)
- Estadisticas: matriz "Etiquetas por tipo de comida" (p.ej. cuantas
cenas son de pescado) y "Platos por etiqueta" del catalogo
- Tests: 4 casos de las razones de incumplimiento; harness que renderiza
las secciones nuevas de stats a /tmp/stats_render.png
- Strings en 6 idiomas
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
- Target MealMoodWatch (watchOS 10, SwiftUI): vista de hoy y de la
semana en TabView vertical; recibe el snapshot del iPhone por
WatchConnectivity (updateApplicationContext) y lo guarda en su app
group para la complicacion
- Target MealMoodWatchWidget: complicacion accessoryRectangular/
Circular/Inline con las comidas de hoy (heuristica comida/cena por
hora); icono del watch generado desde el logo
- WatchWeekPayload compartido entre iOS app, widget iOS, watch app y
complicacion — llega ya localizado desde el iPhone
- Widget iOS nuevo "Semana" (systemMedium/Large) con los 7 dias;
el widget de hoy pasa a WidgetBundle
- Targets creados via gema xcodeproj (sin abrir Xcode); embed del watch
antes del script de Crashlytics para evitar ciclo de build
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
Guideline 3.1.2(c): Apple rechazo la 2.0.5 (77) por no tener enlaces
funcionales a la politica de privacidad y los terminos de uso dentro
del paywall. Anadidos en PremiumView y en el paywall del onboarding
(mealmood.app/privacy.html y terms.html), y ambos enlaces al final de
la descripcion del App Store en los 6 idiomas.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
- MealSlot.isSkipped: opcion "No planificar esta comida" en el picker de
hueco vacio — cuenta como resuelto (semana completa, notificaciones,
stats) y el autocompletado lo respeta; vista gris con guion, tap para
desmarcar; viaja en undo, snapshot KV (junto con isEatingOut, que
faltaba en el payload) y exports (— en imagen, omitido en texto)
- ShoppingItem.isDismissed: "Ya lo tengo" por ingrediente (swipe
izquierdo) y "Ya esta hecho" por plato entero (boton en la cabecera de
su seccion); van a la seccion "Ya en casa" con restauracion de un tap,
y quedan fuera del export de texto. No se borran porque reconcile los
recrearia
- Esquema CloudKit Development: CD_isSkipped, CD_isDismissed y el record
type CD_ShoppingItem entero, que no existia — la lista de la compra no
estaba sincronizando entre dispositivos (mismo bug que photoData)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
- Header elegante: logo fijado a la izquierda y titulo centrado de verdad
(ZStack), fecha a la derecha
- School transpuesto: dias como columnas y comidas como filas — antes con
1-2 comidas quedaba media pagina A4 apaisada en blanco
- Default: las celdas del grid se expanden para llenar la pagina (mismo
sintoma de hueco que el school)
- Estilo nuevo "Story" (9:16, 2160x3840) para stories de IG/WhatsApp y
"Minimal (imprimir)" en A4 con lineas finas sin fondos (ahorro tinta)
- Picker del preview pasa de segmentado a chips desplazables (5 estilos)
- WeekPlanTextExporter + boton "Copiar como texto (WhatsApp)" en el
preview: menu semanal con *negritas* y emojis listo para pegar en
WhatsApp/Telegram (evento analytics format=text)
- Harness de render en tests: genera PNG de los 5 estilos con datos de
ejemplo en /tmp/mealmood-export-previews para revisar el diseno
- Nombres de estilos en espanol corregidos (estaban en ingles)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
- Logo real de la app en el header de los 3 estilos (antes solo en el
default, y pequeno) y en el footer junto a un wordmark MealMood en
degradado coral->verde con tagline localizada en cursiva
- "Powered by MealMood" y "Scan me" estaban hardcodeados en ingles;
ahora share_footer_tagline y share_footer_scan en 6 idiomas
- Titulo del header default con el mismo degradado; QR con borde coral
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
- WeekExportPreviewSheet: preview renderizado del export con picker
segmentado de los 3 estilos (cachea cada render); compartir usa el
estilo elegido sin tocar el predeterminado (salvo primera vez)
- Sustituye al WeekExportStylePickerSheet de thumbnails y al doble
sheet picker+share de HomeView; strings nuevas en 6 idiomas
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
- MealSlot.secondaryDishId (opcional, aditivo CloudKit); se limpia al
sustituir/quitar/vaciar/marcar comer fuera y viaja en swaps, copia de
semana anterior, undo y snapshot KV de iCloud (retrocompatible)
- Picker de hueco lleno: toggle "anadir como segundo plato" + boton para
quitarlo; HomeViewModel.assignSecondaryDish/removeSecondaryDish
- Se muestra "Plato + Segundo" en calendario, widget y export; la lista
de la compra incluye los ingredientes del segundo; stats lo cuentan
- Evento analytics secondary_dish_added; strings en 6 idiomas
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
- Dish.fixedDayOfWeek/fixedMealType (opcionales, aditivos para CloudKit)
- Toggle + pickers de dia/comida en el formulario de plato
- AutocompleteEngine: pre-pasada que asigna los platos fijos a su hueco
antes del bucle MRV, de modo que cuentan para el resto de reglas
- Evento analytics dish_fixed_slot_set; strings en 6 idiomas
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
- attemptAddDish/attemptCreateDish: el limite gratuito se comprueba en el
tap de anadir plato (DishListView y los 3 puntos de Home) y muestra el
DishLimitModal -> paywall directamente; antes solo se comprobaba al
guardar el formulario y el usuario podia rellenarlo entero para nada
- DishLimitModal compartido (era private de DishFormView) y
PaywallPresentation promovido a PremiumView.swift; DishListView usa
sheet(item:) tambien para el banner (source dish_counter vs dish_limit)
- Boton en la toolbar de Mis Platos que alterna orden alfabetico
(persistido en dish_list_alphabetical_sort); borrado por swipe corregido
para usar la lista mostrada
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
La 2.0.3 nunca llego a la App Store, solo a TestFlight, asi que el numero queda
libre y evita saltarselo. El binario lleva la version dentro, y ASC solo adjunta
a una version las builds cuyo CFBundleShortVersionString coincide, asi que la
build 69 no se puede reetiquetar: hay que reconstruir.
Codigo identico al de la build 69, ya verificada en dispositivo.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Ks8uUcMA9mjypVK7F2Pkt
La primera subida la rechazo App Store Connect con 90062: el IPA salio con
CFBundleShortVersionString 2.0. MARKETING_VERSION del pbxproj no manda; la
version real esta hardcodeada en MealMood/Resources/Info.plist y en el
Info.plist del widget.
Corregidos ambos a 2.0.1 y alineados en build 66. Documentado en CLAUDE.md que
un cambio de version toca tres sitios y que fastlane solo automatiza el build
number de la app.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Ks8uUcMA9mjypVK7F2Pkt
- El banner "Your week is ready to export" ahora tiene botón de cerrar (X)
que lo oculta para esa semana (persistido en AppStorage).
- iPad/Mac: handle arrastrable entre calendario y platos para rebalancear
el alto (fracción persistida, clamp 0.28–0.72); el calendario y la lista
se recalculan al arrastrar. Sustituye la altura fija anterior.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3HaWmmtTQ1vdTERtSYU6p
El archive con firma automática no podía incluir la capability Push de
forma headless. Se pasan los targets MealMood y MealMoodWidget (config
Release) a firma manual con "Apple Distribution" y sus perfiles AppStore
(regenerados con Push). Build 63 sube a TestFlight con aps-environment
production para el sync CloudKit en tiempo real.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3HaWmmtTQ1vdTERtSYU6p
NSPersistentCloudKitContainer solo sincronizaba de forma oportunista (sin
push), por lo que un plato nuevo tardaba mucho o no aparecía en el otro
dispositivo. Se activa el push de CloudKit:
- aps-environment en MealMood.entitlements vía variable APS_ENVIRONMENT
(development en Debug, production en Release) para no romper TestFlight.
- AppDelegate con registerForRemoteNotifications() + manejo del push
silencioso; el mirror de SwiftData importa al ser despertado.
- (Capability Push Notifications habilitada en el App ID por API.)
Pendiente: regenerar el provisioning profile AppStore con Push antes de
la build 63, y validar el sync push en iPhone+iPad vía TestFlight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3HaWmmtTQ1vdTERtSYU6p
- Dictado por voz (Speech/SFSpeechRecognizer, on-device) en el nombre de
plato y en ingredientes, con transcripción en vivo.
- Un solo texto dictado se separa en ingredientes individuales vía Apple
Foundation Models on-device (IngredientParser), con fallback heurístico.
- Permisos de micrófono y reconocimiento de voz en Info.plist.
- Cadenas de dictado en los 6 idiomas.
- iPad/Mac: el calendario reclama ~50% de la altura disponible en lugar
de quedar fijo a ~240px; iPhone (compact) sin cambios.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3HaWmmtTQ1vdTERtSYU6p
- Shopping list: dishes under "No ingredients yet" now open the dish editor on
tap (pencil affordance); list reconciles on dismiss. This was a dead end on
devices without Apple Intelligence (no Generate button, e.g. iPhone 14).
- Planner: dish photo as a scrimmed card background in the week calendar,
behind a new Settings toggle (settings_show_dish_photos, default on). New
optional AppSettings.showDishPhotosInPlanner (CloudKit/migration safe).
- FilledSlotView: inlines the meal-card chrome to support the photo layer.
- Toggle localized in all 6 languages.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkanrydYtrme8wipTzWssG