diff options
Diffstat (limited to 'Hutch/Views/Builds/BuildRowView.swift')
| -rw-r--r-- | Hutch/Views/Builds/BuildRowView.swift | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/Hutch/Views/Builds/BuildRowView.swift b/Hutch/Views/Builds/BuildRowView.swift new file mode 100644 index 0000000..e2afd27 --- /dev/null +++ b/Hutch/Views/Builds/BuildRowView.swift @@ -0,0 +1,106 @@ +import SwiftUI + +struct BuildRowView: View { + let job: JobSummary + + var body: some View { + HStack(spacing: 12) { + JobStatusIcon(status: job.status) + .frame(width: 28) + + VStack(alignment: .leading, spacing: 4) { + Text(job.displayLabel) + .font(.subheadline) + .lineLimit(1) + + HStack(spacing: 8) { + if let image = job.image { + Text(image) + .font(.caption) + .foregroundStyle(.secondary) + } + + Spacer() + + Text(job.created.relativeDescription) + .font(.caption) + .foregroundStyle(.tertiary) + } + + if !job.tasks.isEmpty { + TaskProgressView(tasks: job.tasks) + } + } + } + .padding(.vertical, 2) + } +} + +// MARK: - Job Status Icon + +struct JobStatusIcon: View { + let status: JobStatus + + var body: some View { + Image(systemName: iconName) + .foregroundStyle(color) + .symbolEffect(.pulse, isActive: status == .running) + } + + private var iconName: String { + switch status { + case .success: "checkmark.circle.fill" + case .failed, .timeout: "xmark.circle.fill" + case .running: "arrow.trianglehead.2.clockwise.rotate.90" + case .queued: "clock.fill" + case .pending: "circle.dashed" + case .cancelled: "minus.circle.fill" + } + } + + private var color: Color { + switch status { + case .success: .green + case .failed, .timeout: .red + case .running: .yellow + case .queued: .orange + case .pending, .cancelled: .gray + } + } +} + +// MARK: - Task Progress + +struct TaskProgressView: View { + let tasks: [JobTaskSummary] + + var body: some View { + HStack(spacing: 6) { + ProgressView(value: progress, total: 1.0) + .tint(progressColor) + .frame(maxWidth: 80) + + Text("\(completedCount)/\(tasks.count) tasks") + .font(.caption2) + .foregroundStyle(.secondary) + } + } + + private var completedCount: Int { + tasks.filter { $0.status == .success }.count + } + + private var progress: Double { + tasks.isEmpty ? 0 : Double(completedCount) / Double(tasks.count) + } + + private var progressColor: Color { + if tasks.contains(where: { $0.status == .failed }) { + return .red + } + if completedCount == tasks.count { + return .green + } + return .blue + } +} |
