Andromida/Andromida/App/Views/History/HistoryDayDetailSheet.swift

166 lines
5.6 KiB
Swift

//
// HistoryDayDetailSheet.swift
// Andromida
//
// Detail sheet showing habit completions for a specific day.
//
import SwiftUI
import Bedrock
/// A sheet showing habit completion details for a specific day.
struct HistoryDayDetailSheet: View {
let date: Date
let completions: [HabitCompletion]
@Environment(\.dismiss) private var dismiss
private var dateTitle: String {
let formatter = DateFormatter()
formatter.dateStyle = .full
return formatter.string(from: date)
}
private var completionRate: Double {
guard !completions.isEmpty else { return 0 }
let completed = completions.filter { $0.isCompleted }.count
return Double(completed) / Double(completions.count)
}
private var completedCount: Int {
completions.filter { $0.isCompleted }.count
}
var body: some View {
NavigationStack {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading, spacing: Design.Spacing.large) {
// Summary header
summaryHeader
// Habit list grouped by ritual
if completions.isEmpty {
emptyState
} else {
habitList
}
Spacer(minLength: Design.Spacing.xxxLarge)
}
.padding(Design.Spacing.large)
}
.scrollContentBackground(.hidden)
.background(AppSurface.primary)
.navigationTitle(shortDateTitle)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button(String(localized: "Done")) {
dismiss()
}
.foregroundStyle(AppAccent.primary)
}
}
}
.presentationBackground(AppSurface.primary)
.presentationDetents([.medium, .large])
.presentationDragIndicator(.visible)
}
private var emptyState: some View {
VStack(spacing: Design.Spacing.medium) {
Image(systemName: "calendar.badge.clock")
.font(.largeTitle)
.foregroundStyle(AppTextColors.tertiary)
Text(String(localized: "No habits tracked"))
.font(.headline)
.foregroundStyle(AppTextColors.secondary)
Text(String(localized: "This day has no habit data recorded."))
.font(.subheadline)
.foregroundStyle(AppTextColors.tertiary)
.multilineTextAlignment(.center)
}
.frame(maxWidth: .infinity)
.padding(.vertical, Design.Spacing.xxxLarge)
}
private var shortDateTitle: String {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter.string(from: date)
}
private var summaryHeader: some View {
VStack(spacing: Design.Spacing.medium) {
ProgressRing(progress: completionRate, size: 80, lineWidth: 6)
Text("\(completedCount) of \(completions.count)")
.font(.title2)
.bold()
.foregroundStyle(AppTextColors.primary)
Text(String(localized: "habits completed"))
.font(.subheadline)
.foregroundStyle(AppTextColors.secondary)
}
.frame(maxWidth: .infinity)
.padding(.vertical, Design.Spacing.large)
}
private var habitList: some View {
let groupedByRitual = Dictionary(grouping: completions) { $0.ritualTitle }
let sortedRituals = groupedByRitual.keys.sorted()
return VStack(alignment: .leading, spacing: Design.Spacing.large) {
ForEach(sortedRituals, id: \.self) { ritualTitle in
if let habits = groupedByRitual[ritualTitle] {
ritualSection(title: ritualTitle, habits: habits)
}
}
}
}
private func ritualSection(title: String, habits: [HabitCompletion]) -> some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) {
Text(title)
.font(.headline)
.foregroundStyle(AppTextColors.primary)
VStack(spacing: Design.Spacing.xSmall) {
ForEach(habits) { completion in
habitRow(completion)
}
}
}
.padding(Design.Spacing.large)
.background(AppSurface.card)
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
}
private func habitRow(_ completion: HabitCompletion) -> some View {
HStack(spacing: Design.Spacing.medium) {
Image(systemName: completion.habit.symbolName)
.foregroundStyle(completion.isCompleted ? AppStatus.success : AppTextColors.tertiary)
.frame(width: AppMetrics.Size.iconMedium)
Text(completion.habit.title)
.font(.subheadline)
.foregroundStyle(AppTextColors.primary)
Spacer()
Image(systemName: completion.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundStyle(completion.isCompleted ? AppStatus.success : AppTextColors.tertiary)
}
.padding(.vertical, Design.Spacing.small)
}
}
#Preview {
HistoryDayDetailSheet(
date: Date(),
completions: []
)
}