Showing
2 changed files
with
173 additions
and
8 deletions
| ... | @@ -292,8 +292,11 @@ The SwiftWarplyFramework provides ready-to-use view controllers that you can int | ... | @@ -292,8 +292,11 @@ The SwiftWarplyFramework provides ready-to-use view controllers that you can int |
| 292 | 292 | ||
| 293 | | View Controller | Purpose | Description | | 293 | | View Controller | Purpose | Description | |
| 294 | |----------------|---------|-------------| | 294 | |----------------|---------|-------------| |
| 295 | -| `MyRewardsViewController` | Main rewards screen | Displays campaigns, offers, and banners | | 295 | +| `MyRewardsViewController` | Main rewards screen | Displays carousel banners, filter bar, and horizontally-scrollable category offer rows | |
| 296 | | `ProfileViewController` | User profile & coupons | Shows user profile and coupon management | | 296 | | `ProfileViewController` | User profile & coupons | Shows user profile and coupon management | |
| 297 | +| `MyCouponsViewController` | User's coupon list | Full-screen list of the user's active, redeemed, and expired coupons with filter tabs | | ||
| 298 | +| `MapViewController` | Merchant store map | Interactive MKMapView showing merchant store locations; accepts an optional `CouponSetItemModel` coupon and `isMarket` flag | | ||
| 299 | +| `CategoryOffersViewController` | Category offers grid | 2-column grid of all offers in a specific category; opened from the "All" button on a category row; accepts a `SectionModel` | | ||
| 297 | | `CouponViewController` | Individual coupon details | Displays detailed coupon information | | 300 | | `CouponViewController` | Individual coupon details | Displays detailed coupon information | |
| 298 | | `CouponsetViewController` | Coupon set details | Displays detailed coupon set information with expandable description, terms, store locator, and website link | | 301 | | `CouponsetViewController` | Coupon set details | Displays detailed coupon set information with expandable description, terms, store locator, and website link | |
| 299 | 302 | ||
| ... | @@ -328,6 +331,28 @@ class MainViewController: UIViewController { | ... | @@ -328,6 +331,28 @@ class MainViewController: UIViewController { |
| 328 | // Push onto navigation stack | 331 | // Push onto navigation stack |
| 329 | navigationController?.pushViewController(couponsetVC, animated: true) | 332 | navigationController?.pushViewController(couponsetVC, animated: true) |
| 330 | } | 333 | } |
| 334 | + | ||
| 335 | + func showMyCoupons() { | ||
| 336 | + // Create the my coupons view controller | ||
| 337 | + let myCouponsVC = MyCouponsViewController() | ||
| 338 | + navigationController?.pushViewController(myCouponsVC, animated: true) | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + func showMap(for couponSet: CouponSetItemModel? = nil) { | ||
| 342 | + // Create the map view controller | ||
| 343 | + let mapVC = MapViewController() | ||
| 344 | + // Optionally pass a coupon to pre-filter the map to that merchant's stores | ||
| 345 | + // mapVC.coupon = couponItem | ||
| 346 | + // mapVC.isMarket = false | ||
| 347 | + navigationController?.pushViewController(mapVC, animated: true) | ||
| 348 | + } | ||
| 349 | + | ||
| 350 | + func showCategoryOffers(for section: SectionModel) { | ||
| 351 | + // Open the full-grid view for a specific category | ||
| 352 | + let categoryVC = CategoryOffersViewController() | ||
| 353 | + categoryVC.sectionData = section | ||
| 354 | + navigationController?.pushViewController(categoryVC, animated: true) | ||
| 355 | + } | ||
| 331 | } | 356 | } |
| 332 | ``` | 357 | ``` |
| 333 | 358 | ||
| ... | @@ -1070,6 +1095,90 @@ Task { | ... | @@ -1070,6 +1095,90 @@ Task { |
| 1070 | } | 1095 | } |
| 1071 | ``` | 1096 | ``` |
| 1072 | 1097 | ||
| 1098 | +### Get Coupon Filters | ||
| 1099 | + | ||
| 1100 | +Returns the available filter categories for coupon browsing (e.g. "Active", "Redeemed", "Expired"). | ||
| 1101 | + | ||
| 1102 | +```swift | ||
| 1103 | +// Completion handler | ||
| 1104 | +WarplySDK.shared.getCouponFilters(language: "el") { filtersModel in | ||
| 1105 | + guard let filtersModel = filtersModel else { return } | ||
| 1106 | + print("Filters loaded: \(filtersModel)") | ||
| 1107 | +} failureCallback: { errorCode in | ||
| 1108 | + print("Failed to get coupon filters: \(errorCode)") | ||
| 1109 | +} | ||
| 1110 | + | ||
| 1111 | +// Async/await | ||
| 1112 | +Task { | ||
| 1113 | + do { | ||
| 1114 | + let filtersModel = try await WarplySDK.shared.getCouponFilters(language: "el") | ||
| 1115 | + // Use filtersModel to populate filter UI | ||
| 1116 | + } catch { | ||
| 1117 | + print("Error getting coupon filters: \(error)") | ||
| 1118 | + } | ||
| 1119 | +} | ||
| 1120 | +``` | ||
| 1121 | + | ||
| 1122 | +### Get Stores (for a Merchant) | ||
| 1123 | + | ||
| 1124 | +Returns the physical store locations for a specific merchant, used to populate `MapViewController`. | ||
| 1125 | + | ||
| 1126 | +```swift | ||
| 1127 | +// Completion handler | ||
| 1128 | +WarplySDK.shared.getStores(language: "el", merchantUuid: "merchant-uuid-here") { stores in | ||
| 1129 | + guard let stores = stores else { return } | ||
| 1130 | + for store in stores { | ||
| 1131 | + print("Store: \(store._name), lat: \(store._lat), lon: \(store._lon)") | ||
| 1132 | + } | ||
| 1133 | +} failureCallback: { errorCode in | ||
| 1134 | + print("Failed to get stores: \(errorCode)") | ||
| 1135 | +} | ||
| 1136 | + | ||
| 1137 | +// Async/await | ||
| 1138 | +Task { | ||
| 1139 | + do { | ||
| 1140 | + let stores = try await WarplySDK.shared.getStores(language: "el", merchantUuid: "merchant-uuid-here") | ||
| 1141 | + print("Loaded \(stores.count) stores") | ||
| 1142 | + } catch { | ||
| 1143 | + print("Error getting stores: \(error)") | ||
| 1144 | + } | ||
| 1145 | +} | ||
| 1146 | +``` | ||
| 1147 | + | ||
| 1148 | +### Get Carousel Content | ||
| 1149 | + | ||
| 1150 | +Returns the carousel banner items displayed at the top of `MyRewardsViewController`. | ||
| 1151 | + | ||
| 1152 | +```swift | ||
| 1153 | +// Completion handler | ||
| 1154 | +WarplySDK.shared.getCarouselContent { carouselItems in | ||
| 1155 | + guard let items = carouselItems else { return } | ||
| 1156 | + for item in items { | ||
| 1157 | + print("Carousel item: \(item._name), url: \(item._url ?? "none")") | ||
| 1158 | + } | ||
| 1159 | +} failureCallback: { errorCode in | ||
| 1160 | + print("Failed to get carousel content: \(errorCode)") | ||
| 1161 | +} | ||
| 1162 | + | ||
| 1163 | +// Async/await | ||
| 1164 | +Task { | ||
| 1165 | + do { | ||
| 1166 | + let items = try await WarplySDK.shared.getCarouselContent() | ||
| 1167 | + print("Loaded \(items.count) carousel items") | ||
| 1168 | + } catch { | ||
| 1169 | + print("Error getting carousel content: \(error)") | ||
| 1170 | + } | ||
| 1171 | +} | ||
| 1172 | +``` | ||
| 1173 | + | ||
| 1174 | +**Carousel item URL routing** — when a user taps a carousel banner, the `_url` field determines the navigation: | ||
| 1175 | + | ||
| 1176 | +| URL contains | Action | | ||
| 1177 | +|---|---| | ||
| 1178 | +| `"singleOffer"` | Find the `CouponSetItemModel` matching `_uuid` and open `CouponsetViewController` | | ||
| 1179 | +| `"index.html"` | Open the URL in `CampaignViewController` (WebView) | | ||
| 1180 | +| `"Offers?"` | (Future) Navigate to a specific category (uuid TBD) | | ||
| 1181 | + | ||
| 1073 | ### Check Coupon Availability | 1182 | ### Check Coupon Availability |
| 1074 | 1183 | ||
| 1075 | ```swift | 1184 | ```swift |
| ... | @@ -1754,7 +1863,7 @@ Task { | ... | @@ -1754,7 +1863,7 @@ Task { |
| 1754 | 1863 | ||
| 1755 | When reporting issues, please include: | 1864 | When reporting issues, please include: |
| 1756 | 1865 | ||
| 1757 | -1. **Framework Version**: `2.1.0` | 1866 | +1. **Framework Version**: `2.5.1` |
| 1758 | 2. **iOS Version**: Minimum 17.0 | 1867 | 2. **iOS Version**: Minimum 17.0 |
| 1759 | 3. **Error Details**: Full error messages and stack traces | 1868 | 3. **Error Details**: Full error messages and stack traces |
| 1760 | 4. **Code Sample**: Minimal reproducible example | 1869 | 4. **Code Sample**: Minimal reproducible example |
| ... | @@ -1765,7 +1874,7 @@ When reporting issues, please include: | ... | @@ -1765,7 +1874,7 @@ When reporting issues, please include: |
| 1765 | 1874 | ||
| 1766 | ```swift | 1875 | ```swift |
| 1767 | // Get framework version and status | 1876 | // Get framework version and status |
| 1768 | -print("Framework Version: 2.1.0") | 1877 | +print("Framework Version: 2.5.1") |
| 1769 | print("SDK Initialized: \(WarplySDK.shared != nil)") | 1878 | print("SDK Initialized: \(WarplySDK.shared != nil)") |
| 1770 | print("Network Status: \(WarplySDK.shared.getNetworkStatus())") | 1879 | print("Network Status: \(WarplySDK.shared.getNetworkStatus())") |
| 1771 | print("Current Language: \(WarplySDK.shared.applicationLocale)") | 1880 | print("Current Language: \(WarplySDK.shared.applicationLocale)") |
| ... | @@ -1790,6 +1899,31 @@ Start with the Quick Start guide and gradually adopt the advanced features as ne | ... | @@ -1790,6 +1899,31 @@ Start with the Quick Start guide and gradually adopt the advanced features as ne |
| 1790 | 1899 | ||
| 1791 | ## 📋 **Changelog** | 1900 | ## 📋 **Changelog** |
| 1792 | 1901 | ||
| 1902 | +### **Version 2.5.1** - *March 20, 2026* | ||
| 1903 | + | ||
| 1904 | +#### **🆕 New Features** | ||
| 1905 | +- **`MyCouponsViewController` Added**: New full-screen view controller displaying the user's complete coupon list with filter tabs (Active, Redeemed, Expired). Loads coupon data from the API on launch and supports dynamic filter selection. | ||
| 1906 | +- **`MapViewController` Added**: New map view controller using `MKMapView` to display merchant store locations. Accepts an optional `coupon: CouponItemModel?` to pre-filter stores for a specific merchant, and an `isMarket: Bool?` flag to differentiate supermarket vs. regular merchant context. | ||
| 1907 | +- **`CategoryOffersViewController` Added**: New view controller displaying a 2-column grid of all offers within a specific category. Opened automatically from the "All" button on any category row in `MyRewardsViewController`. Accepts a `sectionData: SectionModel` property containing the full offer list. | ||
| 1908 | +- **`MyRewardsViewController` Updated**: | ||
| 1909 | + - Banner section now uses `CarouselItemModel` (from `getCarouselContent()`) instead of legacy `CampaignItemModel`. | ||
| 1910 | + - Added `MyRewardsFiltersTableViewCell` row beneath the banner section on every screen load, showing a search/filter button and a map button (actions are stubbed — `TODO`). | ||
| 1911 | + - Tapping the "All" button on any category row now pushes `CategoryOffersViewController` with the full `SectionModel` for that category. | ||
| 1912 | + - Carousel banner tap routing implemented: `singleOffer` → `CouponsetViewController`; `index.html` → `CampaignViewController`; `Offers?` → TODO. | ||
| 1913 | +- **New API: `getCarouselContent()`** — Fetches the ordered list of carousel banner items (`[CarouselItemModel]`) for the banner section. Each item carries a `_url` field that drives tap-navigation routing. | ||
| 1914 | +- **New API: `getCouponFilters()`** — Fetches the available coupon filter categories (`CouponFiltersDataModel`) used by both `MyCouponsViewController` and `ProfileViewController`. | ||
| 1915 | +- **New API: `getStores()`** — Fetches the physical store locations (`[MerchantModel]`) for a given merchant UUID, used to populate `MapViewController` pin annotations. | ||
| 1916 | +- **New Model: `CarouselItemModel`** — Represents a banner carousel item with fields: `_uuid`, `_name`, `_entity`, `_app_img`, `_app_url`, `_url`, `_web_img`, `_web_img_responsive`. | ||
| 1917 | + | ||
| 1918 | +#### **🔧 Improvements** | ||
| 1919 | +- `MyRewardsOfferCollectionViewCell`: `discountLabel` now auto-scales its font down to 50% (`minimumScaleFactor = 0.5`) instead of truncating when the discount text is long. | ||
| 1920 | +- `MyRewardsFiltersTableViewCell`: Map button now correctly covers the full 44×44 tap area of `mapView`. | ||
| 1921 | + | ||
| 1922 | +#### **🚨 Breaking Changes** | ||
| 1923 | +- **None**: Full backward compatibility maintained | ||
| 1924 | + | ||
| 1925 | +--- | ||
| 1926 | + | ||
| 1793 | ### **Version 2.5.0** - *March 13, 2026* | 1927 | ### **Version 2.5.0** - *March 13, 2026* |
| 1794 | 1928 | ||
| 1795 | #### **🆕 New Features** | 1929 | #### **🆕 New Features** | ... | ... |
| ... | @@ -112,15 +112,23 @@ SwiftWarplyFramework/ | ... | @@ -112,15 +112,23 @@ SwiftWarplyFramework/ |
| 112 | │ │ ├── screens/ # Pre-built view controllers | 112 | │ │ ├── screens/ # Pre-built view controllers |
| 113 | │ │ │ ├── MyRewardsViewController/ # Main rewards screen | 113 | │ │ │ ├── MyRewardsViewController/ # Main rewards screen |
| 114 | │ │ │ ├── ProfileViewController/ # User profile screen | 114 | │ │ │ ├── ProfileViewController/ # User profile screen |
| 115 | +│ │ │ ├── MyCouponsViewController/ # User coupon list screen (NEW 2.5.1) | ||
| 116 | +│ │ │ ├── MapViewController/ # Merchant store map screen (NEW 2.5.1) | ||
| 117 | +│ │ │ ├── CategoryOffersViewController/ # Category offers 2-column grid (NEW 2.5.1) | ||
| 115 | │ │ │ ├── CouponViewController/ # Single coupon detail | 118 | │ │ │ ├── CouponViewController/ # Single coupon detail |
| 116 | │ │ │ ├── CouponsetViewController/ # Coupon set detail | 119 | │ │ │ ├── CouponsetViewController/ # Coupon set detail |
| 117 | │ │ │ └── CampaignViewController/ # WebView for campaign URLs | 120 | │ │ │ └── CampaignViewController/ # WebView for campaign URLs |
| 118 | │ │ ├── cells/ # Reusable table/collection view cells | 121 | │ │ ├── cells/ # Reusable table/collection view cells |
| 119 | │ │ │ ├── MyRewardsBannerOfferCollectionViewCell/ | 122 | │ │ │ ├── MyRewardsBannerOfferCollectionViewCell/ |
| 120 | │ │ │ ├── MyRewardsBannerOffersScrollTableViewCell/ | 123 | │ │ │ ├── MyRewardsBannerOffersScrollTableViewCell/ |
| 124 | +│ │ │ ├── MyRewardsFiltersTableViewCell/ # Filter bar cell (NEW 2.5.1) | ||
| 121 | │ │ │ ├── MyRewardsOfferCollectionViewCell/ | 125 | │ │ │ ├── MyRewardsOfferCollectionViewCell/ |
| 122 | │ │ │ ├── MyRewardsOffersScrollTableViewCell/ | 126 | │ │ │ ├── MyRewardsOffersScrollTableViewCell/ |
| 123 | │ │ │ ├── MyRewardsProfileInfoTableViewCell/ | 127 | │ │ │ ├── MyRewardsProfileInfoTableViewCell/ |
| 128 | +│ │ │ ├── MyCouponsHeaderTableViewCell/ # Header for MyCouponsViewController (NEW 2.5.1) | ||
| 129 | +│ │ │ ├── CategoryOfferCollectionViewCell/ # Grid card cell (NEW 2.5.1) | ||
| 130 | +│ │ │ ├── CategoryOffersGridTableViewCell/ # Table cell housing 2-col grid (NEW 2.5.1) | ||
| 131 | +│ │ │ ├── CategoryOffersHeaderTableViewCell/ # Header for CategoryOffersViewController (NEW 2.5.1) | ||
| 124 | │ │ │ ├── ProfileCouponFiltersTableViewCell/ | 132 | │ │ │ ├── ProfileCouponFiltersTableViewCell/ |
| 125 | │ │ │ ├── ProfileCouponTableViewCell/ | 133 | │ │ │ ├── ProfileCouponTableViewCell/ |
| 126 | │ │ │ ├── ProfileFilterCollectionViewCell/ | 134 | │ │ │ ├── ProfileFilterCollectionViewCell/ |
| ... | @@ -626,8 +634,12 @@ All models use **dictionary-based initialization** (`init(dictionary: [String: A | ... | @@ -626,8 +634,12 @@ All models use **dictionary-based initialization** (`init(dictionary: [String: A |
| 626 | | `TransactionModel` | TransactionModel.swift | `transactionDate`, transaction details | Sorted by date descending | | 634 | | `TransactionModel` | TransactionModel.swift | `transactionDate`, transaction details | Sorted by date descending | |
| 627 | | `PointsHistoryModel` | PointsHistoryModel.swift | `entryDate`, points entries | Sorted by date descending | | 635 | | `PointsHistoryModel` | PointsHistoryModel.swift | `entryDate`, points entries | Sorted by date descending | |
| 628 | | `MarketPassDetailsModel` | Market.swift | Supermarket pass details | Market/supermarket feature | | 636 | | `MarketPassDetailsModel` | Market.swift | Supermarket pass details | Market/supermarket feature | |
| 637 | +| `SectionModel` | SectionModel.swift | `sectionType: SectionType`, `title`, `items`, `itemType`, `count`, `metadata` | Drives `UITableView` row rendering. `SectionType` cases: `.myRewardsProfileInfo`, `.myRewardsFilters` (NEW 2.5.1), `.myRewardsBannerOffers`, `.myRewardsHorizontalCouponsets`, `.profileHeader`, `.profileQuestionnaire`, `.profileCouponFilters`, `.profileCoupon`, `.staticContent` | | ||
| 629 | | `RedeemedSMHistoryModel` | Coupon.swift | `_totalRedeemedValue`, `_redeemedCouponList` | Supermarket coupon redemption history | | 638 | | `RedeemedSMHistoryModel` | Coupon.swift | `_totalRedeemedValue`, `_redeemedCouponList` | Supermarket coupon redemption history | |
| 630 | | `ArticleModel` | ArticleModel.swift | Content/article fields | Carousel content | | 639 | | `ArticleModel` | ArticleModel.swift | Content/article fields | Carousel content | |
| 640 | +| `CarouselItemModel` | CarouselItemModel.swift | `_uuid`, `_name`, `_entity`, `_app_img`, `_app_url`, `_url`, `_web_img`, `_web_img_responsive` | Banner carousel items for MyRewardsViewController; `_url` drives tap navigation routing (NEW 2.5.1) | | ||
| 641 | +| `CouponFiltersDataModel` | CouponFilterModel.swift | Filter category data | Top-level model returned by `getCouponFilters()`; contains filter lists (NEW 2.5.1) | | ||
| 642 | +| `CouponFilterModel` | CouponFilterModel.swift | `id`, `title`, `count` | Individual filter chip (e.g. "Active", "Redeemed"); used by `MyCouponsViewController` and `ProfileViewController` (NEW 2.5.1) | | ||
| 631 | | `MerchantCategoryModel` | MerchantCategoryModel.swift | Category fields | Merchant categorization | | 643 | | `MerchantCategoryModel` | MerchantCategoryModel.swift | Category fields | Merchant categorization | |
| 632 | | `VerifyTicketResponseModel` | Response.swift | `getStatus` (1=success) | Generic API response wrapper | | 644 | | `VerifyTicketResponseModel` | Response.swift | `getStatus` (1=success) | Generic API response wrapper | |
| 633 | | `GenericResponseModel` | Response.swift | Generic response fields | Flexible response wrapper | | 645 | | `GenericResponseModel` | Response.swift | Generic response fields | Flexible response wrapper | |
| ... | @@ -676,8 +688,11 @@ All screens use **XIB-based** layouts (not SwiftUI) with `Bundle.frameworkBundle | ... | @@ -676,8 +688,11 @@ All screens use **XIB-based** layouts (not SwiftUI) with `Bundle.frameworkBundle |
| 676 | 688 | ||
| 677 | | Screen | Storyboard ID | Purpose | | 689 | | Screen | Storyboard ID | Purpose | |
| 678 | |--------|--------------|---------| | 690 | |--------|--------------|---------| |
| 679 | -| `MyRewardsViewController` | — (XIB) | Main rewards dashboard with campaigns, offers, banners | | 691 | +| `MyRewardsViewController` | — (XIB) | Main rewards dashboard: carousel banners (`CarouselItemModel`), filter bar row, horizontal category offer rows. Fetches `getCarouselContent()`, `getCouponSetsNew()`, `getCouponFilters()` on load. | |
| 680 | | `ProfileViewController` | — (XIB) | User profile with coupons, filters, questionnaires | | 692 | | `ProfileViewController` | — (XIB) | User profile with coupons, filters, questionnaires | |
| 693 | +| `MyCouponsViewController` | — (XIB) | Full coupon list for the current user with filter tabs. Fetches coupons on load. Public init via `MyCouponsViewController()`. (NEW 2.5.1) | | ||
| 694 | +| `MapViewController` | — (XIB) | `MKMapView`-based merchant store map. `public var coupon: CouponItemModel?` (optional, filters to merchant's stores). `public var isMarket: Bool?` (supermarket vs regular). Calls `getStores(merchantUuid:)` internally. (NEW 2.5.1) | | ||
| 695 | +| `CategoryOffersViewController` | — (XIB) | 2-column grid of all offers in a category. `public var sectionData: SectionModel?`. Pushed from `MyRewardsOffersScrollTableViewCell` "All" button via `didSelectAllOffers` delegate. Navigation bar shown with back button via `setBackButton()`. (NEW 2.5.1) | | ||
| 681 | | `CouponViewController` | — (XIB) | Single coupon detail view | | 696 | | `CouponViewController` | — (XIB) | Single coupon detail view | |
| 682 | | `CouponsetViewController` | — (XIB) | Coupon set detail view | | 697 | | `CouponsetViewController` | — (XIB) | Coupon set detail view | |
| 683 | | `CampaignViewController` | `"CampaignViewController"` (Storyboard) | WebView for campaign URLs | | 698 | | `CampaignViewController` | `"CampaignViewController"` (Storyboard) | WebView for campaign URLs | |
| ... | @@ -707,11 +722,16 @@ if let navigationController = controller.navigationController { | ... | @@ -707,11 +722,16 @@ if let navigationController = controller.navigationController { |
| 707 | 722 | ||
| 708 | ### Cell Architecture | 723 | ### Cell Architecture |
| 709 | Table/Collection view cells are XIB-based, each in its own directory: | 724 | Table/Collection view cells are XIB-based, each in its own directory: |
| 710 | -- `MyRewardsBannerOfferCollectionViewCell` — Banner offer in collection view | 725 | +- `MyRewardsBannerOfferCollectionViewCell` — Banner carousel item in collection view (configures from `CarouselItemModel`) |
| 711 | -- `MyRewardsOfferCollectionViewCell` — Standard offer in collection view | 726 | +- `MyRewardsOfferCollectionViewCell` — Standard offer card in horizontal-scroll collection view (configures from `CouponSetItemModel`); `discountLabel` auto-scales font to `minimumScaleFactor = 0.5` |
| 712 | -- `MyRewardsBannerOffersScrollTableViewCell` — Horizontal scrolling banner offers | 727 | +- `MyRewardsBannerOffersScrollTableViewCell` — Horizontal scrolling banner carousel with page control; delegate: `MyRewardsBannerOffersScrollTableViewCellDelegate` (`didSelectBannerCarouselItem`) |
| 713 | -- `MyRewardsOffersScrollTableViewCell` — Horizontal scrolling offers | 728 | +- `MyRewardsOffersScrollTableViewCell` — Horizontal scrolling offer cards per category; delegate: `MyRewardsOffersScrollTableViewCellDelegate` (`didSelectCouponSet`, `didSelectAllOffers`). `allButton: UIButton` triggers `didSelectAllOffers` via `addTarget` |
| 714 | - `MyRewardsProfileInfoTableViewCell` — Profile info header | 729 | - `MyRewardsProfileInfoTableViewCell` — Profile info header |
| 730 | +- `MyRewardsFiltersTableViewCell` — Filter bar row with `filtersButton` (search/filter) and `mapButton` (map); both are transparent overlay buttons covering their parent views; actions are `TODO` stubs (NEW 2.5.1) | ||
| 731 | +- `MyCouponsHeaderTableViewCell` — Section header cell for `MyCouponsViewController` (NEW 2.5.1) | ||
| 732 | +- `CategoryOfferCollectionViewCell` — Card cell for `CategoryOffersViewController` 2-column grid; configures from `CouponSetItemModel` only; top half = gray banner + centered logo + discount circle; bottom half = merchant name + bold title + subtitle + expiry (NEW 2.5.1) | ||
| 733 | +- `CategoryOffersGridTableViewCell` — `UITableViewCell` containing a non-scrolling `UICollectionView` (2-column grid); dynamically computes its own height via `collectionViewHeightConstraint`; `didSelectItemAt` pushes `CouponsetViewController` (NEW 2.5.1) | ||
| 734 | +- `CategoryOffersHeaderTableViewCell` — Header cell for `CategoryOffersViewController` showing category title, search/filter button (`filtersView`/`filtersButton`), and map button (`mapView`/`mapButton`) (NEW 2.5.1) | ||
| 715 | - `ProfileCouponTableViewCell` — Coupon row in profile | 735 | - `ProfileCouponTableViewCell` — Coupon row in profile |
| 716 | - `ProfileCouponFiltersTableViewCell` — Filter chips for coupons | 736 | - `ProfileCouponFiltersTableViewCell` — Filter chips for coupons |
| 717 | - `ProfileFilterCollectionViewCell` — Individual filter chip | 737 | - `ProfileFilterCollectionViewCell` — Individual filter chip |
| ... | @@ -804,6 +824,8 @@ public func getCoupons(language: String? = nil, ...) { | ... | @@ -804,6 +824,8 @@ public func getCoupons(language: String? = nil, ...) { |
| 804 | - `getCoupons(language:completion:failureCallback:)` / async variant | 824 | - `getCoupons(language:completion:failureCallback:)` / async variant |
| 805 | - `getCouponsUniversal(language:completion:failureCallback:)` | 825 | - `getCouponsUniversal(language:completion:failureCallback:)` |
| 806 | - `getCouponSets(language:completion:failureCallback:)` / async variant | 826 | - `getCouponSets(language:completion:failureCallback:)` / async variant |
| 827 | +- `getCouponSetsNew(language:completion:failureCallback:)` / async variant — variant used by `MyRewardsViewController` | ||
| 828 | +- `getCouponFilters(language:completion:failureCallback:)` / async variant — returns `CouponFiltersDataModel` (NEW 2.5.1) | ||
| 807 | - `getAvailableCoupons(completion:)` / async variant | 829 | - `getAvailableCoupons(completion:)` / async variant |
| 808 | - `validateCoupon(_:completion:)` / async variant | 830 | - `validateCoupon(_:completion:)` / async variant |
| 809 | - `redeemCoupon(productId:productUuid:merchantId:completion:)` / async variant | 831 | - `redeemCoupon(productId:productUuid:merchantId:completion:)` / async variant |
| ... | @@ -826,9 +848,11 @@ public func getCoupons(language: String? = nil, ...) { | ... | @@ -826,9 +848,11 @@ public func getCoupons(language: String? = nil, ...) { |
| 826 | **Merchants:** | 848 | **Merchants:** |
| 827 | - `getMerchants(language:categories:defaultShown:center:tags:uuid:distance:parentUuids:completion:failureCallback:)` / async variant | 849 | - `getMerchants(language:categories:defaultShown:center:tags:uuid:distance:parentUuids:completion:failureCallback:)` / async variant |
| 828 | - `getMerchantCategories(language:completion:failureCallback:)` / async variant | 850 | - `getMerchantCategories(language:completion:failureCallback:)` / async variant |
| 851 | +- `getStores(language:merchantUuid:completion:failureCallback:)` / async variant — returns `[MerchantModel]` for a specific merchant UUID; used by `MapViewController` (NEW 2.5.1) | ||
| 829 | 852 | ||
| 830 | **Content:** | 853 | **Content:** |
| 831 | - `getArticles(language:categories:completion:failureCallback:)` / async variant | 854 | - `getArticles(language:categories:completion:failureCallback:)` / async variant |
| 855 | +- `getCarouselContent(completion:failureCallback:)` / async variant — returns `[CarouselItemModel]` for the banner carousel in `MyRewardsViewController` (NEW 2.5.1) | ||
| 832 | 856 | ||
| 833 | **Market/Supermarket:** | 857 | **Market/Supermarket:** |
| 834 | - `getMarketPassDetails(completion:failureCallback:)` / async variant | 858 | - `getMarketPassDetails(completion:failureCallback:)` / async variant |
| ... | @@ -1213,6 +1237,13 @@ let keychainDiag = await KeychainManager.shared.getDiagnosticInfo() | ... | @@ -1213,6 +1237,13 @@ let keychainDiag = await KeychainManager.shared.getDiagnosticInfo() |
| 1213 | | Config | `Configuration/WarplyConfiguration.swift` | `WarplyConfiguration` struct | | 1237 | | Config | `Configuration/WarplyConfiguration.swift` | `WarplyConfiguration` struct | |
| 1214 | | Campaigns | `models/Campaign.swift` | `CampaignItemModel` class | | 1238 | | Campaigns | `models/Campaign.swift` | `CampaignItemModel` class | |
| 1215 | | Coupons | `models/Coupon.swift` | `CouponItemModel` class | | 1239 | | Coupons | `models/Coupon.swift` | `CouponItemModel` class | |
| 1240 | +| Carousel items | `models/CarouselItemModel.swift` | `CarouselItemModel` class (NEW 2.5.1) | | ||
| 1241 | +| Coupon filters | `models/CouponFilterModel.swift` | `CouponFiltersDataModel` / `CouponFilterModel` (NEW 2.5.1) | | ||
| 1242 | +| Main rewards screen | `screens/MyRewardsViewController/` | Push via `MyRewardsViewController()` | | ||
| 1243 | +| Coupon list screen | `screens/MyCouponsViewController/` | Push via `MyCouponsViewController()` (NEW 2.5.1) | | ||
| 1244 | +| Map screen | `screens/MapViewController/` | Push via `MapViewController()`; set `coupon` + `isMarket` (NEW 2.5.1) | | ||
| 1245 | +| Category grid screen | `screens/CategoryOffersViewController/` | Push via `CategoryOffersViewController()`; set `sectionData` (NEW 2.5.1) | | ||
| 1246 | +| UI section types | `models/SectionModel.swift` | `SectionType` enum | | ||
| 1216 | 1247 | ||
| 1217 | **Critical Rules for AI Agents:** | 1248 | **Critical Rules for AI Agents:** |
| 1218 | 1. **Always maintain dual event posting** (SwiftEventBus + EventDispatcher) | 1249 | 1. **Always maintain dual event posting** (SwiftEventBus + EventDispatcher) | ... | ... |
-
Please register or login to post a comment