2 Commits

Author SHA1 Message Date
e0dd2f1fbc Merge branch 'main' into feature/ios-delete-confirmation 2026-02-20 15:35:02 +01:00
a39f8b07df 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
2026-02-20 15:32:40 +01:00
3 changed files with 45 additions and 12 deletions

View File

@@ -3,6 +3,8 @@ import SwiftUI
struct ClientsView: View { struct ClientsView: View {
@StateObject private var viewModel = ClientsViewModel() @StateObject private var viewModel = ClientsViewModel()
@State private var showAddClient = false @State private var showAddClient = false
@State private var clientToDelete: Client?
@State private var showDeleteConfirmation = false
var body: some View { var body: some View {
NavigationStack { NavigationStack {
@@ -52,12 +54,21 @@ struct ClientsView: View {
ClientRow(client: client) ClientRow(client: client)
} }
.onDelete { indexSet in .onDelete { indexSet in
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 { Task {
for index in indexSet { await viewModel.deleteClient(client)
await viewModel.deleteClient(viewModel.clients[index])
}
} }
} }
} message: { client in
Text("This will permanently delete '\(client.name)' and all related projects and time entries. This action cannot be undone.")
} }
.refreshable { .refreshable {
await viewModel.loadClients() await viewModel.loadClients()

View File

@@ -3,6 +3,8 @@ import SwiftUI
struct ProjectsView: View { struct ProjectsView: View {
@StateObject private var viewModel = ProjectsViewModel() @StateObject private var viewModel = ProjectsViewModel()
@State private var showAddProject = false @State private var showAddProject = false
@State private var projectToDelete: Project?
@State private var showDeleteConfirmation = false
var body: some View { var body: some View {
NavigationStack { NavigationStack {
@@ -60,12 +62,21 @@ struct ProjectsView: View {
ProjectRow(project: project) ProjectRow(project: project)
} }
.onDelete { indexSet in .onDelete { indexSet in
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 { Task {
for index in indexSet { await viewModel.deleteProject(project)
await viewModel.deleteProject(viewModel.projects[index])
}
} }
} }
} message: { project in
Text("This will permanently delete '\(project.name)' and all related time entries. This action cannot be undone.")
} }
.refreshable { .refreshable {
await viewModel.loadData() await viewModel.loadData()

View File

@@ -3,6 +3,8 @@ import SwiftUI
struct TimeEntriesView: View { struct TimeEntriesView: View {
@StateObject private var viewModel = TimeEntriesViewModel() @StateObject private var viewModel = TimeEntriesViewModel()
@State private var showAddEntry = false @State private var showAddEntry = false
@State private var entryToDelete: TimeEntry?
@State private var showDeleteConfirmation = false
var body: some View { var body: some View {
NavigationStack { NavigationStack {
@@ -50,12 +52,21 @@ struct TimeEntriesView: View {
TimeEntryRow(entry: entry) TimeEntryRow(entry: entry)
} }
.onDelete { indexSet in .onDelete { indexSet in
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 { Task {
for index in indexSet { await viewModel.deleteEntry(entry)
await viewModel.deleteEntry(viewModel.entries[index])
}
} }
} }
} message: { entry in
Text("This will permanently delete the time entry for '\(entry.project.name)'. This action cannot be undone.")
} }
.refreshable { .refreshable {
await viewModel.loadEntries() await viewModel.loadEntries()