Inventory screen selection mode update

This commit is contained in:
2026-04-05 00:02:44 -05:00
parent cd03416e3a
commit c228c26589

View File

@@ -28,13 +28,25 @@ final class InventoryViewModel {
errorMessage = "Failed to delete" errorMessage = "Failed to delete"
} }
} }
func deleteSelected(ids: Set<String>) async {
for id in ids {
guard let chemical = chemicals.first(where: { $0.id == id }) else { continue }
do {
try await client.delete(id: chemical.id)
chemicals.removeAll { $0.id == id }
} catch {
errorMessage = "Failed to delete some items"
}
}
}
} }
struct InventoryView: View { struct InventoryView: View {
@State private var viewModel = InventoryViewModel() @State private var viewModel = InventoryViewModel()
@State private var showAddSheet = false
@State private var showScanSheet = false
@State private var addMode: AddMode? @State private var addMode: AddMode?
@State private var isSelectMode = false
@State private var selectedIDs: Set<String> = []
enum AddMode: String, Identifiable { enum AddMode: String, Identifiable {
case manual, scan case manual, scan
@@ -56,24 +68,79 @@ struct InventoryView: View {
} else { } else {
List { List {
ForEach(viewModel.chemicals) { chemical in ForEach(viewModel.chemicals) { chemical in
NavigationLink(destination: ChemicalDetailView(chemical: chemical)) { if isSelectMode {
Button {
if selectedIDs.contains(chemical.id) {
selectedIDs.remove(chemical.id)
} else {
selectedIDs.insert(chemical.id)
}
} label: {
HStack(spacing: 12) {
Image(systemName: selectedIDs.contains(chemical.id) ? "checkmark.circle.fill" : "circle")
.foregroundStyle(selectedIDs.contains(chemical.id) ? .blue : .secondary)
.font(.title3)
.animation(.easeInOut(duration: 0.15), value: selectedIDs.contains(chemical.id))
ChemicalRowView(chemical: chemical) ChemicalRowView(chemical: chemical)
} }
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
} }
.onDelete { indexSet in .buttonStyle(.plain)
} else {
NavigationLink(destination: ChemicalDetailView(chemical: chemical)) {
ChemicalRowView(chemical: chemical)
.frame(maxWidth: .infinity, alignment: .leading) // 📍 Force it to fill the row
.contentShape(Rectangle()) // 📍 Make the transparent space tappable
}
.simultaneousGesture(
LongPressGesture(minimumDuration: 0.5).onEnded { _ in
withAnimation {
isSelectMode = true
selectedIDs = [chemical.id]
}
}
)
}
}
.onDelete(perform: isSelectMode ? nil : { indexSet in
for index in indexSet { for index in indexSet {
let chemical = viewModel.chemicals[index] let chemical = viewModel.chemicals[index]
Task { await viewModel.delete(chemical: chemical) } Task { await viewModel.delete(chemical: chemical) }
} }
} })
} }
.refreshable { .refreshable {
await viewModel.loadChemicals() await viewModel.loadChemicals()
} }
} }
} }
.navigationTitle("Inventory") .navigationTitle(isSelectMode ? "\(selectedIDs.count) Selected" : "Inventory")
.toolbar { .toolbar {
if isSelectMode {
ToolbarItem(placement: .topBarLeading) {
Button("Cancel") {
withAnimation {
isSelectMode = false
selectedIDs = []
}
}
}
ToolbarItem(placement: .topBarTrailing) {
Button(role: .destructive) {
Task {
await viewModel.deleteSelected(ids: selectedIDs)
withAnimation {
isSelectMode = false
selectedIDs = []
}
}
} label: {
Text("Delete (\(selectedIDs.count))")
}
.disabled(selectedIDs.isEmpty)
}
} else {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
Menu { Menu {
Button { Button {
@@ -91,6 +158,7 @@ struct InventoryView: View {
} }
} }
} }
}
.alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) { .alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) {
Button("OK") { viewModel.errorMessage = nil } Button("OK") { viewModel.errorMessage = nil }
} message: { } message: {