import SwiftUI import LabWiseKit @Observable final class InventoryViewModel { var chemicals: [Chemical] = [] var isLoading = false var errorMessage: String? private let client = ChemicalsClient() func loadChemicals() async { isLoading = true errorMessage = nil defer { isLoading = false } do { chemicals = try await client.list() } catch { errorMessage = "Failed to load chemicals" } } func delete(chemical: Chemical) async { do { try await client.delete(id: chemical.id) chemicals.removeAll { $0.id == chemical.id } } catch { 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 addMode: AddMode? @State private var isSelectMode = false @State private var selectedIDs: Set = [] @State private var showSpreadsheet = false enum AddMode: String, Identifiable { case manual, scan var id: String { rawValue } } var body: some View { NavigationStack { Group { if viewModel.isLoading && viewModel.chemicals.isEmpty { ProgressView("Loading...") .frame(maxWidth: .infinity, maxHeight: .infinity) } else if viewModel.chemicals.isEmpty { ScrollView { ContentUnavailableView( "No Chemicals", systemImage: "flask", description: Text("Add your first chemical using the + button.") ) } .refreshable { await viewModel.loadChemicals() } } else { List { ForEach(viewModel.chemicals) { chemical in 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(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(isSelectMode ? "\(selectedIDs.count) Selected" : "Inventory") .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: .topBarLeading) { Button { showSpreadsheet = true } label: { Image(systemName: "tablecells") } } 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") } } } } .alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) { Button("OK") { viewModel.errorMessage = nil } } message: { Text(viewModel.errorMessage ?? "") } } .sheet(item: $addMode) { mode in switch mode { case .manual: AddChemicalView { saved in addMode = nil if saved { Task { await viewModel.loadChemicals() } } } case .scan: ScanView() .onDisappear { Task { await viewModel.loadChemicals() } } } } .task { await viewModel.loadChemicals() } .fullScreenCover(isPresented: $showSpreadsheet) { SpreadsheetView(chemicals: viewModel.chemicals) { showSpreadsheet = false } } } }