import SwiftUI import SwiftData struct ContentView: View { @State private var viewModel = MoneyCounterViewModel() @FocusState private var focusedField: UUID? @FocusState private var isFloatFocused: Bool @AppStorage("currentSessionNotes") private var sessionNotes: String = "" @Environment(\.modelContext) private var modelContext @State private var showingNotes = false @State private var showingHistory = false @State private var showingResetAlert = false var body: some View { VStack(spacing: 0) { // Header HStack { Menu { Button(action: { showingNotes = true }) { Label("Session Notes", systemImage: "note.text") } Button(action: { showingHistory = true }) { Label("Saved History", systemImage: "clock.arrow.circlepath") } Divider() Button(action: saveCurrentSession) { Label("Save This Total", systemImage: "square.and.arrow.down") } } label: { Image(systemName: "ellipsis.circle") .font(.title) .foregroundColor(.white) } Spacer() Text("Money Counter") .font(.title2).bold() .foregroundColor(.white) Spacer() Button(action: { showingResetAlert = true }) { Image(systemName: "arrow.counterclockwise") .font(.title) .foregroundColor(.white) } } .padding() .background(Color(red: 0.4, green: 0.7, blue: 0.4)) // Scrolling Denominations ScrollView { VStack(spacing: 24) { ForEach($viewModel.looseDenominations) { $denom in DenominationRow(denomination: $denom, focusedField: $focusedField, valueColor: Color(UIColor.systemGreen)) } VStack(alignment: .leading, spacing: 8) { Divider().background(Color.gray) Text("Rolls") .font(.title3) .bold() .foregroundColor(.secondary) } .padding(.top, 8) ForEach($viewModel.rollDenominations) { $denom in DenominationRow(denomination: $denom, focusedField: $focusedField, valueColor: Color(UIColor.systemRed)) } } .padding() } .contentShape(Rectangle()) .onTapGesture { dismissKeyboard() } // 1. Native drag-to-dismiss behavior .scrollDismissesKeyboard(.interactively) // Footer (Math & Totals) VStack(spacing: 8) { HStack { Text("Starting Float") .font(.headline) .foregroundColor(.secondary) Spacer() HStack(spacing: 2) { Text("$") .foregroundColor(.secondary) TextField("0.00", value: $viewModel.startingFloat, format: .number.precision(.fractionLength(2))) .keyboardType(.decimalPad) .focused($isFloatFocused) .multilineTextAlignment(.trailing) } .font(.headline) .padding(6) .frame(width: 100) .background(Color(UIColor.tertiarySystemFill), in: RoundedRectangle(cornerRadius: 8)) } if viewModel.startingFloat > 0 { HStack { Text(viewModel.discrepancy >= 0 ? "Deposit Amount" : "Shortage") .font(.headline) Spacer() Text(String(format: "$%.2f", viewModel.discrepancy)) .font(.headline) .foregroundColor(viewModel.discrepancy >= 0 ? .primary : .red) } } Divider() HStack { Text("Total") .font(.largeTitle) .bold() Spacer() Text(String(format: "$%.2f", viewModel.total)) .font(.largeTitle) .bold() } } .padding() .background(Color(UIColor.secondarySystemBackground)) .foregroundColor(.primary) } .background(Color(UIColor.systemBackground)) // 2. Adds the "Done" button directly above the keyboard .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { dismissKeyboard() } .font(.headline) .foregroundColor(.blue) } } .onChange(of: viewModel.looseDenominations) { _, _ in viewModel.saveCache() } .onChange(of: viewModel.rollDenominations) { _, _ in viewModel.saveCache() } .onChange(of: viewModel.startingFloat) { _, _ in viewModel.saveCache() } .sheet(isPresented: $showingNotes) { NotesView(notes: $sessionNotes, total: viewModel.total, discrepancy: viewModel.discrepancy, expectedFloat: viewModel.startingFloat) } .sheet(isPresented: $showingHistory) { HistoryView(viewModel: viewModel) } .confirmationDialog("Reset Everything?", isPresented: $showingResetAlert) { Button("Reset Counts & Notes", role: .destructive) { withAnimation { viewModel.reset() } sessionNotes = "" } Button("Reset Counts Only", role: .destructive) { withAnimation { viewModel.reset() } } Button("Cancel", role: .cancel) { } } } private func dismissKeyboard() { focusedField = nil isFloatFocused = false } private func saveCurrentSession() { let newSave = SavedCount( total: viewModel.total, startingFloat: viewModel.startingFloat, notes: sessionNotes, snapshotData: viewModel.generateSnapshot() ) modelContext.insert(newSave) } } // DenominationRow remains exactly the same as the previous iteration struct DenominationRow: View { @Binding var denomination: MoneyCounterViewModel.Denomination var focusedField: FocusState.Binding var valueColor: Color var body: some View { HStack(spacing: 16) { HStack { Text(formatValue(denomination.value)) .font(.title2).bold() .foregroundColor(valueColor) .frame(width: 70, alignment: .leading) Spacer() } .contentShape(Rectangle()) .onTapGesture { focusedField.wrappedValue = nil } Button(action: { denomination.count += 1 focusedField.wrappedValue = nil }) { Image(systemName: "plus.circle.fill") .foregroundColor(Color(UIColor.systemGreen)) .font(.title) } Button(action: { if denomination.count > 0 { denomination.count -= 1 } focusedField.wrappedValue = nil }) { Image(systemName: "minus.circle.fill") .foregroundColor(Color(UIColor.systemRed)) .font(.title) } TextField("0", value: $denomination.count, format: .number) .keyboardType(.numberPad) .multilineTextAlignment(.center) .font(.title2).bold() .frame(width: 80) .padding(.vertical, 4) .focused(focusedField, equals: denomination.id) .overlay( Rectangle() .frame(height: 1) .foregroundColor(Color(UIColor.separator)), alignment: .bottom ) .foregroundColor(.primary) } } private func formatValue(_ value: Double) -> String { if value >= 1.0 { return String(format: "%.1f", value) } else { return String(format: "%.2f", value) } } }