Manos Chorianopoulos

MyCouponsViewController part3

...@@ -10,44 +10,58 @@ import UIKit ...@@ -10,44 +10,58 @@ import UIKit
10 @objc(ProfileCouponTableViewCell) 10 @objc(ProfileCouponTableViewCell)
11 public class ProfileCouponTableViewCell: UITableViewCell { 11 public class ProfileCouponTableViewCell: UITableViewCell {
12 @IBOutlet weak var parentView: UIView! 12 @IBOutlet weak var parentView: UIView!
13 - @IBOutlet weak var bannerImage: UIImageView! 13 + // @IBOutlet weak var bannerImage: UIImageView!
14 - @IBOutlet weak var favoriteImage: UIImageView! 14 + // @IBOutlet weak var favoriteImage: UIImageView!
15 - @IBOutlet weak var discountView: UIView! 15 + // @IBOutlet weak var discountView: UIView!
16 - @IBOutlet weak var discountLabel: UILabel! 16 + // @IBOutlet weak var discountLabel: UILabel!
17 +
18 + @IBOutlet weak var logoImageView: UIView!
19 + @IBOutlet weak var logoImage: UIImageView!
20 + @IBOutlet weak var expirationView: UIView!
21 + @IBOutlet weak var expirationImage: UIImageView!
22 + @IBOutlet weak var expirationLabel: UILabel!
23 + @IBOutlet weak var merchantNameLabel: UILabel!
17 @IBOutlet weak var titleLabel: UILabel! 24 @IBOutlet weak var titleLabel: UILabel!
18 @IBOutlet weak var subtitleLabel: UILabel! 25 @IBOutlet weak var subtitleLabel: UILabel!
19 - @IBOutlet weak var expirationLabel: UILabel! 26 + @IBOutlet weak var arrowView: UIView!
20 - @IBOutlet weak var logoImage: UIImageView! 27 + @IBOutlet weak var arrowImage: UIImageView!
21 28
22 public override func awakeFromNib() { 29 public override func awakeFromNib() {
23 super.awakeFromNib() 30 super.awakeFromNib()
24 // Initialization code 31 // Initialization code
25 32
26 - parentView.layer.borderWidth = 1.0 33 + parentView.backgroundColor = UIColor(rgb: 0xDDEFFB)
27 - parentView.layer.borderColor = UIColor(rgb: 0xCCCCCC).cgColor 34 + parentView.layer.borderWidth = 2.0
28 - parentView.layer.cornerRadius = 8.0 35 + parentView.layer.borderColor = UIColor(rgb: 0xA5DAF8).cgColor
36 + parentView.layer.cornerRadius = 12.0
29 parentView.clipsToBounds = true 37 parentView.clipsToBounds = true
38 +
39 + arrowView.backgroundColor = UIColor(rgb: 0xFFFFFF)
40 + arrowView.layer.cornerRadius = arrowView.bounds.height / 2
30 41
31 - discountView.layer.cornerRadius = 42.0 42 + arrowImage.image = UIImage(named: "ic_forward", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
43 + expirationImage.image = UIImage(named: "clock", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
44 +
45 + // discountView.layer.cornerRadius = 42.0
32 46
33 } 47 }
34 48
35 // MARK: - Image Loading Helpers 49 // MARK: - Image Loading Helpers
36 50
37 - private var bannerImageURL: String? { 51 + // private var bannerImageURL: String? {
38 - didSet { 52 + // didSet {
39 - if let url = bannerImageURL, !url.isEmpty { 53 + // if let url = bannerImageURL, !url.isEmpty {
40 - self.bannerImage.image = UIImage() 54 + // self.bannerImage.image = UIImage()
41 - UIImage.loadImageUsingCacheWithUrlString(url) { [weak self] image in 55 + // UIImage.loadImageUsingCacheWithUrlString(url) { [weak self] image in
42 - if url == self?.bannerImageURL { 56 + // if url == self?.bannerImageURL {
43 - self?.bannerImage.image = image 57 + // self?.bannerImage.image = image
44 - } 58 + // }
45 - } 59 + // }
46 - } else { 60 + // } else {
47 - self.bannerImage.image = nil 61 + // self.bannerImage.image = nil
48 - } 62 + // }
49 - } 63 + // }
50 - } 64 + // }
51 65
52 private var logoImageURL: String? { 66 private var logoImageURL: String? {
53 didSet { 67 didSet {
...@@ -67,22 +81,22 @@ public class ProfileCouponTableViewCell: UITableViewCell { ...@@ -67,22 +81,22 @@ public class ProfileCouponTableViewCell: UITableViewCell {
67 // MARK: - Configure with OfferModel (legacy, kept for backward compatibility) 81 // MARK: - Configure with OfferModel (legacy, kept for backward compatibility)
68 82
69 func configureCell(data: OfferModel) { 83 func configureCell(data: OfferModel) {
70 - bannerImage.image = UIImage(named: data.bannerImage, in: Bundle.frameworkResourceBundle, compatibleWith: nil) 84 + // bannerImage.image = UIImage(named: data.bannerImage, in: Bundle.frameworkResourceBundle, compatibleWith: nil)
71 - favoriteImage.image = UIImage(named: data.isFavorite ? "favorite_filled" : "favorite_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil) 85 + // favoriteImage.image = UIImage(named: data.isFavorite ? "favorite_filled" : "favorite_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
72 86
73 - let discountSymbol = 87 + // let discountSymbol =
74 - data.discountType == "percentage" ? "%" 88 + // data.discountType == "percentage" ? "%"
75 - : data.discountType == "price" ? "€" 89 + // : data.discountType == "price" ? "€"
76 - : data.discountType == "buyOneGetOne" ? "1+1" 90 + // : data.discountType == "buyOneGetOne" ? "1+1"
77 - : data.discountType == "free" ? "Δωρεάν" 91 + // : data.discountType == "free" ? "Δωρεάν"
78 - : "" 92 + // : ""
79 93
80 -// discountLabel.text = "\(data.discount)\(discountSymbol)" 94 +// // discountLabel.text = "\(data.discount)\(discountSymbol)"
81 - discountLabel.text = data.discount 95 +// discountLabel.text = data.discount
82 - discountLabel.font = UIFont(name: "PingLCG-Bold", size: 25) 96 +// discountLabel.font = UIFont(name: "PingLCG-Bold", size: 25)
83 - discountLabel.textColor = UIColor(rgb: 0xF2F2F2) 97 +// discountLabel.textColor = UIColor(rgb: 0xF2F2F2)
84 98
85 - discountView.backgroundColor = UIColor(rgb: data.color) 99 + // discountView.backgroundColor = UIColor(rgb: data.color)
86 100
87 titleLabel.text = data.title 101 titleLabel.text = data.title
88 titleLabel.font = UIFont(name: "PingLCG-Bold", size: 22) 102 titleLabel.font = UIFont(name: "PingLCG-Bold", size: 22)
...@@ -103,56 +117,84 @@ public class ProfileCouponTableViewCell: UITableViewCell { ...@@ -103,56 +117,84 @@ public class ProfileCouponTableViewCell: UITableViewCell {
103 117
104 func configureCell(data: CouponItemModel) { 118 func configureCell(data: CouponItemModel) {
105 // Banner image — load from couponset_data img_preview (remote URL) 119 // Banner image — load from couponset_data img_preview (remote URL)
106 - if let imgPreview = data.couponset_data?._img_preview, !imgPreview.isEmpty { 120 + // if let imgPreview = data.couponset_data?._img_preview, !imgPreview.isEmpty {
107 - self.bannerImageURL = imgPreview 121 + // self.bannerImageURL = imgPreview
108 - } else { 122 + // } else {
109 - bannerImage.image = nil 123 + // bannerImage.image = nil
110 - } 124 + // }
111 125
112 // Favorite — default to not favorite for now 126 // Favorite — default to not favorite for now
113 - favoriteImage.image = UIImage(named: "favorite_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil) 127 + // favoriteImage.image = UIImage(named: "favorite_empty", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
114 - 128 +
115 - // Discount label — use coupon discount or couponset discount 129 + // // Discount label — use coupon discount or couponset discount
116 - let discountText = data.discount ?? data.couponset_data?._discount ?? "" 130 + // let discountText = data.discount ?? data.couponset_data?._discount ?? ""
117 - discountLabel.text = discountText 131 + // discountLabel.text = discountText
118 - discountLabel.font = UIFont(name: "PingLCG-Bold", size: 25) 132 + // discountLabel.font = UIFont(name: "PingLCG-Bold", size: 25)
119 - discountLabel.textColor = UIColor(rgb: 0xF2F2F2) 133 + // discountLabel.textColor = UIColor(rgb: 0xF2F2F2)
120 - 134 +
121 - // Discount view color based on discount type 135 + // // Discount view color based on discount type
122 - let discountType = data.couponset_data?._discount_type ?? "" 136 + // let discountType = data.couponset_data?._discount_type ?? ""
123 - let discountColor: UInt = { 137 + // let discountColor: UInt = {
124 - switch discountType { 138 + // switch discountType {
125 - case "percentage": 139 + // case "percentage":
126 - return 0xFF6B35 140 + // return 0xFF6B35
127 - case "value": 141 + // case "value":
128 - return 0x28A745 142 + // return 0x28A745
129 - case "plus_one": 143 + // case "plus_one":
130 - return 0x007AFF 144 + // return 0x007AFF
131 - default: 145 + // default:
132 - return 0xEE417D 146 + // return 0xEE417D
133 - } 147 + // }
134 - }() 148 + // }()
135 - discountView.backgroundColor = UIColor(rgb: discountColor) 149 + // discountView.backgroundColor = UIColor(rgb: discountColor)
150 +
151 + merchantNameLabel.font = UIFont(name: "PingLCG-Bold", size: 14)
152 + merchantNameLabel.textColor = UIColor(rgb: 0x5C6369)
153 + merchantNameLabel.text = data.merchant_details?._name
136 154
137 // Title — from couponset_data name 155 // Title — from couponset_data name
138 titleLabel.text = data.couponset_data?._name ?? "" 156 titleLabel.text = data.couponset_data?._name ?? ""
139 titleLabel.font = UIFont(name: "PingLCG-Bold", size: 22) 157 titleLabel.font = UIFont(name: "PingLCG-Bold", size: 22)
140 - titleLabel.textColor = UIColor(rgb: 0x000F1E) 158 + titleLabel.textColor = UIColor(rgb: 0x1D2023)
141 159
142 // Subtitle — from couponset_data short_description 160 // Subtitle — from couponset_data short_description
143 subtitleLabel.text = data.couponset_data?._short_description ?? "" 161 subtitleLabel.text = data.couponset_data?._short_description ?? ""
144 - subtitleLabel.font = UIFont(name: "PingLCG-Regular", size: 16) 162 + subtitleLabel.font = UIFont(name: "PingLCG-Regular", size: 13)
145 - subtitleLabel.textColor = UIColor(rgb: 0x00111B) 163 + subtitleLabel.textColor = UIColor(rgb: 0x5C6369)
146 164
147 - // Expiration — already formatted as "dd/MM/yyyy" by CouponItemModel 165 + expirationView.layer.cornerRadius = 6.0
148 - if let expiration = data.expiration, !expiration.isEmpty { 166 + expirationView.layer.borderWidth = 1.0
149 - expirationLabel.text = "έως " + expiration 167 +
168 + let isExpiringSoon: Bool
169 + if let expirationDate = data.expirationDate {
170 + let today = Calendar.current.startOfDay(for: Date())
171 + let expDay = Calendar.current.startOfDay(for: expirationDate)
172 + let daysLeft = Calendar.current.dateComponents([.day], from: today, to: expDay).day ?? Int.max
173 + if daysLeft <= 3 {
174 + isExpiringSoon = true
175 + expirationLabel.text = "\(daysLeft) days left"
176 + } else {
177 + isExpiringSoon = false
178 + expirationLabel.text = "Valid until " + data.formattedExpiration(format: "MMMM d, yyyy")
179 + }
150 } else { 180 } else {
181 + isExpiringSoon = false
151 expirationLabel.text = "" 182 expirationLabel.text = ""
152 } 183 }
153 - expirationLabel.font = UIFont(name: "PingLCG-Regular", size: 13) 184 +
154 - expirationLabel.textColor = UIColor(rgb: 0x00111B) 185 + if isExpiringSoon {
186 + expirationView.backgroundColor = UIColor(rgb: 0xFFF5DA)
187 + expirationView.layer.borderColor = UIColor(rgb: 0xFFEABA).cgColor
188 + } else {
189 + expirationView.backgroundColor = UIColor(rgb: 0xF1F2F4)
190 + expirationView.layer.borderColor = UIColor(rgb: 0xD2D6D9).cgColor
191 + }
192 + expirationLabel.font = UIFont(name: "PingLCG-Bold", size: 13)
193 + expirationLabel.textColor = UIColor(rgb: 0x002430)
155 194
195 + logoImageView.backgroundColor = UIColor(rgb: 0xFFFFFF)
196 + logoImageView.layer.cornerRadius = 10.0
197 +
156 // Logo — load from merchant_details img_preview (remote URL) 198 // Logo — load from merchant_details img_preview (remote URL)
157 if let merchantImgPreview = data.merchant_details?._img_preview, !merchantImgPreview.isEmpty { 199 if let merchantImgPreview = data.merchant_details?._img_preview, !merchantImgPreview.isEmpty {
158 self.logoImageURL = merchantImgPreview 200 self.logoImageURL = merchantImgPreview
......
1 <?xml version="1.0" encoding="UTF-8"?> 1 <?xml version="1.0" encoding="UTF-8"?>
2 -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23094" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> 2 +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3 <device id="retina6_12" orientation="portrait" appearance="light"/> 3 <device id="retina6_12" orientation="portrait" appearance="light"/>
4 <dependencies> 4 <dependencies>
5 - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/> 5 + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
6 <capability name="Safe area layout guides" minToolsVersion="9.0"/> 6 <capability name="Safe area layout guides" minToolsVersion="9.0"/>
7 <capability name="System colors in document resources" minToolsVersion="11.0"/> 7 <capability name="System colors in document resources" minToolsVersion="11.0"/>
8 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> 8 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
...@@ -18,153 +18,157 @@ ...@@ -18,153 +18,157 @@
18 <autoresizingMask key="autoresizingMask"/> 18 <autoresizingMask key="autoresizingMask"/>
19 <subviews> 19 <subviews>
20 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="x0X-za-HZ9" userLabel="Parent View"> 20 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="x0X-za-HZ9" userLabel="Parent View">
21 - <rect key="frame" x="22" y="0.0" width="276" height="292"/> 21 + <rect key="frame" x="18" y="0.0" width="284" height="175"/>
22 <subviews> 22 <subviews>
23 - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Ht-7Z-jiW" userLabel="Top View"> 23 + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fZ6-8K-DXr" userLabel="Bottom View">
24 - <rect key="frame" x="0.0" y="0.0" width="276" height="171"/> 24 + <rect key="frame" x="0.0" y="0.0" width="284" height="175"/>
25 <subviews> 25 <subviews>
26 - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="09g-Nj-HG4"> 26 + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Fcn-J9-gqq" userLabel="LogoImageView">
27 - <rect key="frame" x="0.0" y="0.0" width="276" height="171"/> 27 + <rect key="frame" x="10" y="10" width="67" height="67"/>
28 - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
29 - </imageView>
30 - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4xy-g8-Ukx" userLabel="Fav View">
31 - <rect key="frame" x="16" y="13" width="46" height="46"/>
32 <subviews> 28 <subviews>
33 - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="T91-Ra-dmT"> 29 + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="hM2-8T-4vX">
34 - <rect key="frame" x="0.0" y="0.0" width="46" height="46"/> 30 + <rect key="frame" x="10" y="10" width="47" height="47"/>
31 + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
35 </imageView> 32 </imageView>
36 </subviews> 33 </subviews>
37 - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> 34 + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
38 <constraints> 35 <constraints>
39 - <constraint firstAttribute="width" constant="46" id="Cc2-q8-pqb"/> 36 + <constraint firstAttribute="width" constant="67" id="9pk-g2-gcB"/>
40 - <constraint firstAttribute="trailing" secondItem="T91-Ra-dmT" secondAttribute="trailing" id="FHe-DH-ghP"/> 37 + <constraint firstAttribute="bottom" secondItem="hM2-8T-4vX" secondAttribute="bottom" constant="10" id="AQa-wS-Zt5"/>
41 - <constraint firstItem="T91-Ra-dmT" firstAttribute="top" secondItem="4xy-g8-Ukx" secondAttribute="top" id="HMT-10-TaU"/> 38 + <constraint firstAttribute="height" constant="67" id="FfF-lB-tUl"/>
42 - <constraint firstAttribute="bottom" secondItem="T91-Ra-dmT" secondAttribute="bottom" id="K7M-Vf-uo8"/> 39 + <constraint firstItem="hM2-8T-4vX" firstAttribute="top" secondItem="Fcn-J9-gqq" secondAttribute="top" constant="10" id="gus-oR-l57"/>
43 - <constraint firstItem="T91-Ra-dmT" firstAttribute="leading" secondItem="4xy-g8-Ukx" secondAttribute="leading" id="diD-2x-xIG"/> 40 + <constraint firstItem="hM2-8T-4vX" firstAttribute="leading" secondItem="Fcn-J9-gqq" secondAttribute="leading" constant="10" id="pn8-ti-KXs"/>
44 - <constraint firstAttribute="height" constant="46" id="gtM-XV-LnS"/> 41 + <constraint firstAttribute="trailing" secondItem="hM2-8T-4vX" secondAttribute="trailing" constant="10" id="qbD-TE-zfq"/>
45 </constraints> 42 </constraints>
46 </view> 43 </view>
47 - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dnZ-fa-M3o" userLabel="Discount View"> 44 + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9ju-6P-k6F" userLabel="Expiration View">
48 - <rect key="frame" x="179" y="14" width="84" height="84"/> 45 + <rect key="frame" x="197.66666666666666" y="10" width="76.333333333333343" height="34.333333333333336"/>
49 <subviews> 46 <subviews>
50 - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XeI-PB-wG7"> 47 + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="wkd-wh-r1w" userLabel="Expiration Image">
51 - <rect key="frame" x="5" y="31.666666666666664" width="74" height="21"/> 48 + <rect key="frame" x="10" y="10.333333333333332" width="14" height="14"/>
49 + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
50 + <constraints>
51 + <constraint firstAttribute="height" constant="14" id="6zU-V6-H7S"/>
52 + <constraint firstAttribute="width" constant="14" id="vf0-mS-97Q"/>
53 + </constraints>
54 + </imageView>
55 + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZgP-Qt-8og" userLabel="Expiration Label">
56 + <rect key="frame" x="28.000000000000004" y="6.9999999999999982" width="41.333333333333343" height="20.333333333333329"/>
52 <fontDescription key="fontDescription" type="system" pointSize="17"/> 57 <fontDescription key="fontDescription" type="system" pointSize="17"/>
53 <nil key="textColor"/> 58 <nil key="textColor"/>
54 <nil key="highlightedColor"/> 59 <nil key="highlightedColor"/>
55 </label> 60 </label>
56 </subviews> 61 </subviews>
57 - <color key="backgroundColor" systemColor="systemBackgroundColor"/> 62 + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
58 <constraints> 63 <constraints>
59 - <constraint firstAttribute="height" constant="84" id="8gp-O1-LgD"/> 64 + <constraint firstAttribute="trailing" secondItem="ZgP-Qt-8og" secondAttribute="trailing" constant="7" id="AwZ-gG-3PO"/>
60 - <constraint firstAttribute="width" constant="84" id="Mve-Yl-rsU"/> 65 + <constraint firstItem="wkd-wh-r1w" firstAttribute="centerY" secondItem="9ju-6P-k6F" secondAttribute="centerY" id="CnU-ts-AtX"/>
61 - <constraint firstItem="XeI-PB-wG7" firstAttribute="centerY" secondItem="dnZ-fa-M3o" secondAttribute="centerY" id="Rlp-4Z-urE"/> 66 + <constraint firstItem="ZgP-Qt-8og" firstAttribute="leading" secondItem="wkd-wh-r1w" secondAttribute="trailing" constant="4" id="PXB-bc-eTM"/>
62 - <constraint firstAttribute="trailing" secondItem="XeI-PB-wG7" secondAttribute="trailing" constant="5" id="dyI-UO-uvn"/> 67 + <constraint firstAttribute="bottom" secondItem="ZgP-Qt-8og" secondAttribute="bottom" constant="7" id="Wab-ed-1oR"/>
63 - <constraint firstItem="XeI-PB-wG7" firstAttribute="leading" secondItem="dnZ-fa-M3o" secondAttribute="leading" constant="5" id="eon-8Y-X7c"/> 68 + <constraint firstItem="wkd-wh-r1w" firstAttribute="leading" secondItem="9ju-6P-k6F" secondAttribute="leading" constant="10" id="e2L-4Z-2xN"/>
69 + <constraint firstItem="ZgP-Qt-8og" firstAttribute="top" secondItem="9ju-6P-k6F" secondAttribute="top" constant="7" id="gYV-rG-X6r"/>
64 </constraints> 70 </constraints>
65 </view> 71 </view>
66 - </subviews> 72 + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="YtM-Db-ygP" userLabel="InfoView">
67 - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> 73 + <rect key="frame" x="17" y="93" width="260" height="65"/>
68 - <constraints>
69 - <constraint firstItem="09g-Nj-HG4" firstAttribute="leading" secondItem="8Ht-7Z-jiW" secondAttribute="leading" id="1AO-0a-ZpN"/>
70 - <constraint firstItem="dnZ-fa-M3o" firstAttribute="top" secondItem="8Ht-7Z-jiW" secondAttribute="top" constant="14" id="AC6-zX-rmc"/>
71 - <constraint firstItem="09g-Nj-HG4" firstAttribute="top" secondItem="8Ht-7Z-jiW" secondAttribute="top" id="DYc-4B-bTE"/>
72 - <constraint firstAttribute="bottom" secondItem="09g-Nj-HG4" secondAttribute="bottom" id="Rsg-ZQ-0bM"/>
73 - <constraint firstItem="4xy-g8-Ukx" firstAttribute="leading" secondItem="8Ht-7Z-jiW" secondAttribute="leading" constant="16" id="U8A-vw-Pca"/>
74 - <constraint firstItem="4xy-g8-Ukx" firstAttribute="top" secondItem="8Ht-7Z-jiW" secondAttribute="top" constant="13" id="ZGF-7X-iSN"/>
75 - <constraint firstAttribute="trailing" secondItem="09g-Nj-HG4" secondAttribute="trailing" id="qwK-yM-rD6"/>
76 - <constraint firstAttribute="trailing" secondItem="dnZ-fa-M3o" secondAttribute="trailing" constant="13" id="zAy-FM-iqd"/>
77 - </constraints>
78 - </view>
79 - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fZ6-8K-DXr" userLabel="Bottom View">
80 - <rect key="frame" x="0.0" y="171" width="276" height="121"/>
81 - <subviews>
82 - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="YtM-Db-ygP">
83 - <rect key="frame" x="16" y="17" width="130" height="93"/>
84 <subviews> 74 <subviews>
85 - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="63p-dI-yTa"> 75 + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mO2-Xp-IVu" userLabel="Merchant Name Label">
86 - <rect key="frame" x="0.0" y="0.0" width="130" height="21"/> 76 + <rect key="frame" x="0.0" y="0.0" width="260" height="20.333333333333332"/>
87 <fontDescription key="fontDescription" type="system" pointSize="17"/> 77 <fontDescription key="fontDescription" type="system" pointSize="17"/>
88 <nil key="textColor"/> 78 <nil key="textColor"/>
89 <nil key="highlightedColor"/> 79 <nil key="highlightedColor"/>
90 </label> 80 </label>
91 - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="z30-VZ-8h3"> 81 + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="63p-dI-yTa">
92 - <rect key="frame" x="0.0" y="23" width="130" height="20.333333333333329"/> 82 + <rect key="frame" x="0.0" y="22.333333333333343" width="211" height="21"/>
93 <fontDescription key="fontDescription" type="system" pointSize="17"/> 83 <fontDescription key="fontDescription" type="system" pointSize="17"/>
94 <nil key="textColor"/> 84 <nil key="textColor"/>
95 <nil key="highlightedColor"/> 85 <nil key="highlightedColor"/>
96 </label> 86 </label>
97 - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PZ3-qH-3XH"> 87 + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="z30-VZ-8h3">
98 - <rect key="frame" x="0.0" y="72" width="130" height="21"/> 88 + <rect key="frame" x="0.0" y="45.333333333333329" width="211" height="20.333333333333329"/>
99 <fontDescription key="fontDescription" type="system" pointSize="17"/> 89 <fontDescription key="fontDescription" type="system" pointSize="17"/>
100 <nil key="textColor"/> 90 <nil key="textColor"/>
101 <nil key="highlightedColor"/> 91 <nil key="highlightedColor"/>
102 </label> 92 </label>
93 + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="cko-1v-wDx" userLabel="ArrowView">
94 + <rect key="frame" x="221" y="26" width="39" height="39"/>
95 + <subviews>
96 + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZRF-CC-IKd">
97 + <rect key="frame" x="11" y="13" width="17" height="13"/>
98 + <constraints>
99 + <constraint firstAttribute="height" constant="13" id="ZR7-aH-0zM"/>
100 + <constraint firstAttribute="width" constant="17" id="lGj-BM-N9w"/>
101 + </constraints>
102 + </imageView>
103 + </subviews>
104 + <color key="backgroundColor" systemColor="systemBackgroundColor"/>
105 + <constraints>
106 + <constraint firstAttribute="height" constant="39" id="Zh7-U5-7dh"/>
107 + <constraint firstAttribute="width" constant="39" id="eIh-Ke-CJw"/>
108 + <constraint firstItem="ZRF-CC-IKd" firstAttribute="centerY" secondItem="cko-1v-wDx" secondAttribute="centerY" id="jyW-Ir-XED"/>
109 + <constraint firstItem="ZRF-CC-IKd" firstAttribute="centerX" secondItem="cko-1v-wDx" secondAttribute="centerX" id="sdA-PR-KMw"/>
110 + </constraints>
111 + </view>
103 </subviews> 112 </subviews>
104 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> 113 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
105 <constraints> 114 <constraints>
115 + <constraint firstItem="63p-dI-yTa" firstAttribute="top" secondItem="mO2-Xp-IVu" secondAttribute="bottom" constant="2" id="1rR-YP-pAJ"/>
116 + <constraint firstItem="mO2-Xp-IVu" firstAttribute="top" secondItem="YtM-Db-ygP" secondAttribute="top" id="28l-ae-BOD"/>
117 + <constraint firstAttribute="bottom" secondItem="z30-VZ-8h3" secondAttribute="bottom" id="6k0-Nw-BSn"/>
106 <constraint firstItem="z30-VZ-8h3" firstAttribute="leading" secondItem="YtM-Db-ygP" secondAttribute="leading" id="DHf-fQ-lXA"/> 118 <constraint firstItem="z30-VZ-8h3" firstAttribute="leading" secondItem="YtM-Db-ygP" secondAttribute="leading" id="DHf-fQ-lXA"/>
107 - <constraint firstAttribute="trailing" secondItem="z30-VZ-8h3" secondAttribute="trailing" id="DnI-99-e8v"/> 119 + <constraint firstAttribute="trailing" secondItem="mO2-Xp-IVu" secondAttribute="trailing" id="QeV-2c-rkN"/>
108 - <constraint firstAttribute="trailing" secondItem="PZ3-qH-3XH" secondAttribute="trailing" id="JA5-Am-dmE"/> 120 + <constraint firstItem="cko-1v-wDx" firstAttribute="leading" secondItem="z30-VZ-8h3" secondAttribute="trailing" constant="10" id="ZCX-vU-yAg"/>
109 - <constraint firstItem="63p-dI-yTa" firstAttribute="top" secondItem="YtM-Db-ygP" secondAttribute="top" id="Kfa-m3-rWW"/> 121 + <constraint firstAttribute="trailing" secondItem="cko-1v-wDx" secondAttribute="trailing" id="bMz-0j-PnH"/>
110 - <constraint firstAttribute="trailing" secondItem="63p-dI-yTa" secondAttribute="trailing" id="bxa-5c-OE6"/>
111 <constraint firstItem="63p-dI-yTa" firstAttribute="leading" secondItem="YtM-Db-ygP" secondAttribute="leading" id="gk1-Zr-MIl"/> 122 <constraint firstItem="63p-dI-yTa" firstAttribute="leading" secondItem="YtM-Db-ygP" secondAttribute="leading" id="gk1-Zr-MIl"/>
112 - <constraint firstItem="PZ3-qH-3XH" firstAttribute="leading" secondItem="YtM-Db-ygP" secondAttribute="leading" id="hE4-2m-iZk"/>
113 <constraint firstItem="z30-VZ-8h3" firstAttribute="top" secondItem="63p-dI-yTa" secondAttribute="bottom" constant="2" id="hfr-Gi-omT"/> 123 <constraint firstItem="z30-VZ-8h3" firstAttribute="top" secondItem="63p-dI-yTa" secondAttribute="bottom" constant="2" id="hfr-Gi-omT"/>
114 - <constraint firstAttribute="bottom" secondItem="PZ3-qH-3XH" secondAttribute="bottom" id="xMs-dh-ybb"/> 124 + <constraint firstItem="mO2-Xp-IVu" firstAttribute="leading" secondItem="YtM-Db-ygP" secondAttribute="leading" id="hkf-Ek-pmr"/>
125 + <constraint firstItem="cko-1v-wDx" firstAttribute="leading" secondItem="63p-dI-yTa" secondAttribute="trailing" constant="10" id="m2R-vr-DCQ"/>
126 + <constraint firstAttribute="bottom" secondItem="cko-1v-wDx" secondAttribute="bottom" id="pND-ei-iWF"/>
115 </constraints> 127 </constraints>
116 </view> 128 </view>
117 - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="hM2-8T-4vX">
118 - <rect key="frame" x="164" y="17" width="84" height="84"/>
119 - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
120 - <constraints>
121 - <constraint firstAttribute="width" constant="84" id="6to-Dv-ukR"/>
122 - <constraint firstAttribute="height" constant="84" id="QjU-rO-Uqx"/>
123 - </constraints>
124 - </imageView>
125 </subviews> 129 </subviews>
126 - <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> 130 + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
127 <constraints> 131 <constraints>
128 - <constraint firstAttribute="bottom" secondItem="YtM-Db-ygP" secondAttribute="bottom" constant="11" id="6ul-bC-Jpj"/> 132 + <constraint firstAttribute="bottom" secondItem="YtM-Db-ygP" secondAttribute="bottom" constant="17" id="6ul-bC-Jpj"/>
129 - <constraint firstItem="YtM-Db-ygP" firstAttribute="leading" secondItem="fZ6-8K-DXr" secondAttribute="leading" constant="16" id="C7J-bI-8ap"/> 133 + <constraint firstItem="YtM-Db-ygP" firstAttribute="top" secondItem="Fcn-J9-gqq" secondAttribute="bottom" constant="16" id="8dr-sO-7qI"/>
130 - <constraint firstItem="hM2-8T-4vX" firstAttribute="top" secondItem="fZ6-8K-DXr" secondAttribute="top" constant="17" id="HBF-J2-ed0"/> 134 + <constraint firstItem="YtM-Db-ygP" firstAttribute="leading" secondItem="fZ6-8K-DXr" secondAttribute="leading" constant="17" id="C7J-bI-8ap"/>
131 - <constraint firstItem="hM2-8T-4vX" firstAttribute="leading" secondItem="YtM-Db-ygP" secondAttribute="trailing" constant="18" id="WML-nR-cLh"/> 135 + <constraint firstItem="Fcn-J9-gqq" firstAttribute="top" secondItem="fZ6-8K-DXr" secondAttribute="top" constant="10" id="KVH-GO-BRe"/>
132 - <constraint firstItem="YtM-Db-ygP" firstAttribute="top" secondItem="fZ6-8K-DXr" secondAttribute="top" constant="17" id="f0H-m3-OAP"/> 136 + <constraint firstItem="Fcn-J9-gqq" firstAttribute="leading" secondItem="fZ6-8K-DXr" secondAttribute="leading" constant="10" id="Oph-8j-Guf"/>
133 - <constraint firstAttribute="trailing" secondItem="hM2-8T-4vX" secondAttribute="trailing" constant="28" id="jKa-nW-g41"/> 137 + <constraint firstAttribute="trailing" secondItem="9ju-6P-k6F" secondAttribute="trailing" constant="10" id="aON-Fo-H1J"/>
134 - <constraint firstAttribute="height" constant="121" id="ptz-uq-5rq"/> 138 + <constraint firstAttribute="trailing" secondItem="YtM-Db-ygP" secondAttribute="trailing" constant="7" id="ghj-41-twp"/>
139 + <constraint firstItem="9ju-6P-k6F" firstAttribute="top" secondItem="fZ6-8K-DXr" secondAttribute="top" constant="10" id="uXH-9L-517"/>
135 </constraints> 140 </constraints>
136 </view> 141 </view>
137 </subviews> 142 </subviews>
138 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> 143 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
139 <constraints> 144 <constraints>
140 <constraint firstItem="fZ6-8K-DXr" firstAttribute="leading" secondItem="x0X-za-HZ9" secondAttribute="leading" id="DKa-9V-lma"/> 145 <constraint firstItem="fZ6-8K-DXr" firstAttribute="leading" secondItem="x0X-za-HZ9" secondAttribute="leading" id="DKa-9V-lma"/>
141 - <constraint firstAttribute="trailing" secondItem="8Ht-7Z-jiW" secondAttribute="trailing" id="LK1-Cz-JUh"/> 146 + <constraint firstItem="fZ6-8K-DXr" firstAttribute="top" secondItem="x0X-za-HZ9" secondAttribute="top" id="cdY-T8-x47"/>
142 - <constraint firstItem="8Ht-7Z-jiW" firstAttribute="top" secondItem="x0X-za-HZ9" secondAttribute="top" id="P9v-fu-Mhs"/> 147 + <constraint firstAttribute="height" constant="175" id="lA6-op-vsG"/>
143 - <constraint firstAttribute="height" constant="292" id="lA6-op-vsG"/>
144 <constraint firstAttribute="trailing" secondItem="fZ6-8K-DXr" secondAttribute="trailing" id="lAU-n1-MKy"/> 148 <constraint firstAttribute="trailing" secondItem="fZ6-8K-DXr" secondAttribute="trailing" id="lAU-n1-MKy"/>
145 - <constraint firstItem="8Ht-7Z-jiW" firstAttribute="leading" secondItem="x0X-za-HZ9" secondAttribute="leading" id="sK9-Tw-Yqn"/>
146 <constraint firstAttribute="bottom" secondItem="fZ6-8K-DXr" secondAttribute="bottom" id="u4z-JD-qby"/> 149 <constraint firstAttribute="bottom" secondItem="fZ6-8K-DXr" secondAttribute="bottom" id="u4z-JD-qby"/>
147 - <constraint firstItem="fZ6-8K-DXr" firstAttribute="top" secondItem="8Ht-7Z-jiW" secondAttribute="bottom" id="v5X-nL-hKm"/>
148 </constraints> 150 </constraints>
149 </view> 151 </view>
150 </subviews> 152 </subviews>
151 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> 153 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
152 <constraints> 154 <constraints>
153 - <constraint firstItem="x0X-za-HZ9" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="22" id="SzO-Fk-ZCB"/> 155 + <constraint firstItem="x0X-za-HZ9" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="18" id="SzO-Fk-ZCB"/>
154 <constraint firstItem="x0X-za-HZ9" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="uQN-BA-o6w"/> 156 <constraint firstItem="x0X-za-HZ9" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="uQN-BA-o6w"/>
155 - <constraint firstAttribute="trailing" secondItem="x0X-za-HZ9" secondAttribute="trailing" constant="22" id="vi4-dI-lx3"/> 157 + <constraint firstAttribute="trailing" secondItem="x0X-za-HZ9" secondAttribute="trailing" constant="18" id="vi4-dI-lx3"/>
156 - <constraint firstAttribute="bottom" secondItem="x0X-za-HZ9" secondAttribute="bottom" constant="19" id="zsL-aX-JRL"/> 158 + <constraint firstAttribute="bottom" secondItem="x0X-za-HZ9" secondAttribute="bottom" constant="16" id="zsL-aX-JRL"/>
157 </constraints> 159 </constraints>
158 </tableViewCellContentView> 160 </tableViewCellContentView>
159 <viewLayoutGuide key="safeArea" id="njF-e1-oar"/> 161 <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
160 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> 162 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
161 <connections> 163 <connections>
162 - <outlet property="bannerImage" destination="09g-Nj-HG4" id="1Ux-ZI-rVQ"/> 164 + <outlet property="arrowImage" destination="ZRF-CC-IKd" id="9Wh-O0-Lzr"/>
163 - <outlet property="discountLabel" destination="XeI-PB-wG7" id="Kyy-aE-GgR"/> 165 + <outlet property="arrowView" destination="cko-1v-wDx" id="mMW-Dp-q6K"/>
164 - <outlet property="discountView" destination="dnZ-fa-M3o" id="fht-MW-ToM"/> 166 + <outlet property="expirationImage" destination="wkd-wh-r1w" id="5ng-6x-HtH"/>
165 - <outlet property="expirationLabel" destination="PZ3-qH-3XH" id="vIb-60-EVp"/> 167 + <outlet property="expirationLabel" destination="ZgP-Qt-8og" id="bNv-Rl-m7b"/>
166 - <outlet property="favoriteImage" destination="T91-Ra-dmT" id="leZ-Tz-zo8"/> 168 + <outlet property="expirationView" destination="9ju-6P-k6F" id="YiF-Z5-Q6s"/>
167 <outlet property="logoImage" destination="hM2-8T-4vX" id="Zqv-yp-lrD"/> 169 <outlet property="logoImage" destination="hM2-8T-4vX" id="Zqv-yp-lrD"/>
170 + <outlet property="logoImageView" destination="Fcn-J9-gqq" id="mAE-fP-e6I"/>
171 + <outlet property="merchantNameLabel" destination="mO2-Xp-IVu" id="Don-n2-JqF"/>
168 <outlet property="parentView" destination="x0X-za-HZ9" id="OE0-tT-IPd"/> 172 <outlet property="parentView" destination="x0X-za-HZ9" id="OE0-tT-IPd"/>
169 <outlet property="subtitleLabel" destination="z30-VZ-8h3" id="s40-MP-4Jn"/> 173 <outlet property="subtitleLabel" destination="z30-VZ-8h3" id="s40-MP-4Jn"/>
170 <outlet property="titleLabel" destination="63p-dI-yTa" id="YOe-I2-Cly"/> 174 <outlet property="titleLabel" destination="63p-dI-yTa" id="YOe-I2-Cly"/>
......
...@@ -96,7 +96,7 @@ public class CouponSetItemModel { ...@@ -96,7 +96,7 @@ public class CouponSetItemModel {
96 /// Try multiple date formats and return the first successful parse 96 /// Try multiple date formats and return the first successful parse
97 /// - Parameter dateString: The date string to parse 97 /// - Parameter dateString: The date string to parse
98 /// - Returns: Parsed Date or nil if no format matched 98 /// - Returns: Parsed Date or nil if no format matched
99 - private static func parseDate(_ dateString: String) -> Date? { 99 + static func parseDate(_ dateString: String) -> Date? {
100 let formatter = DateFormatter() 100 let formatter = DateFormatter()
101 for format in supportedDateFormats { 101 for format in supportedDateFormats {
102 formatter.dateFormat = format 102 formatter.dateFormat = format
...@@ -411,7 +411,8 @@ public class CouponItemModel { ...@@ -411,7 +411,8 @@ public class CouponItemModel {
411 public let couponset_uuid: String? 411 public let couponset_uuid: String?
412 public let name: String? 412 public let name: String?
413 public let image: String? 413 public let image: String?
414 - public let expiration: String? 414 + public let expiration: String? // raw string from API e.g. "2026-06-30 11:59:00"
415 + public let expiration_formatted: String? // pre-formatted "dd/MM/yyyy" for display
415 public let created: String? 416 public let created: String?
416 public let description: String? 417 public let description: String?
417 public let discount: String? 418 public let discount: String?
...@@ -495,15 +496,16 @@ public class CouponItemModel { ...@@ -495,15 +496,16 @@ public class CouponItemModel {
495 } 496 }
496 // <== 497 // <==
497 498
498 - let expirationString = dictionary["expiration"] as? String? ?? "" 499 + let expirationString = dictionary["expiration"] as? String ?? ""
500 + self.expiration = expirationString
501 +
499 let dateFormatter = DateFormatter() 502 let dateFormatter = DateFormatter()
500 dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" 503 dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
501 - if let date = dateFormatter.date(from: expirationString ?? "") { 504 + if let date = dateFormatter.date(from: expirationString) {
502 dateFormatter.dateFormat = "dd/MM/yyyy" 505 dateFormatter.dateFormat = "dd/MM/yyyy"
503 - let resultString = dateFormatter.string(from: date) 506 + self.expiration_formatted = dateFormatter.string(from: date)
504 - self.expiration = resultString
505 } else { 507 } else {
506 - self.expiration = "" 508 + self.expiration_formatted = ""
507 } 509 }
508 510
509 // Extract created date: try changes_dates.created first (universal coupons), then top-level created 511 // Extract created date: try changes_dates.created first (universal coupons), then top-level created
...@@ -558,6 +560,26 @@ public class CouponItemModel { ...@@ -558,6 +560,26 @@ public class CouponItemModel {
558 public func setCouponSetData(_ couponSet: CouponSetItemModel) { 560 public func setCouponSetData(_ couponSet: CouponSetItemModel) {
559 self.couponset_data = couponSet 561 self.couponset_data = couponSet
560 } 562 }
563 +
564 + /// Returns the expiration as a Date object for date comparisons (e.g. days-left calculation)
565 + public var expirationDate: Date? {
566 + guard let raw = self.expiration, !raw.isEmpty else { return nil }
567 + return CouponSetItemModel.parseDate(raw)
568 + }
569 +
570 + /// Format expiration date with a custom output format.
571 + /// Uses the same multi-format parser as CouponSetItemModel.
572 + /// - Parameter format: DateFormatter format string (e.g. "MMMM d, yyyy", "dd/MM/yyyy")
573 + /// - Returns: Formatted date string, or empty string if expiration is absent or unparseable
574 + public func formattedExpiration(format: String) -> String {
575 + guard let raw = self.expiration, !raw.isEmpty else { return "" }
576 + if let date = CouponSetItemModel.parseDate(raw) {
577 + let formatter = DateFormatter()
578 + formatter.dateFormat = format
579 + return formatter.string(from: date)
580 + }
581 + return ""
582 + }
561 } 583 }
562 584
563 public class RedeemedSMHistoryModel { 585 public class RedeemedSMHistoryModel {
......
...@@ -383,7 +383,7 @@ extension MyCouponsViewController: UITableViewDelegate, UITableViewDataSource { ...@@ -383,7 +383,7 @@ extension MyCouponsViewController: UITableViewDelegate, UITableViewDataSource {
383 } 383 }
384 384
385 public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 385 public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
386 - if (indexPath.section <= 3) { 386 + if (indexPath.section <= 1) {
387 // Do nothing 387 // Do nothing
388 } else { 388 } else {
389 if let items = self.filteredOffersSection?.items, 389 if let items = self.filteredOffersSection?.items,
......