diff --git a/PayfritWorks.xcodeproj/project.pbxproj b/PayfritWorks.xcodeproj/project.pbxproj index 8fe1771..0405281 100644 --- a/PayfritWorks.xcodeproj/project.pbxproj +++ b/PayfritWorks.xcodeproj/project.pbxproj @@ -441,7 +441,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_TEAM = U83YL8VRF3; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = PayfritWorks/Info.plist; @@ -456,8 +456,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.payfrit.works; + MARKETING_VERSION = 1.0.2; + PRODUCT_BUNDLE_IDENTIFIER = com.mizerek.payfritworks; PRODUCT_NAME = "Payfrit Works"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -473,7 +473,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_TEAM = U83YL8VRF3; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = PayfritWorks/Info.plist; @@ -488,8 +488,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.payfrit.works; + MARKETING_VERSION = 1.0.2; + PRODUCT_BUNDLE_IDENTIFIER = com.mizerek.payfritworks; PRODUCT_NAME = "Payfrit Works"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; diff --git a/PayfritWorks/Views/AboutScreen.swift b/PayfritWorks/Views/AboutScreen.swift index 29c1a21..49b402e 100644 --- a/PayfritWorks/Views/AboutScreen.swift +++ b/PayfritWorks/Views/AboutScreen.swift @@ -3,6 +3,28 @@ import SwiftUI struct AboutScreen: View { @Environment(\.dismiss) private var dismiss @State private var appearAnimation = false + @State private var aboutInfo: AboutInfo? + @State private var isLoading = true + @State private var error: String? + + // Fallback content if API fails + private let fallbackInfo = AboutInfo( + description: "Payfrit Works helps you manage tasks, accept cash payments, and earn money. Get notified of new tasks, complete them efficiently, and track your earnings.", + features: [ + AboutFeature(icon: "checkmark.circle", title: "Claim Tasks", description: "View available tasks from businesses you work for and claim them with one tap."), + AboutFeature(icon: "dollarsign.circle", title: "Accept Cash", description: "Collect cash payments from customers with automatic change calculation."), + AboutFeature(icon: "antenna.radiowaves.left.and.right", title: "Beacon Auto-Complete", description: "Tasks complete automatically when you're near the customer's table beacon."), + AboutFeature(icon: "creditcard", title: "Earn & Get Paid", description: "Track your earnings in real-time and receive payouts to your bank account.") + ], + contacts: [ + AboutContact(icon: "globe", label: "Website", url: "https://www.payfrit.com") + ], + copyright: "© 2026 Payfrit. All rights reserved." + ) + + private var displayInfo: AboutInfo { + aboutInfo ?? fallbackInfo + } var body: some View { ScrollView { @@ -19,7 +41,7 @@ struct AboutScreen: View { .fontWeight(.bold) .foregroundColor(.primary) - Text("Version 1.0.0") + Text("Version \(appVersion)") .font(.caption) .foregroundColor(.secondary) } @@ -28,88 +50,57 @@ struct AboutScreen: View { .scaleEffect(appearAnimation ? 1 : 0.9) .opacity(appearAnimation ? 1 : 0) - // Intro - Text("Payfrit Works helps you manage tasks, accept cash payments, and earn money. Get notified of new tasks, complete them efficiently, and track your earnings.") - .font(.caption) - .foregroundColor(.secondary) - .multilineTextAlignment(.leading) - .padding(.horizontal, 16) - .opacity(appearAnimation ? 1 : 0) - .offset(y: appearAnimation ? 0 : 15) + if isLoading { + ProgressView() + .padding(.vertical, 20) + } else { + // Description + if !displayInfo.description.isEmpty { + Text(displayInfo.description) + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.leading) + .padding(.horizontal, 16) + .opacity(appearAnimation ? 1 : 0) + .offset(y: appearAnimation ? 0 : 15) + } - // Feature cards - VStack(spacing: 12) { - featureCard( - icon: "checkmark.circle", - title: "Claim Tasks", - description: "View available tasks from businesses you work for and claim them with one tap." - ) - .staggeredAppear(index: 0, appeared: appearAnimation) - - featureCard( - icon: "dollarsign.circle", - title: "Accept Cash", - description: "Collect cash payments from customers with automatic change calculation." - ) - .staggeredAppear(index: 1, appeared: appearAnimation) - - featureCard( - icon: "antenna.radiowaves.left.and.right", - title: "Beacon Auto-Complete", - description: "Tasks complete automatically when you're near the customer's table beacon." - ) - .staggeredAppear(index: 2, appeared: appearAnimation) - - featureCard( - icon: "creditcard", - title: "Earn & Get Paid", - description: "Track your earnings in real-time and receive payouts to your bank account." - ) - .staggeredAppear(index: 3, appeared: appearAnimation) - } - .padding(.horizontal, 16) - - // Links - VStack(spacing: 8) { - Link(destination: URL(string: "https://www.payfrit.com")!) { - HStack(spacing: 10) { - Image(systemName: "globe") - .font(.subheadline) - .foregroundColor(.payfritGreen) - .frame(width: 28, height: 28) - .background(Color.payfritGreen.opacity(0.1)) - .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous)) - - VStack(alignment: .leading, spacing: 2) { - Text("Website") - .font(.caption) - .fontWeight(.medium) - .foregroundColor(.primary) - Text("www.payfrit.com") - .font(.caption2) - .foregroundColor(.secondary) - } - - Spacer() - - Image(systemName: "arrow.up.right") - .font(.caption2) - .foregroundColor(.secondary) + // Feature cards + VStack(spacing: 12) { + ForEach(Array(displayInfo.features.enumerated()), id: \.offset) { index, feature in + featureCard( + icon: mapIconName(feature.icon), + title: feature.title, + description: feature.description + ) + .staggeredAppear(index: index, appeared: appearAnimation) } - .padding(12) - .background(Color(.secondarySystemGroupedBackground)) - .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) - .contentShape(Rectangle()) + } + .padding(.horizontal, 16) + + // Contact links + if !displayInfo.contacts.isEmpty { + VStack(spacing: 8) { + ForEach(Array(displayInfo.contacts.enumerated()), id: \.offset) { index, contact in + if let url = URL(string: contact.url) { + Link(destination: url) { + contactRow(contact: contact) + } + } + } + } + .padding(.horizontal, 16) + .staggeredAppear(index: displayInfo.features.count, appeared: appearAnimation) + } + + // Copyright + if !displayInfo.copyright.isEmpty { + Text(displayInfo.copyright) + .font(.caption2) + .foregroundColor(.secondary) + .padding(.top, 4) } } - .padding(.horizontal, 16) - .staggeredAppear(index: 4, appeared: appearAnimation) - - // Copyright - Text("\u{00A9} 2026 Payfrit. All rights reserved.") - .font(.caption2) - .foregroundColor(.secondary) - .padding(.top, 4) Spacer() .frame(height: 20) @@ -118,6 +109,9 @@ struct AboutScreen: View { .background(Color(.systemGroupedBackground).ignoresSafeArea()) .navigationTitle("About Payfrit") .navigationBarTitleDisplayMode(.inline) + .task { + await loadAboutInfo() + } .onAppear { withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) { appearAnimation = true @@ -125,6 +119,55 @@ struct AboutScreen: View { } } + private var appVersion: String { + Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0" + } + + private func loadAboutInfo() async { + do { + aboutInfo = try await APIService.shared.getAboutInfo() + } catch { + // Use fallback on error + print("Failed to load about info: \(error)") + } + isLoading = false + } + + /// Map icon names from API to SF Symbols + private func mapIconName(_ apiIcon: String) -> String { + // If it's already an SF Symbol name, use it + if UIImage(systemName: apiIcon) != nil { + return apiIcon + } + // Map common icon names + switch apiIcon.lowercased() { + case "beacon", "radar", "walkup": + return "wave.3.right" + case "group", "friends", "people": + return "person.3" + case "delivery", "bag", "takeaway": + return "bag" + case "payment", "card", "credit": + return "creditcard" + case "globe", "web", "website": + return "globe" + case "email", "mail": + return "envelope" + case "phone", "call": + return "phone" + case "chat", "message": + return "message" + case "task", "tasks", "check": + return "checkmark.circle" + case "cash", "money", "dollar": + return "dollarsign.circle" + case "earn", "payout": + return "creditcard" + default: + return "star" + } + } + private func featureCard(icon: String, title: String, description: String) -> some View { HStack(spacing: 10) { Image(systemName: icon) @@ -151,6 +194,37 @@ struct AboutScreen: View { .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) .contentShape(Rectangle()) } + + private func contactRow(contact: AboutContact) -> some View { + HStack(spacing: 10) { + Image(systemName: mapIconName(contact.icon)) + .font(.subheadline) + .foregroundColor(.payfritGreen) + .frame(width: 28, height: 28) + .background(Color.payfritGreen.opacity(0.1)) + .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous)) + + VStack(alignment: .leading, spacing: 2) { + Text(contact.label) + .font(.caption) + .fontWeight(.medium) + .foregroundColor(.primary) + Text(contact.url.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "http://", with: "")) + .font(.caption2) + .foregroundColor(.secondary) + } + + Spacer() + + Image(systemName: "arrow.up.right") + .font(.caption2) + .foregroundColor(.secondary) + } + .padding(12) + .background(Color(.secondarySystemGroupedBackground)) + .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) + .contentShape(Rectangle()) + } } // MARK: - Staggered Appear Modifier diff --git a/PayfritWorks/Views/TaskDetailScreen.swift b/PayfritWorks/Views/TaskDetailScreen.swift index 1cfaebd..693ddbf 100644 --- a/PayfritWorks/Views/TaskDetailScreen.swift +++ b/PayfritWorks/Views/TaskDetailScreen.swift @@ -183,6 +183,7 @@ struct TaskDetailScreen: View { let fromDetails = details?.orderTotal ?? 0 let fromTask = task.orderTotal let cents = fromDetails > 0 ? fromDetails : fromTask + NSLog("[Cash] orderTotal fromDetails=%.2f, fromTask=%.2f, using=%.2f, asInt=%d", fromDetails, fromTask, cents, Int(cents)) return Int(cents) }