05/02 Phase C
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// API/APIClient.swift
|
||||
// -------------------
|
||||
// Central HTTP client for all JQC API calls.
|
||||
// Phase B adds: uploadPhoto(), submitInspection(), submitIssue()
|
||||
// Phase C adds: fetchInspectionHistory()
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
@@ -99,10 +99,24 @@ actor APIClient {
|
||||
return try await request(endpoint, method: "POST", body: body)
|
||||
}
|
||||
|
||||
// ── Inspection History (Phase C) ──────────────────────────────────────
|
||||
|
||||
/// Fetch the inspector's synced inspection history from the server.
|
||||
/// Returns up to `limit` records starting at `offset`.
|
||||
func fetchInspectionHistory(
|
||||
limit: Int = 50,
|
||||
offset: Int = 0,
|
||||
facilityId: Int? = nil
|
||||
) async throws -> InspectionHistoryResponseData {
|
||||
var endpoint = "/api/v1/inspections?limit=\(limit)&offset=\(offset)&status=completed"
|
||||
if let fid = facilityId {
|
||||
endpoint += "&facility_id=\(fid)"
|
||||
}
|
||||
return try await request(endpoint)
|
||||
}
|
||||
|
||||
// ── Photo Upload (multipart/form-data) ────────────────────────────────
|
||||
|
||||
/// Upload a local photo file to the server.
|
||||
/// Returns the server_path string on success.
|
||||
func uploadPhoto(localPath: String, entityType: String) async throws -> String {
|
||||
guard let url = URL(string: baseURL + "/api/v1/photos/upload") else {
|
||||
throw APIError.invalidURL
|
||||
@@ -115,12 +129,10 @@ actor APIClient {
|
||||
let boundary = "Boundary-\(UUID().uuidString)"
|
||||
var body = Data()
|
||||
|
||||
// -- entity_type field
|
||||
body.append("--\(boundary)\r\n".data(using: .utf8)!)
|
||||
body.append("Content-Disposition: form-data; name=\"entity_type\"\r\n\r\n".data(using: .utf8)!)
|
||||
body.append("\(entityType)\r\n".data(using: .utf8)!)
|
||||
|
||||
// -- file field
|
||||
let filename = URL(fileURLWithPath: localPath).lastPathComponent
|
||||
let ext = (filename as NSString).pathExtension.lowercased()
|
||||
let mimeType = ext == "png" ? "image/png" : "image/jpeg"
|
||||
@@ -171,8 +183,6 @@ actor APIClient {
|
||||
|
||||
// ── Submit Inspection ─────────────────────────────────────────────────
|
||||
|
||||
/// Submit a completed LocalInspection to the server.
|
||||
/// Returns the server-assigned inspection ID.
|
||||
func submitInspection(_ inspection: LocalInspection) async throws -> Int {
|
||||
var body: [String: Any] = [
|
||||
"template_id": inspection.templateServerId,
|
||||
@@ -183,12 +193,8 @@ actor APIClient {
|
||||
"overall_score": inspection.overallScore as Any,
|
||||
]
|
||||
|
||||
if let areaId = inspection.areaServerId {
|
||||
body["area_id"] = areaId
|
||||
}
|
||||
if !inspection.inspectorNotes.isEmpty {
|
||||
body["notes"] = inspection.inspectorNotes
|
||||
}
|
||||
if let areaId = inspection.areaServerId { body["area_id"] = areaId }
|
||||
if !inspection.inspectorNotes.isEmpty { body["notes"] = inspection.inspectorNotes }
|
||||
|
||||
let formatter = ISO8601DateFormatter()
|
||||
body["inspection_date"] = formatter.string(from: inspection.inspectionDate)
|
||||
@@ -207,8 +213,6 @@ actor APIClient {
|
||||
|
||||
// ── Submit Issue ──────────────────────────────────────────────────────
|
||||
|
||||
/// Submit a LocalIssue to the server.
|
||||
/// Returns the server-assigned issue ID.
|
||||
func submitIssue(_ issue: LocalIssue) async throws -> Int {
|
||||
var body: [String: Any] = [
|
||||
"area_id": issue.areaServerId,
|
||||
@@ -217,12 +221,9 @@ actor APIClient {
|
||||
"mobile_local_id": issue.localId,
|
||||
]
|
||||
|
||||
// Link to server inspection if already synced
|
||||
// (inspection must be synced before its issues)
|
||||
if let inspServerId = (issue.inspection?.serverId) {
|
||||
if let inspServerId = issue.inspection?.serverId {
|
||||
body["inspection_id"] = inspServerId
|
||||
}
|
||||
|
||||
if let serverPhotoPath = issue.photoServerPath {
|
||||
body["photo_path"] = serverPhotoPath
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user