From 576bb6c2432a15f5138771c06019b55fd2244e15 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Sun, 12 Apr 2026 22:42:33 -0500 Subject: feat: add ticket label management and bulk actions Implements: https://todo.sr.ht/~ccleberg/hutch/45 Implements: https://todo.sr.ht/~ccleberg/hutch/46 --- Hutch/Models/TicketBulkAction.swift | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Hutch/Models/TicketBulkAction.swift (limited to 'Hutch/Models') 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 +} -- cgit v1.2.3