migration
This commit is contained in:
173
PediFoods/Components/StoreCard.swift
Normal file
173
PediFoods/Components/StoreCard.swift
Normal file
@@ -0,0 +1,173 @@
|
||||
import SwiftUI
|
||||
|
||||
struct FeaturedStoreCard: View {
|
||||
let store: FeaturedStoreCardModel
|
||||
var onFavoriteToggle: (() -> Void)? = nil
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
ZStack(alignment: .topLeading) {
|
||||
mediaBlock
|
||||
|
||||
if let promo = store.promoText {
|
||||
Text(promo)
|
||||
.font(AppTypography.caption)
|
||||
.foregroundStyle(AppColors.textInverse)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 6)
|
||||
.background(Color.red)
|
||||
.clipShape(Capsule())
|
||||
.padding(10)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
Button {
|
||||
onFavoriteToggle?()
|
||||
} label: {
|
||||
Image(systemName: store.isFavorite ? "heart.fill" : "heart")
|
||||
.foregroundStyle(store.isFavorite ? Color.red : AppColors.textMuted)
|
||||
.padding(8)
|
||||
.background(AppColors.surface)
|
||||
.clipShape(Circle())
|
||||
.shadow(color: AppShadow.soft.color, radius: AppShadow.soft.radius, y: AppShadow.soft.y)
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.accessibilityLabel(store.isFavorite ? "Remover loja dos favoritos" : "Adicionar loja aos favoritos")
|
||||
.padding(10)
|
||||
}
|
||||
}
|
||||
|
||||
Text(store.name)
|
||||
.font(AppTypography.heading3)
|
||||
.foregroundStyle(AppColors.textPrimary)
|
||||
|
||||
if store.isOpen {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "star.fill")
|
||||
.font(.caption)
|
||||
.foregroundStyle(Color(hex: "#F5B335"))
|
||||
Text(String(format: "%.1f", store.rating))
|
||||
.font(.caption)
|
||||
.foregroundStyle(AppColors.textPrimary)
|
||||
Text("(\(store.reviews))")
|
||||
.font(.caption)
|
||||
.foregroundStyle(AppColors.textMuted)
|
||||
Text("·")
|
||||
.font(.caption)
|
||||
.foregroundStyle(AppColors.textMuted)
|
||||
Text(store.distance)
|
||||
.font(.caption)
|
||||
.foregroundStyle(AppColors.textMuted)
|
||||
}
|
||||
} else {
|
||||
Text(store.statusLabel?.isEmpty == false ? (store.statusLabel ?? "Fechado") : "Fechado")
|
||||
.font(AppTypography.heading3)
|
||||
.foregroundStyle(AppColors.textPrimary)
|
||||
}
|
||||
|
||||
Text(store.category)
|
||||
.font(.caption)
|
||||
.foregroundStyle(AppColors.textMuted)
|
||||
}
|
||||
.padding(14)
|
||||
.background(AppColors.surface)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusXL, style: .continuous))
|
||||
.saturation(store.isOpen ? 1 : 0)
|
||||
.opacity(store.isOpen ? 1 : 0.9)
|
||||
}
|
||||
|
||||
private var mediaBlock: some View {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)
|
||||
.fill(AppColors.brandSoft)
|
||||
|
||||
mediaImage
|
||||
}
|
||||
.frame(height: 120)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous))
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var mediaImage: some View {
|
||||
AsyncStoreImage(imageURL: store.imageURL)
|
||||
}
|
||||
|
||||
private var storeIconPlaceholder: some View {
|
||||
ZStack {
|
||||
Image("placeholder-product")
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.opacity(0.7)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.clipped()
|
||||
|
||||
Circle()
|
||||
.fill(AppColors.surface.opacity(0.92))
|
||||
.frame(width: 64, height: 64)
|
||||
.overlay(
|
||||
Image(systemName: store.iconName)
|
||||
.font(.title2)
|
||||
.foregroundStyle(AppColors.primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FeaturedStoreCardModel: Identifiable {
|
||||
let id: String
|
||||
let name: String
|
||||
let rating: Double
|
||||
let reviews: String
|
||||
let distance: String
|
||||
let deliveryFee: Double?
|
||||
let category: String
|
||||
let promoText: String?
|
||||
let isFavorite: Bool
|
||||
let iconName: String
|
||||
let imageURL: String?
|
||||
let logoURL: String?
|
||||
let coverURL: String?
|
||||
let isOpen: Bool
|
||||
let statusLabel: String?
|
||||
}
|
||||
|
||||
struct SpecialOfferCard: View {
|
||||
let model: SpecialOfferCardModel
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .leading) {
|
||||
RoundedRectangle(cornerRadius: AppLayout.radiusXL, style: .continuous)
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: model.colors,
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
)
|
||||
|
||||
Circle()
|
||||
.fill(Color.white.opacity(0.18))
|
||||
.frame(width: 120, height: 120)
|
||||
.offset(x: 140, y: 10)
|
||||
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(model.title)
|
||||
.font(AppTypography.heading2)
|
||||
.foregroundStyle(AppColors.textInverse)
|
||||
Text(model.subtitle)
|
||||
.font(AppTypography.body)
|
||||
.foregroundStyle(AppColors.textInverse.opacity(0.85))
|
||||
}
|
||||
.padding(20)
|
||||
}
|
||||
.shadow(color: AppShadow.soft.color, radius: AppShadow.soft.radius, y: AppShadow.soft.y)
|
||||
}
|
||||
}
|
||||
|
||||
struct SpecialOfferCardModel: Identifiable {
|
||||
let id: String
|
||||
let title: String
|
||||
let subtitle: String
|
||||
let colors: [Color]
|
||||
}
|
||||
Reference in New Issue
Block a user