summaryrefslogtreecommitdiff
path: root/Hutch/Views/Tickets
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-03-18 20:23:58 -0500
committerChristian Cleberg <[email protected]>2026-03-18 20:23:58 -0500
commitc41cd437acde4e4f4c0781cebd4d5f4bf9517bb9 (patch)
tree28bf13b89e36774720d015a26a8979b567ad0be9 /Hutch/Views/Tickets
parent1745311950fd68ad81957b3ba64f8566cd86ce19 (diff)
parent4dd920558a1c7174c0e9a5b872325df36e363516 (diff)
downloadhutch-c41cd437acde4e4f4c0781cebd4d5f4bf9517bb9.tar.gz
hutch-c41cd437acde4e4f4c0781cebd4d5f4bf9517bb9.tar.bz2
hutch-c41cd437acde4e4f4c0781cebd4d5f4bf9517bb9.zip
v1.2: Merge branch 'dev'
Diffstat (limited to 'Hutch/Views/Tickets')
-rw-r--r--Hutch/Views/Tickets/TicketDetailView.swift5
-rw-r--r--Hutch/Views/Tickets/TicketDetailViewModel.swift20
-rw-r--r--Hutch/Views/Tickets/TrackerListView.swift27
-rw-r--r--Hutch/Views/Tickets/TrackerListViewModel.swift19
4 files changed, 60 insertions, 11 deletions
diff --git a/Hutch/Views/Tickets/TicketDetailView.swift b/Hutch/Views/Tickets/TicketDetailView.swift
index 6b2be4f..f553bd8 100644
--- a/Hutch/Views/Tickets/TicketDetailView.swift
+++ b/Hutch/Views/Tickets/TicketDetailView.swift
@@ -71,10 +71,7 @@ struct TicketDetailView: View {
if ticket.status == .resolved {
Button {
Task {
- await viewModel.updateStatus(
- status: .reported,
- resolution: .unresolved
- )
+ await viewModel.updateStatus(status: .reported)
}
} label: {
SwiftUI.Label("Reopen", systemImage: "arrow.uturn.backward")
diff --git a/Hutch/Views/Tickets/TicketDetailViewModel.swift b/Hutch/Views/Tickets/TicketDetailViewModel.swift
index 70eaf0a..90e33aa 100644
--- a/Hutch/Views/Tickets/TicketDetailViewModel.swift
+++ b/Hutch/Views/Tickets/TicketDetailViewModel.swift
@@ -130,6 +130,19 @@ final class TicketDetailViewModel {
return lhs.created < rhs.created
}
+ static func statusUpdateInput(
+ status: TicketStatus,
+ resolution: TicketResolution?
+ ) -> [String: any Sendable] {
+ var input: [String: any Sendable] = [
+ "status": status.rawValue
+ ]
+ if status == .resolved, let resolution {
+ input["resolution"] = resolution.rawValue
+ }
+ return input
+ }
+
init(ownerUsername: String, trackerName: String, trackerId: Int, trackerRid: String, ticketId: Int, client: SRHTClient) {
self.ownerUsername = ownerUsername
self.trackerName = trackerName
@@ -360,16 +373,13 @@ final class TicketDetailViewModel {
// MARK: - Ticket Actions
- func updateStatus(status: TicketStatus, resolution: TicketResolution) async {
+ func updateStatus(status: TicketStatus, resolution: TicketResolution? = nil) async {
guard !isPerformingAction else { return }
isPerformingAction = true
error = nil
do {
- let input: [String: any Sendable] = [
- "status": status.rawValue,
- "resolution": resolution.rawValue
- ]
+ let input = Self.statusUpdateInput(status: status, resolution: resolution)
_ = try await client.execute(
service: .todo,
query: Self.updateStatusMutation,
diff --git a/Hutch/Views/Tickets/TrackerListView.swift b/Hutch/Views/Tickets/TrackerListView.swift
index d16246b..ee253ec 100644
--- a/Hutch/Views/Tickets/TrackerListView.swift
+++ b/Hutch/Views/Tickets/TrackerListView.swift
@@ -124,10 +124,17 @@ private struct CreateTrackerSheet: View {
let onCreated: (TrackerSummary) -> Void
@Environment(\.dismiss) private var dismiss
+ @Bindable var viewModelBindable: TrackerListViewModel
@State private var name = ""
@State private var description = ""
@State private var visibility: Visibility = .public
+ init(viewModel: TrackerListViewModel, onCreated: @escaping (TrackerSummary) -> Void) {
+ self.viewModel = viewModel
+ self._viewModelBindable = Bindable(viewModel)
+ self.onCreated = onCreated
+ }
+
var body: some View {
NavigationStack {
Form {
@@ -143,12 +150,30 @@ private struct CreateTrackerSheet: View {
Text("Private").tag(Visibility.private)
}
}
+
+ if let error = viewModel.error {
+ Section {
+ Label {
+ Text(error)
+ } icon: {
+ Image(systemName: "exclamationmark.triangle.fill")
+ .foregroundStyle(.red)
+ }
+ .foregroundStyle(.red)
+ }
+ }
}
.navigationTitle("New Tracker")
.navigationBarTitleDisplayMode(.inline)
+ .onDisappear {
+ viewModelBindable.error = nil
+ }
.toolbar {
ToolbarItem(placement: .cancellationAction) {
- Button("Cancel") { dismiss() }
+ Button("Cancel") {
+ viewModelBindable.error = nil
+ dismiss()
+ }
}
ToolbarItem(placement: .confirmationAction) {
Button {
diff --git a/Hutch/Views/Tickets/TrackerListViewModel.swift b/Hutch/Views/Tickets/TrackerListViewModel.swift
index 9704071..978a610 100644
--- a/Hutch/Views/Tickets/TrackerListViewModel.swift
+++ b/Hutch/Views/Tickets/TrackerListViewModel.swift
@@ -139,7 +139,7 @@ final class TrackerListViewModel {
trackers.insert(tracker, at: 0)
return tracker
} catch {
- self.error = "Couldn’t create the tracker. \(error.localizedDescription)"
+ self.error = trackerCreationErrorMessage(for: error)
return nil
}
}
@@ -163,4 +163,21 @@ final class TrackerListViewModel {
private struct CreateTrackerResponse: Decodable, Sendable {
let createTracker: TrackerSummary
}
+
+ private func trackerCreationErrorMessage(for error: Error) -> String {
+ let message: String
+
+ if let srhtError = error as? SRHTError {
+ switch srhtError {
+ case .graphQLErrors(let errors):
+ message = errors.map(\.message).joined(separator: "\n")
+ default:
+ message = srhtError.localizedDescription
+ }
+ } else {
+ message = error.localizedDescription
+ }
+
+ return "Couldn’t create the tracker. \(message)"
+ }
}