From c228c26589666d5730a05c9cadca8ea430526a3c Mon Sep 17 00:00:00 2001 From: pulipakaa24 Date: Sun, 5 Apr 2026 00:02:44 -0500 Subject: [PATCH] Inventory screen selection mode update --- LabWise/InventoryView.swift | 104 +++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 18 deletions(-) diff --git a/LabWise/InventoryView.swift b/LabWise/InventoryView.swift index 4670827..a246775 100644 --- a/LabWise/InventoryView.swift +++ b/LabWise/InventoryView.swift @@ -28,13 +28,25 @@ final class InventoryViewModel { errorMessage = "Failed to delete" } } + + func deleteSelected(ids: Set) 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 { @State private var viewModel = InventoryViewModel() - @State private var showAddSheet = false - @State private var showScanSheet = false @State private var addMode: AddMode? + @State private var isSelectMode = false + @State private var selectedIDs: Set = [] enum AddMode: String, Identifiable { case manual, scan @@ -56,38 +68,94 @@ struct InventoryView: View { } else { List { ForEach(viewModel.chemicals) { chemical in - NavigationLink(destination: ChemicalDetailView(chemical: chemical)) { - ChemicalRowView(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) + } + .frame(maxWidth: .infinity, alignment: .leading) + .contentShape(Rectangle()) + } + .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 { indexSet in + .onDelete(perform: isSelectMode ? nil : { indexSet in for index in indexSet { let chemical = viewModel.chemicals[index] Task { await viewModel.delete(chemical: chemical) } } - } + }) } .refreshable { await viewModel.loadChemicals() } } } - .navigationTitle("Inventory") + .navigationTitle(isSelectMode ? "\(selectedIDs.count) Selected" : "Inventory") .toolbar { - ToolbarItem(placement: .topBarTrailing) { - Menu { - Button { - addMode = .scan - } label: { - Label("Scan Label", systemImage: "camera.fill") + if isSelectMode { + ToolbarItem(placement: .topBarLeading) { + Button("Cancel") { + withAnimation { + isSelectMode = false + selectedIDs = [] + } } - Button { - addMode = .manual + } + ToolbarItem(placement: .topBarTrailing) { + Button(role: .destructive) { + Task { + await viewModel.deleteSelected(ids: selectedIDs) + withAnimation { + isSelectMode = false + selectedIDs = [] + } + } } label: { - Label("Manual Entry", systemImage: "square.and.pencil") + Text("Delete (\(selectedIDs.count))") + } + .disabled(selectedIDs.isEmpty) + } + } else { + ToolbarItem(placement: .topBarTrailing) { + Menu { + Button { + addMode = .scan + } label: { + Label("Scan Label", systemImage: "camera.fill") + } + Button { + addMode = .manual + } label: { + Label("Manual Entry", systemImage: "square.and.pencil") + } + } label: { + Image(systemName: "plus") } - } label: { - Image(systemName: "plus") } } }