166 lines
5.6 KiB
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: []
|
|
)
|
|
}
|