diff --git a/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift b/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift index c3d39a6..0c45d6d 100644 --- a/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift +++ b/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift @@ -705,18 +705,18 @@ struct AchievementsView: View { private var headerCard: some View { let total = max(achievementStatuses.count, 1) let unlockedCount = unlockedAchievements.count - let progress = Double(unlockedCount) / Double(total) return VStack(alignment: .leading, spacing: 8) { Text(String(localized: "achievements_progress_title")) .font(.headline) - ProgressView(value: progress) - .tint(.appSecondary) + + AchievementMilestoneBar(statuses: achievementStatuses) + Text( String( format: NSLocalizedString("achievements_unlocked_count", comment: ""), unlockedCount, - achievementStatuses.count + total ) ) .font(.subheadline) @@ -833,6 +833,61 @@ private extension MonthlyCheckInView { } +// MARK: - Achievement Milestone Bar + +struct AchievementMilestoneBar: View { + let statuses: [MonthlyCheckInAchievementStatus] + + var body: some View { + let total = max(statuses.count, 1) + let unlockedCount = statuses.filter(\.isUnlocked).count + let progress = Double(unlockedCount) / Double(total) + + GeometryReader { geo in + let barWidth = geo.size.width + let barHeight: CGFloat = 6 + + ZStack(alignment: .leading) { + // Background track + Capsule() + .fill(Color.gray.opacity(0.2)) + .frame(height: barHeight) + + // Filled track + Capsule() + .fill(Color.appSecondary) + .frame(width: barWidth * progress, height: barHeight) + + // Milestone circles + ForEach(Array(statuses.enumerated()), id: \.element.id) { index, status in + let position = total == 1 + ? barWidth / 2 + : barWidth * Double(index) / Double(total - 1) + + Circle() + .fill(status.isUnlocked ? Color.appSecondary : Color.gray.opacity(0.3)) + .frame(width: 14, height: 14) + .overlay( + Circle() + .stroke(Color(.systemBackground), lineWidth: 2) + ) + .overlay( + Group { + if status.isUnlocked { + Image(systemName: "checkmark") + .font(.system(size: 7, weight: .bold)) + .foregroundColor(.white) + } + } + ) + .position(x: position, y: geo.size.height / 2) + } + } + } + .frame(height: 20) + } +} + #Preview { NavigationStack { MonthlyCheckInView()