Manos Chorianopoulos

dynamic campaigns at MyRewardsVC

This diff is collapsed. Click to expand it.
......@@ -16,7 +16,14 @@ public class MyRewardsBannerOfferCollectionViewCell: UICollectionViewCell {
// Initialization code
}
func configureCell(data: OfferModel) {
backgroundImage.image = UIImage(named: data.bannerImage, in: Bundle.frameworkResourceBundle, compatibleWith: nil)
func configureCell(data: CampaignItemModel) {
// Use campaign's banner image - no hardcoded defaults
let imageName = data._banner_img_mobile ?? ""
if !imageName.isEmpty {
backgroundImage.image = UIImage(named: imageName, in: Bundle.frameworkResourceBundle, compatibleWith: nil)
} else {
backgroundImage.image = nil
}
}
}
......
......@@ -93,8 +93,8 @@ public class MyRewardsBannerOffersScrollTableViewCell: UITableViewCell {
func configureCell(data: SectionModel?) {
self.data = data
// Configure page control
let numberOfPages = self.data?.offers.count ?? 0
// Configure page control based on new SectionModel structure
let numberOfPages = self.data?.itemCount ?? 0
pageControl.numberOfPages = numberOfPages
pageControl.currentPage = 0
......@@ -115,24 +115,36 @@ extension MyRewardsBannerOffersScrollTableViewCell: UICollectionViewDataSource,
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.data?.offers.count ?? 0
return self.data?.itemCount ?? 0
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyRewardsBannerOfferCollectionViewCell", for: indexPath) as! MyRewardsBannerOfferCollectionViewCell
// cell.configureCell(offer: self.data?.offers[indexPath.row])
if let offer = self.data?.offers[indexPath.row] {
cell.configureCell(data: offer)
// Handle different item types
guard let data = self.data,
let itemType = data.itemType,
let items = data.items,
indexPath.row < items.count else {
return cell
}
switch itemType {
case .campaigns:
if let campaign = items[indexPath.row] as? CampaignItemModel {
cell.configureCell(data: campaign)
}
default:
// Handle other types if needed in the future
break
}
return cell;
return cell
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// Get the selected offer
if let offer = self.data?.offers[indexPath.row] {
// Call the delegate method to notify the parent
delegate?.didSelectBannerOffer(indexPath.row)
}
// Call the delegate method to notify the parent
delegate?.didSelectBannerOffer(indexPath.row)
}
// MARK: - UICollectionViewDelegateFlowLayout
......
......@@ -42,6 +42,17 @@ public class CampaignItemModel: Codable {
private var show_expiration: String?
private var coupon_img: String?
// New fields from getCampaigns response
private var communication_name: String?
private var category: String?
private var delivery_method: String?
private var display_type: String?
private var audience: String?
private var description: String?
private var workflow_settings: [String: Any]?
private var campaign_url: String? // from extra_fields
private var banner_img_mobile: String? // from extra_fields
public init() {
self.index_url = ""
self.logo_url = ""
......@@ -73,6 +84,17 @@ public class CampaignItemModel: Codable {
self.filter = ""
self.show_expiration = "false"
self.coupon_img = ""
// Initialize new fields
self.communication_name = ""
self.category = ""
self.delivery_method = ""
self.display_type = ""
self.audience = ""
self.description = ""
self.workflow_settings = nil
self.campaign_url = ""
self.banner_img_mobile = ""
}
public init(dictionary: [String: Any]) {
......@@ -90,6 +112,15 @@ public class CampaignItemModel: Codable {
self.ccms = nil
self.coupon_availability = nil
// Parse new fields from getCampaigns response
self.communication_name = dictionary["communication_name"] as? String? ?? ""
self.category = dictionary["category"] as? String? ?? ""
self.delivery_method = dictionary["delivery_method"] as? String? ?? ""
self.display_type = dictionary["display_type"] as? String? ?? ""
self.audience = dictionary["audience"] as? String? ?? ""
self.description = dictionary["description"] as? String? ?? ""
self.workflow_settings = dictionary["workflow_settings"] as? [String: Any]
let startDateString = dictionary["start_date"] as? String? ?? ""
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
......@@ -127,6 +158,10 @@ public class CampaignItemModel: Codable {
self.filter = extra_fields["filter"] as? String ?? ""
self.show_expiration = extra_fields["show_expiration"] as? String? ?? "false"
self.coupon_img = extra_fields["coupon_img"] as? String? ?? ""
// Parse new extra_fields
self.campaign_url = extra_fields["campaign_url"] as? String? ?? ""
self.banner_img_mobile = extra_fields["banner_img_mobile"] as? String? ?? ""
} else {
self.subcategory = ""
self.loyaltyCampaignId = ""
......@@ -141,6 +176,10 @@ public class CampaignItemModel: Codable {
self.filter = ""
self.show_expiration = "false"
self.coupon_img = ""
// Initialize new extra_fields when no extra_fields exist
self.campaign_url = ""
self.banner_img_mobile = ""
}
// campaign_type_settings
......@@ -260,6 +299,53 @@ public class CampaignItemModel: Codable {
get { return self.coupon_img }
set(newValue) { self.coupon_img = newValue }
}
// MARK: - New field accessors
public var _communication_name: String? {
get { return self.communication_name }
set(newValue) { self.communication_name = newValue }
}
public var _category: String? {
get { return self.category }
set(newValue) { self.category = newValue }
}
public var _delivery_method: String? {
get { return self.delivery_method }
set(newValue) { self.delivery_method = newValue }
}
public var _display_type: String? {
get { return self.display_type }
set(newValue) { self.display_type = newValue }
}
public var _audience: String? {
get { return self.audience }
set(newValue) { self.audience = newValue }
}
public var _description: String? {
get { return self.description }
set(newValue) { self.description = newValue }
}
public var _workflow_settings: [String: Any]? {
get { return self.workflow_settings }
set(newValue) { self.workflow_settings = newValue }
}
public var _campaign_url: String? {
get { return self.campaign_url }
set(newValue) { self.campaign_url = newValue }
}
public var _banner_img_mobile: String? {
get { return self.banner_img_mobile }
set(newValue) { self.banner_img_mobile = newValue }
}
}
// MARK: - Loyalty Contextual Offer Model
......
......@@ -8,8 +8,61 @@
import Foundation
// MARK: - Section Types
enum SectionType {
case myRewardsBannerOffers // MyRewardsBannerOffersScrollTableViewCell
case myRewardsHorizontalCouponsets // MyRewardsOffersScrollTableViewCell
case profileHeader // ProfileHeaderTableViewCell (no items)
case profileQuestionnaire // ProfileQuestionnaireTableViewCell (no items)
case profileCouponFilters // ProfileCouponFiltersTableViewCell (no items)
case staticContent // Any cell that displays static content
}
enum ItemType {
case campaigns // [CampaignItemModel]
case couponSets // [CouponSetItemModel]
case coupons // [CouponItemModel]
case filters // [CouponFilterModel]
case none // For sections with no items
}
// MARK: - SectionModel
struct SectionModel {
let title: String
let count: Int
let offers: [OfferModel]
let sectionType: SectionType // MANDATORY - defines which cell to use
let title: String? // OPTIONAL - section title
let items: [Any]? // OPTIONAL - array of items (nil for sections with no items)
let itemType: ItemType? // OPTIONAL - type of items in the array
let count: Int? // OPTIONAL - explicit count (computed from items.count if nil)
let metadata: [String: Any]? // OPTIONAL - additional section-specific data
}
// MARK: - SectionModel Extensions
extension SectionModel {
// Convenience initializer for sections with items
init(sectionType: SectionType, title: String? = nil, items: [Any], itemType: ItemType, metadata: [String: Any]? = nil) {
self.sectionType = sectionType
self.title = title
self.items = items
self.itemType = itemType
self.count = items.count
self.metadata = metadata
}
// Convenience initializer for sections without items
init(sectionType: SectionType, title: String? = nil, count: Int = 1, metadata: [String: Any]? = nil) {
self.sectionType = sectionType
self.title = title
self.items = nil
self.itemType = .none
self.count = count
self.metadata = metadata
}
// Computed property for safe count access
var itemCount: Int {
return count ?? items?.count ?? 0
}
}
......
......@@ -293,15 +293,11 @@ import UIKit
"https://warply.s3.amazonaws.com/dei/campaigns/EnergySaverContest_dev/index.html"
]
var bannerOffersSection: SectionModel?
var topOffersSection: SectionModel?
var favoriteOffersSection: SectionModel?
var sustainableOffersSection: SectionModel?
var familyOffersSection: SectionModel?
var foodOffersSection: SectionModel?
var escapeOffersSection: SectionModel?
var childOffersSection: SectionModel?
var shoppingOffersSection: SectionModel?
// Dynamic sections array - populated by API calls
var sections: [SectionModel] = []
// Campaign data for banners
var bannerCampaigns: [CampaignItemModel] = []
public override func viewDidLoad() {
super.viewDidLoad()
......@@ -319,7 +315,8 @@ import UIKit
tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableView.automaticDimension
initializeSections()
// Start with empty sections - will be populated dynamically by API calls
loadCampaigns()
}
// NEW: Safe XIB registration method
......@@ -432,22 +429,53 @@ import UIKit
self.tableView.reloadData()
}
// MARK: - Campaign Loading
private func loadCampaigns() {
// Load campaigns from WarplySDK
WarplySDK.shared.getCampaigns { [weak self] campaigns in
guard let self = self, let campaigns = campaigns else { return }
// Filter campaigns for banner display (contest campaigns)
self.bannerCampaigns = campaigns.filter { campaign in
// Filter by category "contest" or campaign_type "contest"
return campaign._category == "contest" || campaign._campaign_type == "contest"
}
// Create banner section with real campaign data
if !self.bannerCampaigns.isEmpty {
let bannerSection = SectionModel(
sectionType: .myRewardsBannerOffers,
title: "Διαγωνισμός",
items: self.bannerCampaigns,
itemType: .campaigns
)
self.sections.append(bannerSection)
}
// Reload table view with new sections
DispatchQueue.main.async {
self.tableView.reloadData()
}
} failureCallback: { [weak self] errorCode in
print("Failed to load campaigns: \(errorCode)")
// No sections added on failure - table will be empty
}
}
private func openCampaignViewController(with index: Int) {
// let storyboard = UIStoryboard(name: "Main", bundle: Bundle.frameworkBundle)
// if let vc = storyboard.instantiateViewController(withIdentifier: "CampaignViewController") as? SwiftWarplyFramework.CampaignViewController {
// // vc.campaignUrl = "https://warply.s3.amazonaws.com/dei/campaigns/DehEasterContest_stage/index.html"
// vc.campaignUrl = contestUrls[index]
// vc.showHeader = false
// self.navigationController?.pushViewController(vc, animated: true)
// // self.present(vc, animated: true)
// }
let vc = SwiftWarplyFramework.CampaignViewController(nibName: "CampaignViewController", bundle: Bundle.frameworkBundle)
// vc.campaignUrl = "https://warply.s3.amazonaws.com/dei/campaigns/DehEasterContest_stage/index.html"
vc.campaignUrl = contestUrls[index]
vc.showHeader = false
// Use real campaign URL if available, otherwise fallback to static URLs
if index < bannerCampaigns.count {
// Use campaign URL from real campaign data
let campaign = bannerCampaigns[index]
vc.campaignUrl = campaign._campaign_url ?? campaign.index_url ?? contestUrls[min(index, contestUrls.count - 1)]
} else {
// Fallback to static contest URLs
vc.campaignUrl = contestUrls[min(index, contestUrls.count - 1)]
}
vc.showHeader = false
self.navigationController?.pushViewController(vc, animated: true)
}
......@@ -469,7 +497,7 @@ import UIKit
extension MyRewardsViewController: UITableViewDelegate, UITableViewDataSource{
public func numberOfSections(in tableView: UITableView) -> Int {
return 9
return sections.count
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
......@@ -489,7 +517,6 @@ extension MyRewardsViewController: UITableViewDelegate, UITableViewDataSource{
}
public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
// return CGFloat.leastNormalMagnitude
return 0.0
}
......@@ -498,62 +525,36 @@ extension MyRewardsViewController: UITableViewDelegate, UITableViewDataSource{
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section == 0) {
guard indexPath.section < sections.count else {
return UITableViewCell() // Return empty cell if section doesn't exist
}
let sectionModel = sections[indexPath.section]
switch sectionModel.sectionType {
case .myRewardsBannerOffers:
let cell = tableView.dequeueReusableCell(withIdentifier: "MyRewardsBannerOffersScrollTableViewCell", for: indexPath) as! MyRewardsBannerOffersScrollTableViewCell
cell.delegate = self // Set the banner offers delegate
cell.configureCell(data: self.bannerOffersSection)
// cell.parent = self
cell.delegate = self
cell.configureCell(data: sectionModel)
return cell
} else {
case .myRewardsHorizontalCouponsets:
let cell = tableView.dequeueReusableCell(withIdentifier: "MyRewardsOffersScrollTableViewCell", for: indexPath) as! MyRewardsOffersScrollTableViewCell
cell.delegate = self
cell.configureCell(data: sectionModel)
return cell
cell.delegate = self // Set the offers delegate
if (indexPath.section == 1) {
cell.configureCell(data: self.topOffersSection)
} else if (indexPath.section == 2) {
cell.configureCell(data: self.favoriteOffersSection)
} else if (indexPath.section == 3) {
cell.configureCell(data: self.sustainableOffersSection)
} else if (indexPath.section == 4) {
cell.configureCell(data: self.familyOffersSection)
} else if (indexPath.section == 5) {
cell.configureCell(data: self.foodOffersSection)
} else if (indexPath.section == 6) {
cell.configureCell(data: self.escapeOffersSection)
} else if (indexPath.section == 7) {
cell.configureCell(data: self.childOffersSection)
} else {
cell.configureCell(data: self.shoppingOffersSection)
}
case .profileHeader, .profileQuestionnaire, .profileCouponFilters, .staticContent:
// These will be implemented later when needed
let cell = UITableViewCell()
cell.textLabel?.text = sectionModel.title ?? "Section"
return cell
}
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if (indexPath.section == 0) {
// Do nothing - Each button is handled differently
} else if (indexPath.section == 1) {
// Do nothing
} else if (indexPath.section == 2) {
// Do nothing
} else if (indexPath.section == 3) {
// Do nothing
} else if (indexPath.section == 4) {
// Do nothing
} else if (indexPath.section == 5) {
// Do nothing
} else if (indexPath.section == 6) {
// Do nothing
} else if (indexPath.section == 7) {
// Do nothing
} else {
// Do nothing
}
// Handle selection if needed - currently no action required
tableView.deselectRow(at: indexPath, animated: true)
}
}
......