// // RuleCardView.swift // Severed // import SwiftUI /// A rule tile in the home screen's Rules carousel: icon pair, live status, /// name, and the block summary. Active rules glow green. struct RuleCardView: View { let rule: BlockingRule let now: Date private var status: RuleStatus { rule.status(at: now) } var body: some View { VStack(alignment: .leading, spacing: 12) { RuleIconPair(kind: rule.kind, isActive: status.isActive) statusView Text(rule.name) .font(.system(size: 17, weight: .semibold)) .foregroundStyle(.white) .lineLimit(1) HStack(spacing: 5) { Text(rule.selectionMode.displayName) .foregroundStyle(Theme.textSecondary) Text(blockSummary) .foregroundStyle(Theme.textTertiary) } .font(.system(size: 13)) } .padding(16) .frame(width: 172, alignment: .leading) .background(cardBackground, in: RoundedRectangle(cornerRadius: 22)) .overlay( RoundedRectangle(cornerRadius: 22) .strokeBorder( status.isActive ? Theme.accent.opacity(0.35) : .white.opacity(0.08), lineWidth: 1 ) ) } @ViewBuilder private var statusView: some View { switch status { case .active: Text(status.label(relativeTo: now)) .font(.system(size: 12, weight: .semibold)) .foregroundStyle(.black) .padding(.horizontal, 10) .padding(.vertical, 5) .background(Theme.accent, in: Capsule()) .accessibilityIdentifier("ruleStatus-\(rule.name)") case .paused, .disabled, .dormant, .upcoming: Text(statusText) .font(.system(size: 13, weight: .medium)) .foregroundStyle(Theme.textSecondary) .padding(.vertical, 5) .accessibilityIdentifier("ruleStatus-\(rule.name)") } } private var statusText: String { switch rule.kind { case .schedule: status.label(relativeTo: now) case .timeLimit: rule.isEnabled ? "\(rule.dailyLimitMinutes)m / day" : "Disabled" case .openLimit: rule.isEnabled ? "\(rule.maxOpens) opens / day" : "Disabled" } } private var blockSummary: String { rule.selectionCount == 1 ? "· 1 app" : "· \(rule.selectionCount) apps" } private var cardBackground: some ShapeStyle { if status.isActive { return AnyShapeStyle( LinearGradient( colors: [Theme.activeCardTop, Theme.activeCardBottom], startPoint: .top, endPoint: .bottom ) ) } return AnyShapeStyle(Theme.surface) } }