Showing
4 changed files
with
164 additions
and
43 deletions
... | @@ -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 | ... | ... |
-
Please register or login to post a comment