Jul 13 - Update codes to catch up with the web app updates: scheduled inspection and issue's handler
This commit is contained in:
@@ -60,6 +60,14 @@ class SyncManager: ObservableObject {
|
||||
return f
|
||||
}()
|
||||
|
||||
/// Parses server date-only strings ("YYYY-MM-DD"), e.g. scheduled due dates.
|
||||
nonisolated static let dateOnlyFormatter: DateFormatter = {
|
||||
let f = DateFormatter()
|
||||
f.locale = Locale(identifier: "en_US_POSIX")
|
||||
f.dateFormat = "yyyy-MM-dd"
|
||||
return f
|
||||
}()
|
||||
|
||||
static let shared = SyncManager()
|
||||
private init() {}
|
||||
|
||||
@@ -237,6 +245,7 @@ class SyncManager: ObservableObject {
|
||||
await processIssueQueue(context: context)
|
||||
await pullReferenceData()
|
||||
await pullAssignedIssues(context: context)
|
||||
await pullScheduledInspections(context: context)
|
||||
|
||||
// Poll notifications immediately on every sync rather than waiting
|
||||
// for the 60-second timer — ensures the inspector sees assignments
|
||||
@@ -730,6 +739,15 @@ class SyncManager: ObservableObject {
|
||||
existing.reportedByName = api.reportedByName
|
||||
existing.areaNameCache = api.areaName
|
||||
existing.assignedToName = api.assignedToName
|
||||
// Handler ("Handled By", phase35)
|
||||
existing.handlerType = api.handlerType
|
||||
existing.handlerLabel = api.handlerLabel
|
||||
existing.facilityHandlerName = api.facilityHandlerName
|
||||
existing.facilityHandlerContact = api.facilityHandlerContact
|
||||
existing.facilityHandlerNotes = api.facilityHandlerNotes
|
||||
existing.vendorName = api.vendorName
|
||||
existing.vendorContact = api.vendorContact
|
||||
existing.vendorNotes = api.vendorNotes
|
||||
if let vts = api.verifiedAt,
|
||||
let date = Self.isoFormatter.date(from: vts) {
|
||||
existing.verifiedAt = date
|
||||
@@ -762,6 +780,15 @@ class SyncManager: ObservableObject {
|
||||
local.reportedByName = api.reportedByName
|
||||
local.areaNameCache = api.areaName
|
||||
local.assignedToName = api.assignedToName
|
||||
// Handler ("Handled By", phase35)
|
||||
local.handlerType = api.handlerType
|
||||
local.handlerLabel = api.handlerLabel
|
||||
local.facilityHandlerName = api.facilityHandlerName
|
||||
local.facilityHandlerContact = api.facilityHandlerContact
|
||||
local.facilityHandlerNotes = api.facilityHandlerNotes
|
||||
local.vendorName = api.vendorName
|
||||
local.vendorContact = api.vendorContact
|
||||
local.vendorNotes = api.vendorNotes
|
||||
if let vts = api.verifiedAt,
|
||||
let date = Self.isoFormatter.date(from: vts) {
|
||||
local.verifiedAt = date
|
||||
@@ -808,6 +835,57 @@ class SyncManager: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Scheduled Inspections (phase36) ───────────────────────────────────
|
||||
// Read-only pull of planned/recurring assignments for the Dashboard and
|
||||
// My Inspections "Scheduled" section. Upsert by serverId, then delete rows
|
||||
// the server no longer returns (schedule fulfilled, deactivated, or
|
||||
// reassigned to another inspector). Best-effort — never blocks the pipeline.
|
||||
|
||||
func pullScheduledInspections(context: ModelContext) async {
|
||||
guard isOnline, AuthManager.shared.isAuthenticated else { return }
|
||||
do {
|
||||
let apiRows = try await APIClient.shared.fetchScheduledInspections()
|
||||
|
||||
// Fetch-all + filter/map in Swift — no #Predicate (CLAUDE.md rule 3).
|
||||
let allLocal = (try? context.fetch(FetchDescriptor<LocalScheduledInspection>())) ?? []
|
||||
var byServerId: [Int: LocalScheduledInspection] = [:]
|
||||
for row in allLocal { byServerId[row.serverId] = row }
|
||||
|
||||
for api in apiRows {
|
||||
let row = byServerId[api.id] ?? {
|
||||
let r = LocalScheduledInspection(serverId: api.id)
|
||||
context.insert(r)
|
||||
return r
|
||||
}()
|
||||
row.facilityServerId = api.facilityId
|
||||
row.facilityName = api.facilityName ?? ""
|
||||
row.templateServerId = api.templateId
|
||||
row.templateName = api.templateName ?? ""
|
||||
row.inspectorId = api.inspectorId
|
||||
row.frequency = api.frequency
|
||||
row.frequencyLabel = api.frequencyLabel ?? ""
|
||||
row.dueDateString = api.nextDueDate ?? ""
|
||||
row.nextDue = api.nextDueDate.flatMap { Self.dateOnlyFormatter.date(from: $0) }
|
||||
row.isOverdue = api.isOverdue
|
||||
row.notes = api.notes
|
||||
row.updatedAt = Date()
|
||||
}
|
||||
|
||||
// Delete rows the server no longer returns.
|
||||
let returnedIds = Set(apiRows.map { $0.id })
|
||||
for row in allLocal where !returnedIds.contains(row.serverId) {
|
||||
context.delete(row)
|
||||
}
|
||||
|
||||
try? context.save()
|
||||
|
||||
} catch APIError.notAuthenticated {
|
||||
// Let AuthManager handle session expiry
|
||||
} catch {
|
||||
// Non-fatal — stale scheduled rows stay visible until next pull
|
||||
}
|
||||
}
|
||||
|
||||
// ── Dashboard Stats ───────────────────────────────────────────────────
|
||||
// Best-effort fetch — a network failure silently leaves dashboardStats nil
|
||||
// so the UI falls back to a placeholder card. Never blocks the sync pipeline.
|
||||
|
||||
Reference in New Issue
Block a user