Files
JQC_iOS_App/JanitorialQC/Models/LocalScheduledInspection.swift
T

104 lines
4.0 KiB
Swift

// Models/LocalScheduledInspection.swift
// --------------------------------------
// SwiftData model for planned/recurring inspection assignments (phase36).
//
// Read-only reference data pulled from the server (GET /api/v1/scheduled-inspections)
// and refreshed by SyncManager.pullScheduledInspections() — never created or
// mutated on device. Surfaced in the "Scheduled" section on the Dashboard and
// My Inspections. Tapping "Start" opens the normal new-inspection flow with the
// facility + template preselected; the schedule lifecycle (fulfil / roll-forward)
// stays server-driven.
//
// Follows the same pattern as LocalFacility / LocalArea: a `.unique` serverId
// WITHOUT an inline default (a default on the unique key breaks @Model's
// PersistentModel conformance) and a full init(from:)/update(from:) pair.
import Foundation
import SwiftData
@Model
final class LocalScheduledInspection {
/// Server ID of the ScheduledInspection row — stable unique identity.
@Attribute(.unique) var serverId: Int
var facilityServerId: Int
var facilityName: String
var templateServerId: Int
var templateName: String
var inspectorId: Int?
var frequency: String // once | daily | weekly | monthly
var frequencyLabel: String
/// Raw server date string "YYYY-MM-DD" — sortable (ISO strings sort
/// chronologically) and the source for the parsed `nextDue`.
var dueDateString: String
var isOverdue: Bool
var notes: String?
/// Manager-authored instructions for this occurrence, normalised.
///
/// The wire field, the server model attribute and the DB column are all
/// still `notes` — only the user-facing wording changed to "Instructions"
/// (web form label + both iPad surfaces). Renaming the stored property would
/// break `init(from:)`/`update(from:)` against `APIScheduledInspection.notes`
/// for no benefit, so the rename lives here.
///
/// Returns nil for nil, empty, or whitespace-only text so callers can guard
/// with a single `if let` instead of repeating the emptiness check.
/// Computed, not stored — no SwiftData schema change.
var instructions: String? {
guard let t = notes?.trimmingCharacters(in: .whitespacesAndNewlines),
!t.isEmpty
else { return nil }
return t
}
/// Last time this row was refreshed from the server pull.
var updatedAt: Date
/// Parsed due date for display. Computed properties are not persisted by
/// SwiftData; sort on `dueDateString` (not this) in @Query.
var nextDue: Date? {
Self.dateOnlyFormatter.date(from: dueDateString)
}
private static let dateOnlyFormatter: DateFormatter = {
let f = DateFormatter()
f.locale = Locale(identifier: "en_US_POSIX")
f.dateFormat = "yyyy-MM-dd"
return f
}()
init(from api: APIScheduledInspection) {
self.serverId = api.id
self.facilityServerId = api.facilityId
self.facilityName = api.facilityName ?? ""
self.templateServerId = api.templateId
self.templateName = api.templateName ?? ""
self.inspectorId = api.inspectorId
self.frequency = api.frequency
self.frequencyLabel = api.frequencyLabel ?? ""
self.dueDateString = api.nextDueDate ?? ""
self.isOverdue = api.isOverdue
self.notes = api.notes
self.updatedAt = Date()
}
func update(from api: APIScheduledInspection) {
self.facilityServerId = api.facilityId
self.facilityName = api.facilityName ?? ""
self.templateServerId = api.templateId
self.templateName = api.templateName ?? ""
self.inspectorId = api.inspectorId
self.frequency = api.frequency
self.frequencyLabel = api.frequencyLabel ?? ""
self.dueDateString = api.nextDueDate ?? ""
self.isOverdue = api.isOverdue
self.notes = api.notes
self.updatedAt = Date()
}
}