Files
JQC_iOS_App/JanitorialQC/API/APIModels.swift
T
2026-05-02 11:46:28 -04:00

133 lines
4.2 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?
// Computed display name: full name if present, otherwise username.
// The server's /auth/me endpoint returns username; display_name would
// require a separate field. For Phase A we use username as the display name
// and can extend later.
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]] // Dynamic JSON fields
}
// ── AnyDecodable helper ───────────────────────────────────────────────────────
// Allows decoding JSON values of unknown type (String, Int, Bool, Array, Dict)
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()
}
}