import Foundation import SwiftUI // MARK: - Date Extensions extension Date { var startOfDay: Date { Calendar.current.startOfDay(for: self) } var endOfDay: Date { Calendar.current.date(byAdding: .day, value: 1, to: startOfDay)!.addingTimeInterval(-1) } var isToday: Bool { Calendar.current.isDateInToday(self) } var isYesterday: Bool { Calendar.current.isDateInYesterday(self) } func formatted(style: DateFormatter.Style) -> String { let formatter = DateFormatter() formatter.dateStyle = style formatter.timeStyle = .none return formatter.string(from: self) } func formattedTime() -> String { let formatter = DateFormatter() formatter.dateStyle = .none formatter.timeStyle = .short return formatter.string(from: self) } func formattedRelative() -> String { let formatter = RelativeDateTimeFormatter() formatter.unitsStyle = .abbreviated return formatter.localizedString(for: self, relativeTo: Date()) } func adding(days: Int) -> Date { Calendar.current.date(byAdding: .day, value: days, to: self)! } func adding(hours: Int) -> Date { Calendar.current.date(byAdding: .hour, value: hours, to: self)! } func adding(minutes: Int) -> Date { Calendar.current.date(byAdding: .minute, value: minutes, to: self)! } } // MARK: - Double Extensions extension Double { func formatted(decimals: Int = 1) -> String { String(format: "%.\(decimals)f", self) } var formattedAsInteger: String { String(format: "%.0f", self) } var formattedAsPercentage: String { String(format: "%.1f%%", self * 100) } } // MARK: - Array Extensions extension Array { func chunked(into size: Int) -> [[Element]] { stride(from: 0, to: count, by: size).map { Array(self[$0.. Color { switch severity { case .minor: return .green case .moderate: return .yellow case .significant: return .orange case .major: return .red } } static func forQuality(_ quality: DataQuality) -> Color { switch quality { case .complete: return .green case .partial: return .yellow case .missing: return .gray case .invalid: return .red } } } // MARK: - View Extensions extension View { func cardStyle() -> some View { self .padding() .background(Color(.systemBackground)) .clipShape(RoundedRectangle(cornerRadius: 12)) .shadow(color: .black.opacity(0.05), radius: 4, y: 2) } func sectionHeader(_ title: String) -> some View { VStack(alignment: .leading, spacing: 8) { Text(title) .font(.headline) .foregroundStyle(.primary) self } } } // MARK: - Binding Extensions extension Binding { func onChange(_ handler: @escaping (Value) -> Void) -> Binding { Binding( get: { self.wrappedValue }, set: { newValue in self.wrappedValue = newValue handler(newValue) } ) } } // MARK: - Optional Extensions extension Optional where Wrapped == String { var orEmpty: String { self ?? "" } var isNilOrEmpty: Bool { self?.isEmpty ?? true } } // MARK: - Collection Extensions extension Collection { var isNotEmpty: Bool { !isEmpty } }