Files
JQC_iOS_App/JanitorialQC/API/APIModels.swift
T
2026-05-03 08:31:25 -04:00

154 lines
4.6 KiB
Swift

// API/APIModels.swift
// -------------------
// Codable structs that map to the JSON responses from the JQC Flask API.
// All API responses use the envelope: { "ok": bool, "data": {...}, "error": string? }
import Foundation
// ── API Envelope ──────────────────────────────────────────────────────────────
struct APIResponse<T: Decodable>: Decodable {
let ok: Bool
let data: T?
let error: String?
}
// ── Auth ──────────────────────────────────────────────────────────────────────
struct LoginResponseData: Decodable {
let accessToken: String
let refreshToken: String
let tokenType: String
let expiresIn: Int
let user: APIUser
}
struct RefreshResponseData: Decodable {
let accessToken: String
let refreshToken: String
let tokenType: String
let expiresIn: Int
}
struct MeResponseData: Decodable {
let user: APIUser
}
struct APIUser: Decodable {
let id: Int
let username: String
let email: String
let role: String
let createdAt: String?
var displayName: String { username }
}
// ── Facilities ────────────────────────────────────────────────────────────────
struct FacilitiesResponseData: Decodable {
let facilities: [APIFacility]
let count: Int
}
struct AreasResponseData: Decodable {
let facilityId: Int
let areas: [APIArea]
let count: Int
}
struct APIFacility: Decodable, Identifiable {
let id: Int
let name: String
let address: String
let contactPerson: String
let contactPhone: String
let projectId: Int?
let projectName: String?
let isActive: Bool
}
struct APIArea: Decodable, Identifiable {
let id: Int
let facilityId: Int
let name: String
let areaType: String
}
// ── Templates ─────────────────────────────────────────────────────────────────
struct TemplatesResponseData: Decodable {
let templates: [APITemplateSummary]
let count: Int
}
struct TemplateDetailResponseData: Decodable {
let template: APITemplate
}
struct APITemplateSummary: Decodable, Identifiable {
let id: Int
let name: String
let description: String
let frequency: String
}
struct APITemplate: Decodable, Identifiable {
let id: Int
let name: String
let description: String
let frequency: String
let formSchema: [[String: AnyDecodable]]
}
// ── Inspections (Phase C — history) ──────────────────────────────────────────
struct InspectionHistoryResponseData: Decodable {
let inspections: [APIInspectionSummary]
let total: Int
let limit: Int
let offset: Int
}
struct APIInspectionSummary: Decodable, Identifiable {
let id: Int
let templateId: Int
let templateName: String
let facilityId: Int
let facilityName: String
let areaId: Int?
let areaName: String?
let status: String
let overallScore: Double?
let inspectionDate: String?
let completedAt: String?
let mobileLocalId: String?
var inspectionDateParsed: Date? {
guard let str = inspectionDate else { return nil }
return ISO8601DateFormatter().date(from: str)
}
}
// ── AnyDecodable helper ───────────────────────────────────────────────────────
struct AnyDecodable: Decodable {
let value: Any
init(_ value: Any) { self.value = value }
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let bool = try? container.decode(Bool.self) { value = bool; return }
if let int = try? container.decode(Int.self) { value = int; return }
if let dbl = try? container.decode(Double.self) { value = dbl; return }
if let str = try? container.decode(String.self) { value = str; return }
if let arr = try? container.decode([AnyDecodable].self) {
value = arr.map(\.value); return
}
if let dict = try? container.decode([String: AnyDecodable].self) {
value = dict.mapValues(\.value); return
}
value = NSNull()
}
}