diff --git a/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift b/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift index 257c59a..884406f 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/AuthManager.swift @@ -337,116 +337,6 @@ struct SupabaseError: Codable { let msg: String? } - -// MARK: - Kroger Items - -struct KrogerItem: Codable, Identifiable { - let productId: Int - let name: String - let brand: String? - let price: String? // "1.49;1.49;2.49" — index-aligned with store_ids - let classifier: String? - let categories: String? - let image_url: String? - let size: String? - let search_keyword: String? - let store_ids: String? - - var id: Int { productId } - - var priceList: [Double] { - guard let price else { return [] } - return price.split(separator: ";") - .compactMap { Double($0.trimmingCharacters(in: .whitespaces)) } - } - - var storeIdList: [String] { - guard let store_ids else { return [] } - return store_ids.split(separator: ";") - .map { $0.trimmingCharacters(in: .whitespaces) } - } - - var minPrice: Double? { - priceList.filter { $0 > 0 }.min() - } - - func price(forStoreId storeId: String) -> Double? { - guard let index = storeIdList.firstIndex(of: storeId), - index < priceList.count else { return nil } - return priceList[index] - } - - func displayPrice(forStoreId storeId: String? = nil) -> String { - let p: Double? - if let storeId { - p = price(forStoreId: storeId) ?? minPrice - } else { - p = minPrice - } - guard let p else { return "Price N/A" } - return String(format: "$%.2f", p) - } - - var imageURL: URL? { - guard let img = image_url, !img.isEmpty else { return nil } - return URL(string: img) - } -} - -extension AuthManager { - func fetchKrogerItems( - search: String? = nil, - classifier: String? = nil, - limit: Int = 50, - offset: Int = 0 - ) async throws -> [KrogerItem] { - let base = supabaseURL - .appendingPathComponent("rest/v1") - .appendingPathComponent("kroger_ingredients2") - - var comps = URLComponents(url: base, resolvingAgainstBaseURL: false)! - var q: [URLQueryItem] = [ - URLQueryItem( - name: "select", - value: "productId,name,brand,price,classifier,categories,image_url,size,search_keyword,store_ids" - ), - URLQueryItem(name: "limit", value: "\(limit)"), - URLQueryItem(name: "offset", value: "\(offset)"), - URLQueryItem(name: "order", value: "productId.asc"), - ] - - if let s = search?.trimmingCharacters(in: .whitespacesAndNewlines), !s.isEmpty { - q.append(URLQueryItem( - name: "or", - value: "(name.ilike.*\(s)*,search_keyword.ilike.*\(s)*)" - )) - } - - if let cls = classifier, !cls.isEmpty { - q.append(URLQueryItem(name: "classifier", value: "eq.\(cls)")) - } - - 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 - ) - - do { - return try JSONDecoder().decode([KrogerItem].self, from: data) - } catch { - let raw = String(data: data, encoding: .utf8) ?? "" - print("❌ KrogerItem decode error: \(error)") - print("❌ Raw JSON: \(raw.prefix(500))") - throw error - } - } -} - //Onboard survey fetch/save struct UserPreferencesRow: Codable { let user_id: String @@ -563,6 +453,34 @@ extension AuthManager { return try JSONDecoder().decode([RecipeRow].self, from: data) } } + +//Filtered Recipes +extension AuthManager { + func fetchFilteredRecipes(userId: String, search: String? = nil, limit: Int = 50, offset: Int = 0) async throws -> [RecipeRow] { + let base = supabaseURL + .appendingPathComponent("rest/v1") + .appendingPathComponent("rpc") + .appendingPathComponent("filtered_recipes") + + var body: [String: String] = [ + "p_user_id": userId, + "p_limit": "\(limit)", + "p_offset": "\(offset)" + ] + if let search, !search.isEmpty { + body["p_search"] = search + } + + let (data, _) = try await request( + url: base, + method: "POST", + jsonBody: body, + bearerToken: accessToken + ) + + return try JSONDecoder().decode([RecipeRow].self, from: data) + } +} // MARK: - Recipe Matches diff --git a/SWFrontUI/ShopwiseFrontEndUI/CartView.swift b/SWFrontUI/ShopwiseFrontEndUI/CartView.swift index a75e427..0734ec6 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/CartView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/CartView.swift @@ -33,9 +33,23 @@ struct CartView: View { } if cartStore.items.isEmpty { - Section("Items") { - Text("Cart is empty") - .foregroundStyle(.secondary) + Section { + VStack(spacing: 12) { + Image(systemName: "cart") + .font(.system(size: 44, weight: .semibold)) + .foregroundStyle(.tertiary) + + Text("Your cart is empty") + .font(.headline) + + Text("Add items from Search or Recipes to get started.") + .font(.subheadline) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 20) + } + .frame(maxWidth: .infinity) + .padding(.vertical, 40) } } else { if !recipeGroups.isEmpty { diff --git a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift index 7134f7a..826b6a0 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/ContentView.swift @@ -43,7 +43,6 @@ struct ContentView: View { #Preview { ContentView() .environmentObject(AuthManager()) - .modelContainer(for: Item.self, inMemory: true) } #Preview { diff --git a/SWFrontUI/ShopwiseFrontEndUI/Item.swift b/SWFrontUI/ShopwiseFrontEndUI/Item.swift deleted file mode 100644 index 69cd7a5..0000000 --- a/SWFrontUI/ShopwiseFrontEndUI/Item.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Item.swift -// ShopwiseFrontEndUI -// -// Created by James Chang on 1/13/26. -// - -import Foundation -import SwiftData - -@Model -final class Item { - var timestamp: Date - - init(timestamp: Date) { - self.timestamp = timestamp - } -} diff --git a/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift b/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift index a9e63b4..b867c40 100644 --- a/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift +++ b/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift @@ -594,7 +594,7 @@ struct RecipeView: View { private func loadRecipes(reset: Bool = true) async { if isLoadingMore { return } let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines) - + if reset { offset = 0; recipes = []; hasMore = true; isLoading = true } else { @@ -602,11 +602,19 @@ struct RecipeView: View { isLoadingMore = true } errorText = nil - + do { - let fetched = try await auth.fetchRecipes( + guard let userId = auth.userID else { + errorText = "Please sign in to load personalized recipes." + isLoading = false + isLoadingMore = false + return + } + let fetched = try await auth.fetchFilteredRecipes( + userId: userId, search: trimmed.isEmpty ? nil : trimmed, - limit: pageSize, offset: offset + limit: pageSize, + offset: offset ) if reset { recipes = fetched } else { recipes.append(contentsOf: fetched) } offset += fetched.count diff --git a/SWFrontUI/ShopwiseFrontEndUI/SettingsView.swift b/SWFrontUI/ShopwiseFrontEndUI/SettingsView.swift deleted file mode 100644 index 6656f1a..0000000 --- a/SWFrontUI/ShopwiseFrontEndUI/SettingsView.swift +++ /dev/null @@ -1,46 +0,0 @@ -/*import SwiftUI -import UIKit - -struct SettingsView: View { - @Environment(\.openURL) private var openURL - - var body: some View { - List { - Section("Preferences") { - Button("Notification Settings") { - openAppSettings() - } - - Button("Location Services") { - openAppSettings() - } - } - - Section("Support") { - Button("Help Center") { - // placeholder - } - - Button("About ShopWise") { - // placeholder - } - } - - Section("App Info") { - HStack { - Text("Version") - Spacer() - Text("1.0") - .foregroundStyle(.secondary) - } - } - } - .navigationTitle("Settings") - } - - private func openAppSettings() { - if let url = URL(string: UIApplication.openSettingsURLString) { - openURL(url) - } - } -}*/ diff --git a/SWFrontUI/ShopwiseFrontEndUI/WalmartItems.swift b/SWFrontUI/ShopwiseFrontEndUI/WalmartItems.swift deleted file mode 100644 index c6ea2b9..0000000 --- a/SWFrontUI/ShopwiseFrontEndUI/WalmartItems.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// 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? - let descriptionText: String? -}