import Foundation // MARK: - Tier Status (from tierStatus.php) struct TierStatus: Codable { var tier: Int var stripe: StripeInfo var activation: ActivationInfo enum CodingKeys: String, CodingKey { case tier = "TIER" case stripe = "STRIPE" case activation = "ACTIVATION" } init(tier: Int = 0, stripe: StripeInfo = StripeInfo(), activation: ActivationInfo = ActivationInfo()) { self.tier = tier self.stripe = stripe self.activation = activation } init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: CodingKeys.self) tier = (try? c.decode(Int.self, forKey: .tier)) ?? 0 stripe = (try? c.decode(StripeInfo.self, forKey: .stripe)) ?? StripeInfo() activation = (try? c.decode(ActivationInfo.self, forKey: .activation)) ?? ActivationInfo() } struct StripeInfo: Codable { var hasAccount: Bool var payoutsEnabled: Bool var setupIncomplete: Bool enum CodingKeys: String, CodingKey { case hasAccount = "HasAccount" case payoutsEnabled = "PayoutsEnabled" case setupIncomplete = "SetupIncomplete" } init(hasAccount: Bool = false, payoutsEnabled: Bool = false, setupIncomplete: Bool = false) { self.hasAccount = hasAccount self.payoutsEnabled = payoutsEnabled self.setupIncomplete = setupIncomplete } init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: CodingKeys.self) hasAccount = (try? c.decode(Bool.self, forKey: .hasAccount)) ?? false payoutsEnabled = (try? c.decode(Bool.self, forKey: .payoutsEnabled)) ?? false setupIncomplete = (try? c.decode(Bool.self, forKey: .setupIncomplete)) ?? false } } struct ActivationInfo: Codable { var balanceCents: Int var capCents: Int var remainingCents: Int var isComplete: Bool var progressPercent: Int enum CodingKeys: String, CodingKey { case balanceCents = "BalanceCents" case capCents = "CapCents" case remainingCents = "RemainingCents" case isComplete = "IsComplete" case progressPercent = "ProgressPercent" } init(balanceCents: Int = 0, capCents: Int = 2500, remainingCents: Int = 2500, isComplete: Bool = false, progressPercent: Int = 0) { self.balanceCents = balanceCents self.capCents = capCents self.remainingCents = remainingCents self.isComplete = isComplete self.progressPercent = progressPercent } init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: CodingKeys.self) balanceCents = (try? c.decode(Int.self, forKey: .balanceCents)) ?? 0 capCents = (try? c.decode(Int.self, forKey: .capCents)) ?? 2500 remainingCents = (try? c.decode(Int.self, forKey: .remainingCents)) ?? 2500 isComplete = (try? c.decode(Bool.self, forKey: .isComplete)) ?? false progressPercent = (try? c.decode(Int.self, forKey: .progressPercent)) ?? 0 } var balanceDollars: String { String(format: "$%.2f", Double(balanceCents) / 100.0) } var capDollars: String { String(format: "$%.2f", Double(capCents) / 100.0) } var progress: Double { guard capCents > 0 else { return 0 } return Double(balanceCents) / Double(capCents) } } } // MARK: - Ledger Response (from ledger.php) struct LedgerResponse: Codable { var entries: [LedgerEntry] var totals: LedgerTotals enum CodingKeys: String, CodingKey { case entries = "ENTRIES" case totals = "TOTALS" } init(entries: [LedgerEntry] = [], totals: LedgerTotals = LedgerTotals()) { self.entries = entries self.totals = totals } init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: CodingKeys.self) entries = (try? c.decode([LedgerEntry].self, forKey: .entries)) ?? [] totals = (try? c.decode(LedgerTotals.self, forKey: .totals)) ?? LedgerTotals() } } struct LedgerEntry: Codable, Identifiable { let id: Int let taskId: Int let grossEarningsCents: Int let activationWithheldCents: Int let netTransferCents: Int let status: String let createdAt: String enum CodingKeys: String, CodingKey { case id = "ID" case taskId = "TaskID" case grossEarningsCents = "GrossEarningsCents" case activationWithheldCents = "ActivationWithheldCents" case netTransferCents = "NetTransferCents" case status = "Status" case createdAt = "CreatedAt" } var grossDollars: String { String(format: "$%.2f", Double(grossEarningsCents) / 100.0) } var netDollars: String { String(format: "$%.2f", Double(netTransferCents) / 100.0) } var withheldDollars: String { String(format: "$%.2f", Double(activationWithheldCents) / 100.0) } var statusDisplay: String { switch status { case "pending_charge": return "Pending" case "charged": return "Charged" case "transferred": return "Transferred" case "reversed": return "Reversed" default: return status.capitalized } } } struct LedgerTotals: Codable { var totalGrossCents: Int var totalWithheldCents: Int var totalNetCents: Int enum CodingKeys: String, CodingKey { case totalGrossCents = "TotalGrossCents" case totalWithheldCents = "TotalWithheldCents" case totalNetCents = "TotalNetCents" } init(totalGrossCents: Int = 0, totalWithheldCents: Int = 0, totalNetCents: Int = 0) { self.totalGrossCents = totalGrossCents self.totalWithheldCents = totalWithheldCents self.totalNetCents = totalNetCents } init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: CodingKeys.self) totalGrossCents = (try? c.decode(Int.self, forKey: .totalGrossCents)) ?? 0 totalWithheldCents = (try? c.decode(Int.self, forKey: .totalWithheldCents)) ?? 0 totalNetCents = (try? c.decode(Int.self, forKey: .totalNetCents)) ?? 0 } var totalGrossDollars: String { String(format: "$%.2f", Double(totalGrossCents) / 100.0) } var totalNetDollars: String { String(format: "$%.2f", Double(totalNetCents) / 100.0) } }