Manos Chorianopoulos

MyCouponsViewController part1

Showing 18 changed files with 681 additions and 0 deletions
......@@ -73,6 +73,10 @@
1EDD0AC62E0D68B6005E162B /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = 1EDD0AC52E0D68B6005E162B /* SQLite */; };
622DA0AE2F51CAEE00308AB8 /* CouponsetViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 622DA0AD2F51CAEE00308AB8 /* CouponsetViewController.xib */; };
622DA0AF2F51CAEE00308AB8 /* CouponsetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 622DA0AC2F51CAEE00308AB8 /* CouponsetViewController.swift */; };
62A0A6D32F67FEDC00508534 /* MyCouponsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A0A6D12F67FEDC00508534 /* MyCouponsViewController.swift */; };
62A0A6D42F67FEDC00508534 /* MyCouponsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62A0A6D22F67FEDC00508534 /* MyCouponsViewController.xib */; };
62A0A6D82F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A0A6D62F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift */; };
62A0A6D92F680C6A00508534 /* MyCouponsHeaderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62A0A6D72F680C6A00508534 /* MyCouponsHeaderTableViewCell.xib */; };
7630AD9A6242D60846D6750C /* Pods_SwiftWarplyFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0D5F56DD4E5371A50AD2D87 /* Pods_SwiftWarplyFramework.framework */; };
A07936762885E9CC00064122 /* UIColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A07936752885E9CC00064122 /* UIColorExtensions.swift */; };
E6A77853282933340045BBA8 /* SwiftWarplyFramework.docc in Sources */ = {isa = PBXBuildFile; fileRef = E6A77852282933340045BBA8 /* SwiftWarplyFramework.docc */; };
......@@ -150,6 +154,10 @@
1EDD0ABC2E0D308A005E162B /* XIBLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XIBLoader.swift; sourceTree = "<group>"; };
622DA0AC2F51CAEE00308AB8 /* CouponsetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CouponsetViewController.swift; sourceTree = "<group>"; };
622DA0AD2F51CAEE00308AB8 /* CouponsetViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CouponsetViewController.xib; sourceTree = "<group>"; };
62A0A6D12F67FEDC00508534 /* MyCouponsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCouponsViewController.swift; sourceTree = "<group>"; };
62A0A6D22F67FEDC00508534 /* MyCouponsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MyCouponsViewController.xib; sourceTree = "<group>"; };
62A0A6D62F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCouponsHeaderTableViewCell.swift; sourceTree = "<group>"; };
62A0A6D72F680C6A00508534 /* MyCouponsHeaderTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MyCouponsHeaderTableViewCell.xib; sourceTree = "<group>"; };
A07936752885E9CC00064122 /* UIColorExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorExtensions.swift; sourceTree = "<group>"; };
A9B7BE01A4E812DE49866EF8 /* Pods-SwiftWarplyFramework.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftWarplyFramework.debug.xcconfig"; path = "Target Support Files/Pods-SwiftWarplyFramework/Pods-SwiftWarplyFramework.debug.xcconfig"; sourceTree = "<group>"; };
B9EB8A451EF0C5AD75094EEE /* Pods-SwiftWarplyFramework.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftWarplyFramework.release.xcconfig"; path = "Target Support Files/Pods-SwiftWarplyFramework/Pods-SwiftWarplyFramework.release.xcconfig"; sourceTree = "<group>"; };
......@@ -328,6 +336,7 @@
1E917CD32DDF6472002221D8 /* MyRewardsViewController */,
1E917CD82DDF687E002221D8 /* CouponViewController */,
1E917CDD2DDF68D8002221D8 /* ProfileViewController */,
62A0A6D02F67FE8100508534 /* MyCouponsViewController */,
);
path = screens;
sourceTree = "<group>";
......@@ -335,6 +344,7 @@
1EA8E5BC2DDF34FB00CD3418 /* cells */ = {
isa = PBXGroup;
children = (
62A0A6D52F680C1E00508534 /* MyCouponsHeaderTableViewCell */,
1EDBAF122DE844C500911E79 /* ProfileFilterCollectionViewCell */,
1EDBAF012DE8439000911E79 /* ProfileCouponTableViewCell */,
1EDBAF002DE842A700911E79 /* ProfileCouponFiltersTableViewCell */,
......@@ -439,6 +449,24 @@
path = CouponsetViewController;
sourceTree = "<group>";
};
62A0A6D02F67FE8100508534 /* MyCouponsViewController */ = {
isa = PBXGroup;
children = (
62A0A6D12F67FEDC00508534 /* MyCouponsViewController.swift */,
62A0A6D22F67FEDC00508534 /* MyCouponsViewController.xib */,
);
path = MyCouponsViewController;
sourceTree = "<group>";
};
62A0A6D52F680C1E00508534 /* MyCouponsHeaderTableViewCell */ = {
isa = PBXGroup;
children = (
62A0A6D62F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift */,
62A0A6D72F680C6A00508534 /* MyCouponsHeaderTableViewCell.xib */,
);
path = MyCouponsHeaderTableViewCell;
sourceTree = "<group>";
};
98AD36FA62350CEABCD961A7 /* Frameworks */ = {
isa = PBXGroup;
children = (
......@@ -611,12 +639,14 @@
E6A77A38282BC3530045BBA8 /* Media.xcassets in Resources */,
1E116F682DE845B1009AE791 /* ProfileFilterCollectionViewCell.xib in Resources */,
1EB4F4252DE09AAC00D934C0 /* MyRewardsBannerOffersScrollTableViewCell.xib in Resources */,
62A0A6D92F680C6A00508534 /* MyCouponsHeaderTableViewCell.xib in Resources */,
1EA8E5C02DDF427A00CD3418 /* PingLCG-Bold.otf in Resources */,
622DA0AE2F51CAEE00308AB8 /* CouponsetViewController.xib in Resources */,
1E64E1832DE48E0600543217 /* MyRewardsOfferCollectionViewCell.xib in Resources */,
1EA8E5C12DDF427A00CD3418 /* PingLCG-Light.otf in Resources */,
1EA8E5C22DDF427A00CD3418 /* PingLCG-Regular.otf in Resources */,
1EB4F42B2DE0A0AF00D934C0 /* MyRewardsOffersScrollTableViewCell.xib in Resources */,
62A0A6D42F67FEDC00508534 /* MyCouponsViewController.xib in Resources */,
1E66E4972E30F9F200BCEF9D /* MyRewardsProfileInfoTableViewCell.xib in Resources */,
1EDBAF102DE8443B00911E79 /* ProfileHeaderTableViewCell.xib in Resources */,
1E917CDB2DDF68C7002221D8 /* CouponViewController.xib in Resources */,
......@@ -678,6 +708,7 @@
622DA0AF2F51CAEE00308AB8 /* CouponsetViewController.swift in Sources */,
1E089DFE2DF87C39007459F1 /* Coupon.swift in Sources */,
1E089DFF2DF87C39007459F1 /* OfferModel.swift in Sources */,
62A0A6D82F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift in Sources */,
1ED41E4C2DE0C24D00836ABA /* MyRewardsBannerOfferCollectionViewCell.swift in Sources */,
E6A778E6282933E60045BBA8 /* MyEmptyClass.swift in Sources */,
1E917CE12DDF6909002221D8 /* ProfileViewController.swift in Sources */,
......@@ -689,6 +720,7 @@
E6A77955282933E70045BBA8 /* ViewControllerExtensions.swift in Sources */,
A07936762885E9CC00064122 /* UIColorExtensions.swift in Sources */,
1EB4F42C2DE0A0AF00D934C0 /* MyRewardsOffersScrollTableViewCell.swift in Sources */,
62A0A6D32F67FEDC00508534 /* MyCouponsViewController.swift in Sources */,
1E089E022DF87CCF007459F1 /* WarplySDK.swift in Sources */,
1E0E723B2E0C3AE400BC926F /* DatabaseConfiguration.swift in Sources */,
1E0E723C2E0C3AE400BC926F /* WarplyConfiguration.swift in Sources */,
......
{
"images" : [
{
"filename" : "ic_forward.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_forward 1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_forward 2.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "search.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "search 1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "search 2.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "search_circle.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "search_circle 1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "search_circle 2.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
//
// MyCouponsHeaderTableViewCell.swift
// SwiftWarplyFramework
//
// Created by Manos Chorianopoulos on 16/3/26.
//
import UIKit
// protocol MyCouponsHeaderTableViewCellDelegate: AnyObject {
// func didTapSearchButton()
// }
@objc(MyCouponsHeaderTableViewCell)
public class MyCouponsHeaderTableViewCell: UITableViewCell {
// MARK: - IBOutlets
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var searchButton: UIButton!
// MARK: - Delegate
// weak var delegate: MyCouponsHeaderTableViewCellDelegate?
// MARK: - Lifecycle
public override func awakeFromNib() {
super.awakeFromNib()
selectionStyle = .none
// Title
titleLabel.font = UIFont(name: "PingLCG-Bold", size: 26)
?? UIFont.boldSystemFont(ofSize: 26)
titleLabel.textColor = UIColor(rgb: 0x1D2023)
titleLabel.text = "My Coupons"
// Gear button: render image as-is (no tint) so the gray circle style is preserved
let gearImage = UIImage(named: "search_circle", in: Bundle.frameworkResourceBundle, compatibleWith: nil)?
.withRenderingMode(.alwaysOriginal)
searchButton.setImage(gearImage, for: .normal)
searchButton.setTitle("", for: .normal)
// searchButton.addTarget(self, action: #selector(searchButtonTapped), for: .touchUpInside)
}
public override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
// MARK: - Actions
// @objc private func searchButtonTapped() {
// delegate?.didTapSearchButton()
// }
}
<?xml version="1.0" encoding="UTF-8"?>
<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">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="MyCouponsHeaderTableViewCell" customModule="SwiftWarplyFramework" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9zr-3O-Ndy" userLabel="ParentView">
<rect key="frame" x="0.0" y="0.0" width="320" height="30"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="My Coupons" textAlignment="natural" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QuH-xv-rPx" userLabel="TitleLabel">
<rect key="frame" x="18" y="-0.66666666666666607" width="156.33333333333334" height="31.333333333333329"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="26"/>
<color key="textColor" red="0.067000000000000004" green="0.067000000000000004" blue="0.067000000000000004" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NOc-fo-goN" userLabel="SearchButton">
<rect key="frame" x="254" y="0.0" width="48" height="30"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" constant="48" id="Ubc-A7-ncs"/>
<constraint firstAttribute="height" constant="48" id="boQ-lu-bZd"/>
</constraints>
<state key="normal" title=""/>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="QuH-xv-rPx" firstAttribute="centerY" secondItem="NOc-fo-goN" secondAttribute="centerY" id="7Xg-4c-pen"/>
<constraint firstItem="NOc-fo-goN" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="QuH-xv-rPx" secondAttribute="trailing" constant="8" id="Bd4-gD-OFp"/>
<constraint firstItem="NOc-fo-goN" firstAttribute="top" secondItem="9zr-3O-Ndy" secondAttribute="top" id="G0c-Iw-ghG"/>
<constraint firstAttribute="bottom" secondItem="NOc-fo-goN" secondAttribute="bottom" id="gG3-2v-iwZ"/>
<constraint firstAttribute="trailing" secondItem="NOc-fo-goN" secondAttribute="trailing" constant="18" id="o71-Y7-vdQ"/>
<constraint firstItem="QuH-xv-rPx" firstAttribute="leading" secondItem="9zr-3O-Ndy" secondAttribute="leading" constant="18" id="pLn-uL-UNA"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="9zr-3O-Ndy" secondAttribute="bottom" id="Icc-z0-HGG"/>
<constraint firstAttribute="trailing" secondItem="9zr-3O-Ndy" secondAttribute="trailing" id="Inc-8d-l15"/>
<constraint firstItem="9zr-3O-Ndy" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="UT5-S3-Caq"/>
<constraint firstItem="9zr-3O-Ndy" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="xZK-RL-O9W"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="searchButton" destination="NOc-fo-goN" id="vyT-JX-Kgo"/>
<outlet property="titleLabel" destination="QuH-xv-rPx" id="q9e-Nt-mZk"/>
</connections>
<point key="canvasLocation" x="141" y="154"/>
</tableViewCell>
</objects>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
//
// MyCouponsViewController.swift
// SwiftWarplyFramework
//
// Created by Manos Chorianopoulos on 16/3/26.
//
import UIKit
@objc public class MyCouponsViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
// MARK: - Initializers
public convenience init() {
self.init(nibName: "MyCouponsViewController", bundle: Bundle.frameworkBundle)
}
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
// MARK: - Dynamic Data
var allCoupons: [CouponItemModel] = []
// MARK: - Loading State
private var isLoading: Bool = true
private var loadingIndicator: UIActivityIndicatorView?
let couponFilters: [CouponFilterModel] = [
CouponFilterModel(title: "Ενεργά"),
CouponFilterModel(title: "Αγαπημένα"),
CouponFilterModel(title: "Εξαργυρωμένα")
]
var couponFilterSelected: CouponFilterModel = CouponFilterModel(title: "Ενεργά")
var forYouOffersSection: SectionModel?
var activeOffersSection: SectionModel?
var favoriteOffersSection: SectionModel?
var redeemedOffersSection: SectionModel?
var filteredOffersSection: SectionModel?
public override func viewDidLoad() {
super.viewDidLoad()
// Show navigation bar for this screen (with back button)
self.navigationController?.setNavigationBarHidden(false, animated: false)
setBackButton()
setNavigationTitle("Το προφίλ μου")
// UPDATED: Safe XIB registration with error handling
registerTableViewCells()
// Set up table view
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableView.automaticDimension
// Set up loading indicator
setupLoadingIndicator()
// Fetch coupons from API
fetchCoupons()
}
// NEW: Safe XIB registration method
private func registerTableViewCells() {
let cellConfigs = [
("MyCouponsHeaderTableViewCell", "MyCouponsHeaderTableViewCell"),
("ProfileQuestionnaireTableViewCell", "ProfileQuestionnaireTableViewCell"),
("MyRewardsOffersScrollTableViewCell", "MyRewardsOffersScrollTableViewCell"),
("ProfileCouponFiltersTableViewCell", "ProfileCouponFiltersTableViewCell"),
("ProfileCouponTableViewCell", "ProfileCouponTableViewCell")
]
for (nibName, identifier) in cellConfigs {
XIBLoader.registerTableViewCell(
tableView,
cellClass: UITableViewCell.self,
nibName: nibName,
identifier: identifier
)
}
}
// MARK: - Loading Indicator
private func setupLoadingIndicator() {
let indicator = UIActivityIndicatorView(style: .large)
indicator.color = UIColor(rgb: 0x000F1E)
indicator.translatesAutoresizingMaskIntoConstraints = false
indicator.hidesWhenStopped = true
view.addSubview(indicator)
NSLayoutConstraint.activate([
indicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
indicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
self.loadingIndicator = indicator
}
private func showLoading() {
isLoading = true
loadingIndicator?.startAnimating()
tableView.alpha = 0.3
tableView.isUserInteractionEnabled = false
}
private func hideLoading() {
isLoading = false
loadingIndicator?.stopAnimating()
tableView.alpha = 1.0
tableView.isUserInteractionEnabled = true
}
// MARK: - Data Fetching
private func fetchCoupons() {
showLoading()
// Initialize sections with empty data first (so table view can render skeleton)
initializeSections()
WarplySDK.shared.getCouponsUniversal({ [weak self] couponsData in
guard let self = self else { return }
if let coupons = couponsData {
self.allCoupons = coupons
print("✅ [MyCouponsVC] Fetched \(coupons.count) coupons")
// Debug: Print coupon statuses
let activeCount = coupons.filter { $0.status == 1 }.count
let redeemedCount = coupons.filter { $0.status == 0 }.count
let expiredCount = coupons.filter { $0.status == -1 }.count
print(" Active: \(activeCount), Redeemed: \(redeemedCount), Expired: \(expiredCount)")
} else {
self.allCoupons = []
print("⚠️ [MyCouponsVC] No coupons received")
}
self.initializeSections()
self.hideLoading()
}, failureCallback: { [weak self] errorCode in
guard let self = self else { return }
print("❌ [MyCouponsVC] Failed to fetch coupons, error: \(errorCode)")
self.allCoupons = []
self.initializeSections()
self.hideLoading()
})
}
// MARK: - Section Initialization
func initializeSections() {
// Προτάσεις για εσένα — empty for now
let forYouOffers: [CouponItemModel] = []
forYouOffersSection = SectionModel(
sectionType: .myRewardsHorizontalCouponsets,
title: "Προτάσεις για εσένα",
items: forYouOffers,
itemType: .coupons
)
// Active Coupons — status == 1
let activeCoupons = allCoupons.filter { $0.status == 1 }
activeOffersSection = SectionModel(
sectionType: .profileCoupon,
title: "Ενεργά",
items: activeCoupons,
itemType: .coupons
)
// Set initial filtered section to active
filteredOffersSection = activeOffersSection
// Favorite Coupons — empty for now
let favoriteCoupons: [CouponItemModel] = []
favoriteOffersSection = SectionModel(
sectionType: .profileCoupon,
title: "Αγαπημένα",
items: favoriteCoupons,
itemType: .coupons
)
// Redeemed Coupons — status == 0
let redeemedCoupons = allCoupons.filter { $0.status == 0 }
redeemedOffersSection = SectionModel(
sectionType: .profileCoupon,
title: "Εξαργυρωμένα",
items: redeemedCoupons,
itemType: .coupons
)
self.tableView.reloadData()
}
private func openCouponViewController(with coupon: CouponItemModel) {
let vc = SwiftWarplyFramework.CouponViewController(nibName: "CouponViewController", bundle: Bundle.frameworkBundle)
vc.coupon = coupon
self.navigationController?.pushViewController(vc, animated: true)
}
private func handleFilterPress(with filter: CouponFilterModel) {
switch (filter.title) {
case "Ενεργά":
self.filteredOffersSection = activeOffersSection
case "Αγαπημένα":
self.filteredOffersSection = favoriteOffersSection
case "Εξαργυρωμένα":
self.filteredOffersSection = redeemedOffersSection
default:
self.filteredOffersSection = activeOffersSection
}
}
// MARK: - Empty State Helper
private func getEmptyStateMessage() -> String {
switch couponFilterSelected.title {
case "Ενεργά":
return "Δεν υπάρχουν διαθέσιμα ενεργά κουπόνια"
case "Αγαπημένα":
return "Δεν υπάρχουν διαθέσιμα αγαπημένα κουπόνια"
case "Εξαργυρωμένα":
return "Δεν υπάρχουν διαθέσιμα εξαργυρωμένα κουπόνια"
default:
return "Δεν υπάρχουν διαθέσιμα κουπόνια"
}
}
private func createEmptyStateCell(_ tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "EmptyStateCell")
cell.selectionStyle = .none
cell.backgroundColor = .clear
cell.contentView.backgroundColor = .clear
// Remove any existing subviews
cell.contentView.subviews.forEach { $0.removeFromSuperview() }
let label = UILabel()
label.text = getEmptyStateMessage()
label.font = UIFont(name: "PingLCG-Regular", size: 16)
label.textColor = UIColor(rgb: 0x8B8B8B)
label.textAlignment = .center
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(label)
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 40),
label.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40),
label.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 24),
label.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -24)
])
return cell
}
}
// MARK: - TableView
extension MyCouponsViewController: UITableViewDelegate, UITableViewDataSource {
public func numberOfSections(in tableView: UITableView) -> Int {
// return 4 + (filteredOffersSection?.offers.count ?? 0)
return 5
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return 1
if (section == 2) {
if (forYouOffersSection?.count ?? 0 > 0) {
return 1
} else {
return 0
}
}
if (section <= 3) {
return 1
} else {
let itemCount = filteredOffersSection?.itemCount ?? 0
// Show at least 1 row for empty state message when no coupons
return itemCount > 0 ? itemCount : 1
}
}
public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
}
public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0.0
}
public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.0
}
public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return nil
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section == 0) {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCouponsHeaderTableViewCell", for: indexPath) as! MyCouponsHeaderTableViewCell
return cell
} else if (indexPath.section == 1) {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileQuestionnaireTableViewCell", for: indexPath) as! ProfileQuestionnaireTableViewCell
return cell
} else if (indexPath.section == 2) {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyRewardsOffersScrollTableViewCell", for: indexPath) as! MyRewardsOffersScrollTableViewCell
cell.delegate = self // Set the offers delegate
cell.configureCell(data: self.forYouOffersSection, isInProfile: true)
return cell
} else if (indexPath.section == 3) {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileCouponFiltersTableViewCell", for: indexPath) as! ProfileCouponFiltersTableViewCell
cell.delegate = self // Set the offers delegate
cell.configureCell(data: self.couponFilters, couponFilterSelected: self.couponFilterSelected)
return cell
} else {
// Check if the filtered section is empty — show empty state
let itemCount = filteredOffersSection?.itemCount ?? 0
if itemCount == 0 {
return createEmptyStateCell(tableView, indexPath: indexPath)
}
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileCouponTableViewCell", for: indexPath) as! ProfileCouponTableViewCell
if let items = self.filteredOffersSection?.items,
indexPath.row < items.count,
let coupon = items[indexPath.row] as? CouponItemModel {
cell.configureCell(data: coupon)
}
return cell
}
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if (indexPath.section <= 3) {
// Do nothing
} else {
if let items = self.filteredOffersSection?.items,
indexPath.row < items.count,
let coupon = items[indexPath.row] as? CouponItemModel {
openCouponViewController(with: coupon)
}
}
}
}
// Add delegate conformance
extension MyCouponsViewController: MyRewardsOffersScrollTableViewCellDelegate {
func didSelectOffer(_ offer: OfferModel) {
// Legacy OfferModel handling — no longer used but kept for protocol conformance
print("⚠️ [MyCouponsVC] didSelectOffer called with legacy OfferModel — should not happen")
}
func didSelectCouponSet(_ couponSet: CouponSetItemModel) {
// MyCouponsViewController doesn't handle coupon sets, so just log
print("CouponSet selected in MyCouponsViewController: \(couponSet._name)")
}
}
// Add delegate conformance
extension MyCouponsViewController: ProfileCouponFiltersTableViewCellDelegate {
func didSelectFilter(_ filter: CouponFilterModel) {
self.couponFilterSelected = filter
handleFilterPress(with: filter)
self.tableView.reloadData()
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<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">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MyCouponsViewController" customModule="SwiftWarplyFramework" customModuleProvider="target">
<connections>
<outlet property="tableView" destination="5Lc-C5-PjX" id="pay-eD-2GR"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3MR-Lq-Ca1" userLabel="Main View">
<rect key="frame" x="0.0" y="118" width="393" height="734"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" estimatedSectionHeaderHeight="-1" sectionFooterHeight="28" estimatedSectionFooterHeight="-1" contentViewInsetsToSafeArea="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Lc-C5-PjX">
<rect key="frame" x="0.0" y="0.0" width="393" height="734"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="separatorColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="sectionIndexBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<outlet property="dataSource" destination="-1" id="yKP-hs-r89"/>
<outlet property="delegate" destination="-1" id="hPq-MZ-iBl"/>
</connections>
</tableView>
</subviews>
<color key="backgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="5Lc-C5-PjX" firstAttribute="leading" secondItem="3MR-Lq-Ca1" secondAttribute="leading" id="E1N-Fh-MGi"/>
<constraint firstItem="5Lc-C5-PjX" firstAttribute="top" secondItem="3MR-Lq-Ca1" secondAttribute="top" id="oMH-QW-fBP"/>
<constraint firstAttribute="trailing" secondItem="5Lc-C5-PjX" secondAttribute="trailing" id="qGu-bT-w0t"/>
<constraint firstAttribute="bottom" secondItem="5Lc-C5-PjX" secondAttribute="bottom" id="yQw-1D-xi9"/>
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="3MR-Lq-Ca1" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="3RG-QL-Pay"/>
<constraint firstAttribute="bottom" secondItem="3MR-Lq-Ca1" secondAttribute="bottom" id="H7T-5I-z6N"/>
<constraint firstAttribute="trailing" secondItem="3MR-Lq-Ca1" secondAttribute="trailing" id="c03-MD-sDl"/>
<constraint firstItem="3MR-Lq-Ca1" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" id="hPa-2m-0vn"/>
</constraints>
<point key="canvasLocation" x="140" y="154"/>
</view>
</objects>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>