Files
JQC_iOS_App/JanitorialQC/Views/Dashboard/ScheduledInspectionsView.swift
T

114 lines
4.3 KiB
Swift

// 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
)
}
}
}
}