payfrit-food-ios/PayfritFood/Views/AlternativesTab/SponsoredCard.swift
John Pinkyfloyd 71e7ec34f6 Initial commit: PayfritFood iOS app
- SwiftUI + async/await architecture
- Barcode scanning with AVFoundation
- Product display with score ring, NOVA badge, nutrition
- Alternatives with sort/filter
- Auth (login/register)
- Favorites & history
- Account management
- Dark theme
- Connected to food.payfrit.com API (Open Food Facts proxy)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-16 16:58:21 -07:00

142 lines
4.6 KiB
Swift

import SwiftUI
struct SponsoredCard: View {
let alternative: Alternative
var body: some View {
VStack(alignment: .leading, spacing: 12) {
// Sponsored label
HStack {
Text("Sponsored")
.font(.caption2.bold())
.foregroundColor(.secondary)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color(.systemGray5))
.cornerRadius(4)
Spacer()
}
HStack(spacing: 12) {
// Product Image
if let imageURL = alternative.product.imageURL {
AsyncImage(url: imageURL) { phase in
switch phase {
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
case .failure:
imagePlaceholder
default:
ProgressView()
}
}
.frame(width: 80, height: 80)
.background(Color(.systemGray6))
.cornerRadius(12)
.clipped()
} else {
imagePlaceholder
.frame(width: 80, height: 80)
}
// Product Info
VStack(alignment: .leading, spacing: 6) {
Text(alternative.product.brand)
.font(.caption)
.foregroundColor(.secondary)
Text(alternative.product.name)
.font(.subheadline.bold())
.lineLimit(2)
HStack(spacing: 12) {
// Score
HStack(spacing: 4) {
Circle()
.fill(scoreColor(alternative.product.score))
.frame(width: 10, height: 10)
Text("\(alternative.product.score)")
.font(.caption.bold())
}
// Price
if let price = alternative.formattedPrice {
Text(price)
.font(.caption.bold())
.foregroundColor(.green)
}
}
}
Spacer()
}
// Action Buttons
HStack(spacing: 8) {
if let deliveryUrl = alternative.deliveryUrl, let url = URL(string: deliveryUrl) {
Link(destination: url) {
ActionButton(icon: "truck.box.fill", label: "Delivery")
}
}
if let pickupUrl = alternative.pickupUrl, let url = URL(string: pickupUrl) {
Link(destination: url) {
ActionButton(icon: "building.2.fill", label: "Pickup")
}
}
if let buyUrl = alternative.buyUrl, let url = URL(string: buyUrl) {
Link(destination: url) {
ActionButton(icon: "cart.fill", label: "Buy")
}
}
}
}
.padding()
.background(Color(.secondarySystemBackground))
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.green.opacity(0.3), lineWidth: 1)
)
.cornerRadius(16)
}
private var imagePlaceholder: some View {
ZStack {
Color(.systemGray5)
Image(systemName: "carrot.fill")
.foregroundColor(.gray)
}
.cornerRadius(12)
}
private func scoreColor(_ score: Int) -> Color {
switch score {
case 80...100: return .green
case 50..<80: return .yellow
case 30..<50: return .orange
default: return .red
}
}
}
struct ActionButton: View {
let icon: String
let label: String
var body: some View {
HStack(spacing: 4) {
Image(systemName: icon)
.font(.caption)
Text(label)
.font(.caption.bold())
}
.foregroundColor(.green)
.padding(.horizontal, 12)
.padding(.vertical, 8)
.background(Color.green.opacity(0.15))
.cornerRadius(8)
}
}