Jul 27 - Update for scheduled task instruction
This commit is contained in:
@@ -5,17 +5,54 @@
|
||||
// SyncManager.pullScheduledInspections().
|
||||
//
|
||||
// Two consumers share one ScheduledRow:
|
||||
// • ScheduledInspectionsCard — VStack card for the Dashboard ScrollView
|
||||
// • ScheduledInspectionsCard — VStack card for the Dashboard ScrollView.
|
||||
// Presentational only; it reports taps via `onStart` and DashboardStatsView
|
||||
// owns the .fullScreenCover on its always-present ScrollView.
|
||||
// • MyInspectionsView renders its own "Scheduled" List section inline,
|
||||
// reusing ScheduledRow, with the start cover attached to the List.
|
||||
// reusing ScheduledRow, with the start cover on the enclosing Group.
|
||||
// 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.
|
||||
// Both cover owners are views that outlive the schedule rows themselves —
|
||||
// submitting the last scheduled inspection empties the @Query while the cover is
|
||||
// still up, so a cover owned by the self-hiding card would be torn down with it.
|
||||
//
|
||||
// The schedule lifecycle stays server-driven: the submission carries
|
||||
// `scheduled_inspection_id` and the server deactivates (one-time) or rolls
|
||||
// forward (recurring). ExecuteInspectionView only invalidates the local cache
|
||||
// row; pullScheduledInspections() re-reads the authoritative state.
|
||||
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
// MARK: - Start target snapshot
|
||||
|
||||
/// Plain-value snapshot of the tapped schedule, used as the `.fullScreenCover`
|
||||
/// item instead of the `LocalScheduledInspection` itself.
|
||||
///
|
||||
/// The model object is unsafe to hold across the presentation: the cached row is
|
||||
/// deleted while the cover is still on screen — by `resolveAndFulfillSchedule()`
|
||||
/// the instant Submit is tapped, and by `pullScheduledInspections()` once the
|
||||
/// server stops returning it. Reading a deleted `PersistentModel` traps, and a
|
||||
/// `@Query` that empties out would also tear the cover down mid-submit. Copying
|
||||
/// the values at tap time removes both hazards.
|
||||
struct ScheduledStartTarget: Identifiable {
|
||||
/// Schedule `serverId` — also the identity for `.fullScreenCover(item:)`.
|
||||
let id: Int
|
||||
let templateServerId: Int
|
||||
let facilityServerId: Int
|
||||
/// Manager-authored instructions for this occurrence. Server field is still
|
||||
/// `notes` (API key `notes`, column `scheduled_inspections.notes`); only the
|
||||
/// user-facing wording is "Instructions".
|
||||
let instructions: String?
|
||||
|
||||
init(_ schedule: LocalScheduledInspection) {
|
||||
self.id = schedule.serverId
|
||||
self.templateServerId = schedule.templateServerId
|
||||
self.facilityServerId = schedule.facilityServerId
|
||||
self.instructions = schedule.instructions
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Shared row
|
||||
|
||||
struct ScheduledRow: View {
|
||||
@@ -60,6 +97,24 @@ struct ScheduledRow: View {
|
||||
.foregroundStyle(.tertiary)
|
||||
}
|
||||
}
|
||||
|
||||
// Instructions preview — so the inspector can see there is
|
||||
// something to read before committing to the tap. Truncated to
|
||||
// one line; the full text is shown on the start screen and
|
||||
// again above the form itself.
|
||||
if let instructions = schedule.instructions {
|
||||
HStack(alignment: .top, spacing: 4) {
|
||||
Image(systemName: "info.circle.fill")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.blue)
|
||||
Text(instructions)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
.padding(.top, 1)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(minLength: 8)
|
||||
@@ -81,7 +136,14 @@ struct ScheduledInspectionsCard: View {
|
||||
@Query(sort: \LocalScheduledInspection.dueDateString, order: .forward)
|
||||
private var scheduled: [LocalScheduledInspection]
|
||||
|
||||
@State private var startTarget: LocalScheduledInspection? = nil
|
||||
/// Tap handler. The `.fullScreenCover` deliberately lives in the PARENT
|
||||
/// (`DashboardStatsView`, on its always-present ScrollView) rather than here:
|
||||
/// this card self-hides, and submitting the last scheduled inspection removes
|
||||
/// the final row while the cover is still on screen. A cover owned by a view
|
||||
/// that disappears is torn down with it, yanking the form away from the
|
||||
/// inspector mid-submit. Keeping the card purely presentational also keeps
|
||||
/// the empty case a true `EmptyView`, so the dashboard stack adds no spacing.
|
||||
let onStart: (ScheduledStartTarget) -> Void
|
||||
|
||||
var body: some View {
|
||||
if !scheduled.isEmpty {
|
||||
@@ -92,7 +154,7 @@ struct ScheduledInspectionsCard: View {
|
||||
.tracking(1)
|
||||
|
||||
ForEach(scheduled) { s in
|
||||
Button { startTarget = s } label: {
|
||||
Button { onStart(ScheduledStartTarget(s)) } label: {
|
||||
ScheduledRow(schedule: s)
|
||||
.padding(12)
|
||||
.background(Color(.secondarySystemBackground))
|
||||
@@ -101,14 +163,6 @@ struct ScheduledInspectionsCard: View {
|
||||
.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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user