// // Project.swift // Todos // // Created by Sam Jaffe on 2/28/26. // import Foundation import SwiftData import SwiftUI @Model final class Project: Codable, Ordered, Aggregate { typealias Element = Task var uuid: UUID var sortOrder: Int = 0 var name: String = "New Project" var category: String = "" @Relationship(deleteRule: .cascade, inverse: \Task.project) var tasks: [Task] = [] init(sortOrder: Int = 0) { self.uuid = UUID() self.sortOrder = sortOrder } func move(fromOffsets: IndexSet, toOffset: Int) { tasks.move(fromOffsets: fromOffsets, toOffset: toOffset) reindex() } func remove(_ item: Element) { tasks.removeAll(where: { $0.id == item.id }) reindex() } func reindex() { for (index, item) in tasks.enumerated() { item.sortOrder = index } } func yaml(_ indent: Int = 0) -> String { let hdr = String(repeating: " ", count: indent) var rval = hdr + "\(name):\n" if !category.isEmpty { rval += hdr + " # In Category: \(category)\n" } rval += tasks.map({ $0.yaml(indent + 1) }).joined() return rval } enum CodingKeys: CodingKey { case category, name, tasks } required init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) uuid = UUID() name = try container.decode(String.self, forKey: .name) category = try container.decode(String.self, forKey: .category) tasks = try container.decode([Task].self, forKey: .tasks) for (index, item) in tasks.enumerated() { item.project = self item.sortOrder = index } } func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) try container.encode(category, forKey: .category) try container.encode(tasks, forKey: .tasks) } }