05/21 Fix issue's photo problems

This commit is contained in:
Nguyen Ngo
2026-05-21 13:24:10 -04:00
parent 437ed913cd
commit 6aa3b74e61
8 changed files with 602 additions and 103 deletions
+20 -5
View File
@@ -210,15 +210,30 @@ actor APIClient {
"description": issue.issueDescription,
"mobile_local_id": issue.localId,
]
if let id = issue.inspection?.serverId { body["inspection_id"] = id }
// Send the first uploaded photo as photo_path (server Issue.photo_path is a single column)
if let firstPhoto = issue.photoServerPaths.first { body["photo_path"] = firstPhoto }
if let id = issue.inspection?.serverId { body["inspection_id"] = id }
// photo_path = primary photo. Additional photos are sent via a
// separate PATCH call in processIssueQueue after the issue is created,
// because the server create endpoint only stores a single photo_path.
if let first = issue.photoServerPaths.first { body["photo_path"] = first }
struct R: Decodable, Sendable { let issueId: Int; let duplicate: Bool }
let r: R = try await post("/api/v1/issues", body: body)
return r.issueId
}
// Attach additional photos to an existing issue
// Called after submitIssue when the issue has more than one photo.
// PATCHes /api/v1/issues/{id}/photos with result_photos = [server paths beyond the first].
// The create endpoint only stores photo_path (single); extras go here.
func updateIssuePhotos(issueId: Int, resultPhotos: [String]) async throws {
struct R: Decodable, Sendable { let issueId: Int; let resultPhotosCount: Int }
let _: R = try await request(
"/api/v1/issues/\(issueId)/photos",
method: "PATCH",
body: ["result_photos": resultPhotos]
)
}
// Fetch Issue Detail (status + assigned_to)
func fetchIssueDetail(issueId: Int) async throws -> APIIssueDetail {
@@ -270,7 +285,7 @@ actor APIClient {
private func refreshAccessToken() async -> Bool {
guard let token = KeychainHelper.get(Constants.Keychain.refreshToken),
let url = URL(string: Constants.baseURL + "/api/v1/auth/refresh")
let url = URL(string: ServerConfig.current + "/api/v1/auth/refresh")
else { return false }
var req = URLRequest(url: url)
@@ -299,7 +314,7 @@ actor APIClient {
// Private Helpers
private func buildURL(_ endpoint: String) throws -> URL {
guard let url = URL(string: Constants.baseURL + endpoint) else {
guard let url = URL(string: ServerConfig.current + endpoint) else {
throw APIError.invalidURL
}
return url
+16 -14
View File
@@ -484,27 +484,29 @@ struct APIAssignedIssue: Decodable, Identifiable, Sendable {
let facilityName: String?
let reportedAt: String?
let mobileLocalId: String?
let photoPath: String? // primary issue photo (relative server path)
let resultPhotos: [String] // resolution photos (relative server paths)
let photoPath: String? // primary evidence photo
let mobilePhotoPaths: [String] // extra evidence photos from iPad
let resultPhotos: [String] // resolution photos added via web
nonisolated init(from decoder: any Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
id = try c.decode(Int.self, forKey: .id)
status = try c.decode(String.self, forKey: .status)
severity = try c.decode(String.self, forKey: .severity)
description = try c.decode(String.self, forKey: .description)
assignedTo = try? c.decode(Int.self, forKey: .assignedTo)
facilityId = try? c.decode(Int.self, forKey: .facilityId)
facilityName = try? c.decode(String.self, forKey: .facilityName)
reportedAt = try? c.decode(String.self, forKey: .reportedAt)
mobileLocalId = try? c.decode(String.self, forKey: .mobileLocalId)
photoPath = try? c.decode(String.self, forKey: .photoPath)
resultPhotos = (try? c.decode([String].self, forKey: .resultPhotos)) ?? []
id = try c.decode(Int.self, forKey: .id)
status = try c.decode(String.self, forKey: .status)
severity = try c.decode(String.self, forKey: .severity)
description = try c.decode(String.self, forKey: .description)
assignedTo = try? c.decode(Int.self, forKey: .assignedTo)
facilityId = try? c.decode(Int.self, forKey: .facilityId)
facilityName = try? c.decode(String.self, forKey: .facilityName)
reportedAt = try? c.decode(String.self, forKey: .reportedAt)
mobileLocalId = try? c.decode(String.self, forKey: .mobileLocalId)
photoPath = try? c.decode(String.self, forKey: .photoPath)
mobilePhotoPaths = (try? c.decode([String].self, forKey: .mobilePhotoPaths)) ?? []
resultPhotos = (try? c.decode([String].self, forKey: .resultPhotos)) ?? []
}
private enum CodingKeys: String, CodingKey {
case id, status, severity, description, assignedTo
case facilityId, facilityName, reportedAt, mobileLocalId
case photoPath, resultPhotos
case photoPath, mobilePhotoPaths, resultPhotos
}
}