126 lines
5 KiB
Swift
126 lines
5 KiB
Swift
import SwiftUI
|
|
|
|
struct WorkTask: Identifiable {
|
|
let taskId: Int
|
|
let businessId: Int
|
|
let categoryId: Int
|
|
let taskTypeId: Int // 1 = Service Request, 2 = Chat
|
|
let title: String
|
|
let details: String
|
|
let createdOn: Date
|
|
let statusId: Int
|
|
let sourceType: String
|
|
let sourceId: Int
|
|
let categoryName: String
|
|
let categoryColor: String
|
|
let taskTypeName: String
|
|
let taskTypeColor: String
|
|
let orderTotal: Double
|
|
|
|
// Location (may be included in list responses)
|
|
let servicePointName: String
|
|
let deliveryAddress: String
|
|
|
|
var id: Int { taskId }
|
|
|
|
/// Decode directly from a [String: Any] dictionary (matches Flutter's fromJson)
|
|
init(json: [String: Any]) {
|
|
taskId = Self.parseInt(json["TaskID"]) ?? 0
|
|
businessId = Self.parseInt(json["BusinessID"] ?? json["TaskBusinessID"]) ?? 0
|
|
categoryId = Self.parseInt(json["TaskCategoryID"]) ?? 0
|
|
taskTypeId = Self.parseInt(json["TaskTypeID"]) ?? 1
|
|
title = (json["Title"] as? String) ?? (json["TaskTitle"] as? String) ?? ""
|
|
details = (json["Details"] as? String) ?? (json["TaskDetails"] as? String) ?? ""
|
|
createdOn = Self.parseDate(json["CreatedOn"] ?? json["TaskCreatedOn"]) ?? Date()
|
|
statusId = Self.parseInt(json["StatusID"] ?? json["TaskStatusID"]) ?? 0
|
|
sourceType = (json["SourceType"] as? String) ?? (json["TaskSourceType"] as? String) ?? ""
|
|
sourceId = Self.parseInt(json["SourceID"] ?? json["TaskSourceID"]) ?? 0
|
|
// Server key varies: CategoryName, Name, TaskCategoryName — try all
|
|
categoryName = Self.nonEmpty(json["CategoryName"]) ?? Self.nonEmpty(json["Name"]) ?? Self.nonEmpty(json["TaskCategoryName"]) ?? Self.nonEmpty(json["TaskTypeName"]) ?? "Uncategorized"
|
|
// Category color as fallback
|
|
categoryColor = Self.nonEmpty(json["CategoryColor"]) ?? Self.nonEmpty(json["Color"]) ?? Self.nonEmpty(json["TaskCategoryColor"]) ?? "#888888"
|
|
taskTypeName = (json["TaskTypeName"] as? String) ?? ""
|
|
// TaskType color is the primary color source
|
|
taskTypeColor = Self.nonEmpty(json["TaskTypeColor"]) ?? ""
|
|
orderTotal = Self.parseDouble(json["OrderTotal"]) ?? 0
|
|
servicePointName = (json["ServicePointName"] as? String) ?? ""
|
|
deliveryAddress = (json["DeliveryAddress"] as? String) ?? ""
|
|
}
|
|
|
|
var locationDisplay: String {
|
|
if !deliveryAddress.isEmpty { return deliveryAddress }
|
|
if !servicePointName.isEmpty { return servicePointName }
|
|
return ""
|
|
}
|
|
|
|
/// Primary color from TaskType, falls back to category color
|
|
var color: Color {
|
|
let hexSource = !taskTypeColor.isEmpty ? taskTypeColor : categoryColor
|
|
let hex = hexSource.replacingOccurrences(of: "#", with: "")
|
|
guard hex.count == 6, let val = UInt64(hex, radix: 16) else {
|
|
return Color(red: 0.53, green: 0.53, blue: 0.53)
|
|
}
|
|
return Color(
|
|
red: Double((val >> 16) & 0xFF) / 255,
|
|
green: Double((val >> 8) & 0xFF) / 255,
|
|
blue: Double(val & 0xFF) / 255
|
|
)
|
|
}
|
|
|
|
var statusName: String {
|
|
switch statusId {
|
|
case 0: return "Pending"
|
|
case 1: return "Accepted"
|
|
case 2: return "In Progress"
|
|
case 3: return "Completed"
|
|
default: return "Unknown"
|
|
}
|
|
}
|
|
|
|
var timeAgo: String {
|
|
let diff = Date().timeIntervalSince(createdOn)
|
|
let minutes = Int(diff / 60)
|
|
if minutes < 1 { return "just now" }
|
|
if minutes < 60 { return "\(minutes)m ago" }
|
|
let hours = minutes / 60
|
|
if hours < 24 { return "\(hours)h ago" }
|
|
return "\(hours / 24)d ago"
|
|
}
|
|
|
|
/// Chat or Call Team Member tasks - both involve customer interaction
|
|
var isChat: Bool { taskTypeId == 2 || taskTypeId == 6 }
|
|
var isCash: Bool { taskTypeName.lowercased().contains("cash") }
|
|
|
|
// MARK: - Flexible parsing helpers
|
|
|
|
/// Returns the string value if it's non-nil and non-empty, otherwise nil
|
|
static func nonEmpty(_ value: Any?) -> String? {
|
|
guard let s = value as? String, !s.trimmingCharacters(in: .whitespaces).isEmpty else { return nil }
|
|
return s
|
|
}
|
|
|
|
static func parseDouble(_ value: Any?) -> Double? {
|
|
guard let value = value else { return nil }
|
|
if let d = value as? Double { return d }
|
|
if let i = value as? Int { return Double(i) }
|
|
if let s = value as? String, let d = Double(s) { return d }
|
|
if let n = value as? NSNumber { return n.doubleValue }
|
|
return nil
|
|
}
|
|
|
|
static func parseInt(_ value: Any?) -> Int? {
|
|
guard let value = value else { return nil }
|
|
if let v = value as? Int { return v }
|
|
if let v = value as? Double { return Int(v) }
|
|
if let v = value as? NSNumber { return v.intValue }
|
|
if let v = value as? String, let i = Int(v) { return i }
|
|
return nil
|
|
}
|
|
|
|
static func parseDate(_ value: Any?) -> Date? {
|
|
guard let value = value else { return nil }
|
|
if let d = value as? Date { return d }
|
|
if let s = value as? String { return APIService.parseDate(s) }
|
|
return nil
|
|
}
|
|
}
|