From ca7c09f982eb687ada36e5092259e6e3673d5ac4 Mon Sep 17 00:00:00 2001 From: NguyenND Date: Tue, 25 Aug 2026 12:30:06 -0400 Subject: [PATCH] Aug 25 - Implement new function allow Director (internal & customer) to assign and inspection to another inspector --- JanitorialQC/API/APIModels.swift | 9 +++++++++ JanitorialQC/Models/LocalFollowUpRequest.swift | 11 +++++++++++ .../Views/Dashboard/FollowUpRequestsView.swift | 16 ++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/JanitorialQC/API/APIModels.swift b/JanitorialQC/API/APIModels.swift index c86dcec..10d5419 100644 --- a/JanitorialQC/API/APIModels.swift +++ b/JanitorialQC/API/APIModels.swift @@ -302,6 +302,12 @@ struct APIInspectionSummary: Decodable, Identifiable, Hashable, Sendable { let followUpRequired: Bool let followUpNote: String? let parentInspectionId: Int? + /// phase53 — who was asked to perform the follow-up. nil means it belongs + /// to the inspection's own inspector, which is what it always meant. + /// The list endpoint already returns only follow-ups this user OWNS, so + /// these are for display, not filtering. + let followUpAssignedTo: Int? + let followUpAssignedToName: String? /// Form field values as [fieldId: stringValue] for the grid renderer. var formValues: [String: String] { @@ -368,6 +374,8 @@ struct APIInspectionSummary: Decodable, Identifiable, Hashable, Sendable { followUpRequired = (try? c.decode(Bool.self, forKey: .followUpRequired)) ?? false followUpNote = try? c.decode(String.self, forKey: .followUpNote) parentInspectionId = try? c.decode(Int.self, forKey: .parentInspectionId) + followUpAssignedTo = try? c.decode(Int.self, forKey: .followUpAssignedTo) + followUpAssignedToName = try? c.decode(String.self, forKey: .followUpAssignedToName) } private enum CodingKeys: String, CodingKey { case id, templateId, templateName, facilityId, facilityName @@ -376,6 +384,7 @@ struct APIInspectionSummary: Decodable, Identifiable, Hashable, Sendable { case formData, formSchema case formMedia case followUpRequired, followUpNote, parentInspectionId + case followUpAssignedTo, followUpAssignedToName } // Explicit Hashable — formDataRaw/formSchemaRaw contain JSONValue which diff --git a/JanitorialQC/Models/LocalFollowUpRequest.swift b/JanitorialQC/Models/LocalFollowUpRequest.swift index c31c9e9..fd01f48 100644 --- a/JanitorialQC/Models/LocalFollowUpRequest.swift +++ b/JanitorialQC/Models/LocalFollowUpRequest.swift @@ -49,6 +49,15 @@ final class LocalFollowUpRequest { /// Stored raw; read through `note` for the normalised form. var followUpNote: String? + /// Display name of the inspector this follow-up was handed to, when it was + /// assigned to somebody other than whoever performed the original + /// inspection (phase53). nil = it belongs to the original inspector. + /// + /// Purely for display — the server only ever returns follow-ups this user + /// owns, so nothing here decides what is shown. Optional so existing + /// SwiftData stores migrate lightweight (rule 8). + var assignedToName: String? + /// Set on device the moment a re-inspection of this request is submitted, so /// the FOLLOW-UP REQUESTED lists hide the row immediately — online or /// offline — without waiting for the round trip. @@ -128,6 +137,7 @@ final class LocalFollowUpRequest { self.overallScore = api.overallScore self.inspectionDateString = api.inspectionDate ?? "" self.followUpNote = api.followUpNote + self.assignedToName = api.followUpAssignedToName self.fulfilledLocally = false self.parentFormDataJSON = Self.encode(api) self.updatedAt = Date() @@ -170,6 +180,7 @@ final class LocalFollowUpRequest { self.overallScore = api.overallScore self.inspectionDateString = api.inspectionDate ?? "" self.followUpNote = api.followUpNote + self.assignedToName = api.followUpAssignedToName self.parentFormDataJSON = Self.encode(api) // The server is authoritative. Being returned by the pull at all means // the follow-up is still outstanding, so any local "just did it" flag is diff --git a/JanitorialQC/Views/Dashboard/FollowUpRequestsView.swift b/JanitorialQC/Views/Dashboard/FollowUpRequestsView.swift index bc47ef7..e3eb4de 100644 --- a/JanitorialQC/Views/Dashboard/FollowUpRequestsView.swift +++ b/JanitorialQC/Views/Dashboard/FollowUpRequestsView.swift @@ -111,6 +111,22 @@ struct FollowUpRow: View { } } + // Assigned to somebody other than whoever ran the original + // inspection (phase53). The endpoint only returns follow-ups + // this user owns, so seeing this badge means "you were given + // this one" — worth calling out, because it is NOT the usual + // case of re-inspecting your own work. + if request.assignedToName != nil { + HStack(spacing: 4) { + Image(systemName: "person.crop.circle.badge.checkmark") + .font(.caption2) + Text("Assigned to you") + .font(.caption2.weight(.semibold)) + } + .foregroundStyle(.orange) + .padding(.top, 1) + } + // Note 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. Mirrors the