summaryrefslogtreecommitdiff
path: root/Hutch/Models
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-04-12 22:42:33 -0500
committerChristian Cleberg <[email protected]>2026-04-12 22:42:33 -0500
commit576bb6c2432a15f5138771c06019b55fd2244e15 (patch)
tree167b4704658c11ea6b9e7170390098e13c89b94b /Hutch/Models
parent54d037548261c1546555a89a6a803b542a769880 (diff)
downloadhutch-576bb6c2432a15f5138771c06019b55fd2244e15.tar.gz
hutch-576bb6c2432a15f5138771c06019b55fd2244e15.tar.bz2
hutch-576bb6c2432a15f5138771c06019b55fd2244e15.zip
feat: add ticket label management and bulk actions
Implements: https://todo.sr.ht/~ccleberg/hutch/45 Implements: https://todo.sr.ht/~ccleberg/hutch/46
Diffstat (limited to 'Hutch/Models')
-rw-r--r--Hutch/Models/TicketBulkAction.swift88
1 files changed, 88 insertions, 0 deletions
diff --git a/Hutch/Models/TicketBulkAction.swift b/Hutch/Models/TicketBulkAction.swift
new file mode 100644
index 0000000..dcf7676
--- /dev/null
+++ b/Hutch/Models/TicketBulkAction.swift
@@ -0,0 +1,88 @@
+import Foundation
+
+enum TicketBulkActionKind: String, Sendable {
+ case close
+ case assign
+
+ var displayName: String {
+ switch self {
+ case .close:
+ "Close"
+ case .assign:
+ "Assign"
+ }
+ }
+
+ var pastTenseDisplayName: String {
+ switch self {
+ case .close:
+ "Closed"
+ case .assign:
+ "Assigned"
+ }
+ }
+}
+
+struct TicketBulkActionResult: Identifiable, Sendable {
+ let id = UUID()
+ let action: TicketBulkActionKind
+ let totalCount: Int
+ let updatedCount: Int
+ let unchangedCount: Int
+ let failures: [TicketBulkActionFailure]
+
+ var failedCount: Int {
+ failures.count
+ }
+
+ var title: String {
+ if updatedCount == 0, failedCount > 0 {
+ return "\(action.displayName) Failed"
+ }
+ if failedCount > 0 {
+ return "\(action.displayName) Partially Applied"
+ }
+ return "\(action.displayName) Complete"
+ }
+
+ var message: String {
+ var components: [String] = []
+
+ if updatedCount > 0 {
+ components.append("\(action.pastTenseDisplayName) \(updatedCount) \(ticketWord(for: updatedCount)).")
+ }
+
+ if unchangedCount > 0 {
+ let unchangedDescription: String
+ switch action {
+ case .close:
+ unchangedDescription = "\(unchangedCount) already closed."
+ case .assign:
+ unchangedDescription = "\(unchangedCount) already assigned."
+ }
+ components.append(unchangedDescription)
+ }
+
+ if failedCount > 0 {
+ let ids = failures
+ .map { "#\($0.ticketID)" }
+ .joined(separator: ", ")
+ components.append("Failed: \(ids).")
+ }
+
+ if components.isEmpty {
+ components.append("No tickets were selected.")
+ }
+
+ return components.joined(separator: " ")
+ }
+
+ private func ticketWord(for count: Int) -> String {
+ count == 1 ? "ticket" : "tickets"
+ }
+}
+
+struct TicketBulkActionFailure: Sendable {
+ let ticketID: Int
+ let message: String
+}