05/02 Phase B

This commit is contained in:
Nguyen Ngo
2026-05-02 11:46:28 -04:00
parent ef0a6448cf
commit 8bdcbecd73
22 changed files with 3262 additions and 69 deletions
+132
View File
@@ -0,0 +1,132 @@
// 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()
}
}