home: aviso visible de incumplimientos con acceso al detalle

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
This commit is contained in:
alexandrev-tibco
2026-09-10 21:14:49 +02:00
parent 1c62182f61
commit ff5c6c7895
10 changed files with 77 additions and 2 deletions
@@ -460,3 +460,5 @@
"violations_replace" = "Gericht ändern"; "violations_replace" = "Gericht ändern";
"stats_tag_by_meal_title" = "Tags nach Mahlzeit"; "stats_tag_by_meal_title" = "Tags nach Mahlzeit";
"stats_dishes_per_tag_title" = "Gerichte pro Tag"; "stats_dishes_per_tag_title" = "Gerichte pro Tag";
"violations_banner_title" = "%d Mahlzeit(en) verletzen deine Regeln";
"violations_banner_cta" = "Ansehen";
@@ -460,3 +460,5 @@
"violations_replace" = "Change dish"; "violations_replace" = "Change dish";
"stats_tag_by_meal_title" = "Tags by meal type"; "stats_tag_by_meal_title" = "Tags by meal type";
"stats_dishes_per_tag_title" = "Dishes per tag"; "stats_dishes_per_tag_title" = "Dishes per tag";
"violations_banner_title" = "%d meal(s) break your rules";
"violations_banner_cta" = "View";
@@ -460,3 +460,5 @@
"violations_replace" = "Cambiar plato"; "violations_replace" = "Cambiar plato";
"stats_tag_by_meal_title" = "Etiquetas por tipo de comida"; "stats_tag_by_meal_title" = "Etiquetas por tipo de comida";
"stats_dishes_per_tag_title" = "Platos por etiqueta"; "stats_dishes_per_tag_title" = "Platos por etiqueta";
"violations_banner_title" = "%d comida(s) incumplen tus reglas";
"violations_banner_cta" = "Ver";
@@ -460,3 +460,5 @@
"violations_replace" = "Changer de plat"; "violations_replace" = "Changer de plat";
"stats_tag_by_meal_title" = "Étiquettes par type de repas"; "stats_tag_by_meal_title" = "Étiquettes par type de repas";
"stats_dishes_per_tag_title" = "Plats par étiquette"; "stats_dishes_per_tag_title" = "Plats par étiquette";
"violations_banner_title" = "%d repas ne respecte(nt) pas vos règles";
"violations_banner_cta" = "Voir";
@@ -460,3 +460,5 @@
"violations_replace" = "Cambia piatto"; "violations_replace" = "Cambia piatto";
"stats_tag_by_meal_title" = "Etichette per tipo di pasto"; "stats_tag_by_meal_title" = "Etichette per tipo di pasto";
"stats_dishes_per_tag_title" = "Piatti per etichetta"; "stats_dishes_per_tag_title" = "Piatti per etichetta";
"violations_banner_title" = "%d pasto/i non rispettano le tue regole";
"violations_banner_cta" = "Vedi";
@@ -460,3 +460,5 @@
"violations_replace" = "Trocar prato"; "violations_replace" = "Trocar prato";
"stats_tag_by_meal_title" = "Etiquetas por tipo de refeição"; "stats_tag_by_meal_title" = "Etiquetas por tipo de refeição";
"stats_dishes_per_tag_title" = "Pratos por etiqueta"; "stats_dishes_per_tag_title" = "Pratos por etiqueta";
"violations_banner_title" = "%d refeição(ões) quebram suas regras";
"violations_banner_cta" = "Ver";
+62
View File
@@ -317,11 +317,73 @@ struct HomeView: View {
.padding(.bottom, 10) .padding(.bottom, 10)
} }
/// Banner shown on the home whenever the week breaks rules. The panel used
/// to be reachable only from the "···" menu, so violations went unnoticed.
@ViewBuilder
private func violationsBanner(plan: WeekPlan) -> some View {
let violations = AutocompleteEngine.findViolations(plan: plan, allDishes: dishes, allTags: tags)
if !violations.isEmpty {
Button {
showViolationsPanel = true
} label: {
HStack(spacing: 10) {
Image(systemName: "exclamationmark.triangle.fill")
.font(.system(size: 16))
.foregroundColor(.mealMoodWarning)
VStack(alignment: .leading, spacing: 2) {
Text(String(format: String(localized: "violations_banner_title"), violations.count))
.font(.mealMoodSmall.weight(.semibold))
.foregroundColor(.mealMoodTextPrimary)
Text(violationsSummary(violations))
.font(.mealMoodCaption)
.foregroundColor(.mealMoodTextSecondary)
.lineLimit(1)
}
Spacer()
Text("violations_banner_cta")
.font(.mealMoodCaption.weight(.semibold))
.foregroundColor(.white)
.padding(.horizontal, 10)
.padding(.vertical, 5)
.background(Color.mealMoodWarning)
.clipShape(Capsule())
}
.padding(.horizontal, 12)
.padding(.vertical, 10)
.background(Color.mealMoodWarning.opacity(0.12))
.clipShape(RoundedRectangle(cornerRadius: 12))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color.mealMoodWarning.opacity(0.45), lineWidth: 1)
)
}
.buttonStyle(.plain)
.padding(.horizontal, 16)
.padding(.bottom, 8)
}
}
/// "Lun · Cena, Mié · Comida" where the problems are, at a glance.
private func violationsSummary(_ violations: [AutocompleteEngine.RuleViolation]) -> String {
let language = settings?.languageEnum.resolved() ?? .spanish
let keys = ["day_mon","day_tue","day_wed","day_thu","day_fri","day_sat","day_sun"]
return violations.prefix(3).map { violation in
let day = violation.dayOfWeek >= 0 && violation.dayOfWeek < keys.count
? localizedString(keys[violation.dayOfWeek], language: language) : ""
return "\(day) · \(localizedString(violation.mealType, language: language))"
}.joined(separator: ", ")
}
@ViewBuilder @ViewBuilder
private func mainContent(plan: WeekPlan, settings: AppSettings) -> some View { private func mainContent(plan: WeekPlan, settings: AppSettings) -> some View {
VStack(spacing: 0) { VStack(spacing: 0) {
weekSwitcher(settings: settings) weekSwitcher(settings: settings)
violationsBanner(plan: plan)
if showWeekLimitUpsell && !settings.isPremium { if showWeekLimitUpsell && !settings.isPremium {
PremiumUpsellBanner( PremiumUpsellBanner(
messageKey: "premium_limit_future_weeks", messageKey: "premium_limit_future_weeks",
@@ -111,3 +111,4 @@ final class NextWeekNavigationUITests: XCTestCase {
} }
} }
+1 -1
View File
@@ -17,7 +17,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.1.1</string> <string>2.1.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>86</string> <string>87</string>
<key>NSExtension</key> <key>NSExtension</key>
<dict> <dict>
<key>NSExtensionPointIdentifier</key> <key>NSExtensionPointIdentifier</key>
+1 -1
View File
@@ -17,7 +17,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.1.1</string> <string>2.1.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>86</string> <string>87</string>
<key>NSExtension</key> <key>NSExtension</key>
<dict> <dict>
<key>NSExtensionPointIdentifier</key> <key>NSExtensionPointIdentifier</key>