Manos Chorianopoulos

add ProfileViewController dynamic coupons data

{
"images" : [
{
"filename" : "barcode_2.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "barcode_2 1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "barcode_2 2.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -32,6 +32,40 @@ public class ProfileCouponTableViewCell: UITableViewCell {
}
// MARK: - Image Loading Helpers
private var bannerImageURL: String? {
didSet {
if let url = bannerImageURL, !url.isEmpty {
self.bannerImage.image = UIImage()
UIImage.loadImageUsingCacheWithUrlString(url) { [weak self] image in
if url == self?.bannerImageURL {
self?.bannerImage.image = image
}
}
} else {
self.bannerImage.image = nil
}
}
}
private var logoImageURL: String? {
didSet {
if let url = logoImageURL, !url.isEmpty {
self.logoImage.image = UIImage()
UIImage.loadImageUsingCacheWithUrlString(url) { [weak self] image in
if url == self?.logoImageURL {
self?.logoImage.image = image
}
}
} else {
self.logoImage.image = nil
}
}
}
// MARK: - Configure with OfferModel (legacy, kept for backward compatibility)
func configureCell(data: OfferModel) {
bannerImage.image = UIImage(named: data.bannerImage, in: Bundle.frameworkResourceBundle, compatibleWith: nil)
favoriteImage.image = UIImage(named: data.isFavorite ? "favorite_filled" : "favorite_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
......@@ -64,6 +98,68 @@ public class ProfileCouponTableViewCell: UITableViewCell {
logoImage.image = UIImage(named: data.merchantLogo, in: Bundle.frameworkResourceBundle, compatibleWith: nil)
}
// MARK: - Configure with CouponItemModel (dynamic data)
func configureCell(data: CouponItemModel) {
// Banner image — load from couponset_data img_preview (remote URL)
if let imgPreview = data.couponset_data?._img_preview, !imgPreview.isEmpty {
self.bannerImageURL = imgPreview
} else {
bannerImage.image = nil
}
// Favorite — default to not favorite for now
favoriteImage.image = UIImage(named: "favorite_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
// Discount label — use coupon discount or couponset discount
let discountText = data.discount ?? data.couponset_data?._discount ?? ""
discountLabel.text = discountText
discountLabel.font = UIFont(name: "PingLCG-Bold", size: 25)
discountLabel.textColor = UIColor(rgb: 0xF2F2F2)
// Discount view color based on discount type
let discountType = data.couponset_data?._discount_type ?? ""
let discountColor: UInt = {
switch discountType {
case "percentage":
return 0xFF6B35
case "value":
return 0x28A745
case "plus_one":
return 0x007AFF
default:
return 0xEE417D
}
}()
discountView.backgroundColor = UIColor(rgb: discountColor)
// Title — from couponset_data name
titleLabel.text = data.couponset_data?._name ?? ""
titleLabel.font = UIFont(name: "PingLCG-Bold", size: 22)
titleLabel.textColor = UIColor(rgb: 0x000F1E)
// Subtitle — from couponset_data short_description
subtitleLabel.text = data.couponset_data?._short_description ?? ""
subtitleLabel.font = UIFont(name: "PingLCG-Regular", size: 16)
subtitleLabel.textColor = UIColor(rgb: 0x00111B)
// Expiration — already formatted as "dd/MM/yyyy" by CouponItemModel
if let expiration = data.expiration, !expiration.isEmpty {
expirationLabel.text = "έως " + expiration
} else {
expirationLabel.text = ""
}
expirationLabel.font = UIFont(name: "PingLCG-Regular", size: 13)
expirationLabel.textColor = UIColor(rgb: 0x00111B)
// Logo — load from merchant_details img_preview (remote URL)
if let merchantImgPreview = data.merchant_details?._img_preview, !merchantImgPreview.isEmpty {
self.logoImageURL = merchantImgPreview
} else {
logoImage.image = nil
}
}
public override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
......
......@@ -81,6 +81,32 @@ public class CouponSetItemModel {
// Bound merchant data for performance
private var merchant: MerchantModel?
// MARK: - Multi-Format Date Parsing Helper
/// Supported date input formats for parsing dates from various API responses
private static let supportedDateFormats = [
"yyyy-MM-dd HH:mm:ss", // "2027-01-01 03:21:00"
"yyyy-MM-dd'T'HH:mm:ss", // "2027-01-01T15:00:00" (ISO 8601)
"yyyy-MM-dd HH:mm", // "2026-06-30 11:59"
"yyyy-MM-dd'T'HH:mm:ssZZZZZ", // "2027-01-01T15:00:00+03:00" (ISO 8601 with timezone)
"yyyy-MM-dd HH:mm:ss.SSSSSS", // "2022-08-04T14:06:31.110522"
"yyyy-MM-dd'T'HH:mm:ss.SSSSSS" // "2022-08-04T14:06:31.110522" (ISO 8601 with microseconds)
]
/// Try multiple date formats and return the first successful parse
/// - Parameter dateString: The date string to parse
/// - Returns: Parsed Date or nil if no format matched
private static func parseDate(_ dateString: String) -> Date? {
let formatter = DateFormatter()
for format in supportedDateFormats {
formatter.dateFormat = format
if let date = formatter.date(from: dateString) {
return date
}
}
return nil
}
public init(dictionary: [String: Any]) {
// Existing fields
self.uuid = dictionary["uuid"] as? String? ?? ""
......@@ -253,18 +279,17 @@ public class CouponSetItemModel {
return ""
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
if let date = dateFormatter.date(from: expiration) {
dateFormatter.dateFormat = "dd/MM/yyyy"
return dateFormatter.string(from: date)
if let date = CouponSetItemModel.parseDate(expiration) {
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = "dd/MM/yyyy"
return outputFormatter.string(from: date)
}
return ""
}
/// Format expiration date with custom format
/// Supports multiple input formats: "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd HH:mm", etc.
/// - Parameter format: DateFormatter format string (e.g., "dd-MM", "dd/MM/yyyy")
/// - Returns: Formatted date string or empty string if invalid
public func formattedExpiration(format: String) -> String {
......@@ -272,10 +297,7 @@ public class CouponSetItemModel {
return ""
}
let inputFormatter = DateFormatter()
inputFormatter.dateFormat = "yyyy-MM-dd HH:mm"
if let date = inputFormatter.date(from: expiration) {
if let date = CouponSetItemModel.parseDate(expiration) {
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = format
return outputFormatter.string(from: date)
......@@ -285,6 +307,7 @@ public class CouponSetItemModel {
}
/// Format start date with custom format
/// Supports multiple input formats: "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss", etc.
/// - Parameter format: DateFormatter format string (e.g., "dd/MM/yyyy", "MMM yyyy", "dd-MM")
/// - Returns: Formatted date string or empty string if invalid
public func formattedStartDate(format: String) -> String {
......@@ -292,10 +315,7 @@ public class CouponSetItemModel {
return ""
}
let inputFormatter = DateFormatter()
inputFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
if let date = inputFormatter.date(from: startDate) {
if let date = CouponSetItemModel.parseDate(startDate) {
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = format
return outputFormatter.string(from: date)
......@@ -305,6 +325,7 @@ public class CouponSetItemModel {
}
/// Format end date with custom format
/// Supports multiple input formats: "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss", etc.
/// - Parameter format: DateFormatter format string (e.g., "dd/MM/yyyy", "MMM yyyy", "dd-MM")
/// - Returns: Formatted date string or empty string if invalid
public func formattedEndDate(format: String) -> String {
......@@ -312,10 +333,7 @@ public class CouponSetItemModel {
return ""
}
let inputFormatter = DateFormatter()
inputFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
if let date = inputFormatter.date(from: endDate) {
if let date = CouponSetItemModel.parseDate(endDate) {
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = format
return outputFormatter.string(from: date)
......@@ -488,14 +506,36 @@ public class CouponItemModel {
self.expiration = ""
}
let createdString = dictionary["created"] as? String? ?? ""
let dateFormatter2 = DateFormatter()
dateFormatter2.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSSSS"
if let date = dateFormatter2.date(from: createdString ?? "") {
dateFormatter2.dateFormat = "dd/MM/yyyy"
let resultString = dateFormatter2.string(from: date)
self.created = resultString
// Extract created date: try changes_dates.created first (universal coupons), then top-level created
let createdString: String?
if let changes_dates = dictionary["changes_dates"] as? [String: Any],
let changesCreated = changes_dates["created"] as? String, !changesCreated.isEmpty {
createdString = changesCreated
} else {
createdString = dictionary["created"] as? String? ?? ""
}
let dateFormatter2 = DateFormatter()
// Try multiple date formats for created date
let createdFormats = [
"yyyy-MM-dd HH:mm:ss.SSSSSS", // "2026-03-02 12:01:08.365179"
"yyyy-MM-dd HH:mm:ss", // "2026-03-02 12:01:08"
"yyyy-MM-dd'T'HH:mm:ss.SSSSSS", // ISO 8601 with microseconds
"yyyy-MM-dd'T'HH:mm:ss" // ISO 8601
]
var createdParsed = false
for format in createdFormats {
dateFormatter2.dateFormat = format
if let date = dateFormatter2.date(from: createdString ?? "") {
dateFormatter2.dateFormat = "dd/MM/yyyy"
let resultString = dateFormatter2.string(from: date)
self.created = resultString
createdParsed = true
break
}
}
if !createdParsed {
self.created = ""
}
......
......@@ -80,7 +80,7 @@ import UIKit
@IBOutlet weak var websiteButton: UIButton!
var coupon: OfferModel?
var coupon: CouponItemModel?
private var isDetailsExpanded = false
private var isCouponCodeExpanded = false
private var isCouponQRExpanded = false
......@@ -104,7 +104,7 @@ import UIKit
couponCodeArrowImage.image = UIImage(named: "arrow_down", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
copyButtonImage.image = UIImage(named: "copy", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
couponQRArrowImage.image = UIImage(named: "arrow_down", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
couponQRImage.image = UIImage(named: "barcode", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
couponQRImage.image = UIImage(named: "barcode_2", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
termsButtonArrowImage.image = UIImage(named: "arrow_down", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
infoLabel.font = UIFont(name: "PingLCG-Regular", size: 13)
......@@ -147,54 +147,101 @@ import UIKit
websiteButton.layer.borderColor = UIColor(rgb: 0x000F1E).cgColor
websiteButton.layer.cornerRadius = 4.0
// Configure the view with offer data
if let offer = coupon {
setupUI(with: offer)
// Configure the view with coupon data
if let couponData = coupon {
setupUI(with: couponData)
}
}
// MARK: - Image Loading Helper
private func loadRemoteImage(_ urlString: String, into imageView: UIImageView) {
guard !urlString.isEmpty else {
imageView.image = nil
return
}
imageView.image = UIImage()
UIImage.loadImageUsingCacheWithUrlString(urlString) { image in
imageView.image = image
}
}
private func setupUI(with coupon: OfferModel) {
couponImage.image = UIImage(named: coupon.bannerImage, in: Bundle.frameworkResourceBundle, compatibleWith: nil)
favoriteImage.image = UIImage(named: coupon.isFavorite ? "favorite2_filled" : "favorite2_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
private func setupUI(with coupon: CouponItemModel) {
// Banner image — load from couponset_data img_preview (remote URL)
if let imgPreview = coupon.couponset_data?._img_preview, !imgPreview.isEmpty {
loadRemoteImage(imgPreview, into: couponImage)
} else {
couponImage.image = nil
}
// Favorite — default to not favorite for now
favoriteImage.image = UIImage(named: "favorite2_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
// Title — from couponset_data name
titleLabel.font = UIFont(name: "PingLCG-Bold", size: 24)
titleLabel.textColor = UIColor(rgb: 0xF2709D)
titleLabel.text = coupon.title
titleLabel.text = coupon.couponset_data?._name ?? ""
// Subtitle — from couponset_data short_description
subtitleLabel.font = UIFont(name: "PingLCG-Regular", size: 18)
subtitleLabel.textColor = UIColor(rgb: 0x020E1C)
subtitleLabel.text = coupon.description
subtitleLabel.text = coupon.couponset_data?._short_description ?? ""
// Expiration — already formatted as "dd/MM/yyyy" by CouponItemModel
expirationLabel.font = UIFont(name: "PingLCG-Regular", size: 14)
expirationLabel.textColor = UIColor(rgb: 0x020E1C)
// expirationLabel.text = ("Η προσφορά ισχύει " + coupon.expirationDate)
expirationLabel.text = "Η προσφορά ισχύει έως 30-09-2025"
if let expiration = coupon.expiration, !expiration.isEmpty {
expirationLabel.text = "Η προσφορά ισχύει έως " + expiration
} else {
expirationLabel.text = ""
}
setupExpandableDetails()
// Description — from couponset_data description
setupExpandableDetails(with: coupon)
// Coupon Code section
couponCodeTitleLabel.font = UIFont(name: "PingLCG-Regular", size: 16)
couponCodeTitleLabel.textColor = UIColor(rgb: 0x000F1E)
couponCodeTitleLabel.text = "Κωδικός Κουπονιού"
couponCodeValueLabel.font = UIFont(name: "PingLCG-Bold", size: 24)
couponCodeValueLabel.textColor = UIColor(rgb: 0x000F1E)
couponCodeValueLabel.text = "coupons_ab"
couponCodeValueLabel.text = coupon.coupon ?? ""
copyButton.addTarget(self, action: #selector(copyButtonTapped), for: .touchUpInside)
// QR Code section
couponQRTitleLabel.font = UIFont(name: "PingLCG-Regular", size: 16)
couponQRTitleLabel.textColor = UIColor(rgb: 0x000F1E)
couponQRTitleLabel.text = "QR Κουπονιού"
// Terms — from couponset_data terms
termsLabel.font = UIFont(name: "PingLCG-Regular", size: 16)
termsLabel.textColor = UIColor(rgb: 0x020E1C)
termsLabel.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed ex euismod, feugiat justo eu, faucibus urna. Nulla sodales euismod arcu volutpat finibus. Etiam id urna at justo facilisis tempor. Morbi dignissim erat vitae magna sodales dignissim ac in mauris. Mauris tempor convallis tortor, interdum hendrerit turpis eleifend at. Praesent."
let termsText = coupon.couponset_data?._terms ?? ""
termsLabel.text = termsText.isEmpty ? "Δεν υπάρχουν διαθέσιμοι όροι χρήσης." : termsText
}
private var fullDetailsText = ""
private var shouldTruncaitDetails = false
private func setupExpandableDetails() {
fullDetailsText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed ex euismod, feugiat justo eu, faucibus urna. Nulla sodales euismod arcu volutpat finibus. Etiam id urna at justo facilisis tempor. Morbi dignissim erat vitae magna sodales dignissim ac in mauris. Mauris tempor convallis tortor, interdum hendrerit turpis eleifend at. Praesent."
private func setupExpandableDetails(with coupon: CouponItemModel? = nil) {
// Use couponset_data description if available, otherwise merchant body, or fallback
if let couponData = coupon {
let description = couponData.couponset_data?._description ?? ""
// let merchantBody = couponData.merchant_details?._body ?? ""
if !description.isEmpty {
fullDetailsText = description
}
// else if !merchantBody.isEmpty {
// fullDetailsText = merchantBody
// }
else {
fullDetailsText = ""
}
} else {
fullDetailsText = ""
}
detailsLabel.font = UIFont(name: "PingLCG-Regular", size: 18)
detailsLabel.textColor = UIColor(rgb: 0x020E1C)
......
......@@ -23,7 +23,15 @@ import UIKit
super.init(coder: coder)
}
// MARK: - Dummy Data
// MARK: - Dynamic Data
var allCoupons: [CouponItemModel] = []
// MARK: - Loading State
private var isLoading: Bool = true
private var loadingIndicator: UIActivityIndicatorView?
// MARK: - Dummy Data (commented out - replaced by dynamic allCoupons)
/*
let allOffers: [OfferModel] = [
// Προτάσεις για εσένα
OfferModel(
......@@ -184,6 +192,7 @@ import UIKit
redeemed: true
)
]
*/
let couponFilters: [CouponFilterModel] = [
CouponFilterModel(title: "Ενεργά"),
......@@ -217,7 +226,11 @@ import UIKit
tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableView.automaticDimension
initializeSections()
// Set up loading indicator
setupLoadingIndicator()
// Fetch coupons from API
fetchCoupons()
}
// NEW: Safe XIB registration method
......@@ -240,53 +253,123 @@ import UIKit
}
}
// MARK: - Loading Indicator
private func setupLoadingIndicator() {
let indicator = UIActivityIndicatorView(style: .large)
indicator.color = UIColor(rgb: 0x000F1E)
indicator.translatesAutoresizingMaskIntoConstraints = false
indicator.hidesWhenStopped = true
view.addSubview(indicator)
NSLayoutConstraint.activate([
indicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
indicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
self.loadingIndicator = indicator
}
private func showLoading() {
isLoading = true
loadingIndicator?.startAnimating()
tableView.alpha = 0.3
tableView.isUserInteractionEnabled = false
}
private func hideLoading() {
isLoading = false
loadingIndicator?.stopAnimating()
tableView.alpha = 1.0
tableView.isUserInteractionEnabled = true
}
// MARK: - Data Fetching
private func fetchCoupons() {
showLoading()
// Initialize sections with empty data first (so table view can render skeleton)
initializeSections()
WarplySDK.shared.getCouponsUniversal({ [weak self] couponsData in
guard let self = self else { return }
if let coupons = couponsData {
self.allCoupons = coupons
print("✅ [ProfileVC] Fetched \(coupons.count) coupons")
// Debug: Print coupon statuses
let activeCount = coupons.filter { $0.status == 1 }.count
let redeemedCount = coupons.filter { $0.status == 0 }.count
let expiredCount = coupons.filter { $0.status == -1 }.count
print(" Active: \(activeCount), Redeemed: \(redeemedCount), Expired: \(expiredCount)")
} else {
self.allCoupons = []
print("⚠️ [ProfileVC] No coupons received")
}
self.initializeSections()
self.hideLoading()
}, failureCallback: { [weak self] errorCode in
guard let self = self else { return }
print("❌ [ProfileVC] Failed to fetch coupons, error: \(errorCode)")
self.allCoupons = []
self.initializeSections()
self.hideLoading()
})
}
// MARK: Function
// MARK: - Section Initialization
func initializeSections() {
// Προτάσεις για εσένα
let forYouOffers = allOffers.filter { $0.category == "Προτάσεις για εσένα" }
// Προτάσεις για εσένα — empty for now
let forYouOffers: [CouponItemModel] = []
forYouOffersSection = SectionModel(
sectionType: .myRewardsHorizontalCouponsets,
title: "Προτάσεις για εσένα",
items: forYouOffers,
itemType: .offers
itemType: .coupons
)
// Active Offers
let activeOffers = allOffers.filter { $0.active ?? false }
// Active Coupons — status == 1
let activeCoupons = allCoupons.filter { $0.status == 1 }
activeOffersSection = SectionModel(
sectionType: .profileCoupon,
title: "Ενεργά",
items: activeOffers,
itemType: .offers
items: activeCoupons,
itemType: .coupons
)
// Set initial filtered section to active
filteredOffersSection = activeOffersSection
// Favorite Offers
let favoriteOffers = allOffers.filter { $0.isFavorite }
// Favorite Coupons — empty for now
let favoriteCoupons: [CouponItemModel] = []
favoriteOffersSection = SectionModel(
sectionType: .profileCoupon,
title: "Αγαπημένα",
items: favoriteOffers,
itemType: .offers
items: favoriteCoupons,
itemType: .coupons
)
// Redeemed Offers
let redeemedOffers = allOffers.filter { $0.redeemed ?? false }
// Redeemed Coupons — status == 0
let redeemedCoupons = allCoupons.filter { $0.status == 0 }
redeemedOffersSection = SectionModel(
sectionType: .profileCoupon,
title: "Εξαργυρωμένα",
items: redeemedOffers,
itemType: .offers
items: redeemedCoupons,
itemType: .coupons
)
self.tableView.reloadData()
}
private func openCouponViewController(with offer: OfferModel) {
private func openCouponViewController(with coupon: CouponItemModel) {
let vc = SwiftWarplyFramework.CouponViewController(nibName: "CouponViewController", bundle: Bundle.frameworkBundle)
vc.coupon = offer
vc.coupon = coupon
self.navigationController?.pushViewController(vc, animated: true)
}
......@@ -328,28 +411,13 @@ extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
// if (section <= 3) {
// return nil
// } else {
// // Return clear view for spacing
//// let headerView = UIView()
// let headerView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 19))
// headerView.backgroundColor = UIColor.white
// return headerView
// }
}
public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0.0
// if (section <= 3) {
// return 0.0
// } else {
// return 19.0
// }
}
public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
// return CGFloat.leastNormalMagnitude
return 0.0
}
......@@ -360,9 +428,6 @@ extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section == 0) {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileHeaderTableViewCell", for: indexPath) as! ProfileHeaderTableViewCell
// cell.delegate = self // Set the banner offers delegate
// cell.configureCell(data: self.bannerOffersSection)
// cell.parent = self
return cell
} else if (indexPath.section == 1) {
......@@ -389,8 +454,8 @@ extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileCouponTableViewCell", for: indexPath) as! ProfileCouponTableViewCell
if let items = self.filteredOffersSection?.items,
indexPath.row < items.count,
let offer = items[indexPath.row] as? OfferModel {
cell.configureCell(data: offer)
let coupon = items[indexPath.row] as? CouponItemModel {
cell.configureCell(data: coupon)
}
return cell
}
......@@ -403,8 +468,8 @@ extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
} else {
if let items = self.filteredOffersSection?.items,
indexPath.row < items.count,
let offer = items[indexPath.row] as? OfferModel {
openCouponViewController(with: offer)
let coupon = items[indexPath.row] as? CouponItemModel {
openCouponViewController(with: coupon)
}
}
}
......@@ -413,8 +478,8 @@ extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
// Add delegate conformance
extension ProfileViewController: MyRewardsOffersScrollTableViewCellDelegate {
func didSelectOffer(_ offer: OfferModel) {
// Navigate to CouponViewController
openCouponViewController(with: offer)
// Legacy OfferModel handling — no longer used but kept for protocol conformance
print("⚠️ [ProfileVC] didSelectOffer called with legacy OfferModel — should not happen")
}
func didSelectCouponSet(_ couponSet: CouponSetItemModel) {
......