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 4f911f0..1bfebb6 100644
Binary files a/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.xcworkspace/xcuserdata/jchang.xcuserdatad/UserInterfaceState.xcuserstate and b/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/project.xcworkspace/xcuserdata/jchang.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/xcuserdata/jchang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/xcuserdata/jchang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000..0829a8e
--- /dev/null
+++ b/SWFrontUI/ShopwiseFrontEndUI.xcodeproj/xcuserdata/jchang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,6 @@
+
+
+
diff --git a/SWFrontUI/ShopwiseFrontEndUI/AppToolbar.swift b/SWFrontUI/ShopwiseFrontEndUI/AppToolbar.swift
index e8eeb80..023b3a9 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/AppToolbar.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/AppToolbar.swift
@@ -6,6 +6,16 @@ struct AppToolbar: ViewModifier {
func body(content: Content) -> some View {
content
.toolbar {
+ ToolbarItem(placement: .principal) {
+ HStack(spacing: 8) {
+ Image("ShopwiseLogo")
+ .resizable()
+ .scaledToFit()
+ .frame(width: 22, height: 22)
+ Text("ShopWise")
+ .font(.headline)
+ }
+ }
ToolbarItem(placement: .topBarTrailing) {
Button {
showAccount = true
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AccentColor.colorset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AccentColor.colorset/Contents.json
index 0afb3cf..0bda595 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AccentColor.colorset/Contents.json
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -1,11 +1,38 @@
{
- "colors": [
+ "colors" : [
{
- "idiom": "universal"
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xFF",
+ "green" : "0xB6",
+ "red" : "0x6B"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
}
],
- "info": {
- "author": "xcode",
- "version": 1
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
}
}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/Contents.json
index c70a5bf..8c6ea6d 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -1,35 +1,38 @@
{
- "images": [
+ "images" : [
{
- "idiom": "universal",
- "platform": "ios",
- "size": "1024x1024"
+ "filename" : "ShopwiseLogo_cropped.jpg",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
},
{
- "appearances": [
+ "appearances" : [
{
- "appearance": "luminosity",
- "value": "dark"
+ "appearance" : "luminosity",
+ "value" : "dark"
}
],
- "idiom": "universal",
- "platform": "ios",
- "size": "1024x1024"
+ "filename" : "ShopwiseLogo_cropped 1.jpg",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
},
{
- "appearances": [
+ "appearances" : [
{
- "appearance": "luminosity",
- "value": "tinted"
+ "appearance" : "luminosity",
+ "value" : "tinted"
}
],
- "idiom": "universal",
- "platform": "ios",
- "size": "1024x1024"
+ "filename" : "ShopwiseLogo_cropped 2.jpg",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
}
],
- "info": {
- "author": "xcode",
- "version": 1
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
}
}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/ShopwiseLogo_cropped 1.jpg b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/ShopwiseLogo_cropped 1.jpg
new file mode 100644
index 0000000..1008ea1
Binary files /dev/null and b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/ShopwiseLogo_cropped 1.jpg differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/ShopwiseLogo_cropped 2.jpg b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/ShopwiseLogo_cropped 2.jpg
new file mode 100644
index 0000000..1008ea1
Binary files /dev/null and b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/ShopwiseLogo_cropped 2.jpg differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/ShopwiseLogo_cropped.jpg b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/ShopwiseLogo_cropped.jpg
new file mode 100644
index 0000000..1008ea1
Binary files /dev/null and b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/AppIcon.appiconset/ShopwiseLogo_cropped.jpg differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/PrimaryBlue.colorset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/PrimaryBlue.colorset/Contents.json
new file mode 100644
index 0000000..40efb33
--- /dev/null
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/PrimaryBlue.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xA5",
+ "green" : "0x3D",
+ "red" : "0x00"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xA5",
+ "green" : "0x3D",
+ "red" : "0x00"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/SecondaryGold.colorset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/SecondaryGold.colorset/Contents.json
new file mode 100644
index 0000000..aa448e5
--- /dev/null
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/SecondaryGold.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x1C",
+ "green" : "0xB8",
+ "red" : "0xFF"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x1C",
+ "green" : "0xB8",
+ "red" : "0xFF"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/ShopwiseLogo.imageset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/ShopwiseLogo.imageset/Contents.json
new file mode 100644
index 0000000..58e57b7
--- /dev/null
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/ShopwiseLogo.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "rsz_shopwiselogo_cropped.jpg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/ShopwiseLogo.imageset/rsz_shopwiselogo_cropped.jpg b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/ShopwiseLogo.imageset/rsz_shopwiselogo_cropped.jpg
new file mode 100644
index 0000000..9fcf1e9
Binary files /dev/null and b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/ShopwiseLogo.imageset/rsz_shopwiselogo_cropped.jpg differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/cardenaslogo.imageset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/cardenaslogo.imageset/Contents.json
new file mode 100644
index 0000000..a058811
--- /dev/null
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/cardenaslogo.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "cardenaslogo-removebg-preview.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/cardenaslogo.imageset/cardenaslogo-removebg-preview.png b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/cardenaslogo.imageset/cardenaslogo-removebg-preview.png
new file mode 100644
index 0000000..db4aa41
Binary files /dev/null and b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/cardenaslogo.imageset/cardenaslogo-removebg-preview.png differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/majorlogo.imageset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/majorlogo.imageset/Contents.json
new file mode 100644
index 0000000..98d3a62
--- /dev/null
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/majorlogo.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "majorlogo-removebg-preview.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/majorlogo.imageset/majorlogo-removebg-preview.png b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/majorlogo.imageset/majorlogo-removebg-preview.png
new file mode 100644
index 0000000..5fe7ae5
Binary files /dev/null and b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/majorlogo.imageset/majorlogo-removebg-preview.png differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/northgatelogo.imageset/Adobe Express - file.png b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/northgatelogo.imageset/Adobe Express - file.png
new file mode 100644
index 0000000..06d319a
Binary files /dev/null and b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/northgatelogo.imageset/Adobe Express - file.png differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/northgatelogo.imageset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/northgatelogo.imageset/Contents.json
new file mode 100644
index 0000000..20962aa
--- /dev/null
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/northgatelogo.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "Adobe Express - file.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/superlogo.imageset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/superlogo.imageset/Contents.json
new file mode 100644
index 0000000..ee2a737
--- /dev/null
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/superlogo.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "superlogo.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/superlogo.imageset/superlogo.png b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/superlogo.imageset/superlogo.png
new file mode 100644
index 0000000..097e837
Binary files /dev/null and b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/superlogo.imageset/superlogo.png differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/vallartalogo.imageset/Contents.json b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/vallartalogo.imageset/Contents.json
new file mode 100644
index 0000000..7a23eae
--- /dev/null
+++ b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/vallartalogo.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "vallartalogo.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/vallartalogo.imageset/vallartalogo.png b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/vallartalogo.imageset/vallartalogo.png
new file mode 100644
index 0000000..1b00fca
Binary files /dev/null and b/SWFrontUI/ShopwiseFrontEndUI/Assets.xcassets/vallartalogo.imageset/vallartalogo.png differ
diff --git a/SWFrontUI/ShopwiseFrontEndUI/AuthView.swift b/SWFrontUI/ShopwiseFrontEndUI/AuthView.swift
index b3a8106..2a328f4 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/AuthView.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/AuthView.swift
@@ -32,11 +32,11 @@ struct AuthView: View {
VStack(spacing: 10) {
Image(systemName: "cart")
.font(.system(size: 42, weight: .semibold))
- .foregroundStyle(.blue)
+ .foregroundStyle(Theme.primary)
Text("ShopWise")
.font(.title3)
- .foregroundStyle(.blue)
+ .foregroundStyle(Theme.primary)
.bold()
}
.padding(.top, 10)
diff --git a/SWFrontUI/ShopwiseFrontEndUI/CartStore.swift b/SWFrontUI/ShopwiseFrontEndUI/CartStore.swift
index 88516e3..c0d90c8 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/CartStore.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/CartStore.swift
@@ -10,8 +10,18 @@ struct CartLineItem: Identifiable, Hashable {
var quantity: Int
let groupId: String?
let groupTitle: String?
+ let storeName: String?
- init(id: String = String(),name: String,unit: String,price: Double, quantity: Int = 1,groupId: String? = nil,groupTitle: String? = nil){
+ init(
+ id: String = String(),
+ name: String,
+ unit: String,
+ price: Double,
+ quantity: Int = 1,
+ groupId: String? = nil,
+ groupTitle: String? = nil,
+ storeName: String? = nil
+ ){
self.id = id
self.name = name
self.unit = unit
@@ -19,6 +29,7 @@ struct CartLineItem: Identifiable, Hashable {
self.quantity = quantity
self.groupId = groupId
self.groupTitle = groupTitle
+ self.storeName = storeName
}
var lineTotal: Double{
@@ -49,7 +60,7 @@ final class CartStore: ObservableObject {
add(id: name, name: name, unit: unit, price: price)
}
- func add(id: String, name: String, unit: String, price: Double) {
+ func add(id: String, name: String, unit: String, price: Double, storeName: String? = nil) {
if let idx = items.firstIndex(where: { $0.id == id }) {
items[idx].quantity += 1
} else {
@@ -59,13 +70,14 @@ final class CartStore: ObservableObject {
name: name,
unit: unit,
price: price,
- quantity: 1
+ quantity: 1,
+ storeName: storeName
)
)
}
}
- func add(recipeId: String, recipeTitle: String, name: String, unit: String, price: Double) {
+ func add(recipeId: String, recipeTitle: String, name: String, unit: String, price: Double, storeName: String? = nil) {
let id = "\(recipeId)::\(name)"
if let idx = items.firstIndex(where: { $0.id == id }) {
items[idx].quantity += 1
@@ -77,7 +89,8 @@ final class CartStore: ObservableObject {
price: items[idx].price,
quantity: items[idx].quantity,
groupId: recipeId,
- groupTitle: recipeTitle
+ groupTitle: recipeTitle,
+ storeName: storeName ?? items[idx].storeName
)
}
} else {
@@ -89,7 +102,8 @@ final class CartStore: ObservableObject {
price: price,
quantity: 1,
groupId: recipeId,
- groupTitle: recipeTitle
+ groupTitle: recipeTitle,
+ storeName: storeName
)
)
}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/CartView.swift b/SWFrontUI/ShopwiseFrontEndUI/CartView.swift
index 92fbd61..a75e427 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/CartView.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/CartView.swift
@@ -54,6 +54,14 @@ struct CartView: View {
.foregroundStyle(.secondary)
}
Spacer()
+ Button(role: .destructive) {
+ removeGroup(group.id)
+ } label: {
+ Image(systemName: "trash")
+ .foregroundStyle(.secondary)
+ }
+ .buttonStyle(.plain)
+ .padding(.trailing, 2)
Image(systemName: expandedRecipeIds.contains(group.id) ? "chevron.up" : "chevron.down")
.foregroundStyle(.secondary)
}
@@ -62,9 +70,16 @@ struct CartView: View {
if expandedRecipeIds.contains(group.id) {
Divider()
- ForEach(Array(group.items.enumerated()), id: \.element.id) { index, item in
- itemRow(item)
- if index != group.items.count - 1 {
+ let storeGroups = groupItemsByStore(group.items)
+ ForEach(storeGroups) { storeGroup in
+ storeHeader(storeGroup.store)
+ ForEach(Array(storeGroup.items.enumerated()), id: \.element.id) { index, item in
+ itemRow(item)
+ if index != storeGroup.items.count - 1 {
+ Divider()
+ }
+ }
+ if storeGroup.id != storeGroups.last?.id {
Divider()
}
}
@@ -91,12 +106,11 @@ struct CartView: View {
Text("No individual items")
.foregroundStyle(.secondary)
} else {
- ForEach(individualItems) { item in
- itemRow(item)
- }
- .onDelete { indexSet in
- for idx in indexSet {
- cartStore.remove(individualItems[idx])
+ let storeGroups = groupItemsByStore(individualItems)
+ ForEach(storeGroups) { storeGroup in
+ storeHeader(storeGroup.store)
+ ForEach(storeGroup.items) { item in
+ itemRow(item)
}
}
}
@@ -123,7 +137,7 @@ struct CartView: View {
}
}
}
- .navigationTitle("Cart")
+ .navigationTitle("Shopping Cart")
.appToolbar()
.navigationDestination(isPresented: $showShoppingList) {
ShoppingListView()
@@ -169,6 +183,11 @@ struct CartView: View {
VStack(alignment: .leading, spacing: 4) {
Text(item.name)
.font(.headline)
+ if let store = item.storeName, !store.isEmpty {
+ Text(store)
+ .font(.caption.weight(.semibold))
+ .foregroundStyle(Theme.secondary)
+ }
if !item.unit.isEmpty {
Text(item.unit)
.font(.subheadline)
@@ -201,6 +220,48 @@ struct CartView: View {
}
}
.padding(.vertical, 4)
+ .swipeActions {
+ Button(role: .destructive) {
+ cartStore.remove(item)
+ } label: {
+ Label("Remove", systemImage: "trash")
+ }
+ }
+ }
+
+ private struct StoreGroup: Identifiable {
+ let id: String
+ let store: String
+ let items: [CartLineItem]
+ }
+
+ private func groupItemsByStore(_ items: [CartLineItem]) -> [StoreGroup] {
+ let grouped = Dictionary(grouping: items) { item in
+ let store = item.storeName?.trimmingCharacters(in: .whitespacesAndNewlines)
+ return (store?.isEmpty == false) ? store! : "Unknown Store"
+ }
+ return grouped.map { key, value in
+ StoreGroup(id: key, store: key, items: value)
+ }
+ .sorted { $0.store.localizedCaseInsensitiveCompare($1.store) == .orderedAscending }
+ }
+
+ @ViewBuilder
+ private func storeHeader(_ store: String) -> some View {
+ HStack {
+ Text(store)
+ .font(.subheadline.weight(.semibold))
+ .foregroundStyle(Theme.primary)
+ Spacer()
+ }
+ .padding(.top, 4)
+ }
+
+ private func removeGroup(_ groupId: String) {
+ let itemsToRemove = cartStore.items.filter { $0.groupId == groupId }
+ for item in itemsToRemove {
+ cartStore.remove(item)
+ }
}
private func groupSubtotal(for group: RecipeGroup) -> Double {
diff --git a/SWFrontUI/ShopwiseFrontEndUI/MapView.swift b/SWFrontUI/ShopwiseFrontEndUI/MapView.swift
index 7e5e6ea..296aa40 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/MapView.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/MapView.swift
@@ -219,7 +219,9 @@ struct MapView: View {
ranchMarketSection
locationSection
}
+ .listStyle(.plain)
.navigationTitle("ShopWise")
+ .navigationBarTitleDisplayMode(.inline)
.appToolbar()
.task { } // kroger stores are hardcoded — swap for live fetch once kroger_locations is populated
.onAppear { locationManager.requestPermission() }
diff --git a/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift b/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift
index 3204c3a..e3b9780 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/OnboardingSurveyView.swift
@@ -10,14 +10,12 @@ struct OnboardingSurveyView: View {
@State private var errorMessage: String?
let dietOptions = [
- "Vegetarian", "Vegan", "Pescatarian",
- "Keto", "Gluten-Free", "Dairy-Free",
- "Halal", "Kosher"
+ "Vegan", "Vegetarian", "Pescatarian", "Dairy-Free"
]
let allergyOptions = [
- "Peanuts", "Tree Nuts", "Dairy", "Eggs",
- "Soy", "Wheat", "Shellfish", "Fish"
+ "Peanuts", "Tree Nuts", "Dairy", "Egg",
+ "Fish", "Shellfish", "Soy", "Wheat"
]
var onComplete: (() -> Void)? = nil
@@ -87,7 +85,7 @@ struct OnboardingSurveyView: View {
.disabled(isSaving)
}
}
- .navigationTitle("Your Preferences")
+ .navigationTitle("Dietary Compass")
.task {
await loadPreferences()
}
@@ -157,8 +155,8 @@ private struct PreferenceChipGrid: View {
.padding(.horizontal, 12)
.padding(.vertical, 8)
.frame(maxWidth: .infinity)
- .background(isSelected ? Color.blue.opacity(0.18) : Color(.systemGray6))
- .foregroundStyle(isSelected ? Color.blue : Color.primary)
+ .background(isSelected ? Theme.primary.opacity(0.18) : Color(.systemGray6))
+ .foregroundStyle(isSelected ? Theme.primary : Color.primary)
.clipShape(Capsule())
}
.buttonStyle(.plain)
diff --git a/SWFrontUI/ShopwiseFrontEndUI/ProfileView.swift b/SWFrontUI/ShopwiseFrontEndUI/ProfileView.swift
index 5510e17..d718272 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/ProfileView.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/ProfileView.swift
@@ -17,7 +17,7 @@ struct ProfileView: View {
HStack {
Image(systemName: "person.circle.fill")
.font(.system(size: 36))
- .foregroundStyle(.blue)
+ .foregroundStyle(Theme.primary)
VStack(alignment: .leading) {
Text(greetingText)
diff --git a/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift b/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift
index 8f3ebfe..a9e63b4 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/RecipeView.swift
@@ -35,6 +35,8 @@ struct RecipeView: View {
@State private var isLoadingMore = false
@State private var excludedIngredientsByRecipe: [Int: Set] = [:]
@State private var expandedInstructionIds: Set = []
+ @State private var expandedIngredientIds: Set = []
+ @State private var selectedMatchIndex: [String: Int] = [:]
// Shopping mode – persists across recipe expansions
@State private var shoppingMode: ShoppingMode = .value
@@ -52,6 +54,7 @@ struct RecipeView: View {
List { content }
.listStyle(.plain)
.navigationTitle("ShopWise")
+ .navigationBarTitleDisplayMode(.inline)
.searchable(text: $query, prompt: "Search recipes…")
.appToolbar()
.task { await loadRecipes(reset: true) }
@@ -128,7 +131,9 @@ struct RecipeView: View {
}
VStack(alignment: .leading, spacing: 6) {
- Text(recipe.title).font(.headline).lineLimit(2)
+ Text(recipe.title)
+ .font(.headline)
+ .lineLimit(expandedID == recipe.id ? nil : 2)
}
Spacer()
@@ -143,81 +148,91 @@ struct RecipeView: View {
CardContainer {
VStack(alignment: .leading, spacing: 12) {
- HStack {
- Text("Ingredients").font(.headline)
- Spacer()
+ Button {
+ withAnimation(.easeInOut(duration: 0.2)) {
+ toggleIngredients(recipe.id)
+ }
+ } label: {
+ HStack {
+ Text("Ingredients").font(.headline)
+ Spacer()
+ Image(systemName: expandedIngredientIds.contains(recipe.id) ? "chevron.up" : "chevron.down")
+ .foregroundStyle(.secondary)
+ }
}
-
- // Mode picker – only shown once matches are loaded
- let allMatches = matchesByRecipe[recipe.id] ?? []
- if !allMatches.isEmpty {
- HStack(spacing: 4) {
- ForEach(ShoppingMode.allCases, id: \.self) { mode in
- let isSelected = shoppingMode == mode
- Button {
- shoppingMode = mode
- } label: {
- HStack(spacing: 5) {
- Image(systemName: mode == .value ? "tag.fill" : "clock.arrow.2.circlepath")
- .font(.system(size: 12, weight: .semibold))
- Text(mode.label)
- .font(.system(size: 14, weight: .semibold))
+ .buttonStyle(.plain)
+
+ if expandedIngredientIds.contains(recipe.id) {
+ // Mode picker – only shown once matches are loaded
+ let allMatches = matchesByRecipe[recipe.id] ?? []
+ if !allMatches.isEmpty {
+ HStack(spacing: 4) {
+ ForEach(ShoppingMode.allCases, id: \.self) { mode in
+ let isSelected = shoppingMode == mode
+ Button {
+ shoppingMode = mode
+ } label: {
+ HStack(spacing: 5) {
+ Image(systemName: mode == .value ? "tag.fill" : "clock.arrow.2.circlepath")
+ .font(.system(size: 12, weight: .semibold))
+ Text(mode.label)
+ .font(.system(size: 14, weight: .semibold))
+ }
+ .frame(maxWidth: .infinity)
+ .padding(.vertical, 9)
+ .background(
+ RoundedRectangle(cornerRadius: 10)
+ .fill(isSelected ? Color.primary : Color.clear)
+ )
+ .foregroundStyle(isSelected ? Color(UIColor.systemBackground) : Color.secondary)
}
- .frame(maxWidth: .infinity)
- .padding(.vertical, 9)
- .background(
- RoundedRectangle(cornerRadius: 10)
- .fill(isSelected ? Color.primary : Color.clear)
- )
- .foregroundStyle(isSelected ? Color(UIColor.systemBackground) : Color.secondary)
+ .buttonStyle(.plain)
}
- .buttonStyle(.plain)
}
+ .padding(4)
+ .background(Color(.systemGray5))
+ .clipShape(RoundedRectangle(cornerRadius: 13))
}
- .padding(4)
- .background(Color(.systemGray5))
- .clipShape(RoundedRectangle(cornerRadius: 13))
- }
- let isLoadingMatches = loadingMatchesFor.contains(recipe.id)
- let (allGrouped, ingredients) = processMatches(allMatches)
- // For Time Save mode, compute the optimal store plan once here.
- let timeSavePlan: TimeSavePlan? = shoppingMode == .timeSave
- ? computeTimeSavePlan(allGrouped: allGrouped, ingredients: ingredients)
- : nil
+ let isLoadingMatches = loadingMatchesFor.contains(recipe.id)
+ let (allGrouped, ingredients) = processMatches(allMatches)
+ // For Time Save mode, compute the optimal store plan once here.
+ let timeSavePlan: TimeSavePlan? = shoppingMode == .timeSave
+ ? computeTimeSavePlan(allGrouped: allGrouped, ingredients: ingredients)
+ : nil
- if isLoadingMatches {
- HStack {
- Spacer()
- VStack(spacing: 6) {
- ProgressView()
- Text("Finding products…")
- .font(.caption)
- .foregroundStyle(.secondary)
+ if isLoadingMatches {
+ HStack {
+ Spacer()
+ VStack(spacing: 6) {
+ ProgressView()
+ Text("Finding products…")
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ }
+ Spacer()
}
- Spacer()
- }
- .padding(.vertical, 8)
+ .padding(.vertical, 8)
- } else if allMatches.isEmpty {
- ForEach(recipe.ingredientList, id: \.self) { item in
- ingredientRow(recipeId: recipe.id, item: item)
- }
+ } else if allMatches.isEmpty {
+ ForEach(recipe.ingredientList, id: \.self) { item in
+ ingredientRow(recipeId: recipe.id, item: item)
+ }
- } else {
- // Store summary banner — shown for both modes
- let valueStores: [String] = {
- // Unique stores that would be visited if picking cheapest per ingredient
- var seen = Set()
- var result: [String] = []
- for item in ingredients {
- if let store = (allGrouped[item] ?? []).first?.matched_store,
- seen.insert(store).inserted {
- result.append(store)
+ } else {
+ // Store summary banner — shown for both modes
+ let valueStores: [String] = {
+ // Unique stores that would be visited if picking cheapest per ingredient
+ var seen = Set()
+ var result: [String] = []
+ for item in ingredients {
+ if let store = (allGrouped[item] ?? []).first?.matched_store,
+ seen.insert(store).inserted {
+ result.append(store)
+ }
}
- }
- return result
- }()
+ return result
+ }()
let displayStores = timeSavePlan?.stores ?? valueStores
if !displayStores.isEmpty {
@@ -247,6 +262,7 @@ struct RecipeView: View {
IngredientMatchRow(
ingredient: item,
matches: displayMatches,
+ selectedRank: bindingForSelectedRank(recipeId: recipe.id, ingredient: item, matchCount: displayMatches.count),
isExcluded: excludedIngredientsByRecipe[recipe.id]?.contains(item) == true,
onToggle: { toggleIngredient(recipeId: recipe.id, item: item) },
onAdd: { match in
@@ -255,13 +271,15 @@ struct RecipeView: View {
recipeTitle: recipe.title,
name: match.matched_name ?? item,
unit: match.matched_size ?? "",
- price: match.min_price ?? 0
+ price: match.min_price ?? 0,
+ storeName: match.matched_store
)
}
)
}
}
-
+ }
+
if let instructions = recipe.instructions,
!instructions.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
Divider().padding(.vertical, 2)
@@ -310,7 +328,13 @@ struct RecipeView: View {
if let plan {
return plan.matchFor[item] ?? allForItem.first
}
- return allForItem.first // Value: cheapest
+ // Value mode: honor the user's selected rank if available
+ let key = matchKey(recipeId: recipe.id, ingredient: item)
+ let idx = selectedMatchIndex[key] ?? 0
+ if idx >= 0 && idx < allForItem.count {
+ return allForItem[idx]
+ }
+ return allForItem.first
}()
if let top {
cartStore.add(
@@ -318,7 +342,8 @@ struct RecipeView: View {
recipeTitle: recipe.title,
name: top.matched_name ?? item,
unit: top.matched_size ?? "",
- price: top.min_price ?? 0
+ price: top.min_price ?? 0,
+ storeName: top.matched_store
)
} else {
cartStore.add(
@@ -330,7 +355,11 @@ struct RecipeView: View {
}
} label: {
let excluded = excludedIngredientsByRecipe[recipe.id] ?? []
- let selectedCount = max(0, recipe.ingredientList.count - excluded.count)
+ let matches = matchesByRecipe[recipe.id] ?? []
+ let ingredients = matches.isEmpty
+ ? recipe.ingredientList
+ : orderedIngredients(from: matches)
+ let selectedCount = max(0, ingredients.count - excluded.count)
VStack(spacing: 4) {
Text("\(selectedCount) selected")
.font(.footnote)
@@ -339,6 +368,7 @@ struct RecipeView: View {
}
.frame(maxWidth: .infinity)
}
+ .tint(Theme.primary)
.buttonStyle(.borderedProminent)
}
}
@@ -519,13 +549,39 @@ struct RecipeView: View {
expandedInstructionIds.insert(recipeId)
}
}
+
+ private func toggleIngredients(_ recipeId: Int) {
+ if expandedIngredientIds.contains(recipeId) {
+ expandedIngredientIds.remove(recipeId)
+ } else {
+ expandedIngredientIds.insert(recipeId)
+ }
+ }
+
+ private func matchKey(recipeId: Int, ingredient: String) -> String {
+ "\(recipeId)::\(ingredient)"
+ }
+
+ private func bindingForSelectedRank(recipeId: Int, ingredient: String, matchCount: Int) -> Binding {
+ let key = matchKey(recipeId: recipeId, ingredient: ingredient)
+ return Binding(
+ get: {
+ let idx = selectedMatchIndex[key] ?? 0
+ if matchCount == 0 { return 0 }
+ return min(max(idx, 0), matchCount - 1)
+ },
+ set: { newValue in
+ selectedMatchIndex[key] = newValue
+ }
+ )
+ }
private func ingredientRow(recipeId: Int, item: String) -> some View {
let isExcluded = excludedIngredientsByRecipe[recipeId]?.contains(item) == true
return Button { toggleIngredient(recipeId: recipeId, item: item) } label: {
HStack(spacing: 10) {
Image(systemName: isExcluded ? "minus.circle" : "checkmark.circle.fill")
- .foregroundStyle(isExcluded ? Color.secondary : Color.green)
+ .foregroundStyle(isExcluded ? Color.secondary : Theme.secondary)
Text(item)
.foregroundStyle(isExcluded ? Color.secondary : Color.primary)
.strikethrough(isExcluded, color: .secondary)
@@ -568,19 +624,18 @@ struct RecipeView: View {
struct IngredientMatchRow: View {
let ingredient: String
let matches: [ScrapedRecipeMatch]
+ @Binding var selectedRank: Int
let isExcluded: Bool
let onToggle: () -> Void
let onAdd: (ScrapedRecipeMatch) -> Void
- @State private var selectedRank = 0
-
var body: some View {
VStack(alignment: .leading, spacing: 6) {
Button(action: onToggle) {
HStack(spacing: 10) {
Image(systemName: isExcluded ? "minus.circle" : "checkmark.circle.fill")
- .foregroundStyle(isExcluded ? Color.secondary : Color.green)
+ .foregroundStyle(isExcluded ? Color.secondary : Theme.secondary)
Text(ingredient)
.font(.subheadline)
.foregroundStyle(isExcluded ? Color.secondary : Color.primary)
@@ -637,14 +692,14 @@ struct IngredientMatchRow: View {
VStack(alignment: .trailing, spacing: 4) {
Text(match.displayPrice)
.font(.caption.weight(.semibold))
- .foregroundStyle(.blue)
-
+ .foregroundStyle(Theme.primary)
+
Button { onAdd(match) } label: {
Image(systemName: "cart.badge.plus")
.font(.system(size: 14))
.padding(6)
- .background(Color.blue.opacity(0.12))
- .foregroundStyle(.blue)
+ .background(Theme.primary.opacity(0.12))
+ .foregroundStyle(Theme.primary)
.clipShape(Circle())
}
.buttonStyle(.plain)
@@ -659,6 +714,10 @@ struct IngredientMatchRow: View {
.padding(.vertical, 2)
// Reset the picker whenever the matches array changes (e.g. store filter switched).
// Without this, selectedRank can point past the end of the new array and nothing renders.
- .onChange(of: matches.map { $0.id }) { _, _ in selectedRank = 0 }
+ .onChange(of: matches.map { $0.id }) { _, _ in
+ if selectedRank >= matches.count {
+ selectedRank = 0
+ }
+ }
}
}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/SearchView.swift b/SWFrontUI/ShopwiseFrontEndUI/SearchView.swift
index eb8ece9..82e5d49 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/SearchView.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/SearchView.swift
@@ -34,6 +34,7 @@ struct SearchView: View {
@State private var results: [ScrapedIngredient] = []
@State private var isLoading = false
@State private var errorText: String? = nil
+ @State private var expandedItemId: String? = nil
@State private var pageSize = 50
@State private var offset = 0
@@ -44,9 +45,10 @@ struct SearchView: View {
ScrollView {
VStack(alignment: .leading, spacing: 14) {
- Text("ShopWise")
+ /*Text("ShopWise")
.font(.system(size: 34, weight: .bold))
.padding(.top, 6)
+ .padding(.bottom, 4)*/
// Search bar
HStack(spacing: 10) {
@@ -124,12 +126,24 @@ struct SearchView: View {
unit: item.quantity,
priceText: item.displayPrice, // var not func
storeName: selectedTab.id == "All" ? item.store : nil,
+ isExpanded: expandedItemId == item.id,
+ onToggle: {
+ withAnimation(.easeInOut(duration: 0.2)) {
+ if expandedItemId == item.id {
+ expandedItemId = nil
+ } else {
+ expandedItemId = item.id
+ }
+ }
+ },
onAdd: {
+ let fallbackStore = selectedTab.id == "All" ? nil : selectedTab.id
cartStore.add(
id: item.id,
name: item.name,
unit: item.quantity ?? "",
- price: item.price ?? 0
+ price: item.price ?? 0,
+ storeName: item.store.isEmpty ? fallbackStore : item.store
)
}
)
@@ -220,12 +234,12 @@ struct StoreChip: View {
}
.padding(.horizontal, 14)
.padding(.vertical, 8)
- .background(isSelected ? Color.blue.opacity(0.15) : Color(.systemGray6))
- .foregroundStyle(isSelected ? Color.blue : Color.primary)
+ .background(isSelected ? Theme.primary.opacity(0.15) : Color(.systemGray6))
+ .foregroundStyle(isSelected ? Theme.primary : Color.primary)
.clipShape(Capsule())
.overlay(
Capsule()
- .strokeBorder(isSelected ? Color.blue.opacity(0.4) : Color.clear, lineWidth: 1)
+ .strokeBorder(isSelected ? Theme.primary.opacity(0.4) : Color.clear, lineWidth: 1)
)
}
.buttonStyle(.plain)
@@ -240,6 +254,8 @@ struct ProductCard: View {
let unit: String?
let priceText: String
var storeName: String? = nil // shown in "All" tab so user knows which store
+ let isExpanded: Bool
+ let onToggle: () -> Void
let onAdd: () -> Void
var body: some View {
@@ -264,7 +280,7 @@ struct ProductCard: View {
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.headline)
- .lineLimit(2)
+ .lineLimit(isExpanded ? nil : 2)
if let storeName {
HStack(spacing: 4) {
@@ -304,11 +320,13 @@ struct ProductCard: View {
.padding(.horizontal, 14)
.padding(.vertical, 10)
.foregroundStyle(.white)
- .background(Color.blue)
+ .background(Theme.primary)
.clipShape(Capsule())
}
.buttonStyle(.plain)
}
+ .contentShape(Rectangle())
+ .onTapGesture(perform: onToggle)
.padding(14)
.background(Color(.systemGray6).opacity(0.65))
.clipShape(RoundedRectangle(cornerRadius: 18))
diff --git a/SWFrontUI/ShopwiseFrontEndUI/ShoppingListView.swift b/SWFrontUI/ShopwiseFrontEndUI/ShoppingListView.swift
index eb2e409..cae1a93 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/ShoppingListView.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/ShoppingListView.swift
@@ -46,8 +46,12 @@ struct ShoppingListView: View {
.buttonStyle(.plain)
if expandedRecipeIds.contains(group.id) {
- ForEach(group.items) { item in
- itemRow(item)
+ let storeGroups = groupItemsByStore(group.items)
+ ForEach(storeGroups) { storeGroup in
+ storeHeader(storeGroup.store)
+ ForEach(storeGroup.items) { item in
+ itemRow(item)
+ }
}
}
}
@@ -61,8 +65,12 @@ struct ShoppingListView: View {
Text("No individual items")
.foregroundStyle(.secondary)
} else {
- ForEach(individualItems) { item in
- itemRow(item)
+ let storeGroups = groupItemsByStore(individualItems)
+ ForEach(storeGroups) { storeGroup in
+ storeHeader(storeGroup.store)
+ ForEach(storeGroup.items) { item in
+ itemRow(item)
+ }
}
}
}
@@ -146,6 +154,34 @@ struct ShoppingListView: View {
}
}
+ private struct StoreGroup: Identifiable {
+ let id: String
+ let store: String
+ let items: [CartLineItem]
+ }
+
+ private func groupItemsByStore(_ items: [CartLineItem]) -> [StoreGroup] {
+ let grouped = Dictionary(grouping: items) { item in
+ let store = item.storeName?.trimmingCharacters(in: .whitespacesAndNewlines)
+ return (store?.isEmpty == false) ? store! : "Unknown Store"
+ }
+ return grouped.map { key, value in
+ StoreGroup(id: key, store: key, items: value)
+ }
+ .sorted { $0.store.localizedCaseInsensitiveCompare($1.store) == .orderedAscending }
+ }
+
+ @ViewBuilder
+ private func storeHeader(_ store: String) -> some View {
+ HStack {
+ Text(store)
+ .font(.subheadline.weight(.semibold))
+ .foregroundStyle(Theme.secondary)
+ Spacer()
+ }
+ .padding(.top, 4)
+ }
+
private var recipeGroups: [RecipeGroup] {
let items = cartStore.items
var groups: [String: RecipeGroup] = [:]
@@ -183,6 +219,11 @@ struct ShoppingListView: View {
VStack(alignment: .leading, spacing: 2) {
Text(item.name)
.strikethrough(checkedIDs.contains(item.id))
+ if let store = item.storeName, !store.isEmpty {
+ Text(store)
+ .font(.caption.weight(.semibold))
+ .foregroundStyle(Theme.primary)
+ }
if !item.unit.isEmpty {
Text("\(item.quantity) × \(item.unit)")
.font(.subheadline)
diff --git a/SWFrontUI/ShopwiseFrontEndUI/StoreLogoHelper.swift b/SWFrontUI/ShopwiseFrontEndUI/StoreLogoHelper.swift
index 10dce7a..8c72f2f 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/StoreLogoHelper.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/StoreLogoHelper.swift
@@ -24,6 +24,16 @@ func storeLogoAsset(for storeName: String) -> String? {
return "logo_smartfinal"
case "99 Ranch", "99 Ranch Market":
return "logo_99ranch"
+ case "Vallarta Supermarkets":
+ return "vallartalogo"
+ case "Cardenas Markets":
+ return "cardenaslogo"
+ case "El Super":
+ return "superlogo"
+ case "Northgate Market":
+ return "northgatelogo"
+ case "Major Market":
+ return "majorlogo"
default:
return nil
}
diff --git a/SWFrontUI/ShopwiseFrontEndUI/UIModules.swift b/SWFrontUI/ShopwiseFrontEndUI/UIModules.swift
index 0d08d35..8bd40b0 100644
--- a/SWFrontUI/ShopwiseFrontEndUI/UIModules.swift
+++ b/SWFrontUI/ShopwiseFrontEndUI/UIModules.swift
@@ -1,5 +1,11 @@
import SwiftUI
+enum Theme {
+ static let primary = Color("PrimaryBlue")
+ static let secondary = Color("SecondaryGold")
+ static let muted = Color(.secondaryLabel)
+}
+
struct CategoryChip: View {
let title: String
let isSelected: Bool
@@ -81,3 +87,4 @@ struct ItemCardView: View {
}
}
}
+