Jul 14 - Using CDN - update
This commit is contained in:
@@ -26,6 +26,11 @@ final class LocalIssue {
|
||||
/// JSON-encoded array of resolution photo server paths (issue_result_photos bucket).
|
||||
/// Mirrors Issue.result_photos on the server — shown under "Resolution Details".
|
||||
var resultPhotoServerPathsJSON: String = "[]"
|
||||
/// JSON-encoded absolute display URLs (presigned R2 / absolute static),
|
||||
/// parallel to photoServerPaths / resultPhotoServerPaths (same order).
|
||||
/// Empty ("[]") when talking to an older server that omits the *_url fields.
|
||||
var photoServerUrlsJSON: String = "[]"
|
||||
var resultPhotoServerUrlsJSON: String = "[]"
|
||||
|
||||
// Shared coders — JSONDecoder/Encoder init is expensive (parses locale and
|
||||
// calendar info). Allocating them inside computed property getters means
|
||||
@@ -115,6 +120,22 @@ final class LocalIssue {
|
||||
}
|
||||
}
|
||||
|
||||
/// Absolute display URLs parallel to photoServerPaths (same order).
|
||||
var photoServerUrls: [String] {
|
||||
get { (try? Self.jsonDecoder.decode([String].self,
|
||||
from: Data(photoServerUrlsJSON.utf8))) ?? [] }
|
||||
set { photoServerUrlsJSON = (try? String(data: Self.jsonEncoder.encode(newValue),
|
||||
encoding: .utf8)) ?? "[]" }
|
||||
}
|
||||
|
||||
/// Absolute display URLs parallel to resultPhotoServerPaths (same order).
|
||||
var resultPhotoServerUrls: [String] {
|
||||
get { (try? Self.jsonDecoder.decode([String].self,
|
||||
from: Data(resultPhotoServerUrlsJSON.utf8))) ?? [] }
|
||||
set { resultPhotoServerUrlsJSON = (try? String(data: Self.jsonEncoder.encode(newValue),
|
||||
encoding: .utf8)) ?? "[]" }
|
||||
}
|
||||
|
||||
var createdAt: Date
|
||||
var syncStatus: String // "pending" | "synced" | "failed"
|
||||
var syncRetryCount: Int
|
||||
@@ -193,6 +214,8 @@ final class LocalIssue {
|
||||
self.photoLocalPathsJSON = "[]"
|
||||
self.photoServerPathsJSON = "[]"
|
||||
self.resultPhotoServerPathsJSON = "[]"
|
||||
self.photoServerUrlsJSON = "[]"
|
||||
self.resultPhotoServerUrlsJSON = "[]"
|
||||
self.createdAt = Date()
|
||||
self.syncStatus = "pending"
|
||||
self.syncRetryCount = 0
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
// facility + template preselected; the schedule lifecycle (fulfil / roll-forward)
|
||||
// stays server-driven.
|
||||
//
|
||||
// All non-optional stored properties carry explicit inline defaults so SwiftData
|
||||
// lightweight migration can add the new table without a migration plan.
|
||||
// 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
|
||||
@@ -19,29 +20,66 @@ import SwiftData
|
||||
final class LocalScheduledInspection {
|
||||
|
||||
/// Server ID of the ScheduledInspection row — stable unique identity.
|
||||
@Attribute(.unique) var serverId: Int = 0
|
||||
@Attribute(.unique) var serverId: Int
|
||||
|
||||
var facilityServerId: Int = 0
|
||||
var facilityName: String = ""
|
||||
var templateServerId: Int = 0
|
||||
var templateName: String = ""
|
||||
var inspectorId: Int? = nil
|
||||
var facilityServerId: Int
|
||||
var facilityName: String
|
||||
var templateServerId: Int
|
||||
var templateName: String
|
||||
var inspectorId: Int?
|
||||
|
||||
var frequency: String = "once" // once | daily | weekly | monthly
|
||||
var frequencyLabel: String = "" // human-readable label from server
|
||||
var frequency: String // once | daily | weekly | monthly
|
||||
var frequencyLabel: String
|
||||
|
||||
/// Raw ISO date string "YYYY-MM-DD" from the server (display fallback).
|
||||
var dueDateString: String = ""
|
||||
/// Parsed due date — used for @Query sorting. Nil if the string was absent.
|
||||
var nextDue: Date? = nil
|
||||
/// 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 = false
|
||||
var notes: String? = nil
|
||||
var isOverdue: Bool
|
||||
var notes: String?
|
||||
|
||||
/// Last time this row was refreshed from the server pull.
|
||||
var updatedAt: Date = Date()
|
||||
var updatedAt: Date
|
||||
|
||||
init(serverId: Int) {
|
||||
self.serverId = serverId
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user