migration
This commit is contained in:
291
PediFoods/State/AppState.swift
Normal file
291
PediFoods/State/AppState.swift
Normal file
@@ -0,0 +1,291 @@
|
||||
import Foundation
|
||||
|
||||
struct AppState {
|
||||
var session = SessionState()
|
||||
var profile = ProfileState()
|
||||
var cart = CartState()
|
||||
var address = AddressState()
|
||||
var favorites = FavoritesState()
|
||||
var featureFlags = FeatureFlagsState()
|
||||
var homeFilters = HomeFiltersState()
|
||||
var activeModal: AppModal? = nil
|
||||
var shouldNavigateToOrders: Bool = false
|
||||
/// Set when a push tap (§6 of the push notifications guide) targets a
|
||||
/// specific order — consumed once by `OrdersView`, which routes to it via
|
||||
/// `OrderEntryDestinationView` and clears it.
|
||||
var pendingOrderDeepLink: OrderRouteContext? = nil
|
||||
}
|
||||
|
||||
enum FeatureFlagValue: Codable, Equatable {
|
||||
case boolean(Bool)
|
||||
case text(String)
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
if let boolValue = try? container.decode(Bool.self) {
|
||||
self = .boolean(boolValue)
|
||||
return
|
||||
}
|
||||
if let stringValue = try? container.decode(String.self) {
|
||||
self = .text(stringValue)
|
||||
return
|
||||
}
|
||||
if let intValue = try? container.decode(Int.self) {
|
||||
self = .text(String(intValue))
|
||||
return
|
||||
}
|
||||
if let doubleValue = try? container.decode(Double.self) {
|
||||
self = .text(String(doubleValue))
|
||||
return
|
||||
}
|
||||
self = .text("off")
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
switch self {
|
||||
case .boolean(let value):
|
||||
try container.encode(value)
|
||||
case .text(let value):
|
||||
try container.encode(value)
|
||||
}
|
||||
}
|
||||
|
||||
var boolValue: Bool {
|
||||
switch self {
|
||||
case .boolean(let value):
|
||||
return value
|
||||
case .text(let value):
|
||||
return value.lowercased() == "on" || value.lowercased() == "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FeatureFlagsState: Codable, Equatable {
|
||||
var configVersion: Int = 0
|
||||
var evaluatedAt: String? = nil
|
||||
var source: String = "default"
|
||||
var values: [String: FeatureFlagValue] = [:]
|
||||
var raw: [String: FeatureControlRawFlag] = [:]
|
||||
|
||||
func isEnabled(_ key: String, default defaultValue: Bool = false) -> Bool {
|
||||
if let rawValue = raw[key] {
|
||||
return rawValue.enabled || rawValue.variant.lowercased() == "on"
|
||||
}
|
||||
if let mapped = values[key] {
|
||||
return mapped.boolValue
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
enum AppModal: String, Identifiable {
|
||||
case addressPicker
|
||||
case filters
|
||||
|
||||
var id: String { rawValue }
|
||||
}
|
||||
|
||||
struct SessionState {
|
||||
var isAuthenticated: Bool = false
|
||||
var jwt: String? = nil
|
||||
}
|
||||
|
||||
struct ProfileState {
|
||||
var id: String? = nil
|
||||
var name: String = ""
|
||||
var email: String = ""
|
||||
var phone: String = ""
|
||||
var profilePicture: String = ""
|
||||
var cpf: String = ""
|
||||
var notificationsEnabled: Bool = false
|
||||
var faceIdEnabled: Bool = false
|
||||
}
|
||||
|
||||
struct AddressState {
|
||||
var selectedId: String? = nil
|
||||
var display: String = "Defina seu endereco"
|
||||
var latitude: Double? = nil
|
||||
var longitude: Double? = nil
|
||||
var onboardingMessage: String? = nil
|
||||
}
|
||||
|
||||
struct FavoritesState {
|
||||
var storeIds: Set<String> = []
|
||||
}
|
||||
|
||||
enum HomeSortOption: String, CaseIterable, Identifiable {
|
||||
case relevance
|
||||
case rating
|
||||
case deliveryTime
|
||||
case price
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .relevance: return "Relevância"
|
||||
case .rating: return "Avaliação"
|
||||
case .deliveryTime: return "Tempo de entrega"
|
||||
case .price: return "Preço"
|
||||
}
|
||||
}
|
||||
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .relevance: return "checkmark.seal.fill"
|
||||
case .rating: return "star.fill"
|
||||
case .deliveryTime: return "clock.fill"
|
||||
case .price: return "dollarsign"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum HomePriceTier: String, CaseIterable, Identifiable {
|
||||
case low = "$"
|
||||
case medium = "$$"
|
||||
case high = "$$$"
|
||||
case veryHigh = "$$$$"
|
||||
|
||||
var id: String { rawValue }
|
||||
}
|
||||
|
||||
struct HomeFiltersState {
|
||||
var sortOption: HomeSortOption = .relevance
|
||||
var selectedCategories: Set<String> = []
|
||||
var selectedPriceTier: HomePriceTier? = nil
|
||||
var maxDistanceKm: Double = 10
|
||||
var availableCategories: [String] = []
|
||||
|
||||
mutating func reset() {
|
||||
sortOption = .relevance
|
||||
selectedCategories = []
|
||||
selectedPriceTier = nil
|
||||
maxDistanceKm = 10
|
||||
}
|
||||
}
|
||||
|
||||
struct CartState {
|
||||
var storeId: String? = nil
|
||||
var storeName: String? = nil
|
||||
var items: [CartItemState] = []
|
||||
var total: Double = 0
|
||||
}
|
||||
|
||||
struct CartItemState: Identifiable {
|
||||
let id: String
|
||||
var productId: String
|
||||
var storeId: String
|
||||
var name: String
|
||||
var imageURL: String? = nil
|
||||
var details: String? = nil
|
||||
var choices: [String]? = nil
|
||||
var addons: [CartItemAddonState] = []
|
||||
var quantity: Int
|
||||
var unitPrice: Double
|
||||
}
|
||||
|
||||
struct CartItemAddonState: Identifiable, Hashable {
|
||||
let id: String
|
||||
var name: String
|
||||
var quantity: Int
|
||||
var unitPrice: Double
|
||||
}
|
||||
|
||||
extension CartState {
|
||||
var totalItems: Int {
|
||||
items.reduce(0) { $0 + $1.quantity }
|
||||
}
|
||||
|
||||
mutating func recalculateTotal() {
|
||||
total = items.reduce(0) { $0 + (Double($1.quantity) * $1.unitPrice) }
|
||||
}
|
||||
|
||||
mutating func clear() {
|
||||
storeId = nil
|
||||
storeName = nil
|
||||
items = []
|
||||
total = 0
|
||||
SessionStateStore.clearCart()
|
||||
}
|
||||
|
||||
mutating func add(item: CartItemState) {
|
||||
if let index = items.firstIndex(where: { $0.id == item.id }) {
|
||||
items[index].quantity += item.quantity
|
||||
} else {
|
||||
items.append(item)
|
||||
}
|
||||
recalculateTotal()
|
||||
SessionStateStore.saveCart(self)
|
||||
}
|
||||
|
||||
mutating func set(item: CartItemState) {
|
||||
if let index = items.firstIndex(where: { $0.id == item.id }) {
|
||||
if item.quantity <= 0 {
|
||||
items.remove(at: index)
|
||||
} else {
|
||||
items[index] = item
|
||||
}
|
||||
} else if item.quantity > 0 {
|
||||
items.append(item)
|
||||
}
|
||||
|
||||
if items.isEmpty {
|
||||
storeId = nil
|
||||
storeName = nil
|
||||
}
|
||||
recalculateTotal()
|
||||
if items.isEmpty {
|
||||
SessionStateStore.clearCart()
|
||||
} else {
|
||||
SessionStateStore.saveCart(self)
|
||||
}
|
||||
}
|
||||
|
||||
mutating func increment(itemId: String) {
|
||||
guard let index = items.firstIndex(where: { $0.id == itemId }) else { return }
|
||||
items[index].quantity += 1
|
||||
recalculateTotal()
|
||||
SessionStateStore.saveCart(self)
|
||||
}
|
||||
|
||||
mutating func decrement(itemId: String) {
|
||||
guard let index = items.firstIndex(where: { $0.id == itemId }) else { return }
|
||||
items[index].quantity -= 1
|
||||
if items[index].quantity <= 0 {
|
||||
items.remove(at: index)
|
||||
}
|
||||
if items.isEmpty {
|
||||
storeId = nil
|
||||
storeName = nil
|
||||
}
|
||||
recalculateTotal()
|
||||
if items.isEmpty {
|
||||
SessionStateStore.clearCart()
|
||||
} else {
|
||||
SessionStateStore.saveCart(self)
|
||||
}
|
||||
}
|
||||
|
||||
func toOrderItemsPayload() -> [CreateOrderItemPayload] {
|
||||
items.map { item in
|
||||
CreateOrderItemPayload(
|
||||
productId: item.productId,
|
||||
name: item.name,
|
||||
qty: item.quantity,
|
||||
price: item.unitPrice,
|
||||
addons: item.addons
|
||||
.filter { $0.quantity > 0 }
|
||||
.map {
|
||||
CreateOrderAddonPayload(
|
||||
addonId: $0.id,
|
||||
name: $0.name,
|
||||
qty: $0.quantity,
|
||||
price: $0.unitPrice
|
||||
)
|
||||
},
|
||||
choices: item.choices?.isEmpty == false ? item.choices : nil
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user