import WidgetKit import SwiftUI struct HealthEntry: TimelineEntry { let date: Date let steps: Int let sleep: Double let heartRate: Int let readinessScore: Int } struct HealthProvider: TimelineProvider { func placeholder(in context: Context) -> HealthEntry { HealthEntry(date: Date(), steps: 6234, sleep: 7.5, heartRate: 68, readinessScore: 80) } func getSnapshot(in context: Context, completion: @escaping (HealthEntry) -> Void) { completion(currentEntry()) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { let entry = currentEntry() let next = Calendar.current.date(byAdding: .minute, value: 15, to: Date()) ?? Date() completion(Timeline(entries: [entry], policy: .after(next))) } private func currentEntry() -> HealthEntry { HealthEntry( date: Date(), steps: WidgetData.steps, sleep: WidgetData.sleep, heartRate: WidgetData.heartRate, readinessScore: WidgetData.readinessScore ) } } struct HealthSummaryWidget: Widget { let kind = "HealthSummary" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: HealthProvider()) { entry in HealthWidgetView(entry: entry) .containerBackground(.fill.tertiary, for: .widget) } .configurationDisplayName("Здоровье") .description("Шаги, сон, пульс и готовность") .supportedFamilies([.systemSmall, .systemMedium]) } } struct HealthWidgetView: View { let entry: HealthEntry @Environment(\.widgetFamily) var family var readinessColor: Color { if entry.readinessScore >= 80 { return Color(hex: "0D9488") } if entry.readinessScore >= 60 { return Color(hex: "ffa502") } return Color(hex: "ff4757") } var body: some View { Group { if family == .systemMedium { mediumView } else { smallView } } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(hex: "06060f")) } var smallView: some View { VStack(spacing: 8) { ZStack { Circle().stroke(Color.white.opacity(0.1), lineWidth: 6).frame(width: 50, height: 50) Circle().trim(from: 0, to: CGFloat(entry.readinessScore) / 100) .stroke(readinessColor, style: StrokeStyle(lineWidth: 6, lineCap: .round)) .frame(width: 50, height: 50).rotationEffect(.degrees(-90)) Text("\(entry.readinessScore)").font(.system(size: 16, weight: .bold, design: .rounded)).foregroundColor(readinessColor) } VStack(spacing: 4) { HStack(spacing: 4) { Image(systemName: "figure.walk").font(.system(size: 9)).foregroundColor(Color(hex: "ffa502")) Text("\(entry.steps)").font(.caption2.bold()).foregroundColor(.white) } HStack(spacing: 4) { Image(systemName: "moon.fill").font(.system(size: 9)).foregroundColor(Color(hex: "7c3aed")) Text(String(format: "%.1fч", entry.sleep)).font(.caption2.bold()).foregroundColor(.white) } } } } var mediumView: some View { HStack(spacing: 16) { ZStack { Circle().stroke(Color.white.opacity(0.1), lineWidth: 8).frame(width: 68, height: 68) Circle().trim(from: 0, to: CGFloat(entry.readinessScore) / 100) .stroke(readinessColor, style: StrokeStyle(lineWidth: 8, lineCap: .round)) .frame(width: 68, height: 68).rotationEffect(.degrees(-90)) VStack(spacing: 0) { Text("\(entry.readinessScore)").font(.system(size: 20, weight: .bold, design: .rounded)).foregroundColor(readinessColor) Text("балл").font(.system(size: 9)).foregroundColor(.gray) } } VStack(alignment: .leading, spacing: 6) { Text("Здоровье").font(.subheadline.bold()).foregroundColor(.white) HStack(spacing: 14) { VStack(spacing: 3) { Image(systemName: "figure.walk").font(.caption).foregroundColor(Color(hex: "ffa502")) Text("\(entry.steps)").font(.caption2.bold()).foregroundColor(.white) } VStack(spacing: 3) { Image(systemName: "moon.fill").font(.caption).foregroundColor(Color(hex: "7c3aed")) Text(String(format: "%.1fч", entry.sleep)).font(.caption2.bold()).foregroundColor(.white) } VStack(spacing: 3) { Image(systemName: "heart.fill").font(.caption).foregroundColor(Color(hex: "ff4757")) Text("\(entry.heartRate)").font(.caption2.bold()).foregroundColor(.white) } } } }.padding(16) } }