Manos Chorianopoulos

dynamic couponsets in MyRewardsVC

...@@ -263,13 +263,13 @@ public enum Endpoint { ...@@ -263,13 +263,13 @@ public enum Endpoint {
263 "action": "retrieve_multilingual", 263 "action": "retrieve_multilingual",
264 "active": active, 264 "active": active,
265 "visible": visible, 265 "visible": visible,
266 - "language": language, 266 + "language": language
267 - "exclude": [ 267 + // "exclude": [
268 - [ 268 + // [
269 - "field": "couponset_type", 269 + // "field": "couponset_type",
270 - "value": ["supermarket"] 270 + // "value": ["supermarket"]
271 - ] 271 + // ]
272 - ] 272 + // ]
273 ] 273 ]
274 if let uuids = uuids { 274 if let uuids = uuids {
275 couponParams["uuids"] = uuids 275 couponParams["uuids"] = uuids
......
...@@ -68,7 +68,7 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell { ...@@ -68,7 +68,7 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell {
68 68
69 func configureCell(data: CouponSetItemModel) { 69 func configureCell(data: CouponSetItemModel) {
70 // Use coupon set preview image 70 // Use coupon set preview image
71 - let imageName = data.img_preview ?? "" 71 + let imageName = data._img_preview
72 if !imageName.isEmpty { 72 if !imageName.isEmpty {
73 bannerImage.image = UIImage(named: imageName, in: Bundle.frameworkResourceBundle, compatibleWith: nil) 73 bannerImage.image = UIImage(named: imageName, in: Bundle.frameworkResourceBundle, compatibleWith: nil)
74 } else { 74 } else {
...@@ -79,13 +79,13 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell { ...@@ -79,13 +79,13 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell {
79 favoriteImage.image = UIImage(named: "favorite_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil) 79 favoriteImage.image = UIImage(named: "favorite_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
80 80
81 // Use coupon set discount 81 // Use coupon set discount
82 - discountLabel.text = data.discount ?? "" 82 + discountLabel.text = data._discount
83 discountLabel.font = UIFont(name: "PingLCG-Bold", size: 17) 83 discountLabel.font = UIFont(name: "PingLCG-Bold", size: 17)
84 discountLabel.textColor = UIColor(rgb: 0xF2F2F2) 84 discountLabel.textColor = UIColor(rgb: 0xF2F2F2)
85 85
86 // Color based on discount type 86 // Color based on discount type
87 let discountColor: UInt = { 87 let discountColor: UInt = {
88 - switch data.discount_type { 88 + switch data._discount_type {
89 case "percentage": 89 case "percentage":
90 return 0xFF6B35 90 return 0xFF6B35
91 case "value": 91 case "value":
...@@ -98,20 +98,20 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell { ...@@ -98,20 +98,20 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell {
98 }() 98 }()
99 discountView.backgroundColor = UIColor(rgb: discountColor) 99 discountView.backgroundColor = UIColor(rgb: discountColor)
100 100
101 - titleLabel.text = data.name ?? "" 101 + titleLabel.text = data._name
102 titleLabel.font = UIFont(name: "PingLCG-Bold", size: 17) 102 titleLabel.font = UIFont(name: "PingLCG-Bold", size: 17)
103 titleLabel.textColor = UIColor(rgb: 0x000F1E) 103 titleLabel.textColor = UIColor(rgb: 0x000F1E)
104 104
105 - subtitleLabel.text = data.short_description ?? "" 105 + subtitleLabel.text = data._short_description
106 subtitleLabel.font = UIFont(name: "PingLCG-Regular", size: 14) 106 subtitleLabel.font = UIFont(name: "PingLCG-Regular", size: 14)
107 subtitleLabel.textColor = UIColor(rgb: 0x00111B) 107 subtitleLabel.textColor = UIColor(rgb: 0x00111B)
108 108
109 - expirationLabel.text = data.expiration ?? "" 109 + expirationLabel.text = data._expiration_formatted
110 expirationLabel.font = UIFont(name: "PingLCG-Regular", size: 13) 110 expirationLabel.font = UIFont(name: "PingLCG-Regular", size: 13)
111 expirationLabel.textColor = UIColor(rgb: 0x00111B) 111 expirationLabel.textColor = UIColor(rgb: 0x00111B)
112 112
113 // Use first image from img array if available 113 // Use first image from img array if available
114 - if let imgArray = data.img, !imgArray.isEmpty { 114 + if let imgArray = data._img, !imgArray.isEmpty {
115 let logoName = imgArray[0] 115 let logoName = imgArray[0]
116 if !logoName.isEmpty { 116 if !logoName.isEmpty {
117 logoImage.image = UIImage(named: logoName, in: Bundle.frameworkResourceBundle, compatibleWith: nil) 117 logoImage.image = UIImage(named: logoName, in: Bundle.frameworkResourceBundle, compatibleWith: nil)
......
...@@ -36,31 +36,50 @@ public class ShopAvailabilityItemModel: Codable { ...@@ -36,31 +36,50 @@ public class ShopAvailabilityItemModel: Codable {
36 } 36 }
37 37
38 public class CouponSetItemModel: Codable { 38 public class CouponSetItemModel: Codable {
39 - public let uuid: String? 39 + // Existing fields
40 - public let admin_name: String? 40 + private var uuid: String?
41 - public let name: String? 41 + private var admin_name: String?
42 - public let img: [String]? 42 + private var name: String?
43 - public let img_preview: String? 43 + private var img: [String]?
44 - public let expiration: String? 44 + private var img_preview: String?
45 - public let description: String? 45 + private var expiration: String? // Now stores raw date string like "2026-06-30 11:59"
46 - public let short_description: String? 46 + private var description: String?
47 - public let discount: String? 47 + private var short_description: String?
48 - public let sorting: Int? 48 + private var discount: String?
49 - public let inner_text: String? 49 + private var sorting: Int?
50 - public let buyable: Bool? 50 + private var inner_text: String?
51 - public let visible: Bool? 51 + private var buyable: Bool?
52 - public let terms: String? 52 + private var visible: Bool?
53 - public let merchant_uuid: String? 53 + private var terms: String?
54 - public let discount_type: String? // "value" / "percentage" / "plus_one" 54 + private var merchant_uuid: String?
55 - public let final_price: Float? 55 + private var discount_type: String? // "value" / "percentage" / "plus_one"
56 + private var final_price: Float?
56 57
57 // Universal Coupons 58 // Universal Coupons
58 - public let couponset_type: String? 59 + private var couponset_type: String?
59 60
60 // PopupMerchantsViewController List 61 // PopupMerchantsViewController List
61 - public let shop_availability: Array<ShopAvailabilityItemModel>? 62 + private var shop_availability: Array<ShopAvailabilityItemModel>?
63 +
64 + // New fields from API response
65 + private var eligibilities: [String: Any]?
66 + private var created: String?
67 + private var updated: String?
68 + private var app_uuid: String?
69 + private var active: Bool?
70 + private var is_new: Bool?
71 + private var start_date: String?
72 + private var end_date: String?
73 + private var user_generated: Bool?
74 + private var limits: [String: Any]?
75 + private var points: Int?
76 + private var promoted: Bool?
77 + private var points_cause: String?
78 + private var third_party_service: String?
79 + private var category: String?
62 80
63 public init(dictionary: [String: Any]) { 81 public init(dictionary: [String: Any]) {
82 + // Existing fields
64 self.uuid = dictionary["uuid"] as? String? ?? "" 83 self.uuid = dictionary["uuid"] as? String? ?? ""
65 self.admin_name = dictionary["admin_name"] as? String? ?? "" 84 self.admin_name = dictionary["admin_name"] as? String? ?? ""
66 self.name = dictionary["name"] as? String? ?? "" 85 self.name = dictionary["name"] as? String? ?? ""
...@@ -93,15 +112,27 @@ public class CouponSetItemModel: Codable { ...@@ -93,15 +112,27 @@ public class CouponSetItemModel: Codable {
93 self.final_price = 0.0 112 self.final_price = 0.0
94 } 113 }
95 114
96 - let expirationObject = dictionary["expiration"] as? [String: Any]? ?? ["":""] 115 + // NEW FIELDS from API response
97 - let expirationString = expirationObject?["value"] as? String? ?? "" 116 + self.eligibilities = dictionary["eligibilities"] as? [String: Any]
117 + self.created = dictionary["created"] as? String? ?? ""
118 + self.updated = dictionary["updated"] as? String? ?? ""
119 + self.app_uuid = dictionary["app_uuid"] as? String? ?? ""
120 + self.active = dictionary["active"] as? Bool? ?? false
121 + self.is_new = dictionary["is_new"] as? Bool? ?? false
122 + self.start_date = dictionary["start_date"] as? String? ?? ""
123 + self.end_date = dictionary["end_date"] as? String? ?? ""
124 + self.user_generated = dictionary["user_generated"] as? Bool? ?? false
125 + self.limits = dictionary["limits"] as? [String: Any]
126 + self.points = dictionary["points"] as? Int? ?? 0
127 + self.promoted = dictionary["promoted"] as? Bool? ?? false
128 + self.points_cause = dictionary["points_cause"] as? String? ?? ""
129 + self.third_party_service = dictionary["third_party_service"] as? String? ?? ""
130 + self.category = dictionary["category"] as? String? ?? ""
98 131
99 - let dateFormatter = DateFormatter() 132 + // UPDATED EXPIRATION HANDLING - store raw date string
100 - dateFormatter.dateFormat = "yyyy-MM-dd hh:mm" 133 + if let expirationObject = dictionary["expiration"] as? [String: Any],
101 - if let date = dateFormatter.date(from: expirationString ?? "") { 134 + let expirationValue = expirationObject["value"] as? String {
102 - dateFormatter.dateFormat = "dd/MM/yyyy" 135 + self.expiration = expirationValue // Store "2026-06-30 11:59"
103 - let resultString = dateFormatter.string(from: date)
104 - self.expiration = resultString
105 } else { 136 } else {
106 self.expiration = "" 137 self.expiration = ""
107 } 138 }
...@@ -164,6 +195,63 @@ public class CouponSetItemModel: Codable { ...@@ -164,6 +195,63 @@ public class CouponSetItemModel: Codable {
164 self.shop_availability = [] 195 self.shop_availability = []
165 } 196 }
166 } 197 }
198 +
199 + // MARK: - Public Accessors (Following ProfileModel Pattern)
200 +
201 + // Existing field accessors
202 + public var _uuid: String { get { return self.uuid ?? "" } }
203 + public var _admin_name: String { get { return self.admin_name ?? "" } }
204 + public var _name: String { get { return self.name ?? "" } }
205 + public var _img: [String]? { get { return self.img } }
206 + public var _img_preview: String { get { return self.img_preview ?? "" } }
207 + public var _expiration: String { get { return self.expiration ?? "" } }
208 + public var _description: String { get { return self.description ?? "" } }
209 + public var _short_description: String { get { return self.short_description ?? "" } }
210 + public var _discount: String { get { return self.discount ?? "" } }
211 + public var _sorting: Int { get { return self.sorting ?? 0 } }
212 + public var _inner_text: String { get { return self.inner_text ?? "" } }
213 + public var _buyable: Bool { get { return self.buyable ?? false } }
214 + public var _visible: Bool { get { return self.visible ?? false } }
215 + public var _terms: String { get { return self.terms ?? "" } }
216 + public var _merchant_uuid: String { get { return self.merchant_uuid ?? "" } }
217 + public var _discount_type: String { get { return self.discount_type ?? "" } }
218 + public var _final_price: Float { get { return self.final_price ?? 0.0 } }
219 + public var _couponset_type: String { get { return self.couponset_type ?? "" } }
220 + public var _shop_availability: Array<ShopAvailabilityItemModel>? { get { return self.shop_availability } }
221 +
222 + // New field accessors
223 + public var _eligibilities: [String: Any]? { get { return self.eligibilities } }
224 + public var _created: String { get { return self.created ?? "" } }
225 + public var _updated: String { get { return self.updated ?? "" } }
226 + public var _app_uuid: String { get { return self.app_uuid ?? "" } }
227 + public var _active: Bool { get { return self.active ?? false } }
228 + public var _is_new: Bool { get { return self.is_new ?? false } }
229 + public var _start_date: String { get { return self.start_date ?? "" } }
230 + public var _end_date: String { get { return self.end_date ?? "" } }
231 + public var _user_generated: Bool { get { return self.user_generated ?? false } }
232 + public var _limits: [String: Any]? { get { return self.limits } }
233 + public var _points: Int { get { return self.points ?? 0 } }
234 + public var _promoted: Bool { get { return self.promoted ?? false } }
235 + public var _points_cause: String { get { return self.points_cause ?? "" } }
236 + public var _third_party_service: String { get { return self.third_party_service ?? "" } }
237 + public var _category: String { get { return self.category ?? "" } }
238 +
239 + // Formatted expiration date for display
240 + public var _expiration_formatted: String {
241 + guard let expiration = self.expiration, !expiration.isEmpty else {
242 + return ""
243 + }
244 +
245 + let dateFormatter = DateFormatter()
246 + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
247 +
248 + if let date = dateFormatter.date(from: expiration) {
249 + dateFormatter.dateFormat = "dd/MM/yyyy"
250 + return dateFormatter.string(from: date)
251 + }
252 +
253 + return ""
254 + }
167 } 255 }
168 256
169 public class RedeemedMerchantDetailsModel: Codable { 257 public class RedeemedMerchantDetailsModel: Codable {
......
...@@ -40,6 +40,9 @@ import UIKit ...@@ -40,6 +40,9 @@ import UIKit
40 // Campaign data for banners 40 // Campaign data for banners
41 var bannerCampaigns: [CampaignItemModel] = [] 41 var bannerCampaigns: [CampaignItemModel] = []
42 42
43 + // Coupon sets data
44 + var couponSets: [CouponSetItemModel] = []
45 +
43 // Profile data 46 // Profile data
44 var profileModel: ProfileModel? 47 var profileModel: ProfileModel?
45 var profileSection: SectionModel? 48 var profileSection: SectionModel?
...@@ -64,8 +67,9 @@ import UIKit ...@@ -64,8 +67,9 @@ import UIKit
64 createDefaultProfileSection() 67 createDefaultProfileSection()
65 68
66 // Load data 69 // Load data
67 - loadProfile() // Try to populate profile with real data 70 + loadProfile() // Load Profile
68 loadCampaigns() // Load campaigns 71 loadCampaigns() // Load campaigns
72 + loadCouponSets() // Load couponsets
69 } 73 }
70 74
71 // NEW: Safe XIB registration method 75 // NEW: Safe XIB registration method
...@@ -136,6 +140,35 @@ import UIKit ...@@ -136,6 +140,35 @@ import UIKit
136 } 140 }
137 } 141 }
138 142
143 + // MARK: - Coupon Sets Loading
144 + private func loadCouponSets() {
145 + // Load coupon sets from WarplySDK
146 + WarplySDK.shared.getCouponSets { [weak self] couponSets in
147 + guard let self = self, let couponSets = couponSets else { return }
148 +
149 + self.couponSets = couponSets
150 +
151 + // Create coupon sets section with real data
152 + if !self.couponSets.isEmpty {
153 + let couponSetsSection = SectionModel(
154 + sectionType: .myRewardsHorizontalCouponsets,
155 + title: "Προσφορές",
156 + items: self.couponSets,
157 + itemType: .couponSets
158 + )
159 + self.sections.append(couponSetsSection)
160 + }
161 +
162 + // Reload table view with new sections
163 + DispatchQueue.main.async {
164 + self.tableView.reloadData()
165 + }
166 + } failureCallback: { [weak self] errorCode in
167 + print("Failed to load coupon sets: \(errorCode)")
168 + // No sections added on failure - table will be empty
169 + }
170 + }
171 +
139 // MARK: - Profile Loading 172 // MARK: - Profile Loading
140 public func loadProfile() { 173 public func loadProfile() {
141 // Always attempt to load profile, regardless of authentication status 174 // Always attempt to load profile, regardless of authentication status
......