Jul 14 - Using CDN - update

This commit is contained in:
Nguyen Ngo
2026-07-14 15:40:16 -04:00
parent 6fbd3d1437
commit 2a0b264a5c
8 changed files with 162 additions and 49 deletions
+23
View File
@@ -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()
}
}