1.2.1: surface "Copy previous week" on the empty-week banner

The exact-copy-from-previous-week action already existed but was buried in the
magic-wand long-press context menu. Expose it as a visible secondary button on
the auto-assign banner (shown only when the previous week actually has a menu),
so users landing on a fresh week can one-tap copy last week's plan and then tweak
it. Confirms before overwriting a week that already has dishes.

- Extract requestCopyPreviousWeek() shared by the menu and the banner.
- Add week_copied_previous analytics (copied_slots + source: empty_banner|menu).
- Reuses the existing localized home_copy_previous_week string (all 6 languages).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkanrydYtrme8wipTzWssG
This commit is contained in:
alexandrev-tibco
2026-07-11 10:58:53 +02:00
parent c5fd1088cd
commit 6ba71e6341
3 changed files with 78 additions and 31 deletions
+65 -30
View File
@@ -23,6 +23,7 @@ struct HomeView: View {
@State private var showWeekPicker: Bool = false
@State private var weekPickerDate: Date = Date()
@State private var showCopyPreviousConfirm: Bool = false
@State private var copyPreviousSource: String = "menu"
@State private var showReviewSentimentPrompt: Bool = false
@State private var showReviewSupportPrompt: Bool = false
@State private var showPostOnboardingAutoAssignPrompt: Bool = false
@@ -149,11 +150,7 @@ struct HomeView: View {
.disabled(!viewModel.canUndo(for: plan))
Button {
if plan.slots.contains(where: { $0.dishId != nil }) {
showCopyPreviousConfirm = true
} else {
copyFromPreviousWeek(currentPlan: plan, settings: settings)
}
requestCopyPreviousWeek(plan: plan, settings: settings, source: "menu")
} label: {
Label("home_copy_previous_week", systemImage: "doc.on.doc")
}
@@ -774,32 +771,53 @@ struct HomeView: View {
@ViewBuilder
private func autoAssignBanner(emptyCount: Int, plan: WeekPlan, settings: AppSettings) -> some View {
HStack(spacing: 12) {
VStack(alignment: .leading, spacing: 2) {
Text("home_auto_assign_cta_title")
.font(.mealMoodBodyBold)
.foregroundColor(.mealMoodTextPrimary)
Text(String(format: String(localized: "home_auto_assign_cta_slots"), emptyCount))
.font(.mealMoodCaption)
.foregroundColor(.mealMoodTextSecondary)
}
Spacer()
Button {
viewModel.autoComplete(plan: plan, dishes: dishes, tags: tags, settings: settings, allPlans: weekPlans)
} label: {
HStack(spacing: 6) {
Image(systemName: "wand.and.stars")
Text("home_auto_assign_cta_button")
.font(.mealMoodSmall.weight(.semibold))
VStack(spacing: 10) {
HStack(spacing: 12) {
VStack(alignment: .leading, spacing: 2) {
Text("home_auto_assign_cta_title")
.font(.mealMoodBodyBold)
.foregroundColor(.mealMoodTextPrimary)
Text(String(format: String(localized: "home_auto_assign_cta_slots"), emptyCount))
.font(.mealMoodCaption)
.foregroundColor(.mealMoodTextSecondary)
}
.foregroundColor(.white)
.padding(.horizontal, 14)
.padding(.vertical, 8)
.background(Color.mealMoodCoral)
.clipShape(Capsule())
Spacer()
Button {
viewModel.autoComplete(plan: plan, dishes: dishes, tags: tags, settings: settings, allPlans: weekPlans)
} label: {
HStack(spacing: 6) {
Image(systemName: "wand.and.stars")
Text("home_auto_assign_cta_button")
.font(.mealMoodSmall.weight(.semibold))
}
.foregroundColor(.white)
.padding(.horizontal, 14)
.padding(.vertical, 8)
.background(Color.mealMoodCoral)
.clipShape(Capsule())
}
.buttonStyle(.plain)
.disabled(viewModel.isAutoCompleting)
}
if previousWeekHasMenu() {
Button {
requestCopyPreviousWeek(plan: plan, settings: settings, source: "empty_banner")
} label: {
HStack(spacing: 6) {
Image(systemName: "doc.on.doc")
Text("home_copy_previous_week")
.font(.mealMoodSmall.weight(.semibold))
}
.foregroundColor(.mealMoodCoral)
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
.background(Capsule().fill(Color.white.opacity(0.75)))
.overlay(Capsule().stroke(Color.mealMoodCoral.opacity(0.4), lineWidth: 1))
}
.buttonStyle(.plain)
.disabled(viewModel.isAutoCompleting)
}
.buttonStyle(.plain)
.disabled(viewModel.isAutoCompleting)
}
.padding(12)
.background(
@@ -869,6 +887,22 @@ struct HomeView: View {
.padding(.horizontal, 16)
}
private func previousWeekHasMenu() -> Bool {
guard let previous = fetchWeekPlan(for: viewModel.currentWeekStart.addingDays(-7)) else { return false }
return previous.slots.contains { $0.dishId != nil }
}
/// Copies the previous week, asking to confirm first only when the current
/// week already has dishes that would be overwritten.
private func requestCopyPreviousWeek(plan: WeekPlan, settings: AppSettings, source: String) {
copyPreviousSource = source
if plan.slots.contains(where: { $0.dishId != nil }) {
showCopyPreviousConfirm = true
} else {
copyFromPreviousWeek(currentPlan: plan, settings: settings)
}
}
private func copyFromPreviousWeek(currentPlan: WeekPlan, settings: AppSettings) {
let previousPlan = fetchWeekPlan(for: viewModel.currentWeekStart.addingDays(-7))
viewModel.copyFromPreviousWeek(
@@ -876,7 +910,8 @@ struct HomeView: View {
previousPlan: previousPlan,
settings: settings,
allTags: tags,
allDishes: dishes
allDishes: dishes,
source: copyPreviousSource
)
}