codeburn/mac/Sources/CodeBurnMenubar/Views/PullRequestsSection.swift
iamtoruk 411d52f924 feat(app,menubar): surface parity — Spend punchcard in the app, Pull requests strip in the menubar
The release's new capabilities were unevenly surfaced: the Spend
punchcard existed only on the web dashboard, and the menubar decoded
nothing of the PR-attribution block the payload already carries.

- Desktop app: new Punchcard component (hour-of-day x weekday spend
  matrix, ported from the dash and restyled to the app's tokens) on the
  Spend page. Fed by a dedicated getTimeline bridge channel that fetches
  the payload WITH history.timeline; every other fetch keeps the lean
  --no-timeline path, and the serve child makes the extra fetch cheap.
  Hides gracefully when the payload has no timeline (older CLI).
- Menubar: PullRequestsSection renders the top three PRs by attributed
  spend under the Workflow strip; MenubarPayload now decodes the
  pullRequests block (decodeIfPresent, so older payloads are unchanged).
  Hidden when absent or empty.

Deliberately NOT ported: codex Tok/s (reads rollout files per session -
too heavy for payload cadence; stays a TUI/report analytics view) and
the punchcard in the menubar (a 7x24 matrix has no legible place in a
compact popover).

App suite 468 green (bridge channel pinned, mocks extended), swift
build + 156 tests green, CLI suite green.
2026-08-10 14:29:26 -07:00

36 lines
1.5 KiB
Swift

import SwiftUI
/// Compact top-pull-requests strip: the menubar's slice of the PR attribution
/// the desktop app shows in full. Renders the top three PRs by attributed
/// spend for the selected period; hides entirely when the payload carries no
/// PR block (older CLI, provider-scoped payload, or nothing attributed).
struct PullRequestsSection: View {
@Environment(AppStore.self) private var store
var body: some View {
let rows = Array((store.payload.current.pullRequests?.rows ?? []).prefix(3))
if !rows.isEmpty {
VStack(spacing: 0) {
Divider().opacity(0.5)
VStack(alignment: .leading, spacing: 6) {
SectionCaption(text: "Pull requests")
ForEach(rows, id: \.url) { row in
HStack(spacing: 8) {
Text(row.label)
.font(.system(size: 11.5, design: .monospaced))
.lineLimit(1)
.truncationMode(.middle)
Spacer(minLength: 8)
Text(row.cost.asCurrency())
.font(.system(size: 11.5, weight: .medium))
.monospacedDigit()
}
.frame(maxWidth: .infinity)
}
}
.padding(.horizontal, 14)
.padding(.vertical, 11)
}
}
}
}