From a39f8b07df8799d492c9b47c71b4d202fcbd178c Mon Sep 17 00:00:00 2001 From: Simon Franken Date: Fri, 20 Feb 2026 15:32:40 +0100 Subject: [PATCH] Add delete confirmation dialogs for clients, projects, and time entries - Add confirmation alert when deleting clients with warning about deleted dependencies (projects and time entries) - Add confirmation alert when deleting projects with warning about deleted time entries - Add confirmation alert when deleting time entries - All alerts include item name and emphasize action cannot be undone --- .../Features/Clients/ClientsView.swift | 19 +++++++++++++++---- .../Features/Projects/ProjectsView.swift | 19 +++++++++++++++---- .../TimeEntries/TimeEntriesView.swift | 19 +++++++++++++++---- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/ios/TimeTracker/TimeTracker/Features/Clients/ClientsView.swift b/ios/TimeTracker/TimeTracker/Features/Clients/ClientsView.swift index 3eaedff..9480d3f 100644 --- a/ios/TimeTracker/TimeTracker/Features/Clients/ClientsView.swift +++ b/ios/TimeTracker/TimeTracker/Features/Clients/ClientsView.swift @@ -3,6 +3,8 @@ import SwiftUI struct ClientsView: View { @StateObject private var viewModel = ClientsViewModel() @State private var showAddClient = false + @State private var clientToDelete: Client? + @State private var showDeleteConfirmation = false var body: some View { NavigationStack { @@ -52,13 +54,22 @@ struct ClientsView: View { ClientRow(client: client) } .onDelete { indexSet in - Task { - for index in indexSet { - await viewModel.deleteClient(viewModel.clients[index]) - } + if let index = indexSet.first { + clientToDelete = viewModel.clients[index] + showDeleteConfirmation = true } } } + .alert("Delete Client?", isPresented: $showDeleteConfirmation, presenting: clientToDelete) { client in + Button("Cancel", role: .cancel) {} + Button("Delete", role: .destructive) { + Task { + await viewModel.deleteClient(client) + } + } + } message: { client in + Text("This will permanently delete '\(client.name)' and all related projects and time entries. This action cannot be undone.") + } .refreshable { await viewModel.loadClients() } diff --git a/ios/TimeTracker/TimeTracker/Features/Projects/ProjectsView.swift b/ios/TimeTracker/TimeTracker/Features/Projects/ProjectsView.swift index 37bdfac..4c13d6b 100644 --- a/ios/TimeTracker/TimeTracker/Features/Projects/ProjectsView.swift +++ b/ios/TimeTracker/TimeTracker/Features/Projects/ProjectsView.swift @@ -3,6 +3,8 @@ import SwiftUI struct ProjectsView: View { @StateObject private var viewModel = ProjectsViewModel() @State private var showAddProject = false + @State private var projectToDelete: Project? + @State private var showDeleteConfirmation = false var body: some View { NavigationStack { @@ -60,13 +62,22 @@ struct ProjectsView: View { ProjectRow(project: project) } .onDelete { indexSet in - Task { - for index in indexSet { - await viewModel.deleteProject(viewModel.projects[index]) - } + if let index = indexSet.first { + projectToDelete = viewModel.projects[index] + showDeleteConfirmation = true } } } + .alert("Delete Project?", isPresented: $showDeleteConfirmation, presenting: projectToDelete) { project in + Button("Cancel", role: .cancel) {} + Button("Delete", role: .destructive) { + Task { + await viewModel.deleteProject(project) + } + } + } message: { project in + Text("This will permanently delete '\(project.name)' and all related time entries. This action cannot be undone.") + } .refreshable { await viewModel.loadData() } diff --git a/ios/TimeTracker/TimeTracker/Features/TimeEntries/TimeEntriesView.swift b/ios/TimeTracker/TimeTracker/Features/TimeEntries/TimeEntriesView.swift index 36ed16a..5a475d3 100644 --- a/ios/TimeTracker/TimeTracker/Features/TimeEntries/TimeEntriesView.swift +++ b/ios/TimeTracker/TimeTracker/Features/TimeEntries/TimeEntriesView.swift @@ -3,6 +3,8 @@ import SwiftUI struct TimeEntriesView: View { @StateObject private var viewModel = TimeEntriesViewModel() @State private var showAddEntry = false + @State private var entryToDelete: TimeEntry? + @State private var showDeleteConfirmation = false var body: some View { NavigationStack { @@ -50,13 +52,22 @@ struct TimeEntriesView: View { TimeEntryRow(entry: entry) } .onDelete { indexSet in - Task { - for index in indexSet { - await viewModel.deleteEntry(viewModel.entries[index]) - } + if let index = indexSet.first { + entryToDelete = viewModel.entries[index] + showDeleteConfirmation = true } } } + .alert("Delete Time Entry?", isPresented: $showDeleteConfirmation, presenting: entryToDelete) { entry in + Button("Cancel", role: .cancel) {} + Button("Delete", role: .destructive) { + Task { + await viewModel.deleteEntry(entry) + } + } + } message: { entry in + Text("This will permanently delete the time entry for '\(entry.project.name)'. This action cannot be undone.") + } .refreshable { await viewModel.loadEntries() }