import Charts import SwiftUI struct CostUsageHistoryMenuView: View { let summary: GatewayCostUsageSummary let width: CGFloat var body: some View { VStack(alignment: .leading, spacing: 10) { self.header self.chart self.footer } .padding(.horizontal, 12) .padding(.vertical, 10) .frame(width: max(1, self.width), alignment: .leading) } private var header: some View { let todayKey = CostUsageMenuDateParser.format(Date()) let todayEntry = self.summary.daily.first { $0.date == todayKey } let todayCost = CostUsageFormatting.formatUsd(todayEntry?.totalCost) ?? "n/a" let totalCost = CostUsageFormatting.formatUsd(self.summary.totals.totalCost) ?? "n/a" return HStack(alignment: .firstTextBaseline, spacing: 12) { VStack(alignment: .leading, spacing: 2) { Text("Today") .font(.caption2) .foregroundStyle(.secondary) Text(todayCost) .font(.system(size: 14, weight: .semibold)) } VStack(alignment: .leading, spacing: 2) { Text("Last \(self.summary.days)d") .font(.caption2) .foregroundStyle(.secondary) Text(totalCost) .font(.system(size: 14, weight: .semibold)) } Spacer() } } private var chart: some View { let entries = self.summary.daily.compactMap { entry -> (Date, Double)? in guard let date = CostUsageMenuDateParser.parse(entry.date) else { return nil } return (date, entry.totalCost) } return Chart(entries, id: \.0) { entry in BarMark( x: .value("Day", entry.0), y: .value("Cost", entry.1)) .foregroundStyle(Color.accentColor) .cornerRadius(3) } .chartXAxis { AxisMarks(values: .stride(by: .day, count: 7)) { AxisGridLine().foregroundStyle(.clear) AxisValueLabel(format: .dateTime.month().day()) } } .chartYAxis { AxisMarks(position: .leading) { AxisGridLine() AxisValueLabel() } } .frame(height: 110) } private var footer: some View { if self.summary.totals.missingCostEntries == 0 { return AnyView(EmptyView()) } return AnyView( Text("Partial: \(self.summary.totals.missingCostEntries) entries missing cost") .font(.caption2) .foregroundStyle(.secondary)) } } private enum CostUsageMenuDateParser { static let formatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" formatter.locale = Locale(identifier: "en_US_POSIX") formatter.timeZone = TimeZone.current return formatter }() static func parse(_ value: String) -> Date? { self.formatter.date(from: value) } static func format(_ date: Date) -> String { self.formatter.string(from: date) } }