// Views/Dashboard/ScheduledInspectionsView.swift // ---------------------------------------------- // Displays the inspector's planned/recurring inspection assignments (phase36), // pulled read-only from GET /api/v1/scheduled-inspections by // SyncManager.pullScheduledInspections(). // // Two consumers share one ScheduledRow: // • ScheduledInspectionsCard — VStack card for the Dashboard ScrollView // • MyInspectionsView renders its own "Scheduled" List section inline, // reusing ScheduledRow, with the start cover attached to the List. // Both self-hide when there are no scheduled inspections and present // StartInspectionView (facility + template preselected) when a row is tapped. // The schedule lifecycle (fulfil / roll-forward) stays server-driven; tapping // "Start" simply seeds the normal new-inspection flow. import SwiftUI import SwiftData // MARK: - Shared row struct ScheduledRow: View { let schedule: LocalScheduledInspection private var dueText: String { if let d = schedule.nextDue { return d.formatted(date: .abbreviated, time: .omitted) } return schedule.dueDateString.isEmpty ? "—" : schedule.dueDateString } var body: some View { HStack(alignment: .top, spacing: 12) { Image(systemName: "calendar.badge.clock") .font(.title3) .foregroundStyle(schedule.isOverdue ? .red : .blue) .padding(.top, 2) VStack(alignment: .leading, spacing: 3) { Text(schedule.templateName.isEmpty ? "Inspection" : schedule.templateName) .font(.callout.bold()) Text(schedule.facilityName.isEmpty ? "Facility" : schedule.facilityName) .font(.caption) .foregroundStyle(.secondary) HStack(spacing: 8) { if schedule.isOverdue { Text("Overdue") .font(.caption2.bold()) .padding(.horizontal, 6).padding(.vertical, 2) .background(Color.red.opacity(0.15)) .foregroundStyle(.red) .clipShape(Capsule()) } Text("Due \(dueText)") .font(.caption2) .foregroundStyle(schedule.isOverdue ? .red : .secondary) if !schedule.frequencyLabel.isEmpty { Text("· \(schedule.frequencyLabel)") .font(.caption2) .foregroundStyle(.tertiary) } } } Spacer(minLength: 8) Label("Start", systemImage: "play.fill") .font(.caption.bold()) .foregroundStyle(.white) .padding(.horizontal, 10).padding(.vertical, 5) .background(schedule.isOverdue ? Color.red : Color.blue) .clipShape(Capsule()) } .contentShape(Rectangle()) } } // MARK: - Dashboard card (VStack) struct ScheduledInspectionsCard: View { @Query(sort: \LocalScheduledInspection.dueDateString, order: .forward) private var scheduled: [LocalScheduledInspection] @State private var startTarget: LocalScheduledInspection? = nil var body: some View { if !scheduled.isEmpty { VStack(alignment: .leading, spacing: 10) { Text("SCHEDULED") .font(.caption.bold()) .foregroundStyle(.secondary) .tracking(1) ForEach(scheduled) { s in Button { startTarget = s } label: { ScheduledRow(schedule: s) .padding(12) .background(Color(.secondarySystemBackground)) .clipShape(RoundedRectangle(cornerRadius: 12)) } .buttonStyle(.plain) } } // Cover attached to the stable VStack root (mirrors DraftResumeBanner). .fullScreenCover(item: $startTarget) { s in StartInspectionView( preFillTemplateId: s.templateServerId, preFillFacilityId: s.facilityServerId, preFillScheduleId: s.serverId ) } } } }