From ba850dda56ff17929571e078115851a4a8edbbdf Mon Sep 17 00:00:00 2001 From: James Date: Fri, 6 Mar 2026 17:04:07 -0800 Subject: [PATCH 1/7] modified different views and struct structures to allow API integration and load data from backend DB --- SWFrontUI/ShopwiseFrontEndUI/Ingredient.swift | 0 SWFrontUI/ShopwiseFrontEndUI/LocationManager.swift | 0 SWFrontUI/ShopwiseFrontEndUI/Product.swift | 0 SWFrontUI/ShopwiseFrontEndUI/Recipe.swift | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 SWFrontUI/ShopwiseFrontEndUI/Ingredient.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/LocationManager.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/Product.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/Recipe.swift diff --git a/SWFrontUI/ShopwiseFrontEndUI/Ingredient.swift b/SWFrontUI/ShopwiseFrontEndUI/Ingredient.swift new file mode 100644 index 0000000..e69de29 diff --git a/SWFrontUI/ShopwiseFrontEndUI/LocationManager.swift b/SWFrontUI/ShopwiseFrontEndUI/LocationManager.swift new file mode 100644 index 0000000..e69de29 diff --git a/SWFrontUI/ShopwiseFrontEndUI/Product.swift b/SWFrontUI/ShopwiseFrontEndUI/Product.swift new file mode 100644 index 0000000..e69de29 diff --git a/SWFrontUI/ShopwiseFrontEndUI/Recipe.swift b/SWFrontUI/ShopwiseFrontEndUI/Recipe.swift new file mode 100644 index 0000000..e69de29 From 6ea6e02b844d6bab032d7129091ea321bfcde549 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 6 Mar 2026 17:06:00 -0800 Subject: [PATCH 2/7] modified struct and views to better match backend DB data structure --- .../project.pbxproj | 2 + .../UserInterfaceState.xcuserstate | Bin 69220 -> 111595 bytes SWFrontUI/ShopwiseFrontEndUI/CartStore.swift | 27 +- .../ShopwiseFrontEndUI/ContentView.swift | 454 ++++++++++++++---- SWFrontUI/ShopwiseFrontEndUI/Ingredient.swift | 26 + .../ShopwiseFrontEndUI/LocationManager.swift | 48 ++ SWFrontUI/ShopwiseFrontEndUI/Product.swift | 29 ++ SWFrontUI/ShopwiseFrontEndUI/Recipe.swift | 29 ++ .../ShopwiseFrontEndUI/StoreLocation.swift | 35 +- 9 files changed, 549 insertions(+), 101 deletions(-) diff --git a/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.pbxproj b/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.pbxproj index be25d42..7a94a70 100644 --- a/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.pbxproj +++ b/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.pbxproj @@ -397,6 +397,7 @@ CURRENT_PROJECT_VERSION = 1; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "ShopWise uses your location to show nearby grocery stores."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -428,6 +429,7 @@ CURRENT_PROJECT_VERSION = 1; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "ShopWise uses your location to show nearby grocery stores."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; diff --git a/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.xcworkspace/xcuserdata/jchang.xcuserdatad/UserInterfaceState.xcuserstate b/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.xcworkspace/xcuserdata/jchang.xcuserdatad/UserInterfaceState.xcuserstate index 5660a382d1604af5c42fcb41fced0294dcccd602..e184280c7dca429153a5eb6b19805c85d0391553 100644 GIT binary patch literal 111595 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 L|9=gsKjr@bAx8zT literal 69220 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 IynW#R0rv;C(*OVf diff --git a/SWFrontUI/ShopwiseFrontEndUI/CartStore.swift b/SWFrontUI/ShopwiseFrontEndUI/CartStore.swift index 8cc1cde..267f85d 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/CartStore.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/CartStore.swift @@ -3,13 +3,13 @@ import Combine // Simple cart line item model (separate from your SwiftData Item model) struct CartLineItem: Identifiable, Hashable { - let id: UUID + let id: String let name: String let unit: String let price: Double var quantity: Int - init(id: UUID = UUID(), name: String, unit: String, price: Double, quantity: Int = 1) { + init(id: String = String(), name: String, unit: String, price: Double, quantity: Int = 1) { self.id = id self.name = name self.unit = unit @@ -23,7 +23,6 @@ struct CartLineItem: Identifiable, Hashable { final class CartStore: ObservableObject { @Published private(set) var items: [CartLineItem] = [] - //amount of cart items var itemCount: Int { items.reduce(0) { $0 + $1.quantity } } @@ -32,11 +31,27 @@ final class CartStore: ObservableObject { items.reduce(0) { $0 + $1.lineTotal } } - func add(name: String, unit: String, price: Double) { - if let idx = items.firstIndex(where: { $0.name == name && $0.unit == unit && $0.price == price }) { + func add(product: Product) { + add(id: product.id, name: product.name, unit: product.unit, price: product.price) + } + + func add(ingredient: Ingredient) { + add(id: ingredient.id, name: ingredient.name, unit: ingredient.unit, price: ingredient.price) + } + + func add(id: String, name: String, unit: String, price: Double) { + if let idx = items.firstIndex(where: { $0.id == id }) { items[idx].quantity += 1 } else { - items.append(CartLineItem(name: name, unit: unit, price: price)) + items.append( + CartLineItem( + id: id, + name: name, + unit: unit, + price: price, + quantity: 1 + ) + ) } } diff --git a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift index 12e2ffa..2db350d 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift @@ -88,11 +88,35 @@ struct ProfileView: View { } struct SettingsView: View { + @AppStorage("notificationsEnabled") private var notificationsEnabled = true + @AppStorage("locationEnabled") private var locationEnabled = true + @AppStorage("darkModeEnabled") private var darkModeEnabled = false + var body: some View { List { Section("Preferences") { - Text("Settings options will go here") - .foregroundStyle(.secondary) + Toggle("Enable Notifications", isOn: $notificationsEnabled) + Toggle("Use Location Services", isOn: $locationEnabled) + Toggle("Dark Mode", isOn: $darkModeEnabled) + } + + Section("Support") { + Button("Help Center") { + // placeholder + } + + Button("About ShopWise") { + // placeholder + } + } + + Section("App Info") { + HStack { + Text("Version") + Spacer() + Text("1.0") + .foregroundStyle(.secondary) + } } } .navigationTitle("Settings") @@ -133,6 +157,7 @@ struct SearchView: View { price: 3.99 ) { cartStore.add( + id: "prod_straw", name: "Organic Strawberries", unit: "1 lb", price: 3.99 @@ -146,19 +171,7 @@ struct SearchView: View { price: 5.99 ) { cartStore.add( - name: "Cuties Oranges", - unit: "5 lb", - price: 5.99 - ) - } - - ItemCardView( - imageName: "", - title: "Cuties Oranges", - unit: "5 lb", - price: 5.99 - ) { - cartStore.add( + id: "prod_cuties_orange", name: "Cuties Oranges", unit: "5 lb", price: 5.99 @@ -182,76 +195,137 @@ struct SearchView: View { struct MapView: View { + @StateObject private var locationManager = LocationManager() + @State private var position: MapCameraPosition = .region( MKCoordinateRegion( center: CLLocationCoordinate2D(latitude: 33.978194, longitude: -117.367861), span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05) ) ) - - private struct DroppedPin: Identifiable { - let id = UUID() - let coordinate: CLLocationCoordinate2D - } - @State private var droppedPins: [DroppedPin] = [] - + + @State private var stores: [StoreLocation] = [ + StoreLocation( + id: "store_walmart", + name: "Walmart Supercenter", + latitude: 33.988194, + longitude: -117.361861, + address: nil, + chain: "Walmart" + ), + StoreLocation( + id: "store_ralphs", + name: "Ralphs", + latitude: 33.970194, + longitude: -117.363861, + address: nil, + chain: "Ralphs" + ) + ] + var body: some View { List { - Section() { + Section { CardContainer { - MapReader { proxy in - Map(position: $position) { - ForEach(droppedPins) { pin in - Marker("Selected", coordinate: pin.coordinate) - } - } - .frame(height: 260) - .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) - .onTapGesture { point in - if let coord = proxy.convert(point, from: .local) { - droppedPins.append(DroppedPin(coordinate: coord)) - } + Map(position: $position) { + UserAnnotation() + + ForEach(stores) { store in + Marker(store.name, coordinate: store.coordinate) } } + .frame(height: 260) + .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) } .listRowInsets(EdgeInsets()) } Section("Nearby Grocery Stores") { - HStack { - Image(systemName: "mappin.circle.fill") - VStack(alignment: .leading) { - Text("Walmart Supercenter") - Text("2.5 mi") - .foregroundStyle(.secondary) + ForEach(stores) { store in + Button { + position = .region( + MKCoordinateRegion( + center: store.coordinate, + span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) + ) + ) + } label: { + HStack { + Image(systemName: "mappin.circle.fill") + + VStack(alignment: .leading) { + Text(store.name) + Text(store.chain ?? "Store") + .foregroundStyle(.secondary) + .font(.subheadline) + } + + Spacer() + } } + .buttonStyle(.plain) } + } + + Section("Location") { + Button { + centerOnUser() + } label: { + Label("Center on Me", systemImage: "location.fill") + .frame(maxWidth: .infinity) + } + .disabled(locationManager.userLocation == nil) HStack { - Image(systemName: "mappin.circle.fill") - VStack(alignment: .leading) { - Text("Ralph's") - Text("4.2 mi") - .foregroundStyle(.secondary) - } + Text("Permission") Spacer() + Text(locationStatusText) + .foregroundStyle(.secondary) } } } .navigationTitle("ShopWise") - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - HStack(spacing: 14) { - NavigationLink { SettingsView() } label: { Image(systemName: "gearshape") } - NavigationLink { ProfileView() } label: { Image(systemName: "person.crop.circle") } - } - } + .appToolbar() + .onAppear { + locationManager.requestPermission() + } + .onChange(of: locationManager.userLocation?.latitude ?? 0) { _, newLatitude in + guard newLatitude != 0 else { return } + centerOnUser() } } -} + private var locationStatusText: String { + switch locationManager.authorizationStatus { + case .notDetermined: + return "Not Requested" + case .restricted: + return "Restricted" + case .denied: + return "Denied" + case .authorizedAlways: + return "Allowed" + case .authorizedWhenInUse: + return "Allowed" + @unknown default: + return "Unknown" + } + } + + private func centerOnUser() { + guard let user = locationManager.userLocation else { return } + + position = .region( + MKCoordinateRegion( + center: user, + span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) + ) + ) + } +} struct CartView: View { + @State private var showShoppingList = false @EnvironmentObject private var cartStore: CartStore var body: some View { @@ -281,7 +355,7 @@ struct CartView: View { } label: { Image(systemName: "minus.circle") } - .buttonStyle(.borderless) // ✅ + .buttonStyle(.borderless) Text("\(item.quantity)") .font(.headline) @@ -292,7 +366,7 @@ struct CartView: View { } label: { Image(systemName: "plus.circle") } - .buttonStyle(.borderless) // ✅ + .buttonStyle(.borderless) } } .padding(.vertical, 4) @@ -316,7 +390,7 @@ struct CartView: View { Section { Button { - // placeholder: checkout + showShoppingList = true } label: { Text("Checkout") .frame(maxWidth: .infinity) @@ -326,33 +400,53 @@ struct CartView: View { } .navigationTitle("Cart") .appToolbar() + .navigationDestination(isPresented: $showShoppingList) { + ShoppingListView() + } } } -struct Recipe: Identifiable, Hashable { - let id = UUID() - let name: String - let difficulty: String - let minutes: Int - let ingredients: [String] -} - struct RecipeView: View { @State private var query = "" - @State private var expandedID: Recipe.ID? = nil //which recipe is expanded + @State private var expandedID: String? = nil + @State private var excludedByRecipe: [String: Set] = [:] + + @EnvironmentObject private var cartStore: CartStore private let recipes: [Recipe] = [ Recipe( + id: "recipe_spaghetti", name: "Spaghetti", difficulty: "Easy", minutes: 25, - ingredients: ["Spaghetti pasta", "Marinara sauce", "Garlic", "Onion", "Olive oil", "Parmesan"] + imageURL: nil, + imageName: "spaghetti", + ingredients: [ + Ingredient(id: "ing_spaghetti", name: "Spaghetti pasta", unit: "1 lb", price: 2.49, imageURL: nil, imageName: nil), + Ingredient(id: "ing_marinara", name: "Marinara sauce", unit: "24 oz", price: 3.99, imageURL: nil, imageName: nil), + Ingredient(id: "ing_garlic", name: "Garlic", unit: "3 ct", price: 1.29, imageURL: nil, imageName: nil), + Ingredient(id: "ing_onion", name: "Onion", unit: "1 ct", price: 0.99, imageURL: nil, imageName: nil), + Ingredient(id: "ing_olive_oil", name: "Olive oil", unit: "16.9 oz", price: 6.49, imageURL: nil, imageName: nil), + Ingredient(id: "ing_parmesan", name: "Parmesan", unit: "6 oz", price: 4.49, imageURL: nil, imageName: nil) + ] ), Recipe( + id: "recipe_orange_chicken", name: "Orange Chicken", difficulty: "Medium", minutes: 35, - ingredients: ["Chicken breast", "Cornstarch", "Orange juice", "Soy sauce", "Honey", "Garlic", "Ginger", "Rice"] + imageURL: nil, + imageName: "orangechicken", + ingredients: [ + Ingredient(id: "ing_chicken_breast", name: "Chicken breast", unit: "1 lb", price: 5.99, imageURL: nil, imageName: nil), + Ingredient(id: "ing_cornstarch", name: "Cornstarch", unit: "16 oz", price: 1.99, imageURL: nil, imageName: nil), + Ingredient(id: "ing_orange_juice", name: "Orange juice", unit: "12 oz", price: 3.49, imageURL: nil, imageName: nil), + Ingredient(id: "ing_soy_sauce", name: "Soy sauce", unit: "10 oz", price: 2.29, imageURL: nil, imageName: nil), + Ingredient(id: "ing_honey", name: "Honey", unit: "12 oz", price: 4.79, imageURL: nil, imageName: nil), + Ingredient(id: "ing_garlic", name: "Garlic", unit: "3 ct", price: 1.29, imageURL: nil, imageName: nil), + Ingredient(id: "ing_ginger", name: "Ginger", unit: "3 oz", price: 1.49, imageURL: nil, imageName: nil), + Ingredient(id: "ing_rice", name: "Rice", unit: "2 lb", price: 3.79, imageURL: nil, imageName: nil) + ] ) ] @@ -365,7 +459,6 @@ struct RecipeView: View { List { ForEach(filtered) { recipe in Section { - //Tappable card that expands/collapses Button { withAnimation(.snappy) { expandedID = (expandedID == recipe.id) ? nil : recipe.id @@ -373,13 +466,21 @@ struct RecipeView: View { } label: { CardContainer { HStack(spacing: 12) { - Circle() - .fill(Color(.systemGray5)) - .frame(width: 54, height: 54) - .overlay( - Image(systemName: "fork.knife") - .foregroundStyle(.secondary) - ) + if let imageName = recipe.imageName { + Image(imageName) + .resizable() + .scaledToFill() + .frame(width: 54, height: 54) + .clipShape(RoundedRectangle(cornerRadius: 12)) + } else { + Circle() + .fill(Color(.systemGray5)) + .frame(width: 54, height: 54) + .overlay( + Image(systemName: "fork.knife") + .foregroundStyle(.secondary) + ) + } VStack(alignment: .leading, spacing: 4) { Text(recipe.name) @@ -398,23 +499,56 @@ struct RecipeView: View { } .buttonStyle(.plain) - // Dropdown ingredients(replace hardcoded data) if expandedID == recipe.id { CardContainer { + if let imageName = recipe.imageName { + Image(imageName) + .resizable() + .scaledToFill() + .frame(height: 160) + .clipped() + .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) + } + Text("Ingredients") .font(.headline) - ForEach(recipe.ingredients, id: \.self) { item in - HStack(spacing: 10) { - Image(systemName: "checkmark.circle") - .foregroundStyle(.secondary) - Text(item) + ForEach(recipe.ingredients) { ingredient in + let isExcluded = excludedByRecipe[recipe.id, default: []].contains(ingredient.id) + + Button { + if isExcluded { + excludedByRecipe[recipe.id, default: []].remove(ingredient.id) + } else { + excludedByRecipe[recipe.id, default: []].insert(ingredient.id) + } + } label: { + HStack(spacing: 10) { + Image(systemName: isExcluded ? "square" : "checkmark.square.fill") + .imageScale(.large) + + VStack(alignment: .leading, spacing: 2) { + Text(ingredient.name) + .strikethrough(isExcluded) + Text(ingredient.unit) + .font(.subheadline) + .foregroundStyle(.secondary) + } + + Spacer() + + Text(String(format: "$%.2f", ingredient.price)) + .foregroundStyle(.secondary) + .monospacedDigit() + } + .padding(.vertical, 2) + .contentShape(Rectangle()) } - .padding(.vertical, 2) + .buttonStyle(.plain) } Button { - // placeholder: add ingredients to cart + addIngredientsToCart(recipe) } label: { Label("Add Ingredients to Cart", systemImage: "cart.badge.plus") .frame(maxWidth: .infinity) @@ -439,28 +573,170 @@ struct RecipeView: View { } } } + + private func addIngredientsToCart(_ recipe: Recipe) { + let excluded = excludedByRecipe[recipe.id, default: []] + + for ingredient in recipe.ingredients where !excluded.contains(ingredient.id) { + cartStore.add( + id: ingredient.id, + name: ingredient.name, + unit: ingredient.unit, + price: ingredient.price + ) + } + } } +struct ShoppingListView: View { + @EnvironmentObject private var cartStore: CartStore + @Environment(\.dismiss) private var dismiss + + @State private var checkedIDs: Set = [] + + private var checkedTotal: Double { + cartStore.items + .filter { checkedIDs.contains($0.id) } + .reduce(0) { $0 + ($1.price * Double($1.quantity)) } + } + + private var checkedCount: Int { + cartStore.items.filter { checkedIDs.contains($0.id) }.count + } + + var body: some View { + List { + Section("Shopping List") { + if cartStore.items.isEmpty { + Text("No items to shop for.") + .foregroundStyle(.secondary) + } else { + ForEach(cartStore.items) { item in + Button { + toggle(item.id) + } label: { + HStack(spacing: 12) { + Image(systemName: checkedIDs.contains(item.id) ? "checkmark.circle.fill" : "circle") + .imageScale(.large) + + VStack(alignment: .leading, spacing: 2) { + Text(item.name) + .strikethrough(checkedIDs.contains(item.id)) + Text("\(item.quantity) × \(item.unit)") + .font(.subheadline) + .foregroundStyle(.secondary) + } + + Spacer() + + Text(String(format: "$%.2f", item.price * Double(item.quantity))) + .foregroundStyle(.secondary) + .monospacedDigit() + } + } + .buttonStyle(.plain) + } + } + } + + if !cartStore.items.isEmpty { + Section { + Button("Mark All") { + checkedIDs = Set(cartStore.items.map { $0.id }) + } + + Button("Clear Checks") { + checkedIDs.removeAll() + } + + Button(role: .destructive) { + cartStore.clear() + checkedIDs.removeAll() + dismiss() + } label: { + Text("Clear Cart") + } + } + } + } + .navigationTitle("Shopping List") + .appToolbar() + .safeAreaInset(edge: .bottom) { + if !cartStore.items.isEmpty { + VStack(spacing: 0) { + Divider() + + VStack(spacing: 10) { + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Checked: \(checkedCount)/\(cartStore.items.count)") + .font(.subheadline) + .foregroundStyle(.secondary) + Text("Current Total") + .font(.headline) + } + + Spacer() + + Text(String(format: "$%.2f", checkedTotal)) + .font(.title3) + .monospacedDigit() + } + + Button { + cartStore.clear() + checkedIDs.removeAll() + dismiss() + } label: { + Label("Finish Trip", systemImage: "checkmark.seal.fill") + .frame(maxWidth: .infinity) + } + .buttonStyle(.borderedProminent) + } + .padding(.horizontal, 16) + .padding(.vertical, 12) + .background(.ultraThinMaterial) + } + } + } + } + + private func toggle(_ id: String) { + if checkedIDs.contains(id) { + checkedIDs.remove(id) + } else { + checkedIDs.insert(id) + } + } +} struct ContentView: View { @StateObject private var cartStore = CartStore() + enum Tab: Hashable { + case search, recipe, map, cart + } + + @State private var selectedTab: Tab = .search + var body: some View { - TabView { + TabView(selection: $selectedTab) { NavigationStack { SearchView() } .tabItem { Label("Search", systemImage: "magnifyingglass") } + .tag(Tab.search) NavigationStack { RecipeView() } .tabItem { Label("Recipe", systemImage: "book") } + .tag(Tab.recipe) NavigationStack { MapView() } .tabItem { Label("Map", systemImage: "map") } + .tag(Tab.map) NavigationStack { CartView() } .tabItem { Label("Cart", systemImage: "cart") } - .when(cartStore.itemCount > 0) { view in - view.badge(cartStore.itemCount) - } + .tag(Tab.cart) + .badge(cartStore.itemCount) } .environmentObject(cartStore) } diff --git a/SWFrontUI/ShopwiseFrontEndUI/Ingredient.swift b/SWFrontUI/ShopwiseFrontEndUI/Ingredient.swift index e69de29..76813ea 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/Ingredient.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/Ingredient.swift @@ -0,0 +1,26 @@ +import Foundation + +struct Ingredient: Identifiable, Codable, Hashable { + let id: String + let name: String + let unit: String + let price: Double + let imageURL: String? + let imageName: String? + + init( + id: String, + name: String, + unit: String, + price: Double, + imageURL: String? = nil, + imageName: String? = nil + ) { + self.id = id + self.name = name + self.unit = unit + self.price = price + self.imageURL = imageURL + self.imageName = imageName + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/LocationManager.swift b/SWFrontUI/ShopwiseFrontEndUI/LocationManager.swift index e69de29..e3d3753 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/LocationManager.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/LocationManager.swift @@ -0,0 +1,48 @@ +import Foundation +import CoreLocation +import Combine + +final class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { + private let manager = CLLocationManager() + + @Published var authorizationStatus: CLAuthorizationStatus = .notDetermined + @Published var userLocation: CLLocationCoordinate2D? + + override init() { + super.init() + manager.delegate = self + manager.desiredAccuracy = kCLLocationAccuracyBest + } + + func requestPermission() { + manager.requestWhenInUseAuthorization() + } + + func startUpdating() { + manager.startUpdatingLocation() + } + + func stopUpdating() { + manager.stopUpdatingLocation() + } + + func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { + authorizationStatus = manager.authorizationStatus + + switch manager.authorizationStatus { + case .authorizedWhenInUse, .authorizedAlways: + startUpdating() + case .denied, .restricted: + stopUpdating() + case .notDetermined: + break + @unknown default: + break + } + } + + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + guard let last = locations.last else { return } + userLocation = last.coordinate + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/Product.swift b/SWFrontUI/ShopwiseFrontEndUI/Product.swift index e69de29..378e4be 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/Product.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/Product.swift @@ -0,0 +1,29 @@ +import Foundation + +struct Product: Identifiable, Codable, Hashable { + let id: String + let name: String + let category: String + let unit: String + let price: Double + let imageURL: String? + let imageName: String? + + init( + id: String, + name: String, + category: String, + unit: String, + price: Double, + imageURL: String? = nil, + imageName: String? = nil + ) { + self.id = id + self.name = name + self.category = category + self.unit = unit + self.price = price + self.imageURL = imageURL + self.imageName = imageName + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/Recipe.swift b/SWFrontUI/ShopwiseFrontEndUI/Recipe.swift index e69de29..320d317 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/Recipe.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/Recipe.swift @@ -0,0 +1,29 @@ +import Foundation + +struct Recipe: Identifiable, Codable, Hashable { + let id: String + let name: String + let difficulty: String + let minutes: Int + let imageURL: String? + let imageName: String? + let ingredients: [Ingredient] + + init( + id: String, + name: String, + difficulty: String, + minutes: Int, + imageURL: String? = nil, + imageName: String? = nil, + ingredients: [Ingredient] + ) { + self.id = id + self.name = name + self.difficulty = difficulty + self.minutes = minutes + self.imageURL = imageURL + self.imageName = imageName + self.ingredients = ingredients + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/StoreLocation.swift b/SWFrontUI/ShopwiseFrontEndUI/StoreLocation.swift index 11ae318..9232908 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/StoreLocation.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/StoreLocation.swift @@ -1,8 +1,31 @@ +import Foundation import CoreLocation -//struct StoreLocation: Identifiable, Hashable { - //let id: UUID - //let name: String - //let coordinate: CLLocationCoordinate2D - //} - +struct StoreLocation: Identifiable, Codable, Hashable { + let id: String + let name: String + let latitude: Double + let longitude: Double + let address: String? + let chain: String? + + init( + id: String, + name: String, + latitude: Double, + longitude: Double, + address: String? = nil, + chain: String? = nil + ) { + self.id = id + self.name = name + self.latitude = latitude + self.longitude = longitude + self.address = address + self.chain = chain + } + + var coordinate: CLLocationCoordinate2D { + CLLocationCoordinate2D(latitude: latitude, longitude: longitude) + } +} From 4586e3f6fdc54f95a07b87b07ce0ea6c216b94b9 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 8 Mar 2026 18:50:01 -0700 Subject: [PATCH 3/7] Merged with nicks branch, loads item with supabase --- .../ShopwiseFrontEndUI/ContentView.swift | 327 +++++++++++++++--- 1 file changed, 281 insertions(+), 46 deletions(-) diff --git a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift index 2db350d..6a59f77 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift @@ -124,76 +124,310 @@ struct SettingsView: View { } struct SearchView: View { + @EnvironmentObject private var auth: AuthManager @EnvironmentObject private var cartStore: CartStore @State private var query = "" - @State private var selectedCategory: String = "All" + @State private var selectedFilter: String = "All" + @State private var results: [WalmartItem] = [] + @State private var isLoading = false + @State private var errorText: String? = nil - private let categories = ["All", "Fruits", "Vegetables", "Dairy", "Bakery"] + @State private var pageSize = 50 + @State private var offset = 0 + + @State private var isLoadingMore = false + @State private var hasMore = true + + private let filters = ["All", "Ingredients", "Non-Ingredients"] var body: some View { - List { - // Category row - Section { + ScrollView { + VStack(alignment: .leading, spacing: 14) { + + // Title + Text("ShopWise") + .font(.system(size: 34, weight: .bold)) + .padding(.top, 6) + + // Search bar (custom like your right screenshot) + HStack(spacing: 10) { + Image(systemName: "magnifyingglass") + .foregroundStyle(.secondary) + + TextField("Search products...", text: $query) + .textInputAutocapitalization(.never) + .autocorrectionDisabled(true) + + if !query.isEmpty { + Button { + query = "" + searchItems(reset: true) + } label: { + Image(systemName: "xmark") + .font(.system(size: 12, weight: .bold)) + .foregroundStyle(.secondary) + .padding(10) + .background(.ultraThinMaterial) + .clipShape(Circle()) + } + } + } + .padding(.horizontal, 14) + .padding(.vertical, 12) + .background(Color(.systemGray6)) + .clipShape(RoundedRectangle(cornerRadius: 18)) + + // Category chips row ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 10) { - ForEach(categories, id: \.self) { cat in - CategoryChip(title: cat, isSelected: selectedCategory == cat) { - selectedCategory = cat + ForEach(filters, id: \.self) { f in + SearchCategoryChip(title: f, isSelected: selectedFilter == f) { + selectedFilter = f + searchItems(reset: true) // ✅ ADD HERE } } } - .padding(.vertical, 4) + .padding(.vertical, 2) + } + + // Content + if isLoading { + HStack { + Spacer() + ProgressView("Loading…") + Spacer() + } + .padding(.top, 24) + } else if let errorText { + Text(errorText) + .foregroundStyle(.red) + .padding(.top, 10) + } else { + LazyVStack(spacing: 14) { + ForEach(Array(filteredResults.enumerated()), id: \.element.id) { index, item in + ProductCard( + imageURL: bestImageURL(for: item), + title: item.name, + unit: unitText(for: item), + priceText: formatPrice(item.retail_price), + onAdd: { + cartStore.add(id: String(item.id), name: item.name, unit: unitText(for: item) ?? "", price: item.retail_price ?? 0) + } + ) + .onAppear { + // When the LAST item appears, load more + if index == filteredResults.count - 1 { + searchItems(reset: false) + } + } + } } + .padding(.top, 4) } - .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)) + + Spacer(minLength: 20) } + .padding(.horizontal, 16) + .padding(.bottom, 20) + } + .background(Color(.systemBackground)) + .navigationBarTitleDisplayMode(.inline) + .appToolbar() // <-- uses your existing Settings/Profile toolbar + .onChange(of: query) { _, _ in + searchItems(reset: true) + } + .task { + searchItems(reset: true) + } + } - // Big product card placeholder (like the “milk/strawberries” card) - Section { - ItemCardView( - imageName: "strawberry", - title: "Organic Strawberries", - unit: "1 lb", - price: 3.99 - ) { - cartStore.add( - id: "prod_straw", - name: "Organic Strawberries", - unit: "1 lb", - price: 3.99 - ) + // MARK: - Filtering + private var filteredResults: [WalmartItem] { + switch selectedFilter { + case "Ingredients": + return results.filter { $0.ingredient == true } + case "Non-Ingredients": + return results.filter { $0.ingredient == false } + default: + return results + } + } + + // MARK: - Helpers (image/price/unit) + private func bestImageURL(for item: WalmartItem) -> URL? { + if let s = item.thumbnailImage, let url = URL(string: s), !s.isEmpty { return url } + if let s = item.mediumImage, let url = URL(string: s), !s.isEmpty { return url } + if let s = item.largeImage, let url = URL(string: s), !s.isEmpty { return url } + return nil + } + + private func formatPrice(_ p: Double?) -> String { + guard let p else { return "Price unavailable" } + return String(format: "$%.2f", p) + } + + private func unitText(for item: WalmartItem) -> String? { + // Your Supabase data doesn't include a unit column. + // If classifiers contains something unit-like, show it; otherwise hide. + let cls = (item.classifiers ?? "").trimmingCharacters(in: .whitespacesAndNewlines) + if cls.isEmpty { return nil } + return cls + } + + // MARK: - Supabase fetch + private func searchItems(reset: Bool = true) { + let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines) + + // If we're already loading more, don't double-fire + if isLoadingMore { return } + + if reset { + offset = 0 + results = [] + hasMore = true + } else { + // if we already know there are no more rows, stop + if !hasMore { return } + } + + if reset { + isLoading = true + } else { + isLoadingMore = true + } + errorText = nil + + Task { + do { + let ingredientOnly: Bool? = { + switch selectedFilter { + case "Ingredients": return true + case "Non-Ingredients": return false + default: return nil + } + }() + + let newItems = try await auth.fetchWalmartItems( + search: trimmed.isEmpty ? nil : trimmed, + ingredientOnly: ingredientOnly, // ✅ NEW + limit: pageSize, + offset: offset + ) + + await MainActor.run { + // Append results + results.append(contentsOf: newItems) + + // Move offset forward + offset += newItems.count + + // If we got fewer than pageSize, we reached the end + if newItems.count < pageSize { hasMore = false } + + isLoading = false + isLoadingMore = false } - - ItemCardView( - imageName: "cutiesorange", - title: "Cuties Oranges", - unit: "5 lb", - price: 5.99 - ) { - cartStore.add( - id: "prod_cuties_orange", - name: "Cuties Oranges", - unit: "5 lb", - price: 5.99 - ) + } catch { + await MainActor.run { + errorText = "Search failed: \(error.localizedDescription)" + isLoading = false + isLoadingMore = false } } } - .listStyle(.plain) - .navigationTitle("ShopWise") - .searchable(text: $query, prompt: "Search products…") - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - HStack(spacing: 14) { - NavigationLink { SettingsView() } label: { Image(systemName: "gearshape") } - NavigationLink { ProfileView() } label: { Image(systemName: "person.crop.circle") } + } +} + + +struct SearchCategoryChip: View { + let title: String + let isSelected: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + Text(title) + .font(.subheadline.weight(.semibold)) + .padding(.horizontal, 14) + .padding(.vertical, 8) + .background(isSelected ? Color.blue.opacity(0.18) : Color(.systemGray6)) + .foregroundStyle(isSelected ? Color.blue : Color.primary) + .clipShape(Capsule()) + } + .buttonStyle(.plain) + } +} + +struct ProductCard: View { + let imageURL: URL? + let title: String + let unit: String? + let priceText: String + let onAdd: () -> Void + + var body: some View { + HStack(spacing: 14) { + AsyncImage(url: imageURL) { phase in + switch phase { + case .empty: + ProgressView() + .frame(width: 74, height: 74) + case .success(let image): + image + .resizable() + .scaledToFill() + .frame(width: 74, height: 74) + .clipped() + case .failure: + Image(systemName: "photo") + .frame(width: 74, height: 74) + .foregroundStyle(.secondary) + @unknown default: + EmptyView() + .frame(width: 74, height: 74) } } + .background(Color(.systemGray6)) + .clipShape(RoundedRectangle(cornerRadius: 16)) + + VStack(alignment: .leading, spacing: 6) { + Text(title) + .font(.headline) + .lineLimit(2) + + if let unit, !unit.isEmpty { + Text(unit) + .font(.subheadline) + .foregroundStyle(.secondary) + .lineLimit(1) + } + + Text(priceText) + .font(.headline) + } + + Spacer() + + Button(action: onAdd) { + HStack(spacing: 8) { + Image(systemName: "cart.badge.plus") + Text("Add") + } + .font(.subheadline.weight(.semibold)) + .padding(.horizontal, 14) + .padding(.vertical, 10) + .foregroundStyle(.white) + .background(Color.blue) + .clipShape(Capsule()) + } + .buttonStyle(.plain) } + .padding(14) + .background(Color(.systemGray6).opacity(0.65)) + .clipShape(RoundedRectangle(cornerRadius: 18)) } } - struct MapView: View { @StateObject private var locationManager = LocationManager() @@ -752,3 +986,4 @@ struct ContentView: View { AuthView() .environmentObject(AuthManager()) } + From b10f5389f9e36da12df0c9ae67cd3f5a7dd78301 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 8 Mar 2026 18:58:39 -0700 Subject: [PATCH 4/7] applied changes from nicks branch --- .../UserInterfaceState.xcuserstate | Bin 111595 -> 113624 bytes .../ShopwiseFrontEndUI/AuthManager.swift | 91 +++++++++++++++++- .../ShopwiseFrontEndUI/WalmartItems.swift | 20 ++++ 3 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 SWFrontUI/ShopwiseFrontEndUI/WalmartItems.swift diff --git a/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.xcworkspace/xcuserdata/jchang.xcuserdatad/UserInterfaceState.xcuserstate b/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.xcworkspace/xcuserdata/jchang.xcuserdatad/UserInterfaceState.xcuserstate index e184280c7dca429153a5eb6b19805c85d0391553..3bdbb4cf601a145142e405db8d4e6f4116005017 100644 GIT binary patch literal 113624 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

Zg%yX8c? z-3Jl$J8H2p(XN)r)~Wjc##qg0itHnZfBQ(G`di*owU6%Zjbi&4dm@dsJwd3YjdmK? zvcEF0vrn)m2IIWx%Hn>zA~$} za_Ycqhuk@I@8_H85gzYqU9<&EQiD_eHah|`{G?I`S-_8g<5uxA?` zMGYN=eSsWc)OH?VO!^1Mb@n{_G8uJCWz^LhQCDCmE*gY-YFAMg4Av4myM(T`m)gth z<@V+F3VWr!N~kM^dbm(m33atl*9i3pp&r@tEM%`Ez8cz%>yDD~b@Km>_zEiRSo?8O z*K6%Us7DL+m?r!2_H{x%R;VW&Q1EoSninkO6g#_yn(e3BPqUvc)U`sTu;ZHTXWGw_ zk#)RK*Zun=>q5fiqR!z`^W-rfd_O+HQvzc^{{d3Val;7pKLYbo}(Ewk@48 z=k50LRPvQ_s_aCj%It)eP)}m4&}sWtNbq!5`)2#KfpFYngyShP z9Ghe~a{BN-o4)?ZV7%FWo0R<)8H^Z@F7S5Q1)i~=E-+~Fd+d+MV6@QPwd?HY>FDn| zc6J2eHh3JKG=}2pnKBN~mjAj$24kQrcvf}=+l6{ouq$|>+pgd>yF87<{<@tZ;yFS+ zx5@sN{cWM1C)5k2U%UQ}Co=mkyF7Tq{=T&9`O>b&yxRp`+BJAbxcv+JH(hlzyX|{q zFkB?mi;ZUj`ww{8kDVRz&Cv9>AKaCac6 z!Pj&32Xa>1+8lDPgY(^3)p96?Z(S;V>m%t~7j*J1L;DUL?K^r2b^X8nnZO~hAnQ2! z>Iij&F~sjbkE7HZ9cH0k*}6l_VRJa-SH`L54yRDBYWvFL7|ckY^UVv}PB^a>@{sct zZ6};NhByx4DEmssFitpE8->~|LO9CyLZLpuAJ(P>)H715CsL?QQayih2C^k$9MO(g z1}}~np>Ak�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 Bb<6+& literal 111595 zcmeFa2YeJ&*FQdOcW3*|ZZ_MK&}%|^p$MrEIwYY9EFoDS5R#Bh=-^zMROtdDB>@2` z0s>N{DvA`PD@s$aU_&VuM1JSa?vjKJ5q$D~p7;O%yudEW?wz^id%owKd+wQg`xoTq zxQaqU&k-a+5j4RN5<*JI2t|+D!<~h$ocz3=wKEH|2IatS>9vdU3wqSf??2d?RpinU zHjx5zUDf z#0x}Aq7@NOBoK*2648a|PV^x968(uRVjwYy$R&mn!-zs+I5C14LyRQ^0uhsmDa3SQ z2Js59hFD9iBi4U z9dV2Jp7??InYcsTCw?P-C;lP{QcB851*sy{Bu~~M>yq`z`eXyLA=!v*OoozSWE2@q z#*od(crt-ZAzPDe$o6DEGL!62W|7&XlN>+}BnOj2$vm=<97&EMN0Z~pdE|U@0lAQT zm0UzFCYO+}k;}-HWC{5Oxq;kBZX&mmJITG|KJq>Ceex)Ij66x6BF~YZlAn=ZkvGVj zDV!-c%o|FV&wKKn{52z2R6VyrSGU-*E>Na(kx<~y%{Ym{zJ)&uvp;=l^t7#3bqxH0rHqkcPPS>Do z(sk*2bR)Vk9ZHAMsdQVq9o?SpKzF1&(VgiobXPi!?oRim9rQqY5S>F0qYG&lT||$d zN6{1MN%UlT3O${kMZZGNqvz90>DTFH^bUGAeULsxAErN|kJBgU3-l%W3jHn$v7@pBFdd812FlHu*slha08ZwQT2quz=Vp=k-n0O|ENnzSD z?U*zsoylbim=VlNjKCmf1~Z$P&n#tLXO=NV4rCp_I(jn4Z z=}>8&G+$aE9VRW5x}-(YV(BQUAe}0mCY>%_AYCYZRk}#JRJuaCR=Q5QMY>gbOnOp! zPWpxPvh<4dYw0)AU!?b>f5=D~C9}w^GMmgU3y=lMf@C#hHD$GAwPp2XO=O|6Fj<_e zsjQhSQI;g@DC;EaEbAi6koA;h%KFPj%f`zl%Vx=5kI}d)a;L3HBs=iv5`VggwokVb8LkvtO{6*(>Z-_AB-VdyD;^{ek_Fy~o~{Yvek) zSzbe4Q(jBnNZwc;EpIA+L7pOSEqBPXm=I~BVWyA_8NhZRQ@ zCl#j^=M|R~R}@zj*A(9>zEhG)iBhHXQyP^1${=NJWqoC+GE5n#Y^v<4?56CabSeiZ z2P%gti-w9dF5s073F>9pDI!% zS1D9Vl|f}z*;RE^byf9LO;n+(HmdfjG}Vi$-l{$-r)q#|plX<^ST#z8R1;LMsg|qW zP;FAZrP{38t$J5=NOes0k?OeWtm>TV2i0xWZ>mRXLQSfbYOPwYwyJIF+Uh#$mg-jO zBy~G=nmS#bq3)yZs~)7zRTroQHBwJdPgGA*PgYM+zpS3Eo}-?tUZh^EUa2lo?^W+p zA5?#!{!o2PeOi4+eOCRs`U~}C^*8G8)jz9mYX}Xgp)|CH(MU8>jZCA}a2j4?)R;6j zja^emQ&$tJiPS`C;xtV)Nt$F$il)7$gQkzBug0P2r^(dx*JNp2nj%fHX1HdAW~646 z25BZ}CTbRHmT8u2R%_nSY}Rbk?9}Yl9M&As9M_!CT+!Un+|>N6xuf|_^GHi*Nv%Yy z(yFxvt-rRuwy`!s8>elmP13f}w%4X>GqioQeYFMJBJCLMSnUMuMC~l?TbGVOBh z8tq!`Ztc6;_q6Y8PijwT&uPzVuWG;2exv|8yr5!Zx^ux{+&S(%cY(XaUE#jwzTv**zT*>`KEj`KAunD6Zs@QneV`-@eY0npUV&B^Z0yz1V56W&d=ay^0W9? z_}Tm%elEX+e~n+sm+%|-jr=D5E&gr(9exjgh(F99;g9nt_$&NX{u+Os|C0ZTzro++ zzvFN7_xay+q>j?jI!4FpR60MMLFcav&;{zkbaA?-x>mY)T{~STU7D`DuCLCa8>kzk z8?6&`NH;?_TQ^^~NVibCPPaw3UAIHGTenB|k?y$egzlv7lytC#E5dX3&sZ_r!yHhp7#us%fJL?5aT(}(Mu z>YM4C>yz}!`V@T+eNTNa{fqkE`ab%;dWYVrAD}PL57QUwUHTFFG5U%6N&3n9Df(CR zv-KtVH}&iF8}vK$yY=tt-`5}2pVEJidQIh55z#HT7%em*|(|*U_(&UuVBAei?o}{WAUf`xW_( z^&96m*>8&9%YJkH7WysmEAcD!Tkp5Q?_Iz5{f_#5;CI^Zyx(Vj*ZscqyXE)2-yOfZ z2B|?~&>D;elfi7LVW??nYzQ?(8e$D`h8BiILz1Dbp`GDHLvKSLLtlf#(9e)*7-$$| zC@>5&6dEQOCL5+0UN%fMOfyV3%rMM1EHErItT3!Jlo(14YYgiRZyUB5_8RsX-ZdOD z95x&^oG_d;oHd*?+%ViUd~Nu~@U7uH!!5&I!#%@&!*7O1{)9j2ulM)!H~9Pe8~siG zW`B!+kbe#TM*fZcgZ)GN!~LWEU+{0~-^xGUKh?jje{cV6f2aQd|2+SE{{sKf{^R{8 z_)qtr;XmJhf&Uu+jsBbbclz)3Kj8m?|A+o3{7?Fy_P^-=ss9!KtNuUx-}e8-|5yJz z{&)TF`9CreM$$+b6-K2|WehL|8iR~Aj5Uq5jJ1t*j17#T#%N=VG2WP9Of)7L+Zfv$ z(~TL%ZpQ9Lhq0fr&^X*U&N$gP#rU#uwsDSeu5qbxg>jW}gK?vAyK#r{1LKFrW5$n+ z$Bie9Cyl3!7mOE;pBle3er3F2ylMQ-_@nWj@xJjl<0BJcB25yL)TA)^n=B@)DbN&T zs%@%cYG7(;iZn%;qD?WTW~M|_k}28L!PL>z$<*1@#njEz-Sna<%am<$nsQ8erhHR@ zX}D>GX{2d_X`*S8X|idW=@rv#(;U-c(-PBbrlqD8rVXZzrcI`|Oq)$xOj}JmOnXdw zP5Vsmncg=YHGN?E&~(Cd!F0*=x#_Cun(2n=rs;dr52o9u-%StAq*-cKnbl_AY&2WV zf#zD~2IgRMggMIG%-r1E!W?gIZEk1IFn2R|H}^32H9O2%<^kqh^HB3JbFq1hd93** z^CI(N^AhuG=B4J>&CATo%`40+%_Zhi^D6Ud^Bd+h=C$T^=I!Pk=6B3H&AZII&3nvy z&4=q-L0 zgT>#{*b;0Bu{5!STEZ;hmIzCvCCZX!Nw;KJx>>qgdRTf|dRbny^tSY|^tCuF{Vanl zIhMheA(l~=(Uvimv6gX`@s>%J$(9+GnU)2Xg_fn363bf4I?G#@w=LT&2P_9Ihb)Ip z4=hJ4?^(XET((@XT(w-YT(^8_`O0#`a?|p)5HG$?~)1f#omD zL(AWmM^?frwaTnYtIEn-1Fb>U8rGWDTGraude-{ZM%EZ>3+oHkmeyoz8*8eyleMcg z&FZi^t%IyN)?({$>j>*e>nQ6>R>3;aI>|c6`l@w_^)>4{>zmf~)(zH;)=k!J*6r3^ z*8SGQ){m^mttYG(tQW1HT0gU1vVLj(#(K;8ll5oo9qV1|J?lg3-_}Pq+Q!%+E{F(QdMv z?X~Q+?RD&R?a}rad#pXq-qhaA-rU~8o@`ICx3+h%ceJP5Gwe?L0Q*4uAbXB|uziSq zjD4(qoPE6gCA(lp_G$L%_8InN_T}~!_LcS$d#QbueYJg^eY1Us{T=&3`yu;b`w{yG z_T%=m_H*|0_RIDw_S^Qm_6GrE02QDL-~#*tYytLwIstV9Vgs57BnG4gv<*lP=n>F6 zAUnVrFf<@9KnR!=Fe6}Yz`TIh16BsC4%is5DPTvyI{_aCoCr7@@L9m6fExkd2K*3k zH{f2t!+^g7xj?@_YhaDQnt_c2LjxlNn+LWCObAQ~Y!}!*utQ*%!0v%P0{a9G3>*}g z6X*&o3M>vB9ylU!T;TY?DSH`)eHXj%;dY( z&dSYnxk?G1&f#9iLSML65YoLC&F0=xx{Hydso|BiI zKLVPfPtMEipX*E?ROrmiZk?H%>nw!(S=`^p7Zzsb4TOf|6(u?Q7Y|I$8<6kdcxY2% zeqNDtWRbgza2r2&vn#gN!G*TZbQLA$4=u=b7CEy!8XBR`4M1;$C zs93Bh(80Y}Jr%Xj&k~hDhs?apfzCqT3P+0-)^Kp`M+S>S9_-2*8Y~L0(-l0>O>Pit zhhqfj)ttP6S@{K{{!zxucj+auhFT78dHEU!0jcu~bF%((#+1jbWubF8sJQ&R)NIfg zIRkQ>g)RDL#zuu^XJYdmIUjpD4lc%ho%kLB48cl2t+)W2_$TYFQ05;n zXrDjO+cvLgZ6)3zqE{1d6WfUG#10`!h!Nt1W~+&v#4chtu}5ejqzMCs5kmX&I^lpw zuV@FCTB&LVk8235IaO`lMX_>$Z37?HS>!5z`}f4#$2zzkRo?!2e2FS*J0{lF)WKy{ zUYo~tcKP@&9w(-p$5M$CQ$@w6#EP3&n@8ePm3#hhnz%$nuOrS7XNhyfdEx?bk@%GO zOn5>5ND2(*&m6YRtl}% zO7u4IOYe?p9g2&vW1i+LT1DI@ekJ;Ve+a+0i{BNCJ2-o4LTaK%wRQ5`wXE5-TIC)j z@ELgy|aecd9CDS*Y`_!~_ zM{-hXdZ(_AwB+{5iRr1GIy%}V_pGcya*xjO9n&5c2=ci@x74)Mg!ai@;Wqdl?&0}{ z{H}!jR#+mu=KlSA^1GGzXNUojKaoH8eQbEe`O+%#Cp=>UA7#v#(8fW*VPO&D8V8Mu zjtFlY6cZaUZk)G^zmk6w(HqD+uwOV(0WnG= zBDUPYbs8C* z@c7`QnE0gFIG%PwPI5H(ZF*rUxIxILM zHZn3PDmEo7G%>z>B_^?w(&wurJ}xvRE;=z1+8-Mp92piH7Mu_jn-ClmmmC=%1;0ke zN0zU|E>^Po`6@{XkBg6vg!?BZg(n9`CPaoqB`Gn%5z!FgNQ?`M3k{DbUr9}|k~Pm) zNmOKLd}MNHd~ifeLPBt4au^PnLwBO1qC#V$BSRAtLletaQctX8or7yv74WVi2qFf9 z+v02z5s?@l9uXc99Gx5&6C4>C8yOr6p9Lo+h9`<0NlvIRRE@<7)<0h_LL-wxBcfs= zg2R%d;({aNlB0tYQWB$sV`36Q!{cKU5))$+%J(8ntYqW!RT3Yak`xyikpN>878@Ly z2zbQDM1=*1Mt~^ChQWX(R~V{jv68o*uaZz80*p@r@H{jWdJ+*493K&z92_18h$p9n zMTAF1m+wh4v63y%S4m1@WEe0mDmW%QGBP+a0uwPF#w;QxIW{gdG&CV1p#n!*iIu$l zoR!1_B5{Cpba)gd4zsYGjPIyHluNwlS2p=MKasJTRna0nJFjtK9=vc(6&G2yr{PdF)jESwh3 z3g?B3!e_$g!e!yAa9#Kc7B_AR-w5Bq^2YbVkHXKwFM=Ldzkpguy-F>j7E?>8*Qlk` z>(nx8IkkdXNtIBg)GBH<^#-+uT1%~?-lW!38>o%cCh9F}Gqr`mxE5 zk!gtRkH|tq&O+pJL~canQAD0aT<-_WXGG(N8fW?elr^~mt<6>}8}FKduk$hT>qh)p|Dodc2h)WtFCyja^Q2iLFifp~`4;XiiQw~v>^T0e1c zqbslVe|GqN+jCWH&lv~T_kW;0O>iK|x6T`4o#z}}Qss3%!^l77&fkf5zTn`}E4}k` zPfX&Aiw2b~`d-urlP}Y`$L(${*1IB11zn3mjaH zO25XpO2lhk5GHVNft8j?&BM7Ku-*mBZ@zcQ%^a0qT$DT#Qpa#Y2$ZSidzUP{Rwphk zh2#$y0E;J;-N{w1RBlclw5ReeVFR9D2o?E$odw-V$q)_L8P>l&ZF}Y zDMKU+we2ZiuB2tA&^(y)$TA&dkHHhR8$&0oa-s?{n~yv7?Xrx(zRFc1srR}txlNJA-|Pv?m!02cC$0U&0sC*bgo z@^bpk=h6i0J(}PxdNaKRG{M_M6e5jYJpc;9ipbiCtRsF>7wZBy((hma-ib()SAzF| z1k-!zefWh)Ga@bScUT2z!jazQ9iflnp1nuEk4PIL?WObw^oNKHKx7RO`hS#FF-!Jo zvhJZISWK&5Y%^uz6cI>4fHt!$27t^K?y`6L+ z`p4#rG2Ha$Q1%PovfCyL_j=}Z+`B(w{kLJGPIZK`-gaE2zr?-0h6z8f`7)F8(1H)o*h$trx(TGg& zScb&^vkc;rM~DlIU3FOo#vh2q00ARC#9}N!EXGRj!!JYv96&7m4#bjq0B344bwEm( zS`1J%29dF)OkJiPBI6L*T%=XC*axOD(*)NNjK#Sr7H1}uJcP(*!YjU_$Sw7@Et+Wx zWMpEPSO(eyN&GJ$vgInK8Pl8=5ZMZm@ggN5wf~v*{`0E!f3y^5m_$O$B>9qiS%2eq zk$XFiTrsg=Wzyt+m$4#XT0_}3zGbgzha6*r+I5(?tLLppS?&M8n*Cr<1= zW118S0)sXUo+n!^bTMK1F`96ckY->4~Lx|4(DJFr~l`pz&j0hU?5u+09p_}yl8JHD~s8XwfKOWuAF!Z}cO zu5a16K3^_t*C{c3?sWtCeaMmZWxAYMz+juqEX2a!NfiFYSeJLMRF`vqcn4`YQ{o1E zr3iRe(BBN06L6^D%BSdWW-asfb0NOXgZNGl;(IaT>E#gbUWRzTGQey%5=_8c{#beC9@d z96eTa7S&+KS235E&#}^Y5s|&!O5+T36_C8DJodV__xB{M$*ZGpfNt%o3OF%mV`njO3B`;uYJ+u<=*+sQl)%Lh=IuE3h zWJw!Q4kWEbIT%*e9lY0cl)%E}dPyfqXGs@HS4o;AU6LW`Ch0B#y15Wpgveq<4oBn& zM2dPy%L3JZdyuUijD`eQK|C+zVG!b@dpV8;JBZ`JR-NeVEZh9Ppi zmrq5&C*OI!7wIt)m=c#t#!A3!f?%Uk$x9OWe*z*Wi)^YEw@orxGSw}`FN;z<5zF9o zEX9*5kz%@ww{f#2^MQntIg+`Od5D~XNVt6JD#-%LLQF!iz0>~}_-&Hcfz-=Xv~0fYeopISi?pHwSP5lIe9JOJ|C(C&@8l6%W&7gJ>>6>ioB~)aS?i|n8j-#; zs~S4*xNVZ``j=XKw?~V=>!I%@cx@$#^2Y=l=%Bt=#C-9bH(?0V8$&R%kkcpo|Rm{;&)DR9+3+Wxv*4nQSvDwUq$4SYEs7l*N>qzTEPo#CFFf%Mg z9>11W?8RwHt4^}#J|B8{lTbCX6&qcGfWAQGqqu*lCy;|Q&^sV{`CkhPKXhO`QM zd0*X)zwgq3mD8S)wt%uP_?A7q`{OP1i#n+ne>(l-n5}Ps5PNO6G+vs7dz*m$^>u)F zsWch;>u*->uXpo8*G}3|gj?D{gnI)xZqj7&8o2T){<<_>>Hxf@8Paaj?$RF8p3+{@ z7p1+WeWZO6xe1YPA#yV!w;*yWBHu>jHbibmvtHWIgM79J`9T=@o#l|F6p+grt+OSWS>lL?rlCdrPI0rC=Ljx^%c2Ni;(`8%QLb zDV-$+1@$f>Vb*$Jm2{4DE+)}Igu^bFME}*8iF7fLa)~b~wP%O58PYEyeQ}R(Y-3v$ z&Jqa>c3N7AogTK*&@`Yy>q~nwe`r0p#t2Z&j|sd|x(YKfR|>jHipV3NcgZxsZ1tgWpbH9rj)5< zYDAtx>I62|*;M1FzD%j;!YH|R3G8+4fwgMOt9^e@Xm|MCA6cJ(W4 zSsmbytS%z2diW!20Q`|Plr_RHL|#KA$S{5fcUQqlJ%(KtE{g&wkwwTN5&0D&Z@iH zhsay2WNl^bFps`RBm|uP>&jjRd!)$He0jNOU0oG3u*0l5F9iqFTLRi*4$Hbh+3vn& z=Uu(#+*YH*u6b#ZpRR7dyki-MWxZs5L=MX!uIGoypPpshvSfMx&g+upyS*-%%fsOj zn8UZrd0oGjk@(NYnp-voOWs&S{^FIqm#Wg-vMI7@SmIumO-1A#MBXixO_$9;>Gb4~w~cMd;#ryS}VWN*1q z2PYX&fAB1*ZNr8P+E-)+Z79w-@VAs>J%D z>=H)dQ`u*TA`nHE%08EUfhfrDmxx$b%UX!+y6h(4hF7(5R2)o|5=X^lU(3D$+$b7R zj2AbU9?L439^YJ)OT{lwLQ5^XT?;SiM~S%o3}tWo;uaXv^XGw0T7SNH#^zIln)QR& zgV*B9?#S+A+;U|g%Th!^qDa0Bq=rS5%vb1m-EyxEe#jDTykX4!5GAkjI?qL{l-0q5 zZLExCSvjj>*ax9N1C8AV_QX@)(C@rEmM8StTMClO)H5fLqdN=;8ku|Yq z)%*go?(Cq>c>5gZ2*~Gm#utx-eZt_m ze)l(Gj@18#bhjgHzef`-XVpwr zlVD6KyRR@p5-YHXod7yv64nXeJrbUO zR1-vjFvTINov0JqyLG}d^GHmRVy9t2o{p%-UO}D(g3P|c&c-i91tTiN{SL0`1TM(i zyI0vISkM-+@a$A5qQXkq*Vv_q3P%)}uIgG?XIHYTK`x)n%V6JN*I>DfKomI2ST4`7 z>jCc#zIgxgS%*t0FSPBnBfvWE#_(H|C>w7<+0DLX2XuR&t)G`NYk&KI_xFza0YYG2 zW5T}8?!frxvfD*t0*|8~Vs~Qv!8?D_f#I8(YqbI+g8z6&>s|IeB6q-UYI)hdI+NP^xpxT8u@|wxoM$f}DgjZ6rR=9H zI3h`ig3zVB_n>@Nk@q>c-q|^j?3yzac8<&|>XDP@ZgF}pY;)+n+cg#f_A6Jym7TpG z-DJNZ{7cxc5tUlPev2q@^W_!p)HAcVD1RvILzl%3@?P{4`-_{{;8}w$?obu5-fMo7 zQ`ozE{>(Kw?dG&x=JxaCO3dg^9z zmD>>26;WxW@&Gxg^mIgZgCVIhIq?}1VmzZDuPv{KYpH`RO$N3!^gis^-?Cf3 zN?$p>c8Bn}&d|JM$HDY6I?G$i6GYVI@gnNI0CjpFZny;osPY-Gyp25V-$i5O>F#KZ zyoU$%-Wc`XkE1b^qYU&xWuW)Npl2egj~Dc80A22+_u&_!AZl-Mzk^=Zdj~jIo{vEp zD$heyKSX7g$_wPf5Y-=1VCkw2`r-0XxRwzZ`Ya5+d^C14vMYn0C*fIeF&9fN$RRB4 z6GM zm!JUB0c$Tn*^9nqKThs(`0utI?!J2=y=D5Q*TLRC#_f{)GR7@e{)K3Si(qCZzbek? zihY6O$)CJ(cS8<_0W#I)UyFE;sPa1RML)`ae=flP@BsdhPLw|)f)ykN9OkZ%oi#|G zfGOV;{0a5)W5g8_KwJUy)6pKp6>>map-?FC3sGYbHP-zOH1EhUp5`fZ3Ip^>p;v&> z8jq-#N)`SJBcgyu6RQt-g-rpOR6dVGC~DAxq86f%i2MYMyrM3lRn+rEQ~%5HU-{F? zU(R24`1slJAI-(cD;h%CM!sc(Mwg@>{<`)1Ti)4D{V{Lk%Vo$bLKI*(N)=78!kL5> zjv^e^NvX+|u9Nche&NYr??A;V#Pq0zie@7CQvrNMxF|Gm^;2}cB0G7NTA$ zRpcmO4+HSh=T#%<`3jdC^a^Q>iXufZR?~A31t^MYS}_WM9PJBaVZ9F9x9>^ryyWQa z#dCT!-YtSY4$6-AE&D^_>WfRXt?s^aZdzR9Ik&$r2Rc&VRZPVM3_1jM0QAWibWqx? zFLFH9s#Yt}<$mmRD`qNSff9JH0K*P6S%je%MF!vFY0xiJtb8u$OFYoOK_@Ce=_}sE z&@U+m{W6jFYee3!ErWg&hW;%?z2=2}E9U*%if#CXD40sU?tTZ}H{=4m;=D)kE*_M< z3UC3IBWgvdV!z@5qE;emRW*vhdx{S$`Ew<8ejg-?09Xe_CD!4blDswaiwp*A+M1*u(7r`*j$5SRsSxA6)!2>~AUVJs0-(J=p*0Hu8$UG4|`r zVZX7=$ZvlPy^;dxl{BI@c%WBGt75%Ut>mC9N{v#Bs7;7^t5nG=!IEr7)Z5huxzeb# zR>FFvO=-uh--4*E9@Z;s5L#tTU&zxBTpM|(dnbNF(@R$x@7boqkSpsz*}A@ETV8Ch zoBUDAm#Y=G*Y1k{5saeO$SWHt8)M*dl@RCvv$zd_lfXI^P#!#{rvRr6SK@UudZ98> zM0>|GUDQmOg3*5dX`HgP8+TswzREB#R#yuTT-YgDfcfh@h zL}q1QWhO@6q3nmKJ&4*{s_d`KLKIN{Ks6#iNSO=BKN(|D4pru10spZ@)FO9P*=;<8W`s zV6q>?WdG+0rE;PY9=yQ7gI))kAAW|3oN}ge(etoex!8mD>mIaMVzl2YhxP|$Xn#_M z_G*mw8;E+}%kp)Aw(?DSAATX~s5pPa@1Wq#9<`<1qTGhZV5{;W2MUN${mP; zaL~zWM0}5OKjym6qle0a%0r@V2PJ<3>N}%+4^VvH7saJ$==-03(|YiNLkqajJJDiP zP5B{|J?2|>(4X^;q@QaWI{)~Ah6_%mtSg833FXHay)+&{18XRFz5vW~36(RXUYk z1Ra8LU?JsaRQ`lkW%LEI9a=;k`=xdNt!;kaSlFsABo1lMs4P&{>RYzqq8sVEAGG~x z%hdQjbGDrPxeRnwfT{-WZ6KyOp2(G|YGRvyS=8~qppWs6QhgOXZvmjI8j7G_1<+MB zMPY$UpS;d{QJAVZfc`HYQ&6>V2W?dGZunKn82;;JK^yQZyej^O$LOn40ew|lM1AQ& zU)7;1)2>Qab;o0oq3VXH8;H7Ds_LQYi6{tRLV87Yg=8)C*o7hI;(MOjomy+dxYrxaS(guB zgTR1YP*q|V8h`N0=r4b5eR-v$-kmltr(xmpwqvvk+XVoAtO)#%pyO5jak%E&r{g|R z^~%4q?W);s+N3%__DMM)lv*RcQ1R@y=nuZ?jp*acu_5(8`T!o_DXEKYKQ6_Y}@Z43bHZW z814aF_WBa};Aaos3mn{P#XFmuBUM(=O%cQWQ1*at+0>8Q_3CxFP3;vehV;8Stlz$J z7#>!=k9&Iriy$n*1L==q5&WZ45hQ6Z^e0rJfmo;l0|Dk8=U%9eV(9;PI`rpN*Zw8+ z*FDhR^g#a|hW=qW=piG(!+Ba(#`zyH^gkiWn}wnJ9obI5cHJyl(aQPY zk897UIVj8fmc4jO)^qN*PIuP7p1iorIgUZ>KI0%j>FpusG9@)w6Y9- z8X}(wh&Es<18g<11iTc?uZioOkfQX0T{-@P#wL=ZNz4&B*tFzTkEM9s<`+3DH z2SjGDugGY_W)2=rrtDrH&~VR`%fIoWcnyWJdA?K{ElgdsQTf&|T_Q|?%EW7! zdL*o@steUFb&w>;Ipo*das|SUH}WH>iLMSQTf8Dx8M@>>uy>u z6=_-PS>&Wt{T9xGD7WjA`pwGreL}FmmS^jy4eHGvGHz3ESMN~21K{n#WUO0`j13VT zf#^s?Cx~cz_W5{DHolA5xF6B=ylgxKY*ZgsAHgp~*GF^%_q$M^r@rUx-huc?eG-rO zarFsAH$rsdQuQhI$A}I_bZ9lw@|+s4n1klLAks1fJYMx@*yE*}_?ka{Fl_8BuCuAH zs3A|@XT3%ZkFu$6B03E7k`9MEoKb%ZboZ9GGuVKqv?QX|(W5ZwaNFO;&Mu`t0)#t9Hlq$yNLh45al)A#{LHK12pRmo9} zS!3}eM)1@*@6`bsJm;bpYJx=K!ZuJ&7>-B7>_&se)zjC{QYGU2>q>7t;kHnjr zJ^yKem2aBo?Rzs#OX$0%xu%8sO+>dtbbHWGtJq_jcn##PfJ}Bk^xIe_Bfm$;Lh9;drFv)v_HyH7Xt*4_T{K-aX_|CIcSbaL30)DLhG_UTV~wVprn{zxrl+PC z%(dxmh~9wcjfmca=*^%|a7TzFNoodzV290W(V7P(UM za3kF3o?5&|Q+d8DyBcqI=X76D_F?y&qCqJ+g)TUP)m4xWyR*R|Rkl%`#EtKsQiER> zY><~(0H;uu-3E>=6ZM@YTQh|4U!!qq251Iq25E9MgAv^w(LE5|6Vbg8{UV}!uhHac zhHCOO`I-XFFhuu3^bkakLiA)r&qMSIQEP_+4ne^|Rqk$B;lxA-*ZiL^dHM;Wu=U$N zUtZxXPzM+O3|Bm{0W=8dvs{Ev#Vvo}tAmSuhReJuTTg7h>IP=C2G86zV>DwC-51f0 zQq6eHONj1=XfP?@y=vhMh9YO#IbCp~7aYv%-YBsQXD`f?G&2eRwXk1g4BZ^|jf}v% zNWxB$zY7mx$H=>Q|H!qPS(;aH+(t7OI}nJ@M0A!rWJALelzR&A-A-Tgs%D8>DJ~Y3 zV*h6eC}~z`O0XVYiRkPSO(~+CVn_aQ#^&wA8qIoanjw(_?=PqlljC83K|!-ovq`ke z0}wqBZh1zth0toY`kLD&WbL-G!O2-W_VoTH(RArWY??LOq3jOdvhfj@?hMu?OjtEw z*V~<4Kf+WW_5;*F*{s3_Q z4e-A&++C}=0C;2EFTv{zg!dsNJh($53^)?Qp$6~$u4=Ah2(KYJzeMvTq6?l6!ml;3 z+m4TSrTHE}{1MT^L=+1}2yeqs|KbbFy@s>jTIFinbzgqT+F!p5X(~c^7s}rAE&E2! z8?vSu$;Bo1pg9)?40AwPZ#RC|;3xvv^gl)M#n~_#2v&f1SXnkq*9;MQEna)n!sLI1 z2YRh{?NKWy@Nr0U5Iquysd#d_ry{LJ%Zs>a!EFTGMgwkI@IwH%F;(HFHEIJfZq0$I zO~w6Qqv&RU*3 ziZ5_u-aAjn)=SvE{Y=ExAKNSkE2}-DO@*>;eal*YSbyiIO(_p{Mqj_&<>nYrDPEAZ z9kiWAkhPsekS77iPv6?f+xKo-Je}8!)q({Cz@}ipzy<G)T@@o8+L2yKn~o)o zlX%-bPWuwZY&@c8mS_b;&w4)0CTXW)%s{i2-;PT=O*XVn3{nQQm81;D z4B6Xg=BKSXt-CiQE->?GcHE-YaZmhnC-!s?M3vfUd;A;F+1eNY>|jrFK_)vwa457 z`k^SGOQ5q)pQCy&`dE7!1N{l2mzHSHAo}&^1Nwp%o@4U``g83U80ck)UhW0@8i04* z7rfVK$C{cqQ?qs~>oGBovwtZ9eFMtg^ey}GkInp>ahLB_}QK7EK%;f45TCRaF2wN`xS>x-}w%r#8 zZ<=;oriXZl_6*k;$_D$EO>!Q5Yth#!ku#=ue-zq#7K_Vb3&MpGolEibz&$7Mj#d;5 zpJotzCe%L-h2jwXmbhs$*Nkfpn% zjCIs%HSn7FgGV^HWDX94`lpx&*MSJ;I1$Q!sq(9GZpW?Q)`CjpR&pg=DYuGS&Aq{`LG*D%pFs3UM4v)5 zgjc~{oCeLvy$P=k@Y)11VXPU?i0|jb_b;$=1l{Dj5x)zbi>kPtzwZY4<&F!ep1uBk zx8TRksIGmOIXsc$U?O+ctIaB($ayh4!oezR2?r8*zJ!BW80gXGQR5$Ru&e4S?l^aX z157R=`cp)Iwu<|h`vlwFONjp5ZFm2x8~$(?L5o+iTYB#qWAFF+yIrq0M@L?I`N-}~ zq6zsN%2u*ldT!Lh(7jxn9!pAMLuTIK=9k%g?kWedb!YYM!x40iM>AuILyDpOMC+=rN!zA{HN=fGh{tgFo#kD}LmF4LL+yaJGq!;%W zb`m*wf`P|RFksifJs-O%((9H;FVPnA_{jxcis&02;p6d>3$Swf#Q6}OHt-y;1L5O& zM1Nhv!~794QJzEicq5PVQ=b$*-p1qnRQg*)L&l8g-Sd#2%2$$~dakED#&MuS($Z5k zPUb#*?+q*fJmjbHb$xw^&Z&cb__H|q`2O%qohLhH{9Pshd;`9*C;&XHhlBt0JqW;4 zA02}@_;UnMI_Br4Xasy7R5Zvr)GHe)JcPVU zH7zvF#YlU*Qob$U4$=1zlZ00o#AqVlk%!YXJ#ld!!rS-DzDnm|H`>(@1IH2YbcEyI zUB%#c#%dlS-kP2`>J51{EDm{-5byThvmfsy{8#gte1ATR&qnm`i2ei7ex};^fN?LD><9-*+RZgoDKDC7d;Ut_Kryb56^#;^26{{ z^dX*#@LKaL;Izr+JGA0dW742c*DF*ITr z#7Nfi6ZnaEUW5PgQxPLY4D4#+(KMiM7)<1UJio@*#KSQuPn@;mYvLcDaZ}m-D;Vd0 z;{iOEli?k>dA$31L>?YX0YQ`DW%Z{$T<*PQDZdQU_;tj{OZer8fozuN(Wa&R8qlWv zDt`jAr{txPt8rO88|2}^$`XDTVtB9h+6&f;haJW73o-a) zDf|ZUsSed^#QFDl90%v$2YxXIkzXJ3#}MO>n1)sI>m-lk;QT56WBwEVG=GLa%b!DV z3I}6Cj2ST?qyLyUbbe}TUU%=!%Yg`)>d0AhkLyO{d$?jagTi!V9K`M=M?uK&!t z^{wGq*i(+b|3pwX&|hPqzd=l(7wB76x+%Z#cQCPjMNEwn{w`u_J|D4u=V2$_Rs0|P zpZo*F)IxCB2~%ek|B(M16RR%5C!V;q+kf#O5uJq4>ZHD8S@FGY&ATt8gsiBeJb!=M zO~?_^p3%vntireK_4ZBV=72T_r~bm40)9LAMVX7FQ|s^pOY}k=C=t+S4M3kg_56_P z<(AH*jL@lx~UiiK((b8g_$t2vwTPP*LU|aDHqx&zVRXI@#5S2T8k*m zfwFUb%igYWW?OM)M+R*j@zsRcDWW~ZHcqzyqp%P$@m>@bSEc%ND|C1b0(cAIYv64n zme!|jJ`P7ngP$K%lpmA@$4v(ffFrI&?>=W}L9TNsBoe?BU_gE$_VR;V&dkEBL7w?Q z;~;lB5L}DDDk?0_Dk?5?2IUVBKgfZ(558r#E5s`qZ|XL}MjktwQ>dZkw1E#6bE{b8q_5i2TAKaM)p1e!-}wO`0^xa5)QIO$LiCXyTp7Jb7Mm zxlAM7F83`{9o+K&lTeFaJJ|#-pV^zdv%8)?5x|Tdsnw#cR+VgcL*`<5Yrwp9T1%7 z#B@ST=QX+`3Z?FS-BH~Kx(^Z41#dCHbVW=WV$uP5~%{UgtxT4LxQ-XI@5%Nf-_(>Wk4D>f|LnHiZC z?HtfQtbgc$%<$-#h`9dIagmXc{WIgDI`LFe|AO2cS5au_Ig+9oiBzWOQ5*JO!smqc ztc^JX8y*#=*DlI0=utbr|6mwxmyRH(tb{pO$21TDXLh`&&}mklUYMU>B#`TrDxuc6pDM;X=I(7SY zOzTiwgew-yt*R3c1mD;9eBY=sw95fxakn&iWI-V$*}({knQUTbRtJYP3HrxUkj`0l z8&ML_Xp zq|yQe5=ue|DVyF%f`FBS4HXqTVod^wy(232-WBYNSikf9;5AqgRqLXHZV5|S2D98wXoA!NHhv)HSQfDZ;d!6+w?{0oI)Q6TyMUkLxd5dLos;b8cFHsEVz+|~hm z2Yf!@iveE__(~XZg<**>EE9(1!ceevz&8WF9k6e}cLTl`h9Y6uEDRqBwU1C|x9)^S zLPSA0*ZDqW5~1@#&QDhp~k#8_In3HFW1o zd}DYieTk2c#|AE2Kc>9CraI`@Nv0`M(xXh1g`r*;8k$T=retAQDGcko94vRc_->kN z;vvJUP18)%O*2e0O&O+HCPoTJ3&Sd5SS<`|gy9%rI93>rV+h^zNhQLR1@rxj?>q~^ zxLLkZm4BZi=N2*4e{E*3&E(_3X7;+eMd!)?e{FiQ^XzEL)ML;M%T01OjVWIkPG~e0 z3d4ziYm&fJV&b`lW>cxD%tVecFCc_rU9+juR3#?~P7;Qb{|C$!ni`aTrj?y19n6;% zjIivPvFY6Ro=doIPlCS9%CwrYYdV+x=;2V?{(EK|zV)p27v@xbF8A97O?aG%J)g`u znAq>joWrTiIhX`Bis3YD*>OERZr?w+qG~$LbcRf-^)ji>(39#+nN(*UD5-)fY&5YG z=I^A>Wu(s)?b2t1o<0}o=@YzclWD6Ir`fbc7|s%gvztuYOjirTIl^$>p@mtu2fYUG za=nTDqn%^$7SpXV1~&@Bxq*o)(++%lXJ_9|DBV$*c>m1tr!`G|^qITf zVCv#9fv+F`_^NkLn;CM?q~!xAMDLr|CJyd1Jt(!kp92C6?6>IlNYS9|kDH$I#}21O z5IYwVI|n?+Q(p@(@qUCx(~H7z@!Ao2p;`Zi+PPhwU0z$_zs<|?Hm?Z7C31sc@>+eY z78Lpo(_3;CoAvjMM$_BEaB0h8wCO$52Q3TSrVoYTvevIYF?~hTe2|B{OkbP6k&E4? z?^y0OTrLdkQPEet4ea7*UGz5nWMVhS7SqqBUrfK6eiMc(gyBkIxJnorx0v>u{xJP% z`b!v^gyC*scw87>(xYu;x5n!5R8&N_g{N1S)cK0aXe-CmuPQ5UV6nQ}@?Ab+#Dq?_ zKbq$&EUWeTZxArOSu-C_y!1WJj8XE0p}EQ2%Y1||kY8JM9Xl>;|AUo{In->Y(lGZo z4=|g|X0yd?HPcPoCJdzbc44?i7_JqD>xAKYw%p6<>Yh)UJ!Y@I<(`(7e`t6OHweQ` z^3Sr(?WVap=t}p$++KHXY2`zi={1!_OuimkV|}nDKj_3G&Hnv2W_|yS;YO1FfctNP zzlk=>Wf*geIaU~M7KU4z%;U`Q!f>lF-0ruX{sgCgRx_jh9)d&P(q?HE+5}F|CWWB6WciK4fESl+c%}dUOYsHeQ5rK-;f3$>uIp(P}1N_^EZAcvmucN z_GRhheY%tVJB<&N$v>L^@Mp4_2GJz*ST=@u75Cz5;)GUFZw$?UG62;ZL<{dy5QbM< za#*G@oln^q2Apg$T6*a!SPs`!cvmqtl40S^4cjcUEVC_hESZ+MmU+UkR~SAQhA#w9QyRV!hOf6-vMuxFyq0B=oY(qB zsJ;C2T59kA?ekie0!tCo6qZ6^__opF69&2EH!n1@+x~MGJ5hsaRc5J_!CP*r5QguB z;rk{_m8Du3eh?~o)a_&Wm#=mV_~-GM`8F$7=9LpUxeXT zVPKH*`_*#lNr|wW2!heGf96T1{eGGDDkrk}Gc>r+UgY!l=*F~}SUQxR?!kJZht{9% z+$C7!49hBI*WV*q~&^QYgIeM+WL0O zo&2W9g_a$%?NQ|kb_X1${1f9$e`U+#yN#Cng?fant^any`>*kE=L-9A%agMHeL|>x z8!b->wco)i?B^^m$R9i})X+xDi$d++eTDt9g*lRKmRBsVT3)lfZh6D7tNoZR9o9_&c*VJg}J6C z%deK-gz6Ehx5={K@`q5vgc|+#8w=}UR(f<-TYFdyR@JImjnu~D`*$PAmm0hs^@o5U{Sf!sevU6Ydrcb^*d-b<7vu|7W z_QS(V`*12lzundt%Eops8$0XK`!~NiGh^#VPyXzgxAcP6R=^r>9k18u)7bEWlfh!6?dSd3jS`UHS6!C&qC5Cr(ODt z)6-|No<70LF10R~;w-b~2{m4*2~E~~Yk^P`g~}lG-!*2kmRQTB{VT0yG{Hh0FYR9` z?LVQj{XAj2MG`2dr_;pYZ1G6CVzFI+=EYPUF%R8SwFUZBGfFQW(#$`P!|Ywp->lX zvwmjXD?4M>FJ)&eN2pf@I%AFh+dE^{pRB*hxc%Asi}hEb<_dMOP?t1Yf4AEjpDonZ-!{Nz zveCoO7b?w1p-_v2>Jw_QP)n}1S>@p0<^UHpm0Bt*DA|+aBtiaN_kW~6r`H0uVblV) z;X)0b-)9@y-E$qbC|e8_w=G(z6^%Cf3ygzll@GPzwk6njgI=>O(Kg;TL8w(itrluc zvu%>?C|PmW3U$T*fX<(dbJ%R@og0FgWh=94E2j>;ZphBze}3U4Nc-)!aSofUi*wjM z4A1y6?3?5_PTF6({Nhn(KGoW2*k;-M=dju4=#54_jfQQW?0hs}%MROALMQ)&eLveG z+hUo3IWhrP=?S<*CgAD=C15ad^KE4^)%uZaZBmUqBiq`i8Yxx*RZ!#a6sT+TB;&Y0 zQtf0t)%<~2F9WebsKJ+4+E#TNh$q*l2t+SmZ)Dwg%^pJG^JJw-s z>uqPsyPPhkYuJ&cHQ6@EMrkeX>g22bA#;NUI?r}dS7Cm!ZIcZ1bwcGVLcOPGyNvL; zymP3%8Jhm~10N(OoU!52QLAsf{Bb?huA*#X=dxS=`n+=dnN!j){3g?Q|NPa~Hlen~ z#!)!T9ND(%p>~Q4wGA@V)^-*@DEkdIcJVaXZj?cBnjRFl*lrc-dZAvS8@6Mu9Q@r5 z8!v*o+IFXHr|mA=-L`vdyKMIg^>m@0A=ER4xLt3t4_di)yOI>V%j>qcWk|kZd(%cg z?E;}*DAbFZZSUCLl_7btP#Meq$A{gvPYCf(JBN79lP7%iuinOVemuyZP4hjlmm5%y3{O|kd3 z_p$f2_Y-QfP`3*8YN1}U#opgOz;3deg?gP(?-A;wLfzf&Aq%@h-y5%P$qP*>=G2-4 zK2OnIH?(!Q)MnSc-Rr;dw!F}J-Cnu|}wAMO*w-aYwbp?|H+|4gT^DEMq_aPJ)t|8-S7Q$8M3v$8%rzplg= zxI~J4$dDtzy106t|FgEG{dtlg^Mv`Lo7awb$v)9O$$pg5&pz2c#hzqO&S_b`%V{X% z$=HVcs@lax0oTl*mm$>Kgi2#~i%@s|dxCLr(&J~YelL0T;y`6$Pq)kCiR@GD(}a4v zQ1586&#=!F>JFjaxi({g-*OB5TIGcf=&U3kb`BZ+lu7$Mdsbekr=6`j+P>MIZJ)33 z=ur2?7-{hoWXB(ODNj(ZMRtx&y6UQyZKn1m_B^TRQaeMtT|&LL$-dl9|L{JcJ|ZUx zx(b1RSH$=Op;!h&iBRuv8wln03VWqJS*Q;Qm8$;%-O>L*-GJXOxc$#N(^m)Wv%+4_ zOo{aR9gX$|p*}2)(1k0zHS!ue2URxPkFg(Xr_cMCP#+iS6V3J$>?hi3#Ge%E3qpPI zpSJGb%*Ruu8|X+r)z%GX*w3_YkZyQZsLu%XY26L~Y|SY%apn1cJQq7lN3KG#Sqy05Ru(E7d;Rb5Ut}-iFzHN2cP4R9LaYz@^1nT1H)x|Y_ z@p#7BUo3fLS!Gd{oLs6dsSkb^>i@2_xIbTSm4QP2`$pS9*U5dc!Fu3H z`%~>B;#vEPGN+%jKW`^{KN2c)qMtR}ciZ>aiHy&Mx=*OTwC^vy?r-=%X19Cr3DC-# z61q)sv787HpB5bv7ayG-5gV5n6_FU9lok<_mYg&$HYqVaHa?+k_vjt}g+A?cp~!LR zDM=~GaS>5b<6^i_dQwCJmxxG9PEH(`n3xuo5XCE^*vspGy3zij|3Z5^T_`3YE;cnK zJuM<3Au25*HZhqCMMp4|5~VDtsU_)4gDnhKKoJj@9f_z**%uoU$_697aHnYUCX(F**px#F@j~a4LN1J zCuVhKenY8Hzg?^J6za!9{d$l6SNm^Dza9_Tf4A@F%!1ij`dJjRScb23UeE+onwYbgKO%6p_+V%szl;bc*kGxPTM?z%Olyq!fn;iy+D%<7*eBX7W z959~4=r|nXIZ)=iMn^B9>PIUd#CVRr7|%g({fDlM_m0CFv=1)%5&JmoSon8`L)wQ^ zBU?FS`fK2I$PQ*E8y%yB`de4he(0b_>h~t0^LvXoL!PzoN3h_<$2k(1 zzjVY4^^Zmey~IBm!Y#!gvpGg0tD#PR#Gs2-S&uabCw4a~1J?Fv*>31acBCS!{H)QD zCbW>CsMA9B`b%yO-Z;a-tY5QZmSeVKj?j7t%^)j|zdl6^e z%4&U1e>Lwll1FFMXBBe9rB9yK;y<>eQ-O^9`UZ}qDdG!WM@4?oTWZ*jbfNLWjT|M{ zQRMLXo$PCJ6e&xCcPMd`$vcz^?TAK4xzKta=nh=J+EL@E&EXCl^MjX!)+aA?d|E)yhhFR7qZbUDdc) zzg%?=Mi)eK;7kyI05m)591Y4+4iNGGJt*zb4&Dyg=vXDRevOVbLJO7B{wpHrc01lt z;J4S*#QoSSc(b(*CLXVK2**0dNsf~pr#Mb^oaR{XINfoE&;|(2Bs8TW@3e9z`66ZKe?rL^ybe!ur&vCxv0*8D_gXWgAYKqW2Lh}kOOlaXk8z{6vgj{Y} zeOhH%Ng1c2-zF1gR$5B3V&=%_d zMONy}1^IPl`PDp|)$ZEC;UF>PFR7nD)4=eNBb;(te%$i4U!wH*8_;!xWkX5$Bl_zP?jl!E!Y2_sD#l`L5E)B z;N6v59M?Lob6oGZL1;sSHdJWCgf@JO<0i+=j$0hJ3T=eYjuhHRp^cKUCEI}{PDMJp ztYOVOpL{)9c3Fe;k-gIw3;ZYjboEu>lqdG=b@~-AlH-Iec@|RE%BrgTx;6fQ&+?V% zQx)^|-rM}B_>{Ql&~kqjxGQ9Dv*TXJeUAHuHd<&=LW^75QpY{yc#N)$<6*}mjz@(S zA+#|<8{6C~%khNcCUMw>LW@L2shO;a+H2PC&Y5*(HFYS>0l?iCOwKQ?$nw?Z>t_)8 z&t>Z7vi^JM4fovepkH2au;krIX2;8pR~)YjEm~+XLW^CSF?jN~{$p|qmGGg5DdGNx z?k$IxqnFfw;OM1mkLYbO%M*)6kM$qEw8ww=QcJBAJb{T5j~b{E3syGx>Nzm!@U=a1 zvQ`!r`g|OmyvJhgYje0{yKB@DTo< zRd$Sz`xK{F=9kp-ciA=$o-)j@fB&ALYqhmTqH_!z^nyoB&z~+YlFUPTyN8cBa%5L$ zGX-wcbmXYkqnU<}h#0ek*1t?Y`-=c{z0aB9*rrAVpZ z)TCpT^~xE_2IXvJlX9hUwQ{R+yRt*ssobsXQXW>GQT8ZrDIY3dD&HvklatVn*(|9zT*SOqmGXp zAM4pLPH6EYgwPU%mbk6w+8(?j>_=(4*uB!M!!v`317`#nF0|yiPJQJ~ApTg>g${YE*1QY-Cb; zL_%t8Vnk|EVq#QmWL)gH=r{)HTwq#${j9PQ>LOo}Udhz6m$hnk7H_7c+ab>!(>LPC zuCBiZ^vlKFlJn~p=GQOaa3BiFH(F-V!>RP~4R6fk!n*5x2aqq+(V|RtO-*G%e%8I1DYOis%@W#dq0zI=yawM4b`Eh4bq;e5caBgZoFk=o<_hf% zp}i@z_k{MoFy19QBgT8Ai$al~Da}>0{pY&VL(@NM`D;?04} z7(>^HfMU}S2L$RJv9T?`3O?YXhx`2LZGI$s92}HLL+qnB=Wh9hJiWj5(zJL2^w^1znAbyiorelcnhGFUioD-w$s2{p^|sx}x=ZA^ndnch3tQ z*;4AjieL;*!QqmFS_-$nVKxamM)o1=I1@7b0EJqzk50(}iX;=L6}2^G4A=Z3>K*C> zshLi#L|6x>H(HbmhFQL|h&d={fs;U3F0}k6r?2OeLMxC@DdKorq4aUS>>m24t>jUU z`iPGfQvZgPlZNTlwJRI4c@1h_=#Z%R)YzDmn8=8v0*p&3hh{UAG=!lr4==9X| zq`2hxq}1T8E1h+mo#w1^Ry%8)wayhnD-xPdXvIQf;8nWSS?_G<`K0q`=PIF<39Ux% z^wjEwwo(da#-W^?$czgw*p+8JX7lO>B7~2Fy81tnz1Vb?`(*cmSr#@cR{Ps|76@e3A^ep!^PpP=dszE9YB zk&^>mf>K-}v|6MHUgQ|SR?MM4jU&>*)frt)X z=NjjAt@pTIXmTG>@VlFwH~Xg>X$p@$Hga@$M8ff-!;g)L@G)EOcx_tI&@+c^2qe=VQ*t zoliKQbUx*L+WCz0S?6=k=Y@8H&`uN@?iWJC{U-_SWTBlRv{Qw4+O^IXox7cToG&?F zcD~|#l^XnYS?jJB8m?!AeWuVh2<+4s_s?$H~->I$+Xx>t#cdL@W zUhlSutcjW5YSHF^mAe1NvQ8$nQfg}THtO%y%Li%Ey05kmpoRY8c71>Lf3;;kv}W(% zjq4$`b$_Gv3|150I)9Ya#6IVD&hMQ+2<<$foiDTtgm&SUo=-S`cK*`y32_)b@r#6Z zF@Mk-1S)wdcfIqQyhgUO`fOx>Ib+#U$;*!!Q_inuCqoy7?fz@{cr2{rb!0Wv?lM_5 zbP^d$21Is+xOz}Mx)@AsYIGTd=HFFyz@Kq^f~%+Na91z3RTb70$$opLJXx9k5S1Kr zv3fHrv`dAyc~0o99moDX=}*eGH;FMgXYm!uu~m*diCmX^X}x~Xb)aQj#(D&UjNpOP zF>=lL)Th?oc-2)+hxcykbwtZi(1RZc_YX9i`t%(rlsMd12 z<+2zZRF||KothVP?*JDEQ*L#cTxOTWWp&wvcDc~55E>2YRYGgr>SCX_65(>YnE7my zqYnPjXjr$%*yJ7Yfu2eco-HdbqZ`GV6Cp)y)e=>DZIir458J^VzVpw|w+V5*lpK|} zy+WJ*iLQ-asLK~U>Vsp~P<>{x`h{)dCIw|CHf1j}><79Ktu^yk|N|u2dLku^%QiOIbff}r=8Gle8UpJ?J$heMi z8~QI*$T#d(o{HRbRb$WHhxa<7Uv@34!T#mrX$S?S;O#f>ZL%JI#g-uZ*Cf3>Y_2Q zSlcuP{&xo}!Ny?WlKhIAlQ+Gv`{mc(|NN_;{Pu2X6m0G7ulyCPO=q|=T5AI)b$G>H zi;?LeW8@n#vay4aL;L^hMt-Qt*k^ao!+Z7aVC2Rlx-@a9vOA=2zuwFA3cTfvewt7` zc7xA!Rnq`VlgZqL(X6%u8co`)YTe1FZ)$AXy8V{B?|rO;)$ARt_I%K4*)I9cz%F}5 zeg35KT_Rt}%nT-#FYkIzYOQ>iyX2{_fxbzh&~9mCZid>J`&P`otb@5N_J7UXubp#N z-Q+FCKB136+qp*n-c3ggpf#Oe?LQb@zOf}RNX1L$-qJ0v)op$x zr_oAjn=IYra5p(!UHHf2ZR4NQ!u=+f??o7*j8P^kGdWE=M_ESQRHf7?Cn#%`QfbyjBH19{)!`l&FS3cm)2wy8d`!CT__k^|gVhDxx-L9^3R}EftRk$i$ zRjz8G-7d5{gtkLycj|pxS6yr0cISUb-!>@MF|OlekQ^(tyBb|g2r{?LGgk-gz%uA1 ze(w1P`ly$!`tyfF?W3L)s+&7lEgfPXHK_V2t~0t#tg~7Z>)v*Wb*}6D*2H3Xa$lRo z+N2xieiEy=Lt>d7|6*d*m1pM5#2V0bV*Tx!?A82-1L<`HiDc3fX}9ufou$#j=T>|3 z5l7e%d3uLqP(!sQr>Du~?jpUsVQtbY&}QfhXkDe($;zq9nKb{+%68>C-tBZc>qrtBFV?ujUcGcy|RO=i|OHaYyxtdc=kD*NV-v1_L{G2}d$iQXAN?+ZYOxDAz z*ql)(w`l~HvE<%bJ_*dT1|9OOOYS{%J?DDf#rhqSX8ilbX4h`l9@k4k+by&`LVH~r z-z|rpuc`{{VLu6v+Eb3y(KjH#&Fl~Jc*>eqi2YG{-2$`#XNh>3`1~XOdq|bt>%RS z`pZXB8Q>0VS2BeuiV{}4+LqCWm~Mq_Z0-=Dy}NdEUg(4aPRq62DtM(a?I!LR2)EQZ z0ZrVRyFW9xZlk-W`*3$J_Yv;i?mq6m?tbo2p?x5<4~6!T&^{L0CqnyFXrBoUH-0X( zFRpbDaGTs_x5aIB+uU}y!x8Ov3GGW^JVF?I3u7N)>?@2eVZ2co?-c6q!nji(=i`%9 zUvd7*$_9P*)xTIc(!ZJ@=danK#5)o6StxeV)dj}u{#8T&+*Ekr_nC654X@R%?l2=% zpWC+d;MzJ~yMRrLzq+i7 zjTuFH8P=(Sizyv0F+I4ql_CLwP`XU+)R1>zYmF4ke=>2<86(^gw6g9a-6P$j+@po| zmC(Kx+BZV`c8hzAd#p0r9VN7VLi>qVJQ)vTl_SGf+#sd$XG4pSEvC)&Utbz7XI@!J zsa`mEw*>bjdACIOc=rVNM4^2rwC{!XgV285LiIm7Trani3s`>SPhgFD;3h`pcg z`R)bog+lvHXuk_>f3us@aNLW9_J>?l(f6Tp+16QtCGu~rEBT@PRo-q^ehKeNUs>d9 zal+s!EB$+uf-)DnOS-yUsS@EX7e;*o%osu|)9RYRQ`RqUThX{{-0XGS?yhyOaM!u( z-3{)Q?xWqSgt3P(8iY|5Mok!v!q`(74;RK>+hw!O|MkOE<(~p=H(r$cFk1eRwv#)$ zT2?NKXrStAF^M)%qnJR0fImN+iwq8mYEy5Toj5c9( z3S&QEH0OoJVr2cveQ6T?4`XU8SC-Hi;*YWNj}c|ni=&o~rNQ?%Xp3W)`m^r}c4xb< z6h_{{y~o|;<{U>iOPB6#xvh7cugfBg16axwMpK7zuHJ^buW|E0rJK9APJIZ@>b{L3Gie>POmIhR zllyjg{jR>EQ{&#@zSF%kSI$KFzSn&p zk2JLTh#qt%%fG%N>H9Q#nrr;Oe8Bx6@2X0|Cju= zlyR^y4$=P^DjOXtC=YGGN*-FGoylaw*gPNI&-w=X2>PDb#Zr}-gd;QfZ5sTgfu@!Q zFDiyziv9h6_wVfr)Mfpq^K7f!>lNr+$N@;;VtM}Jt$F&M`y)E$?)TjvxIYxeVZu0E z7)La_KX!l8qed8y6h_Wl_wUE&lJ!aT)98`VgVWn0vNPB>;LFzFXKO}gS#`CqsNJXa zetXKtXxv{51&K`&T8y6GC1W z1)iI2=Ve(_(Z6K0;gcKPWYrkhvESb7C{Oj>;A`Fb$%de6eFs<|UOJXH`Sh+-JK-^O zyWhyWw|$SlxV{UYO5A4>Y2s?(y`Mb&oKP>9VTnwt8SapJ}u)`Eq;hn4$)`>fWwD%{Hyp zmG3oydA6y%x|0DJPc64})8#HFe_#b`nD+hh&KC)m3p5uVr^icA!sGI|J!Dd>Fvbbv zxMok7C!A5IFL?dclD_FqPKO0v7p8w}{Mw#>v8%EZ3|3d#e18TL%=VACp^C!;=sGeF7~X%6dWe z!;))ORu|EKbhdmGkTNB|`e-(SqCTrX9lvbzXpX;xqhpCV?BuE|E0UWo{96!O3iy*Z z@YlhU-=BJ;`Iz^o1x;|2Cy7HUJd-_Bgpq%entHz0^L23;4obs8OQg+%?aPv)GT8yH z;u)dp;C3m`G|vpCAw1KCF{QERXX3CK@>g9Ioa32GkJytbjA@OYdBUi#@dxU=fH$Rm zf^seJES0WV=vm~+@#K0IdzJ{}RAHPZjNEF5FwPXljBTD}o;=U;zB!%(&f_r762|Sq zc#Y^mK3@C3{meH{rKeu8Zu3-msy#KHTF(kkoiNT8#yP@>3FitUCd}IAY4EJ{9PL@< zS?yUPjM>6iDU7Ry@nm7#D2$is8J0kThew2WcQnP=poeGWSCbue!G))Z2mfj72@_*$ z{f9V=sV!g)s-~QmaUcBWW81zXiHATB;TN6G2aY_1AJHE!V$)MlvDicSNvDOlU}UcI zoFX&kBo9`eFN_PCJg0h26UK$Y7}zSTx+vur{^xu-NINTj6Pv3Zuac*+{+tAxNG_lzyH^J0EOx^Q*rN z{ABu!g_|O7IQ?lO?}X#{InNW6eX?`eRNn(PUi?XV?3rgQ{3~+FSv^}1LGnD~d0s!w z$iuPpW??L64zb6F@&u&8*s{a%b9teuExieU7o=UQowmBPtbk5NrmrNwer0{IlTzf1 z2z(Q04}#`@#q+w%xL0MyRp}Y`hRnF?17%$Bckg;WmAQ2`+0`buNG#H;eQuFcd*KWC zN}om&#u`1VI9HJ7{%Ae7{DHPlM&5VASR0JI9|Mus?{H-_vrijEkJbEVL{NzRovx8S{44G+zDP-ZA4_-S4${?YjHDHr@TJaDU%F zaDNZ(-_iX6)xBQtKz`#5lYXOlTF~SjB>i^GLHy<&?j3uGb)q*?)`{L2h?8}qemtP` znEoE&))?NI-Z@>l zEz>(!x{ZH0ZP4$w`M6?1XIC8i$jdzvh9}>3`|g-qzM6Ig@7Tj_Ih4)qT-N#Jwcmez zL;CM`j(h*@xgQ_vYvZ=1UU@Edj|;u{ovb|xw;8^aS~uZdn81}0EnoKI71C|%gmHb)ZKnj&ZFMl+ z&S;Zv-VNTfrQgmH#xokd=LqAOf6s5{doS+FZ=1ZANWX0m#n*Pq&Y8#V4IzF*an#jD(1^!aMF>@@b0wjm5g#_EPrq&SjHg-uS+JK=Qig;+?l< z)_?U=8@GMs{nqccZ*;d^irab*l5XP-TL((FpS-_HxBV>LcDe4h-=y2FIGEf13ZrE> zq@H6~PwBR>BcP9T+m%7LHR^8L*6Oyf{k$G)nr(_?zgM) zTc6iu>o&Ht-~4vrOphjpWp(;kEGuZO6wK$T<+I=iQz;fYIdqEX!3M5d){f{fb<7hI z>FJTlsp;|Y5eZSLNt|>OlNON}6BiS~0TqcU zEem7cS*I1LutFs+j4tu51d{PKESA$E$lxg_wOfh~D-Wyg>IyZ)<_clFLl4LudJxH- z5W)3KxzICs%8Cc{#ime&{V@KO!aTX6m|df<6$TYu9d?XVln%$;jY?c#El^Is)XT?; z0+Xn6s$ounJajPl$W_>hVe42V4_g~1gmITJ-m7ma@n?!Om8Inp89c7%EAl^>Bp+Cu z*YZtZ%qXAVZCU6*~1O8GJQ!g+9vY4=qX*k5fIKW0GY;HOa z(Ak8}re@fxC}tyISF;t6(d-27Z9Wc8fVIHA&94D^m_LQR@P(pSro#eQ1i7#Tu7jO$ zH|zrJYDHHo{;^g=Ez~KBjeFV#0^6-@Ljieh*xB|5d;r|X_Jg9>4~HY55Aaxqy+5F* z-2yg1Upw(&_dqnDvmKr72{0Ze!emH>G?)f60bkiy!v%nh_U{POK~M_Y;2lMA+JWCW z=fEOBM<+Tum%(ztU(O1^Uruc1#9vPQ<-}&rb#OAA3cKN5cpp9l?C6>T$l&7IuDOr} zcfn&o?75zTXB5Rf1fl>r+^LWb=)e-Y;?98CkO}jESaSQI1dz>*Y;I(8Bbyu9+{or8 zmfYt6IpDq#o&@6B{Wdf!Ggw3$R7lZ?GT!RFrUJ3O@`Cpg~W-PvOLUIB_42(Bb4#IQ9x3 z3dkFNB#eRxmx78fmX1?NWdop=feV62suy;*m~d^z)l0P(Lii8PyliaJOxez>@#pPTn5C=z^kAM z@Xx?)fUEJ1^2@J@C0E0!NlU==K&c8Bg5eL0euI50-wR>!2Jhv|H1P9 z_+|+4KV&Hs0X83k3`5Fc1=Iub48aCN@WT-F9dZwF?IE86*BkOB;O`+^cgQdBo1zTu z0RzARF)#%dLJlkj?l&|K@}UrXPy%I80p#6KY(5lw4_yyuzy>%QHo|#8eheiRhvJ8! z$TbxI54{GigB#!`MHv1k*1|eC2R6dFa6ViGjetEyVvmuxz^!l_ z+yM{4!|(_^2IxBq`;W4M9h~3>c z!$dd=&}~#Qpx3B$m5}QY2^T?B6Bb)~pz(w#C z{09309itQ|g_S^_MXiBj;Wa>?s80dgM17$s(dZS8tkK9Cy#N-$Q-DpQUxzmVyT%|x z3}s`mK@2vC!3Hr~;a0dEcEC?YU@KPXCE2=o9IjDSDl z@JAf}h{GRo_#+Pg#r+Du134J?m!ga-2EH4Ie&f(@9QuuW9^Qg?;63<2QQ{+E5};o^ z`o*JPJo?4w!V*{p+%vuaih%3JW2gAzVJ%?m_*3C@*Z^n4xqvS5=n{`E@%SzNCHO^A z5}a@paIJ*(a5dn|1mY+GJrcfweegZtt3>opMDN7I;Rxsh{U8)PFbIai2*8$!=#v-& z;~)!;24qa+UWsP{@+F=R$d-5sTn5(x@tJrxJPgEFA~GdnuSDcdB+e5*gD>D~K)ytD zNyOfX#L{?tG~NK*e>|~09=nXk9^<*k_-o*Lzy{-Y!Gl2TjNb$JVEp&+6X3H6y`evt z!3J&!19Y5#juS?~c$f-H0N+f&Hxuy91nfBh-%Kcla;SuAsD)Di{U@OR1oWSPeN@1&m43$W)TH-rJQOd1X&0iR7GW+oxqBxIYE4@FP{yI1#YnBy2bd8&1N8ld$2WX4nSX;X1exu;nD`fJwyDB^bB#!`eNR9^NPfh@2PfmrofQ-q=m|P3T12QC^55!M0 z_f5t}$@nPwX23_u_$c`<;J(TDDEU=*3*Lnf;8XYTvw4X%Of;U?g|soXd9IoJcQ!0Yf1ybn?y?3((MqNMcz z?l4LQ@6Lm?n<8g@)82josW8n9#9 zv2Ze+1smZ!xDc>q8n#SBkF@LHMz{rbz+JEl?g!!_4gaU(|8x^r!2xa{7Se|SagaV5 zh=FusGd&F!!D1+a6@U!srvZLU=f3He!IiKDkRcsgren+WdjMOeKMqgAEAS>@$8_wN z{t0{rU%=P!hoVgF1z|wGOvSfT@$FQ6J9P};+o`b-5BPZMB$x;D0Xt5`j#IJYRO~nv zJ5KchcAQ!PRZs&P02@tx9`-59G~#L61jvIE;BvSd9)`yNxu-o1FTvaJE+G4~58-1V z4yOG8zrud_OHrm9zzFCvy$=uv(`NzlP3Jz-3jo=s5v0Ufa}lTnlny;^+24=*bL}5gSuqKR=66jg&SZO zpz92LGXwo*d<5t=<8$~5zJ>4MCqUO3=sL4Mn85~4@IW{aOEZT8zMDA;B48{KPcyOg z%%flmOb7fo6aUR5#%3;tWq|)?VxyTwPz>b2%++u#oB$`msc<^r&zU#DGw?dR2l!^@ zC$JZgdnR(vMDCf$JrlVzR2Trrn1PHLQGiStb0Hh@f%|5Z0shHY0mzVXG^_#anQ-S9HJ2E_fW4*(rzeFn%s>jy=d9RkF_Y+_(`I7Gk{NP$e?zOxqrzMZ`s z3IW^B#n5UaDX?QCp2`z9bhXTJz9!K?5FybbRGww;Z?XJgmd zU&1$vGRFW$z@BsZ0QQ`NJ?CK0Id*UX_M8(AgJ3c&gLSY4u)!Saq)g%@6B}ouOC~nU zBnC2xfz0FJ1VHx8jey*l7r;fZ36MYYYPcSdKl4_&6Yhq4;Q>Hy+@s7zzPZ?NE;gL& z1P|cjxx-;3L_j3uz?r}`=iUJi0sfi$8W3Z1k#`>cn1}B3`UASoBX;HyJM)N}d4qwt znRg^4KpM<|S%7}?@YTG9kPAzp63}(t*>EnP-@JU8ozX*HaTSb}Q4`Kix&c|o-uY`x;1C|<* zdw~~-rv=Ep0J#^81!7=95~RXZK=uX1!h$(47qVbE_@ERjpcd-kXy85zu;&6~U4XwA zJPpqQaxHiTUV}H`9l*Z}ep8f%$g+_5T!>#6_JaOE>@UQo3!Q*}7pB8%xCENuCcr-n z9|f+zkZUgd8ur1Tin6E&XmB|6g-|eo6^24Ipx+|&TZDd#G6CHdEr1+Y0(npX=(^|_ zI3Cslep`g!7U8!=_-)bIa4wJwi_m=$zFX7?Ti|NA25tp>x9DCVwiZ1I_+k{ zz@j(cZNNW^J_hR4MZ{T7PZ$OGCT9{%ffSeqGXeQ?kUs}`b1DH}&PVVm`~bhge)vmKa`8_t_RPhexxK*!BLI1FCj&8) zi%hv`fL(K^1G42JTQ0GaI~R(f43IIm2I^oXtOo3xdpuy@+;wm=oCO=|)suK?_s zdktI%Hvn;vdkfqL*faMrcoK+-T=dDMe#ynQxxXpOV&ZA>5Wtp;$3PUs!UQ-9&|z^Z zApc_Qxp*nm0`^>t9T%Stm%){AJ#gQ}x5J&V3+@MEaxp$#%zYPg-^CxnUicEefgj*! zkn;S&azD0Q;($nqfpL%s69Ky}Nd|1X1Ya+~o=axKTquPKsD>5L0IL8yF2RmVu;UW^ zz68H7Sr2Cbwp)UImVC~;g}Coh?zxnBUn+oDSo#QjsBb?|mL1N2DYL8}^am#lhGBr; zmtn(Y5ik~zbs4r>hV7PNyJZ@XQaIL&Q6=it{7y!MO9|3&;v|6X_ikSqTc zzz_Mzl8?>uxo-hBE5K$2*sK5<3b0uLHY>np1;k3hXh?)~m<}00TolZM`LGDERY56K zKs6v^0kK>_EElW+5{RFoNFcw9#sM}dnhkRy8;IeeTv!Ur0lAB?OA&S{Du+r~3FuLT?~2f+ z=u|+5B79bKK3oXIQ_&@W9!1x|jc^O>0Q4!^1=LYRZ^2)R;_D6l!3;KVLl_{x5BYt_ z>ze{oVFnQQKH}boyguahA+Ha4eT9IGzBRA`kju9Tw!%)h2cCdu;RVn&Vvh}5wK-3F;RRyV9Vm$;ZC?4UWMNjrNjump&twY zWG_MX66{%mJxj(w6dVO0LWOH1lYC| z+m_A%Y+H(LOR;TfHY@;aT1uRj5~rmlfK5yBf9bJsJe&vuP6BLNN_>`Xf=l5lK%Y`< zTZ(NlO8H@60Awof3B3THm-hwaE64BU zW;hZ?1M-zeLmXh+@(F-#%du@awk=Nw;-GvkVAJx2kOSDXyaLdr99_yAfLJKUw&lkH zwk;AWkcg zyJ8|t2IQ)k52e75Dpmvctyl}#wPHP-3D~s)yH;@D3hrC64Q_*-fL$xFYsJHWJQdir z0=rhc37>(K=PyO641pe?0-RoJ!aBsdkg zZx#2gYJ?jBdsY#rRd)gQta=b0fydz$_z->pd|ZV*Revf9>M7O8R872AYk*wU_`JF| z41^&t97aL}L_!RVgG3+>sj_N<8qg(LnvP;yB>GE3oGZY`NkpXol@@9oz`W zv*H1G7LaEJ@~l9f709#VYuE=r0QJ%e?7HF)MX6K30(NkL7w~r-aaxC*b=b2Gd)8sk zI_z18%yq{&-V)FFRe4lD-jS+^XpXI%|6z$(C&btl3)z?OCE0onh*mhL;c z%c@Nm@Eet)pa_KCAxH@!A%MN1g4nV5f}bdMkPun|1VRZ3B#_WU??sd{blEC|hmf(A zjIAPg5*b=G;d9*Esu|7s8H>}(LtC{bk-iLIFqw=(m#xM#iMeF68<|?|;{bBCI>IsZ z+UgWCw$g3Pz2xP73Q~w7=r*PVr71@RWREfb7};Z@h~_DD8DlqN>}HJj#CT84JG@6z z>}AaN{6tIi8l%@3y~fyynEB+efz51VC-TP}ME02D$Q^Sj2-Fd@E=qCaYF&%xc!9TY zZ|e{FgfG!+YrVGCYiswmc5mw*q>xHK1~LSBT94!p79vmUt!zi8*1PzJz3fN6)`v0o z);G8v1Xe$Y)oZL?WAz%V*I2WUElenK$J*Oiea41SjT-1PR-duY(g3~2zRW9pfE|ss zqp?lVX{=6TbsGCC-ViHutjw`ltYkIokUw?@vd7wsSh-{ELF|Z3kM5k?1NJXAD{Ta)24dFiW@c;!WL=j|etIM_(s7zIK+175heF9y!eTL^~z>C<;w(s*1 z`fU3Z|3jZ`_1U&NlbFK-7PAcb+sfZ|E%vmn`L{j5SuXQ0=HJ%*+sWHb-gfe~dyt1I z#Useo?r9qG4)(I0y=>Q-_QcbfZs@aJFM5-STSItGPc)gdpp^_4CSdv4Wg-!ooN3gvbTSh*U)AAclm%% z`GTf=gY50cV)pIlqRaNOx0k*BD)iW1kL@?JjlG;g#tt%exF56aAVY^*=&gf$J2c=W zUg34#L6DKxjM+zL9PxvIYTZN zxxzJWawiDlWR25jTwe4UXXbH5kv*;yWe6jJDD)Yp&p3U?c~e{?bQ$Lzao!Q9%easE zj4zQrZUj@A$sA;llRYjAdl|Qyb@;C0{^lq$#>p6eABB-2z8dv#Z~W6d$4kf%ug`dW z#(zq4enyw^t#fgtcpYf?=B2TG&UYGH@jMrtnF5`6>f0}b# z;4=SWFXPR;qaHinLmu?lQI8!9qNk3}^CEI~Y>b{de!^#b!B>3EH?$;{cEpiDBKEOk zFD9@CbM3f?{T$*rCpm*zcQo5h1t>x>N@A9sDpQr}gi{+CJL$30hsf2*9(K}Qr>=A- zoso=TJd>EpAIv~+otC4wPI~L4w@!NNq_<96IS~Y%^AUzFI(u{H&LlB_p$umf&hPB} z&d%@b{LTwm%?@_q&7Hluvp07>ikU&=GS;NTdhmkkFenbd%r>34<7d4idaQ!P^t;OoH4A@+RmY!Tuzi z;{x_4;VRd;83bMO#9~Lf$ks)+F0yrzt&1#O2BXI= zzw;+EnZqKMlEq5&+GPv!bdh>_^v^co`jbeFOb4Fc!0B*L*iA;A@Noabi0?l+)qIs z!W_DlqXLzQr7PXgc{lse&0cmp$$2i}%x=yUGwANj?#}G)%|<%O&g+@YW=`l6*r+Ly#*;t|YmVrZbDbn2(tzd3(}&yftY%f3ueZ z9Of8yxz|19AwPQW^$>-TvsVM;>h&6L@-}AD>r=j# zZ!_)PlyCTsANZLTw8MLPdr$8K^xZq1{&-VwGw(f=VNB+C{$wU|unWDtqqiCNKF=kt zay(i20yeV2vQ$Frqn_dLAKPA*oD;cRH7!jPK_d(r_gh% zo>TRl`WkQW7H^~LRBuW3mQ-D*{=mN)SDIXDa;4Qrmua%4J&QbPpYSDL(+oM%usU=gm?J>d(pQ$z0qmkbOxZ?z9aFLzT@$ZzB8~7edX*cXJ0w{p5{&v^pmHb zZ>e8Vic^a6R3eOOJV7Jg=R-c`Q$9zx{hIPWexo&Q=|D#k=!&fUlITZ&1~CLV`%T3T z_xlrB``LwlbI4*9YuUh7cCZWIT0d{-r{8`z@s@ONNxzSL*o*YXc$Nmdgf7$H#D1o~ zhqt7Eg6!$`AzjXNInz_g#N5;6Nne0{OqVBpB{HS^w$it;6M542agbB^&eE@9-Wl>_ z$dmB^zOjsAl*fEC9_J}!%#bld#ta!V%sWH24B0YtobeShW_-){{74K5$eW?pj1>Bk z!9aAIF@aeuV+A>^VLhAJi@X{3G~*&yxWVlpFruKpZ>@hx-r_UNy1(zJe>0l%106}m zz5U(W-@W~ZVF&x$z5Zs~-)#F&XC|}R$eAD*P=p#h$}>FAi!{Q%4`{->e1M%BFpB?v zcP9u27T{s*?ZD!cL9YWV5r#7dzK-7wly#ua2L3`zVrWBq;z=Quet7smyE$+u!|{%R zW6<5eBSA1IFEx1u=ML&dA9O!R2ZIJMf{9FKD&9ZnPiC-$w6y>GDh4Stn4^gMVxdL6uy)ofuu7r7DyL(FhUJ|5sfiV#W(N~8ZFwP;0W zyk|&Hl1U>SZyGX~Da=I|L)KuAhHPRhJMbM0@m&r%%{jbj$YuV;UJbd;-5?k$(@^gl z>U~2CQCHOHY7n1$>^ zWgjZ@&;wlJY7k`Ji{3Nk%9JZprcB*u>ONEVnKkK%^E365nT}pEouBFaOuLymmN{f$ z9+~EmxeoiDX)c-Ol6f)+hLuD9VIT21U-3Vh^CR9htOfCy$*`_;Ckgit^R8j;8|J=Y z-ZgA8zvJ#<^H|6dma~G*>}D_CJnS&X$ieIgfvS zxPN}Qe}1@se#FCg(+GPs!n{UAzFA@{ZKk$X@g&4Ko@!0J}Rc=NIJj#qm>3Ni%M>%hl^G2QE zGroeiV6;v~+u_l#@&-B?{XY5_?QNsIZFCE~WpoOu*yqvx8N^VAWB#N6WF~Vk z_tD-s+T2Gk!`w&faP*NN7^A~6)o8>I^k5P*n9W?~lZCg8*?^wLY+)O^8skl4ylIS% z#^`8__l&t11Y>nIwlX!SMI_NY!PEF|$NFx^dh=K_8aopC#;#!}r_j&Xt3fdCUW!wS zM+m1jb$ArrjeCk`(cL)n7^jDEU)LXU^lyVw_owJBK-pyNtg} z<8E*}2*wwr5Jd>31f?lQMXC@*edHJ~$N1;*w(;IJzA+zSkH>$9p2z=;S&nbT0LCzx z-!Z%KW;b5O@pftaQnFadM)siN@j4!VGYBS_;e;}j$1Eq*Bm#X;kY_>zUPP7&vP^iD z*Ljmq`HKJXExwltW-*~9F?1)L(TrmvQ<#Q6C+Ks6J}1aFVKq9PAmfD1Y-1<8*~=-; za-K_=>xAn;FtIFlWTFly*2O$0+L4JmoGACiM!bT|6W`}+TGN&U`e4=*$D+T9`kSb~ ziL+Tu4zf*L%X;LSDBr|=9Of7&IfrZ$Wt%A5q&zgoU6a}qPiMN(lVo}`giMAb$D{+C z#f&Ch;BpX5?vD;9|G^Aon>?2V$T)c!8<1pEoen7?MS2tz35FE?w;iDy`R;Rj+p-}IcCW*OO9D7xOhn4M$fY!MaJ2!8OnT|JKMRl?bB@g zG}}JScK&SV&vyQ7=g-l>oDSHXIr^HTuQ~dfGYIF*8IJv#V_tLgF=q!yIf1-$oHxgL zbDTHlzw_k%>wXGS2wDG<^)FffDnmIc@)Awy$^=&8+`0GQcXR!2uHVh|ySaWhw>%Aa zllSq~xu5a{&1lY#{6b5HV0LrOXYSP?n0GJ5DMe)>d4Yz!!-ssr=X}jKe1|=oXRqeP zv5CDLLe_aYnCJX?=edM?=I2N5`C(L}CJ{tYmq)Q{^BeOT-ZTGg-b04@GR(JQ^X=ID zpYWFXF|o#^#X@sf*qtO&NTnbBv9Alqp_7GEn8tKw@fY*>FUv;swQxIs z^AATj&MD4fo(s)$kv(11gl;%%k#{Z1!a0jpvw_WQV<$Ra{0#bB{4t-Q&&B#&tk1>G z`H^3U$1E3{OD)n zXK71f7>16Qp67ZHEXzwN?7%YTFLUOyhBW4V?A)@?`HE&ZbD1-jIdfTOdXvcrMl+6y zOko;2TDBB(S!OQF%w^d+Hn5p(n9VXXUS>Ya&X9{vmtEz*jJJYdc>zkIi{<9Nyb9F` zr#5xa{c`VI-jQzfB$+wfugMqvia&0x9Cm+O4_4i4kq*LjO~uq!Lfb43SqwnDZQ8OX6h zjupCD;ocQXSWXUWFuxUcXvIEGW0zK3;wm?}9R%3{_fmukgi(!}$d_%;vh8m6n&Sv*^9A%*;&}X?Ct0( z`#7gK%SEnm4f$7=*rRTR2d)e7COa_%bUuF~-;JF?1- ztg<7k41Sd!SLfjY9^_$)q1V-AD39}2H$tzgy<>GVn)4&S;5%L&LmPCvItksbPR0GJ zy>0a%hA@oz*vB<`Tw~YPyohtxbi(h}^d*CVn8_M5Su>h(n8})>c<-9)+~RH!ti6Z( zDM%rT;2mq9r3KDh>;AP9aR1sx=y>f`j&YK!*r~O5f?!<;d2!}Ce^=J&bzM1X5=k`m zd6K7jju&_vIoH|kb<>%J?$-E0AC^D}P=W*=ydimGe zt@UYD8m`W7-U<&goAkJ2K{f){|4u7aP9{4 z+|Ux|Z*cyGjwFys4|=hHQ$et?Dtg%X7*FyHI@s8d#=OSov_ig(_F-c``ePq9%C=Fq zjk0YV%S?2&(VlL!ryJ$mxSVWqSi|uk*km4?BGC0Fv)$y}O;ho^O@6n@?>70}CcoQc zUpAda=bP>Z!RC9&Lw@YZ<|62Pb4kje=gqO$z0D)}ouy>4j$ND~7rVD5g!{;c-P>YM zwiG6mDm;Q2ZPDG96lC3^gDuYAGK68cXUh!a-m-zsY-1<8*~ccW8?J+h$+3nZdSpbVSB& zGH#P`+hE2so4G7tG0Rv%4)$c5Ic?j+ehzVz6UeviEa$l$1Y!reZii~ZSQe|Bu;IL_V~!hPh!Om;qq zzc)LJQjsdyk)72Er#4YU4f6jJF& zfAq9VPrLr&AIy1oC7#E*yW`O7ZZq5MUAucTfDxG8?lFvG0+X1-JQlKq<>-9(3A}5! zneDzC1pj#7KY1ufA-wM&@B7F5{%JrfGBCe?CbNj8*pGj9aFR1zN3VPIx<{{j?nk$K zbh}5ld(3Q4Wn$689`D%W9eetaj<@U?OeWLtjqLGF>{*Zb?Xeqs+_%RL?%BhB&T)au z{EJ=Ob2|w326)$A@7nABz3$%|N^vSsoto65Huh$(z1jO5FYz+3@)qy#J|EGVLFjp} zF82P;pUh@1^I60?d^daL+_Fm#D`r2y-d(B{9essQ1u6^OuL-+gM<3qmT zdw$|qVu+<3ap-ZMT>FMG61%W(B2&=izUgG4$9;O+cZj2$;52&Nr^kJlgJA!?z_n?tsn@==?w^r6@}U%>O_`zQ#Kac*6m2 zIN%KjI^o+mkVFcp^urz;I2!~9oq5px2g7jx!6$i;J~w2TexWH);`z+ue# zP%am_!ZmIL!C`+(4(sS}0lew(do;xx4mYDYi&(=toPSuJBXS&hmgji^JsugzSj_pz zG^R5PogG<>d`DKW5*d!_?&uqQz{h;X7nsFSb2zH6qk2E8_oMx>Q%8q#kW>82E$#-v zv3u~AV-N5kg(yxrqVWwKGn->AXoIZBd@slJdu$_{@UCON>ti}Qb`<+~%sw7FkMHEz zRrGee8ZsaMlCROz@$WF}<7R!_tdA#<$Sw|%i|&tm_woPExXJAxI8g-WoiNiAPodKj z4S0#yc$2p=mlN(h(G8uRFxwNyIEmSwIENli>fvM_@?)kaeWn>{HG@_m5` z^I|5az4vr6{O)vh!l_Li>LKIlR5BUGSSB!)KbXNR7Oc(d`)-&$#c5 z`_8!UOi3yr-7^F@n+P^xQZmGKIOU!F}hv?VLN$9pngh;GF$GcP9vP z^Wgs6Qj|rXxs|C(HKM4?qdbmx=QcsU+;_3RxgYZ_-xG_RxpC+vSKeHCbJLM6cRFUB zI|mtavss0#xw7WUn!AO6&{3}MHun&@T;)2qxEloL?ce$Pk^j8CIB$06Wj+5R8Q8V+ zdO2^`&QIod{=gj0yX*XR+;Ltn=TC6~cU-s^XJ2sk1!rGy_JzVcLL^UMA1-+31@F7y zt_wqP*9Gsp;C&bV!r%N0i&%op7j=E{A)IxwJhEPV6z5%Z-o@v5frd23o?U#Cx6$v# z=6Ku1pJ_oWVv*xw9QwW}&qaM-OvAe`E@c;Ig5Z*OT`Easd?T0MqAAX~B=aSCFX{i% zP7aaFHEv>8E{AX*5ArZYDNZTMP!DHb{()hPVG{Q7vhy!5U@^-ugUc(~%5L^z)|U@s z7MIU*o=aQ_f-CyE@;Ys4PY?Rgmkb6noKcv=6>q=d?kh8x$MPWfcP$(EhZCIU92dCE zzuX9dYx#Hp@4V)n*SzzZcV6?(Yu+*reUHlh0)$I!(MUEI*c4PD&O#f|^Y*2Rt6L2xtR9*QCVO}l#2oNxXQ z`**V|1DJ|FZf;{IyD`_B=6cgyZ@TN|N$lcHyLIy-`nq`ym)z3vEgjz~i_UK8?3T`M zx$~CJZt3in&Ti@K)?+-$GdxFY%>0&}y7f1*-!93s{74!zImq20xMQYwba%)3cbtF6 z`FEnI%cDGw8Ql4fJ`80%cIu9I-uVN&b!Rq9$->=tRuz5%7|0MZnZjHa;l8`>ySs|DY`{G4?&26azN_QAI=-vpyH|pc;2!dj zp8`C@!<3^URd|FNg!2rq@&-+Kmk;=uZ~2bq{6H5{8N^VAGm5eN&Y#R+HglQJcFuD* z2nngcW4wg(Lq6k6zNQ)O3Hgy%c^yhb~mb?+Q@1tIqpqCOuI$0Ypk?pwlgvRTDiwy~4l?BxK5xE_S$ zc@Q1vX@EE8`JSKnmEUMhThhs79t&|#UT@ADkGy$xn%7(MJw;>8Cf^5qjC1qpE}!o5 z{Y(p55lcJb=tu_UlJCEBZU!Owi%^ELl;=^N<9S}-Ek5E?zMv`J;Ew$6$nTE)?#S2lc!b61##cn+0dw9tAP^cSrtI$w}GYUIZ z$W9f~U7?xGVIB)vj0}ZN1|bjMgR>s4Nd!^Ux< z;@+ZVC{HEKrf4;4@GfRqv^U;e)EkSgM%P6*vIXxgs`H|HF8VLJEqa}sK}e{6L-iY~ z-%$OA>Nm6m?g;hPP~Spmb?W1tp>`nD4um$OF|Y9k-_wRf>{MtU`jUa!gbv4SLS+o~ zeS~J?`wR8SR~Qtx0rs5xwqKM=(X7Ue8i`GgHDUJLXXAVRctWR(Lph9 zFQ(UG-do(+#r0QQZ^fNk{0W}M_fz~O8u1F=Q@l0KDQ=I8k7FW}Il%?wEm4%>IJZPu zDo`1nmC#p-aO_EmIyk$8vrEWd!d{ek2k$819VNV@#246+65dndTfC*jB+RhHPHqPw zC5usm$MKtz9no`1Z!S5IAq- zO5F@XO1rKK}flPd&$fF6r>Q1up{N{NV)g<5N|H`E6y+17Uz~T z|8l1#_ujE){;J1@YLG3SH?=5-Ft87k5=~SA|vV z;1sulkctoSFy#oN8a0Wa9y+i1B+npE#rOCL_f&L#Mdw%ShwdxRV>{P_kV^KuQVsm3 z(z|?#jFmd$x0Ph9Bx5CKRB}coXH;@VrGfm(Ow6&8Gb=4*3CmfF*;d-jRt|8OW1PgU zR}R6uDtlMuqL@cz{Z)2$<*`g*8ak;wiv=uZ87s)aw^PL|s>omEbw1`Zyrat3G{YWN z`4M|k#hz5LCsh(iqzAq5zAB?w%3kgUAz@{B0<#T!3!R49y|7Pkewg#aoFC?V|4z1$ zurBB*Oh;k%Cu|Tyu}5Lv5;hla2=j(8ZwT{-u$AoQIG6dC8{7^;{Ga$CRqrJ)`l(u+ zQk10vl?me!o=2XlofwC+tNz0YuAuL#H@Opp_;;{{JaQlT2&Dw2DMv-B;LJzt#UtM^ zhV4N}wFh{H81z@IJ9eX*?yB`A1GB3(gsCiNBl|eW5xldSovwD4^IQr-s^5dZ8`aIR zdI9W2^}@(j-J7d>bM;60gg8dB1m{-wrW)Q-!&_>|SEC@sD37<)&{2&ryr)KW>hm~H z@hlB^iSO{P8Xf3}_ti)wnLc=54ezVreKnS1?`oP!O|z(}rlDSqPickjK*V1{dXL*_Tkgt|}wb~FzC%VvsBvO#A)^MgE zOD$Py$x>?pi&@4Bc5oPTsdWWi*3xCIJ3&Z92=|eX;@GE%vQ)r+L{vq-h?@9&8u2Jk z@HFy9$Q#jwcaS^c13tn&MtDPnH$><-LdOxUh-EP2`5n0<de+A`HP|JvqXyAk%S_SeW#TbA0g)cy^#uHBXn^u~N^Phu*6 zFoW65MW3}7vz|?CWe2<1gM76Qa)h&-=MwVPmN!zjk>(yLcckw(@?nag<47Gx>Nrxz zk#;!Jdm`WF3!2fKANd8DBV~@1IkF=O^ul)+Iho(lW2F8f<%^Ur@))N%#|5r(4fBtb zE$Tr^BTJMlQL;qUB!Vb(8`Y3^Xv#O}GD??GKhuI%#L|tPB-00f7oz-Kh>|bL%%d_H z$3&(eZc~|`raIlH2o@n1t^m?|jliln^=4hFtWsW|}d9DW`b@Nh+vgom{{_4tCSH8OM^9i5x z70vh#^RFvg-8j0@i{9w8Za;=HoKcLycU5;fv-pd7tRfftSg#PFgkx^?p5j>=@Dje| zdUDkJAILt{(r9j{ij0D|M%bj L|9=gsKjr@bAx8zT diff --git a/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift b/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift index 569fdd2..4d88d36 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift @@ -50,6 +50,13 @@ import Foundation import SwiftUI import Combine +struct PostgrestError: Decodable { + let message: String? + let details: String? + let hint: String? + let code: String? +} + @MainActor final class AuthManager: ObservableObject { // MARK: - Supabase config @@ -279,11 +286,35 @@ final class AuthManager: ObservableObject { guard let http = resp as? HTTPURLResponse else { throw URLError(.badServerResponse) } if !(200...299).contains(http.statusCode) { - // Try to decode Supabase error message - if let sbErr = try? JSONDecoder().decode(SupabaseError.self, from: data) { - throw NSError(domain: "SupabaseAuth", code: http.statusCode, userInfo: [NSLocalizedDescriptionKey: sbErr.msg ?? sbErr.error_description ?? "Auth error"]) + + // ✅ 1) Print raw body in Xcode console + let raw = String(data: data, encoding: .utf8) ?? "" + print("❌ Supabase HTTP \(http.statusCode) \(url.absoluteString)") + print("❌ Body: \(raw)") + + // ✅ 2) Try PostgREST error first + if let pg = try? JSONDecoder().decode(PostgrestError.self, from: data), + let msg = pg.message, !msg.isEmpty { + var full = msg + if let details = pg.details, !details.isEmpty { full += "\nDetails: \(details)" } + if let hint = pg.hint, !hint.isEmpty { full += "\nHint: \(hint)" } + + throw NSError(domain: "Supabase", code: http.statusCode, + userInfo: [NSLocalizedDescriptionKey: full]) } - throw NSError(domain: "SupabaseAuth", code: http.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP \(http.statusCode)"]) + + // ✅ 3) Then try auth-style error + if let sb = try? JSONDecoder().decode(SupabaseError.self, from: data) { + let msg = sb.msg ?? sb.error_description ?? sb.error ?? "" + if !msg.isEmpty { + throw NSError(domain: "Supabase", code: http.statusCode, + userInfo: [NSLocalizedDescriptionKey: msg]) + } + } + + // ✅ 4) Last resort: show raw response + throw NSError(domain: "Supabase", code: http.statusCode, + userInfo: [NSLocalizedDescriptionKey: "HTTP \(http.statusCode)\n\(raw)"]) } return (data, http) @@ -329,3 +360,55 @@ struct SupabaseError: Codable { let error_description: String? let msg: String? } + + +// ------ For Walmart Items ------ + +extension AuthManager { + func fetchWalmartItems( + search: String? = nil, + ingredientOnly: Bool? = nil, + limit: Int = 50, + offset: Int = 0 + ) async throws -> [WalmartItem] { + + let tableName = "classified_ingredients_aa" // <-- CHANGE THIS + + let base = supabaseURL + .appendingPathComponent("rest/v1") + .appendingPathComponent(tableName) + + var comps = URLComponents(url: base, resolvingAgainstBaseURL: false)! + var q: [URLQueryItem] = [ + URLQueryItem( + name: "select", + value: "id,name,ingredient,classifiers,retail_price,thumbnailImage,mediumImage,largeImage,color" + ), + URLQueryItem(name: "limit", value: "\(limit)"), + URLQueryItem(name: "offset", value: "\(offset)"), + URLQueryItem(name: "order", value: "id.asc") + ] + + if let s = search?.trimmingCharacters(in: .whitespacesAndNewlines), + !s.isEmpty { + q.append(URLQueryItem(name: "name", value: "ilike.*\(s)*")) + } + + if let ingredientOnly { + // PostgREST boolean filter + q.append(URLQueryItem(name: "ingredient", value: "eq.\(ingredientOnly ? "true" : "false")")) + } + + comps.queryItems = q + guard let url = comps.url else { throw URLError(.badURL) } + + let (data, _) = try await request( + url: url, + method: "GET", + jsonBody: Optional.none, + bearerToken: accessToken + ) + + return try JSONDecoder().decode([WalmartItem].self, from: data) + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/WalmartItems.swift b/SWFrontUI/ShopwiseFrontEndUI/WalmartItems.swift new file mode 100644 index 0000000..44b2e4c --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/WalmartItems.swift @@ -0,0 +1,20 @@ +// +// WalmartItems.swift +// ShopwiseFrontEndUI +// +// Created by Nicholas Castellanos on 2/27/26. +// + +import Foundation + +struct WalmartItem: Identifiable, Decodable { + let id: Int + let name: String + let ingredient: Bool? + let classifiers: String? + let retail_price: Double? + let thumbnailImage: String? + let mediumImage: String? + let largeImage: String? + let color: String? +} From d07cfb472612847e60497a0ff12e6caa0e4e69ef Mon Sep 17 00:00:00 2001 From: James Date: Tue, 10 Mar 2026 14:48:55 -0700 Subject: [PATCH 5/7] user onboard shell, still needs to be linked to supabase --- .../ShopwiseFrontEndUI/AuthGateView.swift | 10 +- .../ShopwiseFrontEndUI/ContentView.swift | 10 +- .../OnboardingSurveyView.swift | 112 ++++++++++++++++++ .../ShopwiseFrontEndUI/UserPreferences.swift | 11 ++ 4 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/UserPreferences.swift diff --git a/SWFrontUI/ShopwiseFrontEndUI/AuthGateView.swift b/SWFrontUI/ShopwiseFrontEndUI/AuthGateView.swift index 92b7a4e..b85c2ec 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/AuthGateView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/AuthGateView.swift @@ -9,17 +9,23 @@ import SwiftUI struct AuthGateView: View { @EnvironmentObject var auth: AuthManager + @AppStorage("hasCompletedOnboarding") private var hasCompletedOnboarding = false var body: some View { Group { if auth.isSignedIn { - ContentView() + if hasCompletedOnboarding { + ContentView() + } else { + NavigationStack { + OnboardingSurveyView() + } + } } else { AuthView() } } .task { - // Restore session if tokens exist await auth.restoreSession() } } diff --git a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift index 6a59f77..efdfa2e 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift @@ -55,9 +55,7 @@ struct ProfileView: View { var body: some View { List { - Section("Account") { - HStack { Image(systemName: "person.circle.fill") .font(.system(size: 36)) @@ -75,6 +73,14 @@ struct ProfileView: View { .padding(.vertical, 6) } + Section("Preferences") { + NavigationLink { + OnboardingSurveyView() + } label: { + Label("Edit Diet & Allergies", systemImage: "slider.horizontal.3") + } + } + Section { Button(role: .destructive) { Task { await auth.signOut() } diff --git a/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift b/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift new file mode 100644 index 0000000..065f5aa --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift @@ -0,0 +1,112 @@ +import SwiftUI + +struct OnboardingSurveyView: View { + @Environment(\.dismiss) private var dismiss + + @AppStorage("hasCompletedOnboarding") private var hasCompletedOnboarding = false + @AppStorage("savedDietPreferences") private var savedDietPreferencesData = Data() + @AppStorage("savedAllergies") private var savedAllergiesData = Data() + + @State private var selectedDiets: Set = [] + @State private var selectedAllergies: Set = [] + + let dietOptions = [ + "Vegetarian", "Vegan", "Pescatarian", + "Keto", "Gluten-Free", "Dairy-Free", + "Halal", "Kosher" + ] + + let allergyOptions = [ + "Peanuts", "Tree Nuts", "Dairy", "Eggs", + "Soy", "Wheat", "Shellfish", "Fish", "Sesame" + ] + + var body: some View { + List { + Section("Diet Preferences") { + ForEach(dietOptions, id: \.self) { option in + PreferenceRow( + title: option, + isSelected: selectedDiets.contains(option) + ) { + toggle(option, in: &selectedDiets) + } + } + } + + Section("Allergies") { + ForEach(allergyOptions, id: \.self) { option in + PreferenceRow( + title: option, + isSelected: selectedAllergies.contains(option) + ) { + toggle(option, in: &selectedAllergies) + } + } + } + + Section { + Button { + savePreferences() + } label: { + Text("Save Preferences") + .frame(maxWidth: .infinity) + } + .buttonStyle(.borderedProminent) + } + } + .navigationTitle("Your Preferences") + .onAppear { + loadSavedPreferences() + } + } + + private func toggle(_ value: String, in set: inout Set) { + if set.contains(value) { + set.remove(value) + } else { + set.insert(value) + } + } + + private func savePreferences() { + if let dietData = try? JSONEncoder().encode(Array(selectedDiets).sorted()) { + savedDietPreferencesData = dietData + } + + if let allergyData = try? JSONEncoder().encode(Array(selectedAllergies).sorted()) { + savedAllergiesData = allergyData + } + + hasCompletedOnboarding = true + dismiss() + } + + private func loadSavedPreferences() { + if let savedDiets = try? JSONDecoder().decode([String].self, from: savedDietPreferencesData) { + selectedDiets = Set(savedDiets) + } + + if let savedAllergies = try? JSONDecoder().decode([String].self, from: savedAllergiesData) { + selectedAllergies = Set(savedAllergies) + } + } +} + +struct PreferenceRow: View { + let title: String + let isSelected: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + HStack { + Text(title) + Spacer() + Image(systemName: isSelected ? "checkmark.circle.fill" : "circle") + .foregroundStyle(isSelected ? .blue : .secondary) + } + } + .buttonStyle(.plain) + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/UserPreferences.swift b/SWFrontUI/ShopwiseFrontEndUI/UserPreferences.swift new file mode 100644 index 0000000..bf1cf62 --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/UserPreferences.swift @@ -0,0 +1,11 @@ +import Foundation + +struct UserPreferences: Codable, Hashable { + var dietPreferences: [String] + var allergies: [String] + + static let empty = UserPreferences( + dietPreferences: [], + allergies: [] + ) +} From ed312b4dbf12ce58573859d7b0a9393d43c371a2 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 10 Mar 2026 19:34:11 -0700 Subject: [PATCH 6/7] Onboard survey linked with supabase --- .../ShopwiseFrontEndUI/AuthGateView.swift | 43 +++++++-- .../ShopwiseFrontEndUI/AuthManager.swift | 96 ++++++++++++++++++- .../OnboardingSurveyView.swift | 75 ++++++++++----- 3 files changed, 180 insertions(+), 34 deletions(-) diff --git a/SWFrontUI/ShopwiseFrontEndUI/AuthGateView.swift b/SWFrontUI/ShopwiseFrontEndUI/AuthGateView.swift index b85c2ec..a059b6d 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/AuthGateView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/AuthGateView.swift @@ -9,24 +9,49 @@ import SwiftUI struct AuthGateView: View { @EnvironmentObject var auth: AuthManager - @AppStorage("hasCompletedOnboarding") private var hasCompletedOnboarding = false + + @State private var isCheckingPreferences = false + @State private var hasCompletedOnboarding = false var body: some View { Group { - if auth.isSignedIn { - if hasCompletedOnboarding { - ContentView() - } else { - NavigationStack { - OnboardingSurveyView() + if !auth.isSignedIn { + AuthView() + } else if isCheckingPreferences { + ProgressView("Loading...") + } else if hasCompletedOnboarding { + ContentView() + } else { + NavigationStack { + OnboardingSurveyView { + hasCompletedOnboarding = true } } - } else { - AuthView() } } .task { await auth.restoreSession() } + .task(id: auth.isSignedIn) { + await checkOnboardingStatus() + } + } + + private func checkOnboardingStatus() async { + guard auth.isSignedIn else { + hasCompletedOnboarding = false + isCheckingPreferences = false + return + } + + isCheckingPreferences = true + defer { isCheckingPreferences = false } + + do { + let prefs = try await auth.fetchUserPreferences() + hasCompletedOnboarding = (prefs != nil) + } catch { + hasCompletedOnboarding = false + } } } diff --git a/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift b/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift index 4d88d36..9353e75 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift @@ -68,6 +68,7 @@ final class AuthManager: ObservableObject { @Published var errorMessage: String? = nil @Published var isSignedIn: Bool = false @Published var userEmail: String? = nil + @Published var userID: String? = nil // Tokens private(set) var accessToken: String? = nil @@ -114,6 +115,7 @@ final class AuthManager: ObservableObject { let session = try JSONDecoder().decode(SupabaseSession.self, from: data) self.applySession(session) self.userEmail = email + self.userID = session.user?.id } } @@ -148,6 +150,7 @@ final class AuthManager: ObservableObject { !session.access_token.isEmpty { self.applySession(session) self.userEmail = email + self.userID = session.user?.id } else { self.userEmail = email throw AuthError.emailConfirmationRequired @@ -198,11 +201,13 @@ final class AuthManager: ObservableObject { let user = try JSONDecoder().decode(SupabaseUser.self, from: data) self.userEmail = user.email self.isSignedIn = true + self.userID = user.id } catch { // token might be expired -> try refresh if let refreshed = try? await refreshSession() { self.userEmail = refreshed.user?.email self.isSignedIn = true + self.userID = refreshed.user?.id } else { clearSession() } @@ -250,6 +255,7 @@ final class AuthManager: ObservableObject { private func applySession(_ session: SupabaseSession) { self.accessToken = session.access_token self.refreshToken = session.refresh_token + self.userID = session.user?.id saveTokensToStorage() self.isSignedIn = true } @@ -258,6 +264,7 @@ final class AuthManager: ObservableObject { self.accessToken = nil self.refreshToken = nil self.userEmail = nil + self.userID = nil self.isSignedIn = false deleteTokensFromStorage() } @@ -266,7 +273,8 @@ final class AuthManager: ObservableObject { url: URL, method: String, jsonBody: T?, - bearerToken: String? + bearerToken: String?, + extraHeaders: [String: String] = [:] ) async throws -> (Data, HTTPURLResponse) { var req = URLRequest(url: url) @@ -278,6 +286,10 @@ final class AuthManager: ObservableObject { req.setValue("Bearer \(bearerToken)", forHTTPHeaderField: "Authorization") } + for (key, value) in extraHeaders { + req.setValue(value, forHTTPHeaderField: key) + } + if let jsonBody { req.httpBody = try JSONEncoder().encode(jsonBody) } @@ -412,3 +424,85 @@ extension AuthManager { return try JSONDecoder().decode([WalmartItem].self, from: data) } } + +//Onboard survey fetch/save +struct UserPreferencesRow: Codable { + let user_id: String + let diet_preferences: [String] + let allergies: [String] +} + +struct UserPreferencesUpsertBody: Codable { + let user_id: String + let diet_preferences: [String] + let allergies: [String] +} + +extension AuthManager { + func fetchUserPreferences() async throws -> UserPreferences? { + guard let token = accessToken, let uid = userID else { + throw URLError(.userAuthenticationRequired) + } + + let base = supabaseURL + .appendingPathComponent("rest/v1") + .appendingPathComponent("user_preferences") + + var comps = URLComponents(url: base, resolvingAgainstBaseURL: false)! + comps.queryItems = [ + URLQueryItem(name: "select", value: "user_id,diet_preferences,allergies"), + URLQueryItem(name: "user_id", value: "eq.\(uid)"), + URLQueryItem(name: "limit", value: "1") + ] + + guard let url = comps.url else { throw URLError(.badURL) } + + let (data, _) = try await request( + url: url, + method: "GET", + jsonBody: Optional.none, + bearerToken: token + ) + + let rows = try JSONDecoder().decode([UserPreferencesRow].self, from: data) + guard let row = rows.first else { return nil } + + return UserPreferences( + dietPreferences: row.diet_preferences, + allergies: row.allergies + ) + } + + func saveUserPreferences(_ preferences: UserPreferences) async throws { + guard let token = accessToken, let uid = userID else { + throw URLError(.userAuthenticationRequired) + } + + let base = supabaseURL + .appendingPathComponent("rest/v1") + .appendingPathComponent("user_preferences") + + var comps = URLComponents(url: base, resolvingAgainstBaseURL: false)! + comps.queryItems = [ + URLQueryItem(name: "on_conflict", value: "user_id") + ] + + guard let url = comps.url else { throw URLError(.badURL) } + + let body = UserPreferencesUpsertBody( + user_id: uid, + diet_preferences: preferences.dietPreferences, + allergies: preferences.allergies + ) + + _ = try await request( + url: url, + method: "POST", + jsonBody: body, + bearerToken: token, + extraHeaders: [ + "Prefer": "resolution=merge-duplicates,return=representation" + ] + ) + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift b/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift index 065f5aa..ef884db 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift @@ -1,14 +1,13 @@ import SwiftUI struct OnboardingSurveyView: View { + @EnvironmentObject var auth: AuthManager @Environment(\.dismiss) private var dismiss - @AppStorage("hasCompletedOnboarding") private var hasCompletedOnboarding = false - @AppStorage("savedDietPreferences") private var savedDietPreferencesData = Data() - @AppStorage("savedAllergies") private var savedAllergiesData = Data() - @State private var selectedDiets: Set = [] @State private var selectedAllergies: Set = [] + @State private var isSaving = false + @State private var errorMessage: String? let dietOptions = [ "Vegetarian", "Vegan", "Pescatarian", @@ -21,6 +20,8 @@ struct OnboardingSurveyView: View { "Soy", "Wheat", "Shellfish", "Fish", "Sesame" ] + var onComplete: (() -> Void)? = nil + var body: some View { List { Section("Diet Preferences") { @@ -45,19 +46,34 @@ struct OnboardingSurveyView: View { } } + if let errorMessage { + Section { + Text(errorMessage) + .foregroundStyle(.red) + } + } + Section { Button { - savePreferences() + Task { + await savePreferences() + } } label: { - Text("Save Preferences") - .frame(maxWidth: .infinity) + if isSaving { + ProgressView() + .frame(maxWidth: .infinity) + } else { + Text("Save Preferences") + .frame(maxWidth: .infinity) + } } .buttonStyle(.borderedProminent) + .disabled(isSaving) } } .navigationTitle("Your Preferences") - .onAppear { - loadSavedPreferences() + .task { + await loadPreferences() } } @@ -69,26 +85,37 @@ struct OnboardingSurveyView: View { } } - private func savePreferences() { - if let dietData = try? JSONEncoder().encode(Array(selectedDiets).sorted()) { - savedDietPreferencesData = dietData + private func loadPreferences() async { + do { + if let prefs = try await auth.fetchUserPreferences() { + selectedDiets = Set(prefs.dietPreferences) + selectedAllergies = Set(prefs.allergies) + } + } catch { + errorMessage = error.localizedDescription } + } - if let allergyData = try? JSONEncoder().encode(Array(selectedAllergies).sorted()) { - savedAllergiesData = allergyData - } + private func savePreferences() async { + isSaving = true + errorMessage = nil + defer { isSaving = false } - hasCompletedOnboarding = true - dismiss() - } + do { + let prefs = UserPreferences( + dietPreferences: Array(selectedDiets).sorted(), + allergies: Array(selectedAllergies).sorted() + ) - private func loadSavedPreferences() { - if let savedDiets = try? JSONDecoder().decode([String].self, from: savedDietPreferencesData) { - selectedDiets = Set(savedDiets) - } + try await auth.saveUserPreferences(prefs) - if let savedAllergies = try? JSONDecoder().decode([String].self, from: savedAllergiesData) { - selectedAllergies = Set(savedAllergies) + if let onComplete { + onComplete() + } else { + dismiss() + } + } catch { + errorMessage = error.localizedDescription } } } From 409fe99e1b4ba1f39875f60b8691e965325bf45c Mon Sep 17 00:00:00 2001 From: James Date: Thu, 12 Mar 2026 16:03:30 -0700 Subject: [PATCH 7/7] Moved other screen views out to individual files from contentView. --- SWFrontUI/ShopwiseFrontEndUI/AppToolbar.swift | 41 + SWFrontUI/ShopwiseFrontEndUI/CartView.swift | 83 ++ .../ShopwiseFrontEndUI/ContentView.swift | 942 ------------------ SWFrontUI/ShopwiseFrontEndUI/MapView.swift | 133 +++ .../ShopwiseFrontEndUI/ProfileView.swift | 44 + SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift | 183 ++++ SWFrontUI/ShopwiseFrontEndUI/SearchView.swift | 306 ++++++ .../ShopwiseFrontEndUI/SettingsView.swift | 37 + .../ShopwiseFrontEndUI/ShoppingListView.swift | 123 +++ 9 files changed, 950 insertions(+), 942 deletions(-) create mode 100644 SWFrontUI/ShopwiseFrontEndUI/AppToolbar.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/CartView.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/MapView.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/ProfileView.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/SearchView.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/SettingsView.swift create mode 100644 SWFrontUI/ShopwiseFrontEndUI/ShoppingListView.swift diff --git a/SWFrontUI/ShopwiseFrontEndUI/AppToolbar.swift b/SWFrontUI/ShopwiseFrontEndUI/AppToolbar.swift new file mode 100644 index 0000000..311c036 --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/AppToolbar.swift @@ -0,0 +1,41 @@ +import SwiftUI + +struct AppToolbar: ViewModifier { + func body(content: Content) -> some View { + content + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + HStack(spacing: 14) { + NavigationLink { + SettingsView() + } label: { + Image(systemName: "gearshape") + } + + NavigationLink { + ProfileView() + } label: { + Image(systemName: "person.crop.circle") + } + } + } + } + } +} + +extension View { + func appToolbar() -> some View { + self.modifier(AppToolbar()) + } +} + +extension View { + @ViewBuilder + func when(_ condition: Bool, transform: (Self) -> some View) -> some View { + if condition { + transform(self) + } else { + self + } + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/CartView.swift b/SWFrontUI/ShopwiseFrontEndUI/CartView.swift new file mode 100644 index 0000000..f7d5414 --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/CartView.swift @@ -0,0 +1,83 @@ +import SwiftUI + +struct CartView: View { + @State private var showShoppingList = false + @EnvironmentObject private var cartStore: CartStore + + var body: some View { + List { + Section("Items") { + if cartStore.items.isEmpty { + Text("Cart is empty") + .foregroundStyle(.secondary) + } else { + ForEach(cartStore.items) { item in + HStack(alignment: .top, spacing: 12) { + VStack(alignment: .leading, spacing: 4) { + Text(item.name) + .font(.headline) + Text(item.unit) + .font(.subheadline) + .foregroundStyle(.secondary) + Text(String(format: "$%.2f", item.price)) + .foregroundStyle(.secondary) + } + + Spacer() + + HStack(spacing: 10) { + Button { + cartStore.decrement(item) + } label: { + Image(systemName: "minus.circle") + } + .buttonStyle(.borderless) + + Text("\(item.quantity)") + .font(.headline) + .frame(minWidth: 24) + + Button { + cartStore.increment(item) + } label: { + Image(systemName: "plus.circle") + } + .buttonStyle(.borderless) + } + } + .padding(.vertical, 4) + } + .onDelete { indexSet in + for idx in indexSet { + cartStore.remove(cartStore.items[idx]) + } + } + } + } + + Section("Summary") { + HStack { + Text("Total") + Spacer() + Text(String(format: "$%.2f", cartStore.total)) + .foregroundStyle(.secondary) + } + } + + Section { + Button { + showShoppingList = true + } label: { + Text("Checkout") + .frame(maxWidth: .infinity) + } + .disabled(cartStore.items.isEmpty) + } + } + .navigationTitle("Cart") + .appToolbar() + .navigationDestination(isPresented: $showShoppingList) { + ShoppingListView() + } + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift index efdfa2e..7134f7a 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift @@ -7,948 +7,6 @@ import SwiftUI import SwiftData -import MapKit -import CoreLocation - -struct AppToolbar: ViewModifier { - func body(content: Content) -> some View { - content - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - HStack(spacing: 14) { - NavigationLink { - SettingsView() - } label: { - Image(systemName: "gearshape") - } - - NavigationLink { - ProfileView() - } label: { - Image(systemName: "person.crop.circle") - } - } - } - } - } -} - -extension View { - func appToolbar() -> some View { - self.modifier(AppToolbar()) - } -} - -extension View { - @ViewBuilder - func when(_ condition: Bool, transform: (Self) -> some View) -> some View { - if condition { - transform(self) - } else { - self - } - } -} - -struct ProfileView: View { - @EnvironmentObject var auth: AuthManager - - var body: some View { - List { - Section("Account") { - HStack { - Image(systemName: "person.circle.fill") - .font(.system(size: 36)) - .foregroundStyle(.blue) - - VStack(alignment: .leading) { - Text(auth.userEmail ?? "No email") - .font(.headline) - - Text("Signed in") - .font(.caption) - .foregroundStyle(.secondary) - } - } - .padding(.vertical, 6) - } - - Section("Preferences") { - NavigationLink { - OnboardingSurveyView() - } label: { - Label("Edit Diet & Allergies", systemImage: "slider.horizontal.3") - } - } - - Section { - Button(role: .destructive) { - Task { await auth.signOut() } - } label: { - Text("Sign Out") - } - } - } - .navigationTitle("Profile") - } -} - -struct SettingsView: View { - @AppStorage("notificationsEnabled") private var notificationsEnabled = true - @AppStorage("locationEnabled") private var locationEnabled = true - @AppStorage("darkModeEnabled") private var darkModeEnabled = false - - var body: some View { - List { - Section("Preferences") { - Toggle("Enable Notifications", isOn: $notificationsEnabled) - Toggle("Use Location Services", isOn: $locationEnabled) - Toggle("Dark Mode", isOn: $darkModeEnabled) - } - - Section("Support") { - Button("Help Center") { - // placeholder - } - - Button("About ShopWise") { - // placeholder - } - } - - Section("App Info") { - HStack { - Text("Version") - Spacer() - Text("1.0") - .foregroundStyle(.secondary) - } - } - } - .navigationTitle("Settings") - } -} - -struct SearchView: View { - @EnvironmentObject private var auth: AuthManager - @EnvironmentObject private var cartStore: CartStore - - @State private var query = "" - @State private var selectedFilter: String = "All" - @State private var results: [WalmartItem] = [] - @State private var isLoading = false - @State private var errorText: String? = nil - - @State private var pageSize = 50 - @State private var offset = 0 - - @State private var isLoadingMore = false - @State private var hasMore = true - - private let filters = ["All", "Ingredients", "Non-Ingredients"] - - var body: some View { - ScrollView { - VStack(alignment: .leading, spacing: 14) { - - // Title - Text("ShopWise") - .font(.system(size: 34, weight: .bold)) - .padding(.top, 6) - - // Search bar (custom like your right screenshot) - HStack(spacing: 10) { - Image(systemName: "magnifyingglass") - .foregroundStyle(.secondary) - - TextField("Search products...", text: $query) - .textInputAutocapitalization(.never) - .autocorrectionDisabled(true) - - if !query.isEmpty { - Button { - query = "" - searchItems(reset: true) - } label: { - Image(systemName: "xmark") - .font(.system(size: 12, weight: .bold)) - .foregroundStyle(.secondary) - .padding(10) - .background(.ultraThinMaterial) - .clipShape(Circle()) - } - } - } - .padding(.horizontal, 14) - .padding(.vertical, 12) - .background(Color(.systemGray6)) - .clipShape(RoundedRectangle(cornerRadius: 18)) - - // Category chips row - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 10) { - ForEach(filters, id: \.self) { f in - SearchCategoryChip(title: f, isSelected: selectedFilter == f) { - selectedFilter = f - searchItems(reset: true) // ✅ ADD HERE - } - } - } - .padding(.vertical, 2) - } - - // Content - if isLoading { - HStack { - Spacer() - ProgressView("Loading…") - Spacer() - } - .padding(.top, 24) - } else if let errorText { - Text(errorText) - .foregroundStyle(.red) - .padding(.top, 10) - } else { - LazyVStack(spacing: 14) { - ForEach(Array(filteredResults.enumerated()), id: \.element.id) { index, item in - ProductCard( - imageURL: bestImageURL(for: item), - title: item.name, - unit: unitText(for: item), - priceText: formatPrice(item.retail_price), - onAdd: { - cartStore.add(id: String(item.id), name: item.name, unit: unitText(for: item) ?? "", price: item.retail_price ?? 0) - } - ) - .onAppear { - // When the LAST item appears, load more - if index == filteredResults.count - 1 { - searchItems(reset: false) - } - } - } } - .padding(.top, 4) - } - - Spacer(minLength: 20) - } - .padding(.horizontal, 16) - .padding(.bottom, 20) - } - .background(Color(.systemBackground)) - .navigationBarTitleDisplayMode(.inline) - .appToolbar() // <-- uses your existing Settings/Profile toolbar - .onChange(of: query) { _, _ in - searchItems(reset: true) - } - .task { - searchItems(reset: true) - } - } - - // MARK: - Filtering - private var filteredResults: [WalmartItem] { - switch selectedFilter { - case "Ingredients": - return results.filter { $0.ingredient == true } - case "Non-Ingredients": - return results.filter { $0.ingredient == false } - default: - return results - } - } - - // MARK: - Helpers (image/price/unit) - private func bestImageURL(for item: WalmartItem) -> URL? { - if let s = item.thumbnailImage, let url = URL(string: s), !s.isEmpty { return url } - if let s = item.mediumImage, let url = URL(string: s), !s.isEmpty { return url } - if let s = item.largeImage, let url = URL(string: s), !s.isEmpty { return url } - return nil - } - - private func formatPrice(_ p: Double?) -> String { - guard let p else { return "Price unavailable" } - return String(format: "$%.2f", p) - } - - private func unitText(for item: WalmartItem) -> String? { - // Your Supabase data doesn't include a unit column. - // If classifiers contains something unit-like, show it; otherwise hide. - let cls = (item.classifiers ?? "").trimmingCharacters(in: .whitespacesAndNewlines) - if cls.isEmpty { return nil } - return cls - } - - // MARK: - Supabase fetch - private func searchItems(reset: Bool = true) { - let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines) - - // If we're already loading more, don't double-fire - if isLoadingMore { return } - - if reset { - offset = 0 - results = [] - hasMore = true - } else { - // if we already know there are no more rows, stop - if !hasMore { return } - } - - if reset { - isLoading = true - } else { - isLoadingMore = true - } - errorText = nil - - Task { - do { - let ingredientOnly: Bool? = { - switch selectedFilter { - case "Ingredients": return true - case "Non-Ingredients": return false - default: return nil - } - }() - - let newItems = try await auth.fetchWalmartItems( - search: trimmed.isEmpty ? nil : trimmed, - ingredientOnly: ingredientOnly, // ✅ NEW - limit: pageSize, - offset: offset - ) - - await MainActor.run { - // Append results - results.append(contentsOf: newItems) - - // Move offset forward - offset += newItems.count - - // If we got fewer than pageSize, we reached the end - if newItems.count < pageSize { hasMore = false } - - isLoading = false - isLoadingMore = false - } - } catch { - await MainActor.run { - errorText = "Search failed: \(error.localizedDescription)" - isLoading = false - isLoadingMore = false - } - } - } - } -} - - -struct SearchCategoryChip: View { - let title: String - let isSelected: Bool - let action: () -> Void - - var body: some View { - Button(action: action) { - Text(title) - .font(.subheadline.weight(.semibold)) - .padding(.horizontal, 14) - .padding(.vertical, 8) - .background(isSelected ? Color.blue.opacity(0.18) : Color(.systemGray6)) - .foregroundStyle(isSelected ? Color.blue : Color.primary) - .clipShape(Capsule()) - } - .buttonStyle(.plain) - } -} - -struct ProductCard: View { - let imageURL: URL? - let title: String - let unit: String? - let priceText: String - let onAdd: () -> Void - - var body: some View { - HStack(spacing: 14) { - AsyncImage(url: imageURL) { phase in - switch phase { - case .empty: - ProgressView() - .frame(width: 74, height: 74) - case .success(let image): - image - .resizable() - .scaledToFill() - .frame(width: 74, height: 74) - .clipped() - case .failure: - Image(systemName: "photo") - .frame(width: 74, height: 74) - .foregroundStyle(.secondary) - @unknown default: - EmptyView() - .frame(width: 74, height: 74) - } - } - .background(Color(.systemGray6)) - .clipShape(RoundedRectangle(cornerRadius: 16)) - - VStack(alignment: .leading, spacing: 6) { - Text(title) - .font(.headline) - .lineLimit(2) - - if let unit, !unit.isEmpty { - Text(unit) - .font(.subheadline) - .foregroundStyle(.secondary) - .lineLimit(1) - } - - Text(priceText) - .font(.headline) - } - - Spacer() - - Button(action: onAdd) { - HStack(spacing: 8) { - Image(systemName: "cart.badge.plus") - Text("Add") - } - .font(.subheadline.weight(.semibold)) - .padding(.horizontal, 14) - .padding(.vertical, 10) - .foregroundStyle(.white) - .background(Color.blue) - .clipShape(Capsule()) - } - .buttonStyle(.plain) - } - .padding(14) - .background(Color(.systemGray6).opacity(0.65)) - .clipShape(RoundedRectangle(cornerRadius: 18)) - } -} - -struct MapView: View { - @StateObject private var locationManager = LocationManager() - - @State private var position: MapCameraPosition = .region( - MKCoordinateRegion( - center: CLLocationCoordinate2D(latitude: 33.978194, longitude: -117.367861), - span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05) - ) - ) - - @State private var stores: [StoreLocation] = [ - StoreLocation( - id: "store_walmart", - name: "Walmart Supercenter", - latitude: 33.988194, - longitude: -117.361861, - address: nil, - chain: "Walmart" - ), - StoreLocation( - id: "store_ralphs", - name: "Ralphs", - latitude: 33.970194, - longitude: -117.363861, - address: nil, - chain: "Ralphs" - ) - ] - - var body: some View { - List { - Section { - CardContainer { - Map(position: $position) { - UserAnnotation() - - ForEach(stores) { store in - Marker(store.name, coordinate: store.coordinate) - } - } - .frame(height: 260) - .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) - } - .listRowInsets(EdgeInsets()) - } - - Section("Nearby Grocery Stores") { - ForEach(stores) { store in - Button { - position = .region( - MKCoordinateRegion( - center: store.coordinate, - span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) - ) - ) - } label: { - HStack { - Image(systemName: "mappin.circle.fill") - - VStack(alignment: .leading) { - Text(store.name) - Text(store.chain ?? "Store") - .foregroundStyle(.secondary) - .font(.subheadline) - } - - Spacer() - } - } - .buttonStyle(.plain) - } - } - - Section("Location") { - Button { - centerOnUser() - } label: { - Label("Center on Me", systemImage: "location.fill") - .frame(maxWidth: .infinity) - } - .disabled(locationManager.userLocation == nil) - - HStack { - Text("Permission") - Spacer() - Text(locationStatusText) - .foregroundStyle(.secondary) - } - } - } - .navigationTitle("ShopWise") - .appToolbar() - .onAppear { - locationManager.requestPermission() - } - .onChange(of: locationManager.userLocation?.latitude ?? 0) { _, newLatitude in - guard newLatitude != 0 else { return } - centerOnUser() - } - } - - private var locationStatusText: String { - switch locationManager.authorizationStatus { - case .notDetermined: - return "Not Requested" - case .restricted: - return "Restricted" - case .denied: - return "Denied" - case .authorizedAlways: - return "Allowed" - case .authorizedWhenInUse: - return "Allowed" - @unknown default: - return "Unknown" - } - } - - private func centerOnUser() { - guard let user = locationManager.userLocation else { return } - - position = .region( - MKCoordinateRegion( - center: user, - span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) - ) - ) - } -} - -struct CartView: View { - @State private var showShoppingList = false - @EnvironmentObject private var cartStore: CartStore - - var body: some View { - List { - Section("Items") { - if cartStore.items.isEmpty { - Text("Cart is empty") - .foregroundStyle(.secondary) - } else { - ForEach(cartStore.items) { item in - HStack(alignment: .top, spacing: 12) { - VStack(alignment: .leading, spacing: 4) { - Text(item.name) - .font(.headline) - Text(item.unit) - .font(.subheadline) - .foregroundStyle(.secondary) - Text(String(format: "$%.2f", item.price)) - .foregroundStyle(.secondary) - } - - Spacer() - - HStack(spacing: 10) { - Button { - cartStore.decrement(item) - } label: { - Image(systemName: "minus.circle") - } - .buttonStyle(.borderless) - - Text("\(item.quantity)") - .font(.headline) - .frame(minWidth: 24) - - Button { - cartStore.increment(item) - } label: { - Image(systemName: "plus.circle") - } - .buttonStyle(.borderless) - } - } - .padding(.vertical, 4) - } - .onDelete { indexSet in - for idx in indexSet { - cartStore.remove(cartStore.items[idx]) - } - } - } - } - - Section("Summary") { - HStack { - Text("Total") - Spacer() - Text(String(format: "$%.2f", cartStore.total)) - .foregroundStyle(.secondary) - } - } - - Section { - Button { - showShoppingList = true - } label: { - Text("Checkout") - .frame(maxWidth: .infinity) - } - .disabled(cartStore.items.isEmpty) - } - } - .navigationTitle("Cart") - .appToolbar() - .navigationDestination(isPresented: $showShoppingList) { - ShoppingListView() - } - } -} - -struct RecipeView: View { - @State private var query = "" - @State private var expandedID: String? = nil - @State private var excludedByRecipe: [String: Set] = [:] - - @EnvironmentObject private var cartStore: CartStore - - private let recipes: [Recipe] = [ - Recipe( - id: "recipe_spaghetti", - name: "Spaghetti", - difficulty: "Easy", - minutes: 25, - imageURL: nil, - imageName: "spaghetti", - ingredients: [ - Ingredient(id: "ing_spaghetti", name: "Spaghetti pasta", unit: "1 lb", price: 2.49, imageURL: nil, imageName: nil), - Ingredient(id: "ing_marinara", name: "Marinara sauce", unit: "24 oz", price: 3.99, imageURL: nil, imageName: nil), - Ingredient(id: "ing_garlic", name: "Garlic", unit: "3 ct", price: 1.29, imageURL: nil, imageName: nil), - Ingredient(id: "ing_onion", name: "Onion", unit: "1 ct", price: 0.99, imageURL: nil, imageName: nil), - Ingredient(id: "ing_olive_oil", name: "Olive oil", unit: "16.9 oz", price: 6.49, imageURL: nil, imageName: nil), - Ingredient(id: "ing_parmesan", name: "Parmesan", unit: "6 oz", price: 4.49, imageURL: nil, imageName: nil) - ] - ), - Recipe( - id: "recipe_orange_chicken", - name: "Orange Chicken", - difficulty: "Medium", - minutes: 35, - imageURL: nil, - imageName: "orangechicken", - ingredients: [ - Ingredient(id: "ing_chicken_breast", name: "Chicken breast", unit: "1 lb", price: 5.99, imageURL: nil, imageName: nil), - Ingredient(id: "ing_cornstarch", name: "Cornstarch", unit: "16 oz", price: 1.99, imageURL: nil, imageName: nil), - Ingredient(id: "ing_orange_juice", name: "Orange juice", unit: "12 oz", price: 3.49, imageURL: nil, imageName: nil), - Ingredient(id: "ing_soy_sauce", name: "Soy sauce", unit: "10 oz", price: 2.29, imageURL: nil, imageName: nil), - Ingredient(id: "ing_honey", name: "Honey", unit: "12 oz", price: 4.79, imageURL: nil, imageName: nil), - Ingredient(id: "ing_garlic", name: "Garlic", unit: "3 ct", price: 1.29, imageURL: nil, imageName: nil), - Ingredient(id: "ing_ginger", name: "Ginger", unit: "3 oz", price: 1.49, imageURL: nil, imageName: nil), - Ingredient(id: "ing_rice", name: "Rice", unit: "2 lb", price: 3.79, imageURL: nil, imageName: nil) - ] - ) - ] - - private var filtered: [Recipe] { - if query.isEmpty { return recipes } - return recipes.filter { $0.name.localizedCaseInsensitiveContains(query) } - } - - var body: some View { - List { - ForEach(filtered) { recipe in - Section { - Button { - withAnimation(.snappy) { - expandedID = (expandedID == recipe.id) ? nil : recipe.id - } - } label: { - CardContainer { - HStack(spacing: 12) { - if let imageName = recipe.imageName { - Image(imageName) - .resizable() - .scaledToFill() - .frame(width: 54, height: 54) - .clipShape(RoundedRectangle(cornerRadius: 12)) - } else { - Circle() - .fill(Color(.systemGray5)) - .frame(width: 54, height: 54) - .overlay( - Image(systemName: "fork.knife") - .foregroundStyle(.secondary) - ) - } - - VStack(alignment: .leading, spacing: 4) { - Text(recipe.name) - .font(.headline) - Text("\(recipe.difficulty) • \(recipe.minutes) min") - .font(.subheadline) - .foregroundStyle(.secondary) - } - - Spacer() - - Image(systemName: expandedID == recipe.id ? "chevron.up" : "chevron.down") - .foregroundStyle(.secondary) - } - } - } - .buttonStyle(.plain) - - if expandedID == recipe.id { - CardContainer { - if let imageName = recipe.imageName { - Image(imageName) - .resizable() - .scaledToFill() - .frame(height: 160) - .clipped() - .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) - } - - Text("Ingredients") - .font(.headline) - - ForEach(recipe.ingredients) { ingredient in - let isExcluded = excludedByRecipe[recipe.id, default: []].contains(ingredient.id) - - Button { - if isExcluded { - excludedByRecipe[recipe.id, default: []].remove(ingredient.id) - } else { - excludedByRecipe[recipe.id, default: []].insert(ingredient.id) - } - } label: { - HStack(spacing: 10) { - Image(systemName: isExcluded ? "square" : "checkmark.square.fill") - .imageScale(.large) - - VStack(alignment: .leading, spacing: 2) { - Text(ingredient.name) - .strikethrough(isExcluded) - Text(ingredient.unit) - .font(.subheadline) - .foregroundStyle(.secondary) - } - - Spacer() - - Text(String(format: "$%.2f", ingredient.price)) - .foregroundStyle(.secondary) - .monospacedDigit() - } - .padding(.vertical, 2) - .contentShape(Rectangle()) - } - .buttonStyle(.plain) - } - - Button { - addIngredientsToCart(recipe) - } label: { - Label("Add Ingredients to Cart", systemImage: "cart.badge.plus") - .frame(maxWidth: .infinity) - .foregroundStyle(.white) - } - .buttonStyle(.borderedProminent) - } - .transition(.opacity.combined(with: .move(edge: .top))) - } - } - } - } - .listStyle(.plain) - .navigationTitle("ShopWise") - .searchable(text: $query, prompt: "Search recipes…") - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - HStack(spacing: 14) { - NavigationLink { SettingsView() } label: { Image(systemName: "gearshape") } - NavigationLink { ProfileView() } label: { Image(systemName: "person.crop.circle") } - } - } - } - } - - private func addIngredientsToCart(_ recipe: Recipe) { - let excluded = excludedByRecipe[recipe.id, default: []] - - for ingredient in recipe.ingredients where !excluded.contains(ingredient.id) { - cartStore.add( - id: ingredient.id, - name: ingredient.name, - unit: ingredient.unit, - price: ingredient.price - ) - } - } -} - -struct ShoppingListView: View { - @EnvironmentObject private var cartStore: CartStore - @Environment(\.dismiss) private var dismiss - - @State private var checkedIDs: Set = [] - - private var checkedTotal: Double { - cartStore.items - .filter { checkedIDs.contains($0.id) } - .reduce(0) { $0 + ($1.price * Double($1.quantity)) } - } - - private var checkedCount: Int { - cartStore.items.filter { checkedIDs.contains($0.id) }.count - } - - var body: some View { - List { - Section("Shopping List") { - if cartStore.items.isEmpty { - Text("No items to shop for.") - .foregroundStyle(.secondary) - } else { - ForEach(cartStore.items) { item in - Button { - toggle(item.id) - } label: { - HStack(spacing: 12) { - Image(systemName: checkedIDs.contains(item.id) ? "checkmark.circle.fill" : "circle") - .imageScale(.large) - - VStack(alignment: .leading, spacing: 2) { - Text(item.name) - .strikethrough(checkedIDs.contains(item.id)) - Text("\(item.quantity) × \(item.unit)") - .font(.subheadline) - .foregroundStyle(.secondary) - } - - Spacer() - - Text(String(format: "$%.2f", item.price * Double(item.quantity))) - .foregroundStyle(.secondary) - .monospacedDigit() - } - } - .buttonStyle(.plain) - } - } - } - - if !cartStore.items.isEmpty { - Section { - Button("Mark All") { - checkedIDs = Set(cartStore.items.map { $0.id }) - } - - Button("Clear Checks") { - checkedIDs.removeAll() - } - - Button(role: .destructive) { - cartStore.clear() - checkedIDs.removeAll() - dismiss() - } label: { - Text("Clear Cart") - } - } - } - } - .navigationTitle("Shopping List") - .appToolbar() - .safeAreaInset(edge: .bottom) { - if !cartStore.items.isEmpty { - VStack(spacing: 0) { - Divider() - - VStack(spacing: 10) { - HStack { - VStack(alignment: .leading, spacing: 2) { - Text("Checked: \(checkedCount)/\(cartStore.items.count)") - .font(.subheadline) - .foregroundStyle(.secondary) - Text("Current Total") - .font(.headline) - } - - Spacer() - - Text(String(format: "$%.2f", checkedTotal)) - .font(.title3) - .monospacedDigit() - } - - Button { - cartStore.clear() - checkedIDs.removeAll() - dismiss() - } label: { - Label("Finish Trip", systemImage: "checkmark.seal.fill") - .frame(maxWidth: .infinity) - } - .buttonStyle(.borderedProminent) - } - .padding(.horizontal, 16) - .padding(.vertical, 12) - .background(.ultraThinMaterial) - } - } - } - } - - private func toggle(_ id: String) { - if checkedIDs.contains(id) { - checkedIDs.remove(id) - } else { - checkedIDs.insert(id) - } - } -} struct ContentView: View { @StateObject private var cartStore = CartStore() diff --git a/SWFrontUI/ShopwiseFrontEndUI/MapView.swift b/SWFrontUI/ShopwiseFrontEndUI/MapView.swift new file mode 100644 index 0000000..d438a9a --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/MapView.swift @@ -0,0 +1,133 @@ +import SwiftUI +import MapKit +import CoreLocation + +struct MapView: View { + @StateObject private var locationManager = LocationManager() + + @State private var position: MapCameraPosition = .region( + MKCoordinateRegion( + center: CLLocationCoordinate2D(latitude: 33.978194, longitude: -117.367861), + span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05) + ) + ) + + @State private var stores: [StoreLocation] = [ + StoreLocation( + id: "store_walmart", + name: "Walmart Supercenter", + latitude: 33.988194, + longitude: -117.361861, + address: nil, + chain: "Walmart" + ), + StoreLocation( + id: "store_ralphs", + name: "Ralphs", + latitude: 33.970194, + longitude: -117.363861, + address: nil, + chain: "Ralphs" + ) + ] + + var body: some View { + List { + Section { + CardContainer { + Map(position: $position) { + UserAnnotation() + + ForEach(stores) { store in + Marker(store.name, coordinate: store.coordinate) + } + } + .frame(height: 260) + .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) + } + .listRowInsets(EdgeInsets()) + } + + Section("Nearby Grocery Stores") { + ForEach(stores) { store in + Button { + position = .region( + MKCoordinateRegion( + center: store.coordinate, + span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) + ) + ) + } label: { + HStack { + Image(systemName: "mappin.circle.fill") + + VStack(alignment: .leading) { + Text(store.name) + Text(store.chain ?? "Store") + .foregroundStyle(.secondary) + .font(.subheadline) + } + + Spacer() + } + } + .buttonStyle(.plain) + } + } + + Section("Location") { + Button { + centerOnUser() + } label: { + Label("Center on Me", systemImage: "location.fill") + .frame(maxWidth: .infinity) + } + .disabled(locationManager.userLocation == nil) + + HStack { + Text("Permission") + Spacer() + Text(locationStatusText) + .foregroundStyle(.secondary) + } + } + } + .navigationTitle("ShopWise") + .appToolbar() + .onAppear { + locationManager.requestPermission() + } + .onChange(of: locationManager.userLocation?.latitude ?? 0) { _, newLatitude in + guard newLatitude != 0 else { return } + centerOnUser() + } + } + + private var locationStatusText: String { + switch locationManager.authorizationStatus { + case .notDetermined: + return "Not Requested" + case .restricted: + return "Restricted" + case .denied: + return "Denied" + case .authorizedAlways: + return "Allowed" + case .authorizedWhenInUse: + return "Allowed" + @unknown default: + return "Unknown" + } + } + + private func centerOnUser() { + guard let user = locationManager.userLocation else { return } + + position = .region( + MKCoordinateRegion( + center: user, + span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) + ) + ) + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/ProfileView.swift b/SWFrontUI/ShopwiseFrontEndUI/ProfileView.swift new file mode 100644 index 0000000..ab5aa69 --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/ProfileView.swift @@ -0,0 +1,44 @@ +import SwiftUI + +struct ProfileView: View { + @EnvironmentObject var auth: AuthManager + + var body: some View { + List { + Section("Account") { + HStack { + Image(systemName: "person.circle.fill") + .font(.system(size: 36)) + .foregroundStyle(.blue) + + VStack(alignment: .leading) { + Text(auth.userEmail ?? "No email") + .font(.headline) + + Text("Signed in") + .font(.caption) + .foregroundStyle(.secondary) + } + } + .padding(.vertical, 6) + } + + Section("Preferences") { + NavigationLink { + OnboardingSurveyView() + } label: { + Label("Edit Diet & Allergies", systemImage: "slider.horizontal.3") + } + } + + Section { + Button(role: .destructive) { + Task { await auth.signOut() } + } label: { + Text("Sign Out") + } + } + } + .navigationTitle("Profile") + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift b/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift new file mode 100644 index 0000000..2b2485a --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift @@ -0,0 +1,183 @@ +import SwiftUI + +struct RecipeView: View { + @State private var query = "" + @State private var expandedID: String? = nil + @State private var excludedByRecipe: [String: Set] = [:] + + @EnvironmentObject private var cartStore: CartStore + + private let recipes: [Recipe] = [ + Recipe( + id: "recipe_spaghetti", + name: "Spaghetti", + difficulty: "Easy", + minutes: 25, + imageURL: nil, + imageName: "spaghetti", + ingredients: [ + Ingredient(id: "ing_spaghetti", name: "Spaghetti pasta", unit: "1 lb", price: 2.49, imageURL: nil, imageName: nil), + Ingredient(id: "ing_marinara", name: "Marinara sauce", unit: "24 oz", price: 3.99, imageURL: nil, imageName: nil), + Ingredient(id: "ing_garlic", name: "Garlic", unit: "3 ct", price: 1.29, imageURL: nil, imageName: nil), + Ingredient(id: "ing_onion", name: "Onion", unit: "1 ct", price: 0.99, imageURL: nil, imageName: nil), + Ingredient(id: "ing_olive_oil", name: "Olive oil", unit: "16.9 oz", price: 6.49, imageURL: nil, imageName: nil), + Ingredient(id: "ing_parmesan", name: "Parmesan", unit: "6 oz", price: 4.49, imageURL: nil, imageName: nil) + ] + ), + Recipe( + id: "recipe_orange_chicken", + name: "Orange Chicken", + difficulty: "Medium", + minutes: 35, + imageURL: nil, + imageName: "orangechicken", + ingredients: [ + Ingredient(id: "ing_chicken_breast", name: "Chicken breast", unit: "1 lb", price: 5.99, imageURL: nil, imageName: nil), + Ingredient(id: "ing_cornstarch", name: "Cornstarch", unit: "16 oz", price: 1.99, imageURL: nil, imageName: nil), + Ingredient(id: "ing_orange_juice", name: "Orange juice", unit: "12 oz", price: 3.49, imageURL: nil, imageName: nil), + Ingredient(id: "ing_soy_sauce", name: "Soy sauce", unit: "10 oz", price: 2.29, imageURL: nil, imageName: nil), + Ingredient(id: "ing_honey", name: "Honey", unit: "12 oz", price: 4.79, imageURL: nil, imageName: nil), + Ingredient(id: "ing_garlic", name: "Garlic", unit: "3 ct", price: 1.29, imageURL: nil, imageName: nil), + Ingredient(id: "ing_ginger", name: "Ginger", unit: "3 oz", price: 1.49, imageURL: nil, imageName: nil), + Ingredient(id: "ing_rice", name: "Rice", unit: "2 lb", price: 3.79, imageURL: nil, imageName: nil) + ] + ) + ] + + private var filtered: [Recipe] { + if query.isEmpty { return recipes } + return recipes.filter { $0.name.localizedCaseInsensitiveContains(query) } + } + + var body: some View { + List { + ForEach(filtered) { recipe in + Section { + Button { + withAnimation(.snappy) { + expandedID = (expandedID == recipe.id) ? nil : recipe.id + } + } label: { + CardContainer { + HStack(spacing: 12) { + if let imageName = recipe.imageName { + Image(imageName) + .resizable() + .scaledToFill() + .frame(width: 54, height: 54) + .clipShape(RoundedRectangle(cornerRadius: 12)) + } else { + Circle() + .fill(Color(.systemGray5)) + .frame(width: 54, height: 54) + .overlay( + Image(systemName: "fork.knife") + .foregroundStyle(.secondary) + ) + } + + VStack(alignment: .leading, spacing: 4) { + Text(recipe.name) + .font(.headline) + Text("\(recipe.difficulty) • \(recipe.minutes) min") + .font(.subheadline) + .foregroundStyle(.secondary) + } + + Spacer() + + Image(systemName: expandedID == recipe.id ? "chevron.up" : "chevron.down") + .foregroundStyle(.secondary) + } + } + } + .buttonStyle(.plain) + + if expandedID == recipe.id { + CardContainer { + if let imageName = recipe.imageName { + Image(imageName) + .resizable() + .scaledToFill() + .frame(height: 160) + .clipped() + .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) + } + + Text("Ingredients") + .font(.headline) + + ForEach(recipe.ingredients) { ingredient in + let isExcluded = excludedByRecipe[recipe.id, default: []].contains(ingredient.id) + + Button { + if isExcluded { + excludedByRecipe[recipe.id, default: []].remove(ingredient.id) + } else { + excludedByRecipe[recipe.id, default: []].insert(ingredient.id) + } + } label: { + HStack(spacing: 10) { + Image(systemName: isExcluded ? "square" : "checkmark.square.fill") + .imageScale(.large) + + VStack(alignment: .leading, spacing: 2) { + Text(ingredient.name) + .strikethrough(isExcluded) + Text(ingredient.unit) + .font(.subheadline) + .foregroundStyle(.secondary) + } + + Spacer() + + Text(String(format: "$%.2f", ingredient.price)) + .foregroundStyle(.secondary) + .monospacedDigit() + } + .padding(.vertical, 2) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + } + + Button { + addIngredientsToCart(recipe) + } label: { + Label("Add Ingredients to Cart", systemImage: "cart.badge.plus") + .frame(maxWidth: .infinity) + .foregroundStyle(.white) + } + .buttonStyle(.borderedProminent) + } + .transition(.opacity.combined(with: .move(edge: .top))) + } + } + } + } + .listStyle(.plain) + .navigationTitle("ShopWise") + .searchable(text: $query, prompt: "Search recipes…") + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + HStack(spacing: 14) { + NavigationLink { SettingsView() } label: { Image(systemName: "gearshape") } + NavigationLink { ProfileView() } label: { Image(systemName: "person.crop.circle") } + } + } + } + } + + private func addIngredientsToCart(_ recipe: Recipe) { + let excluded = excludedByRecipe[recipe.id, default: []] + + for ingredient in recipe.ingredients where !excluded.contains(ingredient.id) { + cartStore.add( + id: ingredient.id, + name: ingredient.name, + unit: ingredient.unit, + price: ingredient.price + ) + } + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/SearchView.swift b/SWFrontUI/ShopwiseFrontEndUI/SearchView.swift new file mode 100644 index 0000000..4529793 --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/SearchView.swift @@ -0,0 +1,306 @@ +import SwiftUI + +struct SearchView: View { + @EnvironmentObject private var auth: AuthManager + @EnvironmentObject private var cartStore: CartStore + + @State private var query = "" + @State private var selectedFilter: String = "All" + @State private var results: [WalmartItem] = [] + @State private var isLoading = false + @State private var errorText: String? = nil + + @State private var pageSize = 50 + @State private var offset = 0 + + @State private var isLoadingMore = false + @State private var hasMore = true + + private let filters = ["All", "Ingredients", "Non-Ingredients"] + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 14) { + + // Title + Text("ShopWise") + .font(.system(size: 34, weight: .bold)) + .padding(.top, 6) + + // Search bar (custom like your right screenshot) + HStack(spacing: 10) { + Image(systemName: "magnifyingglass") + .foregroundStyle(.secondary) + + TextField("Search products...", text: $query) + .textInputAutocapitalization(.never) + .autocorrectionDisabled(true) + + if !query.isEmpty { + Button { + query = "" + searchItems(reset: true) + } label: { + Image(systemName: "xmark") + .font(.system(size: 12, weight: .bold)) + .foregroundStyle(.secondary) + .padding(10) + .background(.ultraThinMaterial) + .clipShape(Circle()) + } + } + } + .padding(.horizontal, 14) + .padding(.vertical, 12) + .background(Color(.systemGray6)) + .clipShape(RoundedRectangle(cornerRadius: 18)) + + // Category chips row + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 10) { + ForEach(filters, id: \.self) { f in + SearchCategoryChip(title: f, isSelected: selectedFilter == f) { + selectedFilter = f + searchItems(reset: true) // ✅ ADD HERE + } + } + } + .padding(.vertical, 2) + } + + // Content + if isLoading { + HStack { + Spacer() + ProgressView("Loading…") + Spacer() + } + .padding(.top, 24) + } else if let errorText { + Text(errorText) + .foregroundStyle(.red) + .padding(.top, 10) + } else { + LazyVStack(spacing: 14) { + ForEach(Array(filteredResults.enumerated()), id: \.element.id) { index, item in + ProductCard( + imageURL: bestImageURL(for: item), + title: item.name, + unit: unitText(for: item), + priceText: formatPrice(item.retail_price), + onAdd: { + cartStore.add(id: String(item.id), name: item.name, unit: unitText(for: item) ?? "", price: item.retail_price ?? 0) + } + ) + .onAppear { + // When the LAST item appears, load more + if index == filteredResults.count - 1 { + searchItems(reset: false) + } + } + } } + .padding(.top, 4) + } + + Spacer(minLength: 20) + } + .padding(.horizontal, 16) + .padding(.bottom, 20) + } + .background(Color(.systemBackground)) + .navigationBarTitleDisplayMode(.inline) + .appToolbar() // <-- uses your existing Settings/Profile toolbar + .onChange(of: query) { _, _ in + searchItems(reset: true) + } + .task { + searchItems(reset: true) + } + } + + // MARK: - Filtering + private var filteredResults: [WalmartItem] { + switch selectedFilter { + case "Ingredients": + return results.filter { $0.ingredient == true } + case "Non-Ingredients": + return results.filter { $0.ingredient == false } + default: + return results + } + } + + // MARK: - Helpers (image/price/unit) + private func bestImageURL(for item: WalmartItem) -> URL? { + if let s = item.thumbnailImage, let url = URL(string: s), !s.isEmpty { return url } + if let s = item.mediumImage, let url = URL(string: s), !s.isEmpty { return url } + if let s = item.largeImage, let url = URL(string: s), !s.isEmpty { return url } + return nil + } + + private func formatPrice(_ p: Double?) -> String { + guard let p else { return "Price unavailable" } + return String(format: "$%.2f", p) + } + + private func unitText(for item: WalmartItem) -> String? { + // Your Supabase data doesn't include a unit column. + // If classifiers contains something unit-like, show it; otherwise hide. + let cls = (item.classifiers ?? "").trimmingCharacters(in: .whitespacesAndNewlines) + if cls.isEmpty { return nil } + return cls + } + + // MARK: - Supabase fetch + private func searchItems(reset: Bool = true) { + let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines) + + // If we're already loading more, don't double-fire + if isLoadingMore { return } + + if reset { + offset = 0 + results = [] + hasMore = true + } else { + // if we already know there are no more rows, stop + if !hasMore { return } + } + + if reset { + isLoading = true + } else { + isLoadingMore = true + } + errorText = nil + + Task { + do { + let ingredientOnly: Bool? = { + switch selectedFilter { + case "Ingredients": return true + case "Non-Ingredients": return false + default: return nil + } + }() + + let newItems = try await auth.fetchWalmartItems( + search: trimmed.isEmpty ? nil : trimmed, + ingredientOnly: ingredientOnly, // ✅ NEW + limit: pageSize, + offset: offset + ) + + await MainActor.run { + // Append results + results.append(contentsOf: newItems) + + // Move offset forward + offset += newItems.count + + // If we got fewer than pageSize, we reached the end + if newItems.count < pageSize { hasMore = false } + + isLoading = false + isLoadingMore = false + } + } catch { + await MainActor.run { + errorText = "Search failed: \(error.localizedDescription)" + isLoading = false + isLoadingMore = false + } + } + } + } +} + + +struct SearchCategoryChip: View { + let title: String + let isSelected: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + Text(title) + .font(.subheadline.weight(.semibold)) + .padding(.horizontal, 14) + .padding(.vertical, 8) + .background(isSelected ? Color.blue.opacity(0.18) : Color(.systemGray6)) + .foregroundStyle(isSelected ? Color.blue : Color.primary) + .clipShape(Capsule()) + } + .buttonStyle(.plain) + } +} + +struct ProductCard: View { + let imageURL: URL? + let title: String + let unit: String? + let priceText: String + let onAdd: () -> Void + + var body: some View { + HStack(spacing: 14) { + AsyncImage(url: imageURL) { phase in + switch phase { + case .empty: + ProgressView() + .frame(width: 74, height: 74) + case .success(let image): + image + .resizable() + .scaledToFill() + .frame(width: 74, height: 74) + .clipped() + case .failure: + Image(systemName: "photo") + .frame(width: 74, height: 74) + .foregroundStyle(.secondary) + @unknown default: + EmptyView() + .frame(width: 74, height: 74) + } + } + .background(Color(.systemGray6)) + .clipShape(RoundedRectangle(cornerRadius: 16)) + + VStack(alignment: .leading, spacing: 6) { + Text(title) + .font(.headline) + .lineLimit(2) + + if let unit, !unit.isEmpty { + Text(unit) + .font(.subheadline) + .foregroundStyle(.secondary) + .lineLimit(1) + } + + Text(priceText) + .font(.headline) + } + + Spacer() + + Button(action: onAdd) { + HStack(spacing: 8) { + Image(systemName: "cart.badge.plus") + Text("Add") + } + .font(.subheadline.weight(.semibold)) + .padding(.horizontal, 14) + .padding(.vertical, 10) + .foregroundStyle(.white) + .background(Color.blue) + .clipShape(Capsule()) + } + .buttonStyle(.plain) + } + .padding(14) + .background(Color(.systemGray6).opacity(0.65)) + .clipShape(RoundedRectangle(cornerRadius: 18)) + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/SettingsView.swift b/SWFrontUI/ShopwiseFrontEndUI/SettingsView.swift new file mode 100644 index 0000000..ab559b8 --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/SettingsView.swift @@ -0,0 +1,37 @@ +import SwiftUI + +struct SettingsView: View { + @AppStorage("notificationsEnabled") private var notificationsEnabled = true + @AppStorage("locationEnabled") private var locationEnabled = true + @AppStorage("darkModeEnabled") private var darkModeEnabled = false + + var body: some View { + List { + Section("Preferences") { + Toggle("Enable Notifications", isOn: $notificationsEnabled) + Toggle("Use Location Services", isOn: $locationEnabled) + Toggle("Dark Mode", isOn: $darkModeEnabled) + } + + Section("Support") { + Button("Help Center") { + // placeholder + } + + Button("About ShopWise") { + // placeholder + } + } + + Section("App Info") { + HStack { + Text("Version") + Spacer() + Text("1.0") + .foregroundStyle(.secondary) + } + } + } + .navigationTitle("Settings") + } +} diff --git a/SWFrontUI/ShopwiseFrontEndUI/ShoppingListView.swift b/SWFrontUI/ShopwiseFrontEndUI/ShoppingListView.swift new file mode 100644 index 0000000..24421e7 --- /dev/null +++ b/SWFrontUI/ShopwiseFrontEndUI/ShoppingListView.swift @@ -0,0 +1,123 @@ +import SwiftUI + +struct ShoppingListView: View { + @EnvironmentObject private var cartStore: CartStore + @Environment(\.dismiss) private var dismiss + + @State private var checkedIDs: Set = [] + + private var checkedTotal: Double { + cartStore.items + .filter { checkedIDs.contains($0.id) } + .reduce(0) { $0 + ($1.price * Double($1.quantity)) } + } + + private var checkedCount: Int { + cartStore.items.filter { checkedIDs.contains($0.id) }.count + } + + var body: some View { + List { + Section("Shopping List") { + if cartStore.items.isEmpty { + Text("No items to shop for.") + .foregroundStyle(.secondary) + } else { + ForEach(cartStore.items) { item in + Button { + toggle(item.id) + } label: { + HStack(spacing: 12) { + Image(systemName: checkedIDs.contains(item.id) ? "checkmark.circle.fill" : "circle") + .imageScale(.large) + + VStack(alignment: .leading, spacing: 2) { + Text(item.name) + .strikethrough(checkedIDs.contains(item.id)) + Text("\(item.quantity) × \(item.unit)") + .font(.subheadline) + .foregroundStyle(.secondary) + } + + Spacer() + + Text(String(format: "$%.2f", item.price * Double(item.quantity))) + .foregroundStyle(.secondary) + .monospacedDigit() + } + } + .buttonStyle(.plain) + } + } + } + + if !cartStore.items.isEmpty { + Section { + Button("Mark All") { + checkedIDs = Set(cartStore.items.map { $0.id }) + } + + Button("Clear Checks") { + checkedIDs.removeAll() + } + + Button(role: .destructive) { + cartStore.clear() + checkedIDs.removeAll() + dismiss() + } label: { + Text("Clear Cart") + } + } + } + } + .navigationTitle("Shopping List") + .appToolbar() + .safeAreaInset(edge: .bottom) { + if !cartStore.items.isEmpty { + VStack(spacing: 0) { + Divider() + + VStack(spacing: 10) { + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Checked: \(checkedCount)/\(cartStore.items.count)") + .font(.subheadline) + .foregroundStyle(.secondary) + Text("Current Total") + .font(.headline) + } + + Spacer() + + Text(String(format: "$%.2f", checkedTotal)) + .font(.title3) + .monospacedDigit() + } + + Button { + cartStore.clear() + checkedIDs.removeAll() + dismiss() + } label: { + Label("Finish Trip", systemImage: "checkmark.seal.fill") + .frame(maxWidth: .infinity) + } + .buttonStyle(.borderedProminent) + } + .padding(.horizontal, 16) + .padding(.vertical, 12) + .background(.ultraThinMaterial) + } + } + } + } + + private func toggle(_ id: String) { + if checkedIDs.contains(id) { + checkedIDs.remove(id) + } else { + checkedIDs.insert(id) + } + } +}