// Left rail — tactical scan scope and sector list.
// Exports: LeftRailView.
import SwiftUI
struct LeftRailView: View {
@Environment(\.theme) private var theme
let sectors: [Sector]
@Binding var selectedSectorID: String?
var body: some View {
ScrollView(.vertical, showsIndicators: true) {
VStack(alignment: .leading, spacing: theme.spacing.lg) {
ThemedPanel {
ContactScope(missions: sectors.flatMap(\.missions))
.padding(theme.spacing.md)
}
MonoLabel(text: "sectors")
ForEach(sectors) { sector in
sectorRow(sector)
}
}
.padding(theme.spacing.md)
.frame(maxWidth: .infinity, alignment: .leading)
}
.frame(minHeight: 0, maxHeight: .infinity)
.background(theme.bgDeep)
}
private func countMark(count: Int, state: MissionDisplayState) -> some View {
HStack(spacing: 2) {
Text("\(count)")
.font(theme.font(.caption))
.foregroundStyle(missionColor(state))
StateMark(state: state, size: 10)
}
}
private func sectorRow(_ sector: Sector) -> some View {
let selected = selectedSectorID == sector.id
let segments = sector.missions.map { missionColor($0.state) }
let done = sector.missions.filter { $0.state == .done }.count
let run = sector.missions.filter { $0.state == .run }.count
let fail = sector.missions.filter { $0.state == .fail }.count
return Button {
selectedSectorID = sector.id
} label: {
VStack(alignment: .leading, spacing: theme.spacing.xs) {
HStack {
MonoLabel(text: sector.tag, color: selected ? theme.accent : theme.ink2)
Text(sector.name)
.font(theme.font(.body))
.foregroundStyle(theme.ink)
.lineLimit(1)
}
HStack(spacing: 6) {
countMark(count: done, state: .done)
countMark(count: run, state: .run)
countMark(count: fail, state: .fail)
}
SegmentBar(segments: segments, count: max(segments.count, 1))
}
.padding(theme.spacing.sm)
.frame(maxWidth: .infinity, alignment: .leading)
.background(selected ? theme.accent.opacity(0.1) : Color.clear)
.overlay(alignment: .leading) {
if selected {
Rectangle().fill(theme.accent).frame(width: 2)
}
}
}
.buttonStyle(.plain)
}
private func missionColor(_ state: MissionDisplayState) -> Color {
switch state {
case .run: return theme.run
case .done: return theme.done
case .fail: return theme.fail
case .stop: return theme.stop
}
}
}